aboutsummaryrefslogtreecommitdiffstats
path: root/extension/filefuncs.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2012-08-17 12:38:04 +0300
committerArnold D. Robbins <arnold@skeeve.com>2012-08-17 12:38:04 +0300
commite6b05afd9971b457c0b46907a91185b66be8ff4e (patch)
treee7d3b6fce47a2ac7b32cdf923ceb6e50db9fe441 /extension/filefuncs.c
parent3b23e177cd166e96c700379491b4a99bddf9aa4d (diff)
parent76cc4d241f328876b18e48639d631823c3d304d6 (diff)
downloadegawk-e6b05afd9971b457c0b46907a91185b66be8ff4e.tar.gz
egawk-e6b05afd9971b457c0b46907a91185b66be8ff4e.tar.bz2
egawk-e6b05afd9971b457c0b46907a91185b66be8ff4e.zip
Merge branch 'extgawk'
Diffstat (limited to 'extension/filefuncs.c')
-rw-r--r--extension/filefuncs.c771
1 files changed, 579 insertions, 192 deletions
diff --git a/extension/filefuncs.c b/extension/filefuncs.c
index 9758ba8e..e27e51bf 100644
--- a/extension/filefuncs.c
+++ b/extension/filefuncs.c
@@ -4,10 +4,13 @@
*
* Arnold Robbins, update for 3.1, Mon Nov 23 12:53:39 EST 1998
* Arnold Robbins and John Haque, update for 3.1.4, applied Mon Jun 14 13:55:30 IDT 2004
+ * Arnold Robbins and Andrew Schorr, revised for new extension API, May 2012.
+ * Arnold Robbins, add fts(), August 2012
*/
/*
- * Copyright (C) 2001, 2004, 2005, 2010, 2011 the Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2004, 2005, 2010, 2011, 2012
+ * the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Programming Language.
@@ -27,28 +30,60 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "awk.h"
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "config.h"
+#include "gawkapi.h"
+
+#include "gettext.h"
+#define _(msgid) gettext(msgid)
+#define N_(msgid) msgid
+
+#if defined(HAVE_FTS_H) && defined(HAVE_FTS_OPEN) && defined(HAVE_FTS_READ)
+#define HAVE_FTS_ROUTINES
+#endif
+
+
+#ifdef HAVE_FTS_ROUTINES
+#include <fts.h>
+#include "stack.h"
+#endif
+
+static const gawk_api_t *api; /* for convenience macros to work */
+static awk_ext_id_t *ext_id;
+static awk_bool_t init_filefuncs(void);
+static awk_bool_t (*init_func)(void) = init_filefuncs;
int plugin_is_GPL_compatible;
/* do_chdir --- provide dynamically loaded chdir() builtin for gawk */
-static NODE *
-do_chdir(int nargs)
+static awk_value_t *
+do_chdir(int nargs, awk_value_t *result)
{
- NODE *newdir;
+ awk_value_t newdir;
int ret = -1;
+ assert(result != NULL);
+
if (do_lint && nargs != 1)
- lintwarn("chdir: called with incorrect number of arguments");
+ lintwarn(ext_id, _("chdir: called with incorrect number of arguments, expecting 1"));
- newdir = get_scalar_argument(0, false);
- (void) force_string(newdir);
- ret = chdir(newdir->stptr);
- if (ret < 0)
- update_ERRNO_int(errno);
+ if (get_argument(0, AWK_STRING, & newdir)) {
+ ret = chdir(newdir.str_value.str);
+ if (ret < 0)
+ update_ERRNO_int(errno);
+ }
- return make_number((AWKNUM) ret);
+ return make_number(ret, result);
}
/* format_mode --- turn a stat mode field into something readable */
@@ -57,99 +92,76 @@ static char *
format_mode(unsigned long fmode)
{
static char outbuf[12];
- int i;
-
- strcpy(outbuf, "----------");
- /* first, get the file type */
- i = 0;
- switch (fmode & S_IFMT) {
+ static struct ftype_map {
+ unsigned int mask;
+ int charval;
+ } ftype_map[] = {
+ { S_IFREG, '-' }, /* redundant */
+ { S_IFBLK, 'b' },
+ { S_IFCHR, 'c' },
+ { S_IFDIR, 'd' },
#ifdef S_IFSOCK
- case S_IFSOCK:
- outbuf[i] = 's';
- break;
+ { S_IFSOCK, 's' },
+#endif
+#ifdef S_IFIFO
+ { S_IFIFO, 'p' },
#endif
#ifdef S_IFLNK
- case S_IFLNK:
- outbuf[i] = 'l';
- break;
+ { S_IFLNK, 'l' },
#endif
- case S_IFREG:
- outbuf[i] = '-'; /* redundant */
- break;
- case S_IFBLK:
- outbuf[i] = 'b';
- break;
- case S_IFDIR:
- outbuf[i] = 'd';
- break;
#ifdef S_IFDOOR /* Solaris weirdness */
- case S_IFDOOR:
- outbuf[i] = 'D';
- break;
+ { S_IFDOOR, 'D' },
#endif /* S_IFDOOR */
- case S_IFCHR:
- outbuf[i] = 'c';
- break;
-#ifdef S_IFIFO
- case S_IFIFO:
- outbuf[i] = 'p';
- break;
-#endif
- }
+ };
+ static struct mode_map {
+ unsigned int mask;
+ int rep;
+ } map[] = {
+ { S_IRUSR, 'r' }, { S_IWUSR, 'w' }, { S_IXUSR, 'x' },
+ { S_IRGRP, 'r' }, { S_IWGRP, 'w' }, { S_IXGRP, 'x' },
+ { S_IROTH, 'r' }, { S_IWOTH, 'w' }, { S_IXOTH, 'x' },
+ };
+ static struct setuid_map {
+ unsigned int mask;
+ int index;
+ int small_rep;
+ int big_rep;
+ } setuid_map[] = {
+ { S_ISUID, 3, 's', 'S' }, /* setuid bit */
+ { S_ISGID, 6, 's', 'l' }, /* setgid without execute == locking */
+ { S_ISVTX, 9, 't', 'T' }, /* the so-called "sticky" bit */
+ };
+ int i, j, k;
- i++;
- if ((fmode & S_IRUSR) != 0)
- outbuf[i] = 'r';
- i++;
- if ((fmode & S_IWUSR) != 0)
- outbuf[i] = 'w';
- i++;
- if ((fmode & S_IXUSR) != 0)
- outbuf[i] = 'x';
- i++;
-
- if ((fmode & S_IRGRP) != 0)
- outbuf[i] = 'r';
- i++;
- if ((fmode & S_IWGRP) != 0)
- outbuf[i] = 'w';
- i++;
- if ((fmode & S_IXGRP) != 0)
- outbuf[i] = 'x';
- i++;
-
- if ((fmode & S_IROTH) != 0)
- outbuf[i] = 'r';
- i++;
- if ((fmode & S_IWOTH) != 0)
- outbuf[i] = 'w';
- i++;
- if ((fmode & S_IXOTH) != 0)
- outbuf[i] = 'x';
- i++;
-
- outbuf[i] = '\0';
+ strcpy(outbuf, "----------");
- if ((fmode & S_ISUID) != 0) {
- if (outbuf[3] == 'x')
- outbuf[3] = 's';
- else
- outbuf[3] = 'S';
+ /* first, get the file type */
+ i = 0;
+ for (j = 0, k = sizeof(ftype_map)/sizeof(ftype_map[0]); j < k; j++) {
+ if ((fmode & S_IFMT) == ftype_map[j].mask) {
+ outbuf[i] = ftype_map[j].charval;
+ break;
+ }
}
- /* setgid without execute == locking */
- if ((fmode & S_ISGID) != 0) {
- if (outbuf[6] == 'x')
- outbuf[6] = 's';
- else
- outbuf[6] = 'l';
+ /* now the permissions */
+ for (j = 0, k = sizeof(map)/sizeof(map[0]); j < k; j++) {
+ i++;
+ if ((fmode & map[j].mask) != 0)
+ outbuf[i] = map[j].rep;
}
- if ((fmode & S_ISVTX) != 0) {
- if (outbuf[9] == 'x')
- outbuf[9] = 't';
- else
- outbuf[9] = 'T';
+ i++;
+ outbuf[i] = '\0';
+
+ /* tweaks for the setuid / setgid / sticky bits */
+ for (j = 0, k = sizeof(setuid_map)/sizeof(setuid_map[0]); j < k; j++) {
+ if (fmode & setuid_map[j].mask) {
+ if (outbuf[setuid_map[j].index] == 'x')
+ outbuf[setuid_map[j].index] = setuid_map[j].small_rep;
+ else
+ outbuf[setuid_map[j].index] = setuid_map[j].big_rep;
+ }
}
return outbuf;
@@ -158,7 +170,7 @@ format_mode(unsigned long fmode)
/* read_symlink -- read a symbolic link into an allocated buffer.
This is based on xreadlink; the basic problem is that lstat cannot be relied
upon to return the proper size for a symbolic link. This happens,
- for example, on linux in the /proc filesystem, where the symbolic link
+ for example, on GNU/Linux in the /proc filesystem, where the symbolic link
sizes are often 0. */
#ifndef SIZE_MAX
@@ -176,10 +188,12 @@ read_symlink(const char *fname, size_t bufsize, ssize_t *linksize)
if (bufsize)
bufsize += 2;
else
- bufsize = BUFSIZ*2;
+ bufsize = BUFSIZ * 2;
+
/* Make sure that bufsize >= 2 and within range */
- if ((bufsize > MAXSIZE) || (bufsize < 2))
+ if (bufsize > MAXSIZE || bufsize < 2)
bufsize = MAXSIZE;
+
while (1) {
char *buf;
@@ -212,136 +226,509 @@ read_symlink(const char *fname, size_t bufsize, ssize_t *linksize)
/* array_set --- set an array element */
static void
-array_set(NODE *array, const char *sub, NODE *value)
+array_set(awk_array_t array, const char *sub, awk_value_t *value)
{
- NODE *tmp;
- NODE **aptr;
-
- tmp = make_string(sub, strlen(sub));
- aptr = assoc_lookup(array, tmp);
- unref(tmp);
- unref(*aptr);
- *aptr = value;
+ awk_value_t index;
+
+ set_array_element(array,
+ make_const_string(sub, strlen(sub), & index),
+ value);
+
}
-/* do_stat --- provide a stat() function for gawk */
+/* array_set_numeric --- set an array element with a number */
-static NODE *
-do_stat(int nargs)
+static void
+array_set_numeric(awk_array_t array, const char *sub, double num)
{
- NODE *file, *array;
- struct stat sbuf;
- int ret;
- char *pmode; /* printable mode */
- char *type = "unknown";
+ awk_value_t tmp;
- if (do_lint && nargs > 2)
- lintwarn("stat: called with too many arguments");
+ array_set(array, sub, make_number(num, & tmp));
+}
- /* file is first arg, array to hold results is second */
- file = get_scalar_argument(0, false);
- array = get_array_argument(1, false);
+/* fill_stat_array --- do the work to fill an array with stat info */
- /* empty out the array */
- assoc_clear(array);
+static int
+fill_stat_array(const char *name, awk_array_t array, struct stat *sbuf)
+{
+ char *pmode; /* printable mode */
+ const char *type = "unknown";
+ awk_value_t tmp;
+ static struct ftype_map {
+ unsigned int mask;
+ const char *type;
+ } ftype_map[] = {
+ { S_IFREG, "file" },
+ { S_IFBLK, "blockdev" },
+ { S_IFCHR, "chardev" },
+ { S_IFDIR, "directory" },
+#ifdef S_IFSOCK
+ { S_IFSOCK, "socket" },
+#endif
+#ifdef S_IFIFO
+ { S_IFIFO, "fifo" },
+#endif
+#ifdef S_IFLNK
+ { S_IFLNK, "symlink" },
+#endif
+#ifdef S_IFDOOR /* Solaris weirdness */
+ { S_IFDOOR, "door" },
+#endif /* S_IFDOOR */
+ };
+ int j, k;
- /* lstat the file, if error, set ERRNO and return */
- (void) force_string(file);
- ret = lstat(file->stptr, & sbuf);
- if (ret < 0) {
- update_ERRNO_int(errno);
- return make_number((AWKNUM) ret);
- }
+ /* empty out the array */
+ clear_array(array);
/* fill in the array */
- array_set(array, "name", dupnode(file));
- array_set(array, "dev", make_number((AWKNUM) sbuf.st_dev));
- array_set(array, "ino", make_number((AWKNUM) sbuf.st_ino));
- array_set(array, "mode", make_number((AWKNUM) sbuf.st_mode));
- array_set(array, "nlink", make_number((AWKNUM) sbuf.st_nlink));
- array_set(array, "uid", make_number((AWKNUM) sbuf.st_uid));
- array_set(array, "gid", make_number((AWKNUM) sbuf.st_gid));
- array_set(array, "size", make_number((AWKNUM) sbuf.st_size));
- array_set(array, "blocks", make_number((AWKNUM) sbuf.st_blocks));
- array_set(array, "atime", make_number((AWKNUM) sbuf.st_atime));
- array_set(array, "mtime", make_number((AWKNUM) sbuf.st_mtime));
- array_set(array, "ctime", make_number((AWKNUM) sbuf.st_ctime));
+ array_set(array, "name", make_const_string(name, strlen(name), & tmp));
+ array_set_numeric(array, "dev", sbuf->st_dev);
+ array_set_numeric(array, "ino", sbuf->st_ino);
+ array_set_numeric(array, "mode", sbuf->st_mode);
+ array_set_numeric(array, "nlink", sbuf->st_nlink);
+ array_set_numeric(array, "uid", sbuf->st_uid);
+ array_set_numeric(array, "gid", sbuf->st_gid);
+ array_set_numeric(array, "size", sbuf->st_size);
+ array_set_numeric(array, "blocks", sbuf->st_blocks);
+ array_set_numeric(array, "atime", sbuf->st_atime);
+ array_set_numeric(array, "mtime", sbuf->st_mtime);
+ array_set_numeric(array, "ctime", sbuf->st_ctime);
/* for block and character devices, add rdev, major and minor numbers */
- if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) {
- array_set(array, "rdev", make_number((AWKNUM) sbuf.st_rdev));
- array_set(array, "major", make_number((AWKNUM) major(sbuf.st_rdev)));
- array_set(array, "minor", make_number((AWKNUM) minor(sbuf.st_rdev)));
+ if (S_ISBLK(sbuf->st_mode) || S_ISCHR(sbuf->st_mode)) {
+ array_set_numeric(array, "rdev", sbuf->st_rdev);
+ array_set_numeric(array, "major", major(sbuf->st_rdev));
+ array_set_numeric(array, "minor", minor(sbuf->st_rdev));
}
#ifdef HAVE_ST_BLKSIZE
- array_set(array, "blksize", make_number((AWKNUM) sbuf.st_blksize));
+ array_set_numeric(array, "blksize", sbuf->st_blksize);
#endif /* HAVE_ST_BLKSIZE */
- pmode = format_mode(sbuf.st_mode);
- array_set(array, "pmode", make_string(pmode, strlen(pmode)));
+ pmode = format_mode(sbuf->st_mode);
+ array_set(array, "pmode", make_const_string(pmode, strlen(pmode), & tmp));
/* for symbolic links, add a linkval field */
- if (S_ISLNK(sbuf.st_mode)) {
+ if (S_ISLNK(sbuf->st_mode)) {
char *buf;
ssize_t linksize;
- if ((buf = read_symlink(file->stptr, sbuf.st_size,
- &linksize)) != NULL)
- array_set(array, "linkval", make_str_node(buf, linksize, ALREADY_MALLOCED));
+ if ((buf = read_symlink(name, sbuf->st_size,
+ & linksize)) != NULL)
+ array_set(array, "linkval", make_malloced_string(buf, linksize, & tmp));
else
- warning(_("unable to read symbolic link `%s'"),
- file->stptr);
+ warning(ext_id, "stat: unable to read symbolic link `%s'", name);
}
/* add a type field */
- switch (sbuf.st_mode & S_IFMT) {
-#ifdef S_IFSOCK
- case S_IFSOCK:
- type = "socket";
- break;
-#endif
-#ifdef S_IFLNK
- case S_IFLNK:
- type = "symlink";
- break;
-#endif
- case S_IFREG:
- type = "file";
- break;
- case S_IFBLK:
- type = "blockdev";
- break;
- case S_IFDIR:
- type = "directory";
- break;
-#ifdef S_IFDOOR
- case S_IFDOOR:
- type = "door";
- break;
-#endif
- case S_IFCHR:
- type = "chardev";
- break;
-#ifdef S_IFIFO
- case S_IFIFO:
- type = "fifo";
- break;
+ type = "unknown"; /* shouldn't happen */
+ for (j = 0, k = sizeof(ftype_map)/sizeof(ftype_map[0]); j < k; j++) {
+ if ((sbuf->st_mode & S_IFMT) == ftype_map[j].mask) {
+ type = ftype_map[j].type;
+ break;
+ }
+ }
+
+ array_set(array, "type", make_const_string(type, strlen(type), &tmp));
+
+ return 0;
+}
+
+/* do_stat --- provide a stat() function for gawk */
+
+static awk_value_t *
+do_stat(int nargs, awk_value_t *result)
+{
+ awk_value_t file_param, array_param;
+ char *name;
+ awk_array_t array;
+ int ret;
+ struct stat sbuf;
+
+ assert(result != NULL);
+
+ if (do_lint && nargs != 2) {
+ lintwarn(ext_id, _("stat: called with wrong number of arguments"));
+ return make_number(-1, result);
+ }
+
+ /* file is first arg, array to hold results is second */
+ if ( ! get_argument(0, AWK_STRING, & file_param)
+ || ! get_argument(1, AWK_ARRAY, & array_param)) {
+ warning(ext_id, _("stat: bad parameters"));
+ return make_number(-1, result);
+ }
+
+ name = file_param.str_value.str;
+ array = array_param.array_cookie;
+
+ /* lstat the file, if error, set ERRNO and return */
+ ret = lstat(name, & sbuf);
+ if (ret < 0) {
+ update_ERRNO_int(errno);
+ return make_number(ret, result);
+ }
+
+ ret = fill_stat_array(name, array, & sbuf);
+
+ return make_number(ret, result);
+}
+
+/* init_filefuncs --- initialization routine */
+
+static awk_bool_t
+init_filefuncs(void)
+{
+ int errors = 0;
+
+ /* at least right now, only FTS needs initializing */
+#ifdef HAVE_FTS_ROUTINES
+ int i;
+ awk_value_t value;
+
+ static struct flagtab {
+ const char *name;
+ int value;
+ } opentab[] = {
+#define ENTRY(x) { #x, x }
+ ENTRY(FTS_COMFOLLOW),
+ ENTRY(FTS_LOGICAL),
+ ENTRY(FTS_NOCHDIR),
+ ENTRY(FTS_PHYSICAL),
+ ENTRY(FTS_SEEDOT),
+ ENTRY(FTS_XDEV),
+ { NULL, 0 }
+ };
+
+ for (i = 0; opentab[i].name != NULL; i++) {
+ (void) make_number(opentab[i].value, & value);
+ if (! sym_constant(opentab[i].name, & value)) {
+ warning(ext_id, "fts init: could not create constant %s",
+ opentab[i].name);
+ errors++;
+ }
+ }
#endif
+ return errors == 0;
+}
+
+#ifdef HAVE_FTS_ROUTINES
+static int fts_errors = 0;
+
+/* fill_stat_element --- fill in stat element of array */
+
+static void
+fill_stat_element(awk_array_t element_array, const char *name, struct stat *sbuf)
+{
+ awk_value_t index, value;
+ awk_array_t stat_array;
+
+ stat_array = create_array();
+ if (stat_array == NULL) {
+ warning(ext_id, _("fill_stat_element: could not create array"));
+ fts_errors++;
+ return;
+ }
+ fill_stat_array(name, stat_array, sbuf);
+ (void) make_const_string("stat", 4, & index);
+ value.val_type = AWK_ARRAY;
+ value.array_cookie = stat_array;
+ if (! set_array_element(element_array, & index, & value)) {
+ warning(ext_id, _("fill_stat_element: could not set element"));
+ fts_errors++;
+ }
+}
+
+/* fill_path_element --- fill in path element of array */
+
+static void
+fill_path_element(awk_array_t element_array, const char *path)
+{
+ awk_value_t index, value;
+
+ (void) make_const_string("path", 4, & index);
+ (void) make_const_string(path, strlen(path), & value);
+ if (! set_array_element(element_array, & index, & value)) {
+ warning(ext_id, _("fill_path_element: could not set element"));
+ fts_errors++;
+ }
+}
+
+/* fill_error_element --- fill in error element of array */
+
+static void
+fill_error_element(awk_array_t element_array, const int errcode)
+{
+ awk_value_t index, value;
+ const char *err = strerror(errcode);
+
+ (void) make_const_string("error", 5, & index);
+ (void) make_const_string(err, strlen(err), & value);
+ if (! set_array_element(element_array, & index, & value)) {
+ warning(ext_id, _("fill_error_element: could not set element"));
+ fts_errors++;
}
+}
+
+/* fill_default_elements --- fill in stat and path elements */
+
+static void
+fill_default_elements(awk_array_t element_array, const FTSENT *const fentry, int bad_ret)
+{
+ /* full path */
+ fill_path_element(element_array, fentry->fts_path);
+
+ /* stat info */
+ if (! bad_ret) {
+ fill_stat_element(element_array,
+ fentry->fts_name,
+ fentry->fts_statp);
+ }
+
+ /* error info */
+ if (bad_ret || fentry->fts_errno != 0) {
+ fill_error_element(element_array, fentry->fts_errno);
+ }
+}
+
+/* process --- process the heirarchy */
+
+static void
+process(FTS *heirarchy, awk_array_t destarray, int seedot)
+{
+ FTSENT *fentry;
+ awk_value_t index, value;
+ awk_array_t element_array, newdir_array, dot_array;
+ int bad_ret = 0;
+
+ /* path is full path, pathlen is length thereof */
+ /* name is name in directory, namelen is length thereof */
+ while ((fentry = fts_read(heirarchy)) != NULL) {
+ bad_ret = 0;
+
+ switch (fentry->fts_info) {
+ case FTS_D:
+ /* directory */
+ /* create array to hold entries */
+ newdir_array = create_array();
+ if (newdir_array == NULL) {
+ warning(ext_id, _("fts-process: could not create array"));
+ fts_errors++;
+ break;
+ }
+
+ /* store new directory in its parent directory */
+ (void) make_const_string(fentry->fts_name, fentry->fts_namelen, & index);
+ value.val_type = AWK_ARRAY;
+ value.array_cookie = newdir_array;
+ if (! set_array_element(destarray, & index, & value)) {
+ warning(ext_id, _("fts-process: could not set element"));
+ fts_errors++;
+ break;
+ }
+ newdir_array = value.array_cookie;
+
+ /* push current directory */
+ stack_push(destarray);
+
+ /* new directory becomes current */
+ destarray = newdir_array;
+ break;
+
+ case FTS_DNR:
+ case FTS_DC:
+ case FTS_ERR:
+ case FTS_NS:
+ /* error */
+ bad_ret = 1;
+ /* fall through */
+
+ case FTS_NSOK:
+ case FTS_SL:
+ case FTS_SLNONE:
+ case FTS_F:
+ case FTS_DOT:
+ /* if see dot, skip "." */
+ if (seedot && strcmp(fentry->fts_name, ".") == 0)
+ break;
+
+ /*
+ * File case.
+ * destarray is the directory we're reading.
+ * step 1: create new empty array
+ */
+ element_array = create_array();
+ if (element_array == NULL) {
+ warning(ext_id, _("fts-process: could not create array"));
+ fts_errors++;
+ break;
+ }
+
+ /* step 2: add element array to parent array */
+ (void) make_const_string(fentry->fts_name, fentry->fts_namelen, & index);
+ value.val_type = AWK_ARRAY;
+ value.array_cookie = element_array;
+ if (! set_array_element(destarray, & index, & value)) {
+ warning(ext_id, _("fts-process: could not set element"));
+ fts_errors++;
+ break;
+ }
+
+ /* step 3: fill in path, stat, error elements */
+ fill_default_elements(element_array, fentry, bad_ret);
+ break;
+
+ case FTS_DP:
+ /* create "." subarray */
+ dot_array = create_array();
+
+ /* add it to parent */
+ (void) make_const_string(".", 1, & index);
+ value.val_type = AWK_ARRAY;
+ value.array_cookie = dot_array;
+ if (! set_array_element(destarray, & index, & value)) {
+ warning(ext_id, _("fts-process: could not set element"));
+ fts_errors++;
+ break;
+ }
+
+ /* fill it in with path, stat, error elements */
+ fill_default_elements(dot_array, fentry, bad_ret);
+
+ /* now pop the parent directory off the stack */
+ if (! stack_empty()) {
+ /* pop stack */
+ destarray = stack_pop();
+ }
- array_set(array, "type", make_string(type, strlen(type)));
+ break;
- return make_number((AWKNUM) ret);
+ case FTS_DEFAULT:
+ /* nothing to do */
+ break;
+ }
+ }
}
+#endif
-/* dlload --- load new builtins in this library */
+/* do_fts --- walk a heirarchy and fill in an array */
+
+/*
+ * Usage from awk:
+ * flags = or(FTS_PHYSICAL, ...)
+ * result = fts(pathlist, flags, filedata)
+ */
-NODE *
-dlload(NODE *tree, void *dl)
+static awk_value_t *
+do_fts(int nargs, awk_value_t *result)
{
- make_builtin("chdir", do_chdir, 1);
- make_builtin("stat", do_stat, 2);
+#ifdef HAVE_FTS_ROUTINES
+ awk_value_t pathlist, flagval, dest;
+ awk_flat_array_t *path_array = NULL;
+ char **pathvector = NULL;
+ FTS *heirarchy;
+ int flags;
+ size_t i, count;
+ int ret = -1;
+ static const int mask = (
+ FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR | FTS_PHYSICAL
+ | FTS_SEEDOT | FTS_XDEV);
+
+ assert(result != NULL);
+ fts_errors = 0; /* ensure a fresh start */
+
+ if (do_lint && nargs != 3)
+ lintwarn(ext_id, _("fts: called with incorrect number of arguments, expecting 3"));
+
+ if (! get_argument(0, AWK_ARRAY, & pathlist)) {
+ warning(ext_id, _("fts: bad first parameter"));
+ update_ERRNO_int(EINVAL);
+ goto out;
+ }
+
+ if (! get_argument(1, AWK_NUMBER, & flagval)) {
+ warning(ext_id, _("fts: bad second parameter"));
+ update_ERRNO_int(EINVAL);
+ goto out;
+ }
+
+ if (! get_argument(2, AWK_ARRAY, & dest)) {
+ warning(ext_id, _("fts: bad third parameter"));
+ update_ERRNO_int(EINVAL);
+ goto out;
+ }
+
+ /* flatten pathlist */
+ if (! flatten_array(pathlist.array_cookie, & path_array)) {
+ warning(ext_id, _("fts: could not flatten array\n"));
+ goto out;
+ }
+
+ /* check the flags first, before the array flattening */
+
+ /* get flags */
+ flags = flagval.num_value;
+
+ /* enforce physical or logical but not both, and not no_stat */
+ if ((flags & (FTS_PHYSICAL|FTS_LOGICAL)) == 0
+ || (flags & (FTS_PHYSICAL|FTS_LOGICAL)) == (FTS_PHYSICAL|FTS_LOGICAL)) {
+ update_ERRNO_int(EINVAL);
+ goto out;
+ }
+ if ((flags & FTS_NOSTAT) != 0) {
+ flags &= ~FTS_NOSTAT;
+ if (do_lint)
+ lintwarn(ext_id, _("fts: ignoring sneaky FTS_NOSTAT flag. nyah, nyah, nyah."));
+ }
+ flags &= mask; /* turn off anything else */
+
+ /* make pathvector */
+ count = path_array->count + 1;
+ emalloc(pathvector, char **, count * sizeof(char *), "do_fts");
+ memset(pathvector, 0, count * sizeof(char *));
+
+ /* fill it in */
+ count--; /* ignore final NULL at end of vector */
+ for (i = 0; i < count; i++)
+ pathvector[i] = path_array->elements[i].value.str_value.str;
- return make_number((AWKNUM) 0);
+
+ /* clear dest array */
+ if (! clear_array(dest.array_cookie)) {
+ warning(ext_id, _("fts: clear_array failed\n"));
+ goto out;
+ }
+
+ /* let's do it! */
+ if ((heirarchy = fts_open(pathvector, flags, NULL)) != NULL) {
+ process(heirarchy, dest.array_cookie, (flags & FTS_SEEDOT) != 0);
+ fts_close(heirarchy);
+
+ if (fts_errors == 0)
+ ret = 0;
+ } else
+ update_ERRNO_int(errno);
+
+out:
+ if (pathvector != NULL)
+ free(pathvector);
+ if (path_array != NULL)
+ (void) release_flattened_array(pathlist.array_cookie, path_array);
+
+ return make_number(ret, result);
+#else
+ update_ERRNO_int(EINVAL);
+ return make_number(-1, result);
+#endif
}
+
+static awk_ext_func_t func_table[] = {
+ { "chdir", do_chdir, 1 },
+ { "stat", do_stat, 2 },
+ { "fts", do_fts, 3 },
+};
+
+
+/* define the dl_load function using the boilerplate macro */
+
+dl_load_func(func_table, filefuncs, "")