summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@suse.de>2009-06-22 19:07:20 +0200
committerAndreas Gruenbacher <agruen@suse.de>2009-06-22 20:46:31 +0200
commitcb44d79564db216af18d9beb632700e40634f166 (patch)
treede775ecb8eb7b4b6886da78f1ddc259637a3f904
downloadattr-cb44d79564db216af18d9beb632700e40634f166.tar.gz
Put libmisc on its own branch
-rw-r--r--include/misc.h23
-rw-r--r--include/walk_tree.h38
-rw-r--r--libmisc/Makefile31
-rw-r--r--libmisc/high_water_alloc.c44
-rw-r--r--libmisc/next_line.c57
-rw-r--r--libmisc/quote.c57
-rw-r--r--libmisc/unquote.c54
-rw-r--r--libmisc/walk_tree.c228
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 **)&quoted_str, &quoted_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);
+}