diff options
author | Andreas Gruenbacher <agruen@suse.de> | 2009-06-22 19:07:20 +0200 |
---|---|---|
committer | Andreas Gruenbacher <agruen@suse.de> | 2009-06-22 20:46:31 +0200 |
commit | cb44d79564db216af18d9beb632700e40634f166 (patch) | |
tree | de775ecb8eb7b4b6886da78f1ddc259637a3f904 | |
download | attr-cb44d79564db216af18d9beb632700e40634f166.tar.gz |
Put libmisc on its own branch
-rw-r--r-- | include/misc.h | 23 | ||||
-rw-r--r-- | include/walk_tree.h | 38 | ||||
-rw-r--r-- | libmisc/Makefile | 31 | ||||
-rw-r--r-- | libmisc/high_water_alloc.c | 44 | ||||
-rw-r--r-- | libmisc/next_line.c | 57 | ||||
-rw-r--r-- | libmisc/quote.c | 57 | ||||
-rw-r--r-- | libmisc/unquote.c | 54 | ||||
-rw-r--r-- | libmisc/walk_tree.c | 228 |
8 files changed, 532 insertions, 0 deletions
diff --git a/include/misc.h b/include/misc.h new file mode 100644 index 0000000..58c2640 --- /dev/null +++ b/include/misc.h @@ -0,0 +1,23 @@ +/* + Copyright (C) 2009 Andreas Gruenbacher <agruen@suse.de> + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +extern int high_water_alloc(void **buf, size_t *bufsize, size_t newsize); + +extern const char *quote(const char *str); +extern char *unquote(char *str); + +extern char *next_line(FILE *file); diff --git a/include/walk_tree.h b/include/walk_tree.h new file mode 100644 index 0000000..53a8fc5 --- /dev/null +++ b/include/walk_tree.h @@ -0,0 +1,38 @@ +/* + File: walk_tree.h + + Copyright (C) 2007 Andreas Gruenbacher <a.gruenbacher@computer.org> + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __WALK_TREE_H +#define __WALK_TREE_H + +#define WALK_TREE_RECURSIVE 0x1 +#define WALK_TREE_PHYSICAL 0x2 +#define WALK_TREE_LOGICAL 0x4 +#define WALK_TREE_DEREFERENCE 0x8 + +#define WALK_TREE_TOPLEVEL 0x100 +#define WALK_TREE_SYMLINK 0x200 +#define WALK_TREE_FAILED 0x400 + +struct stat; + +extern int walk_tree(const char *path, int walk_flags, unsigned int num, + int (*func)(const char *, const struct stat *, int, + void *), void *arg); + +#endif diff --git a/libmisc/Makefile b/libmisc/Makefile new file mode 100644 index 0000000..e64a287 --- /dev/null +++ b/libmisc/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (C) 2009 Andreas Gruenbacher <agruen@suse.de> +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +TOPDIR = .. +include $(TOPDIR)/include/builddefs + +LTLIBRARY = libmisc.la +LTLDFLAGS = + +CFILES = quote.c unquote.c high_water_alloc.c next_line.c walk_tree.c + +default: $(LTLIBRARY) +install install-dev install-lib: + +include $(BUILDRULES) + diff --git a/libmisc/high_water_alloc.c b/libmisc/high_water_alloc.c new file mode 100644 index 0000000..c127dc1 --- /dev/null +++ b/libmisc/high_water_alloc.c @@ -0,0 +1,44 @@ +/* + File: high_water_alloc.c + + Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include "misc.h" + +int high_water_alloc(void **buf, size_t *bufsize, size_t newsize) +{ +#define CHUNK_SIZE 256 + /* + * Goal here is to avoid unnecessary memory allocations by + * using static buffers which only grow when necessary. + * Size is increased in fixed size chunks (CHUNK_SIZE). + */ + if (*bufsize < newsize) { + void *newbuf; + + newsize = (newsize + CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); + newbuf = realloc(*buf, newsize); + if (!newbuf) + return 1; + + *buf = newbuf; + *bufsize = newsize; + } + return 0; +} diff --git a/libmisc/next_line.c b/libmisc/next_line.c new file mode 100644 index 0000000..0566d7a --- /dev/null +++ b/libmisc/next_line.c @@ -0,0 +1,57 @@ +/* + Copyright (C) 2009 Andreas Gruenbacher <agruen@suse.de> + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include "misc.h" + +#define LINE_SIZE getpagesize() + +char *next_line(FILE *file) +{ + static char *line; + static size_t line_size; + char *c; + int eol = 0; + + if (!line) { + if (high_water_alloc((void **)&line, &line_size, LINE_SIZE)) + return NULL; + } + c = line; + do { + if (!fgets(c, line_size - (c - line), file)) + return NULL; + c = strrchr(c, '\0'); + while (c > line && (*(c-1) == '\n' || *(c-1) == '\r')) { + c--; + *c = '\0'; + eol = 1; + } + if (feof(file)) + break; + if (!eol) { + if (high_water_alloc((void **)&line, &line_size, + 2 * line_size)) + return NULL; + c = strrchr(line, '\0'); + } + } while (!eol); + return line; +} diff --git a/libmisc/quote.c b/libmisc/quote.c new file mode 100644 index 0000000..4fb7035 --- /dev/null +++ b/libmisc/quote.c @@ -0,0 +1,57 @@ +/* + File: quote.c + + Copyright (C) 2003 Andreas Gruenbacher <a.gruenbacher@bestbits.at> + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include "misc.h" + +const char *quote(const char *str) +{ + static char *quoted_str; + static size_t quoted_str_len; + const unsigned char *s; + char *q; + size_t nonpr; + + if (!str) + return str; + + for (nonpr = 0, s = (unsigned char *)str; *s != '\0'; s++) + if (!isprint(*s) || isspace(*s) || *s == '\\' || *s == '=') + nonpr++; + if (nonpr == 0) + return str; + + if (high_water_alloc((void **)"ed_str, "ed_str_len, + (s - (unsigned char *)str) + nonpr * 3 + 1)) + return NULL; + for (s = (unsigned char *)str, q = quoted_str; *s != '\0'; s++) { + if (!isprint(*s) || isspace(*s) || *s == '\\' || *s == '=') { + *q++ = '\\'; + *q++ = '0' + ((*s >> 6) ); + *q++ = '0' + ((*s >> 3) & 7); + *q++ = '0' + ((*s ) & 7); + } else + *q++ = *s; + } + *q++ = '\0'; + + return quoted_str; +} diff --git a/libmisc/unquote.c b/libmisc/unquote.c new file mode 100644 index 0000000..bffebf9 --- /dev/null +++ b/libmisc/unquote.c @@ -0,0 +1,54 @@ +/* + File: unquote.c + + Copyright (C) 2003 Andreas Gruenbacher <a.gruenbacher@bestbits.at> + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include "misc.h" + +char *unquote(char *str) +{ + unsigned char *s, *t; + + if (!str) + return str; + + for (s = (unsigned char *)str; *s != '\0'; s++) + if (*s == '\\') + break; + if (*s == '\0') + return str; + +#define isoctal(c) \ + ((c) >= '0' && (c) <= '7') + + t = s; + do { + if (*s == '\\' && + isoctal(*(s+1)) && isoctal(*(s+2)) && isoctal(*(s+3))) { + *t++ = ((*(s+1) - '0') << 6) + + ((*(s+2) - '0') << 3) + + ((*(s+3) - '0') ); + s += 3; + } else + *t++ = *s; + } while (*s++ != '\0'); + + return str; +} diff --git a/libmisc/walk_tree.c b/libmisc/walk_tree.c new file mode 100644 index 0000000..2777145 --- /dev/null +++ b/libmisc/walk_tree.c @@ -0,0 +1,228 @@ +/* + File: walk_tree.c + + Copyright (C) 2007 Andreas Gruenbacher <a.gruenbacher@computer.org> + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "walk_tree.h" + +struct entry_handle { + struct entry_handle *prev, *next; + dev_t dev; + ino_t ino; + DIR *stream; + off_t pos; +}; + +struct entry_handle head = { + .next = &head, + .prev = &head, + /* The other fields are unused. */ +}; +struct entry_handle *closed = &head; +unsigned int num_dir_handles; + +static int walk_tree_visited(dev_t dev, ino_t ino) +{ + struct entry_handle *i; + + for (i = head.next; i != &head; i = i->next) + if (i->dev == dev && i->ino == ino) + return 1; + return 0; +} + +static int walk_tree_rec(const char *path, int walk_flags, + int (*func)(const char *, const struct stat *, int, + void *), void *arg, int depth) +{ + int follow_symlinks = (walk_flags & WALK_TREE_LOGICAL) || + (!(walk_flags & WALK_TREE_PHYSICAL) && + depth == 0); + int have_dir_stat = 0, flags = walk_flags, err; + struct entry_handle dir; + struct stat st; + + /* + * If (walk_flags & WALK_TREE_PHYSICAL), do not traverse symlinks. + * If (walk_flags & WALK_TREE_LOGICAL), traverse all symlinks. + * Otherwise, traverse only top-level symlinks. + */ + if (depth == 0) + flags |= WALK_TREE_TOPLEVEL; + + if (lstat(path, &st) != 0) + return func(path, NULL, flags | WALK_TREE_FAILED, arg); + if (S_ISLNK(st.st_mode)) { + flags |= WALK_TREE_SYMLINK; + if (flags & WALK_TREE_DEREFERENCE) { + if (stat(path, &st) != 0) + return func(path, NULL, + flags | WALK_TREE_FAILED, arg); + dir.dev = st.st_dev; + dir.ino = st.st_ino; + have_dir_stat = 1; + } + } else if (S_ISDIR(st.st_mode)) { + dir.dev = st.st_dev; + dir.ino = st.st_ino; + have_dir_stat = 1; + } + err = func(path, &st, flags, arg); + + /* + * Recurse if WALK_TREE_RECURSIVE and the path is: + * a dir not from a symlink + * a link and follow_symlinks + */ + if ((flags & WALK_TREE_RECURSIVE) && + (!(flags & WALK_TREE_SYMLINK) && S_ISDIR(st.st_mode)) || + ((flags & WALK_TREE_SYMLINK) && follow_symlinks)) { + struct dirent *entry; + + /* + * Check if we have already visited this directory to break + * endless loops. + * + * If we haven't stat()ed the file yet, do an opendir() for + * figuring out whether we have a directory, and check whether + * the directory has been visited afterwards. This saves a + * system call for each non-directory found. + */ + if (have_dir_stat && walk_tree_visited(dir.dev, dir.ino)) + return err; + + if (num_dir_handles == 0 && closed->prev != &head) { +close_another_dir: + /* Close the topmost directory handle still open. */ + closed = closed->prev; + closed->pos = telldir(closed->stream); + closedir(closed->stream); + closed->stream = NULL; + num_dir_handles++; + } + + dir.stream = opendir(path); + if (!dir.stream) { + if (errno == ENFILE && closed->prev != &head) { + /* Ran out of file descriptors. */ + num_dir_handles = 0; + goto close_another_dir; + } + + /* + * PATH may be a symlink to a regular file, or a dead + * symlink which we didn't follow above. + */ + if (errno != ENOTDIR && errno != ENOENT) + err += func(path, NULL, flags | + WALK_TREE_FAILED, arg); + return err; + } + + /* See walk_tree_visited() comment above... */ + if (!have_dir_stat) { + if (stat(path, &st) != 0) + goto skip_dir; + dir.dev = st.st_dev; + dir.ino = st.st_ino; + if (walk_tree_visited(dir.dev, dir.ino)) + goto skip_dir; + } + + /* Insert into the list of handles. */ + dir.next = head.next; + dir.prev = &head; + dir.prev->next = &dir; + dir.next->prev = &dir; + num_dir_handles--; + + while ((entry = readdir(dir.stream)) != NULL) { + char *path_end; + + if (!strcmp(entry->d_name, ".") || + !strcmp(entry->d_name, "..")) + continue; + path_end = strchr(path, 0); + if ((path_end - path) + strlen(entry->d_name) + 1 >= + FILENAME_MAX) { + errno = ENAMETOOLONG; + err += func(path, NULL, + flags | WALK_TREE_FAILED, arg); + continue; + } + *path_end++ = '/'; + strcpy(path_end, entry->d_name); + err += walk_tree_rec(path, walk_flags, func, arg, + depth + 1); + *--path_end = 0; + if (!dir.stream) { + /* Reopen the directory handle. */ + dir.stream = opendir(path); + if (!dir.stream) + return err + func(path, NULL, flags | + WALK_TREE_FAILED, arg); + seekdir(dir.stream, dir.pos); + + closed = closed->next; + num_dir_handles--; + } + } + + /* Remove from the list of handles. */ + dir.prev->next = dir.next; + dir.next->prev = dir.prev; + num_dir_handles++; + + skip_dir: + if (closedir(dir.stream) != 0) + err += func(path, NULL, flags | WALK_TREE_FAILED, arg); + } + return err; +} + +int walk_tree(const char *path, int walk_flags, unsigned int num, + int (*func)(const char *, const struct stat *, int, void *), + void *arg) +{ + char path_copy[FILENAME_MAX]; + + num_dir_handles = num; + if (num_dir_handles < 1) { + struct rlimit rlimit; + + num_dir_handles = 1; + if (getrlimit(RLIMIT_NOFILE, &rlimit) == 0 && + rlimit.rlim_cur >= 2) + num_dir_handles = rlimit.rlim_cur / 2; + } + if (strlen(path) >= FILENAME_MAX) { + errno = ENAMETOOLONG; + return func(path, NULL, WALK_TREE_FAILED, arg); + } + strcpy(path_copy, path); + return walk_tree_rec(path_copy, walk_flags, func, arg, 0); +} |