aboutsummaryrefslogtreecommitdiffstats
path: root/extension/readdir.c
diff options
context:
space:
mode:
Diffstat (limited to 'extension/readdir.c')
-rw-r--r--extension/readdir.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/extension/readdir.c b/extension/readdir.c
new file mode 100644
index 00000000..6aebf091
--- /dev/null
+++ b/extension/readdir.c
@@ -0,0 +1,254 @@
+/*
+ * readdir.c --- Provide an input parser to read directories
+ *
+ * Arnold Robbins
+ * arnold@skeeve.com
+ * Written 7/2012
+ *
+ * Andrew Schorr and Arnold Robbins: further fixes 8/2012.
+ * Simplified 11/2012.
+ */
+
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _BSD_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else
+#error Cannot compile the dirent extension on this system!
+#endif
+
+#include "gawkapi.h"
+
+#include "gettext.h"
+#define _(msgid) gettext(msgid)
+#define N_(msgid) msgid
+
+static const gawk_api_t *api; /* for convenience macros to work */
+static awk_ext_id_t *ext_id;
+static const char *ext_version = "readdir extension: version 1.0";
+
+static awk_bool_t init_readdir(void);
+static awk_bool_t (*init_func)(void) = init_readdir;
+
+int plugin_is_GPL_compatible;
+
+/* data type for the opaque pointer: */
+
+typedef struct open_directory {
+ DIR *dp;
+ char *buf;
+} open_directory_t;
+
+/* ftype --- return type of file as a single character string */
+
+static const char *
+ftype(struct dirent *entry)
+{
+#ifdef DT_BLK
+ switch (entry->d_type) {
+ case DT_BLK: return "b";
+ case DT_CHR: return "c";
+ case DT_DIR: return "d";
+ case DT_FIFO: return "p";
+ case DT_LNK: return "l";
+ case DT_REG: return "f";
+ case DT_SOCK: return "s";
+ default:
+ case DT_UNKNOWN: return "u";
+ }
+#else
+ return "u";
+#endif
+}
+
+/* dir_get_record --- get one record at a time out of a directory */
+
+static int
+dir_get_record(char **out, awk_input_buf_t *iobuf, int *errcode,
+ char **rt_start, size_t *rt_len)
+{
+ DIR *dp;
+ struct dirent *dirent;
+ int len;
+ open_directory_t *the_dir;
+ const char *ftstr;
+
+ /*
+ * The caller sets *errcode to 0, so we should set it only if an
+ * error occurs.
+ */
+
+ if (out == NULL || iobuf == NULL || iobuf->opaque == NULL)
+ return EOF;
+
+ the_dir = (open_directory_t *) iobuf->opaque;
+ dp = the_dir->dp;
+
+ /*
+ * Initialize errno, since readdir does not set it to zero on EOF.
+ */
+ errno = 0;
+ dirent = readdir(dp);
+ if (dirent == NULL) {
+ *errcode = errno; /* in case there was an error */
+ return EOF;
+ }
+
+#ifdef ZOS_USS
+ len = sprintf(the_dir->buf, "%lu/%s",
+ (unsigned long) dirent->d_ino,
+ dirent->d_name);
+#else
+ len = sprintf(the_dir->buf, "%llu/%s",
+ (unsigned long long) dirent->d_ino,
+ dirent->d_name);
+#endif
+
+ ftstr = ftype(dirent);
+ len += sprintf(the_dir->buf + len, "/%s", ftstr);
+
+ *out = the_dir->buf;
+
+ *rt_len = 0; /* set RT to "" */
+ return len;
+}
+
+/* dir_close --- close up when done */
+
+static void
+dir_close(awk_input_buf_t *iobuf)
+{
+ open_directory_t *the_dir;
+
+ if (iobuf == NULL || iobuf->opaque == NULL)
+ return;
+
+ the_dir = (open_directory_t *) iobuf->opaque;
+
+ closedir(the_dir->dp);
+ free(the_dir->buf);
+ free(the_dir);
+
+ iobuf->fd = -1;
+}
+
+/* dir_can_take_file --- return true if we want the file */
+
+static awk_bool_t
+dir_can_take_file(const awk_input_buf_t *iobuf)
+{
+ if (iobuf == NULL)
+ return awk_false;
+
+ return (iobuf->fd != INVALID_HANDLE && S_ISDIR(iobuf->sbuf.st_mode));
+}
+
+/*
+ * dir_take_control_of --- set up input parser.
+ * We can assume that dir_can_take_file just returned true,
+ * and no state has changed since then.
+ */
+
+static awk_bool_t
+dir_take_control_of(awk_input_buf_t *iobuf)
+{
+ DIR *dp;
+ open_directory_t *the_dir;
+ size_t size;
+
+ errno = 0;
+#ifdef HAVE_FDOPENDIR
+ dp = fdopendir(iobuf->fd);
+#else
+ dp = opendir(iobuf->name);
+ if (dp != NULL)
+ iobuf->fd = dirfd(dp);
+#endif
+ if (dp == NULL) {
+ warning(ext_id, _("dir_take_control_of: opendir/fdopendir failed: %s"),
+ strerror(errno));
+ update_ERRNO_int(errno);
+ return awk_false;
+ }
+
+ emalloc(the_dir, open_directory_t *, sizeof(open_directory_t), "dir_take_control_of");
+ the_dir->dp = dp;
+ size = sizeof(struct dirent) + 21 /* max digits in inode */ + 2 /* slashes */;
+ emalloc(the_dir->buf, char *, size, "dir_take_control_of");
+
+ iobuf->opaque = the_dir;
+ iobuf->get_record = dir_get_record;
+ iobuf->close_func = dir_close;
+
+ return awk_true;
+}
+
+static awk_input_parser_t readdir_parser = {
+ "readdir",
+ dir_can_take_file,
+ dir_take_control_of,
+ NULL
+};
+
+#ifdef TEST_DUPLICATE
+static awk_input_parser_t readdir_parser2 = {
+ "readdir2",
+ dir_can_take_file,
+ dir_take_control_of,
+ NULL
+};
+#endif
+
+/* init_readdir --- set things ups */
+
+static awk_bool_t
+init_readdir()
+{
+ register_input_parser(& readdir_parser);
+#ifdef TEST_DUPLICATE
+ register_input_parser(& readdir_parser2);
+#endif
+
+ return awk_true;
+}
+
+static awk_ext_func_t func_table[] = {
+ { NULL, NULL, 0 }
+};
+
+/* define the dl_load function using the boilerplate macro */
+
+dl_load_func(func_table, readdir, "")