diff options
Diffstat (limited to 'extension/filefuncs.c')
-rw-r--r-- | extension/filefuncs.c | 838 |
1 files changed, 612 insertions, 226 deletions
diff --git a/extension/filefuncs.c b/extension/filefuncs.c index ad7828f3..391ed11a 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,30 +30,57 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "awk.h" +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#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 "gawkapi.h" + +#include "gettext.h" +#define _(msgid) gettext(msgid) +#define N_(msgid) msgid + +#include "gawkfts.h" +#include "stack.h" -#include <sys/sysmacros.h> +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; +static const char *ext_version = "filefuncs extension: version 1.0"; 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; - if (do_lint && get_curfunc_arg_count() != 1) - lintwarn("chdir: called with incorrect number of arguments"); + assert(result != NULL); - newdir = get_scalar_argument(0, FALSE); - (void) force_string(newdir); - ret = chdir(newdir->stptr); - if (ret < 0) - update_ERRNO(); + if (do_lint && nargs != 1) + lintwarn(ext_id, _("chdir: called with incorrect number of arguments, expecting 1")); - return make_number((AWKNUM) ret); + if (get_argument(0, AWK_STRING, & newdir)) { + ret = chdir(newdir.str_value.str); + if (ret < 0) + update_ERRNO_int(errno); + } + + return make_number(ret, result); } /* format_mode --- turn a stat mode field into something readable */ @@ -59,280 +89,636 @@ 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; + + strcpy(outbuf, "----------"); + + /* 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; + } } - 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++; + /* 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_IRGRP) != 0) - outbuf[i] = 'r'; - i++; - if ((fmode & S_IWGRP) != 0) - outbuf[i] = 'w'; - i++; - if ((fmode & S_IXGRP) != 0) - outbuf[i] = 'x'; i++; + outbuf[i] = '\0'; - 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++; + /* 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; + } + } - outbuf[i] = '\0'; + return outbuf; +} + +/* 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 GNU/Linux in the /proc filesystem, where the symbolic link + sizes are often 0. */ + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif +#ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) +#endif - if ((fmode & S_ISUID) != 0) { - if (outbuf[3] == 'x') - outbuf[3] = 's'; +#define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX) + +static char * +read_symlink(const char *fname, size_t bufsize, ssize_t *linksize) +{ + if (bufsize) + bufsize += 2; + else + bufsize = BUFSIZ * 2; + + /* Make sure that bufsize >= 2 and within range */ + if (bufsize > MAXSIZE || bufsize < 2) + bufsize = MAXSIZE; + + while (1) { + char *buf; + + emalloc(buf, char *, bufsize, "read_symlink"); + if ((*linksize = readlink(fname, buf, bufsize)) < 0) { + /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink + returns -1 with errno == ERANGE if the buffer is + too small. */ + if (errno != ERANGE) { + free(buf); + return NULL; + } + } + /* N.B. This test is safe because bufsize must be >= 2 */ + else if ((size_t)*linksize <= bufsize-2) { + buf[*linksize] = '\0'; + return buf; + } + free(buf); + if (bufsize <= MAXSIZE/2) + bufsize *= 2; + else if (bufsize < MAXSIZE) + bufsize = MAXSIZE; else - outbuf[3] = 'S'; + return NULL; } + return NULL; +} - /* setgid without execute == locking */ - if ((fmode & S_ISGID) != 0) { - if (outbuf[6] == 'x') - outbuf[6] = 's'; - else - outbuf[6] = 'l'; +/* array_set --- set an array element */ + +static void +array_set(awk_array_t array, const char *sub, awk_value_t *value) +{ + awk_value_t index; + + set_array_element(array, + make_const_string(sub, strlen(sub), & index), + value); + +} + +/* array_set_numeric --- set an array element with a number */ + +static void +array_set_numeric(awk_array_t array, const char *sub, double num) +{ + awk_value_t tmp; + + array_set(array, sub, make_number(num, & tmp)); +} + +/* fill_stat_array --- do the work to fill an array with stat info */ + +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; + + /* empty out the array */ + clear_array(array); + + /* fill in the array */ + 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_numeric(array, "rdev", sbuf->st_rdev); + array_set_numeric(array, "major", major(sbuf->st_rdev)); + array_set_numeric(array, "minor", minor(sbuf->st_rdev)); } - if ((fmode & S_ISVTX) != 0) { - if (outbuf[9] == 'x') - outbuf[9] = 't'; +#ifdef HAVE_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_const_string(pmode, strlen(pmode), & tmp)); + + /* for symbolic links, add a linkval field */ + if (S_ISLNK(sbuf->st_mode)) { + char *buf; + ssize_t linksize; + + if ((buf = read_symlink(name, sbuf->st_size, + & linksize)) != NULL) + array_set(array, "linkval", make_malloced_string(buf, linksize, & tmp)); else - outbuf[9] = 'T'; + warning(ext_id, _("stat: unable to read symbolic link `%s'"), name); } - return outbuf; + /* add a type field */ + 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 NODE * -do_stat(int nargs) +static awk_value_t * +do_stat(int nargs, awk_value_t *result) { - NODE *file, *array, *tmp; - struct stat sbuf; + awk_value_t file_param, array_param; + char *name; + awk_array_t array; int ret; - NODE **aptr; - char *pmode; /* printable mode */ - char *type = "unknown"; + struct stat sbuf; + + assert(result != NULL); - if (do_lint && get_curfunc_arg_count() > 2) - lintwarn("stat: called with too many arguments"); + 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 */ - file = get_scalar_argument(0, FALSE); - array = get_array_argument(1, FALSE); + 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); + } - /* empty out the array */ - assoc_clear(array); + name = file_param.str_value.str; + array = array_param.array_cookie; + + /* always empty out the array */ + clear_array(array); /* lstat the file, if error, set ERRNO and return */ - (void) force_string(file); - ret = lstat(file->stptr, & sbuf); + ret = lstat(name, & sbuf); if (ret < 0) { - update_ERRNO(); - return make_number((AWKNUM) ret); + update_ERRNO_int(errno); + return make_number(ret, result); } - /* fill in the array */ - aptr = assoc_lookup(array, tmp = make_string("name", 4), FALSE); - *aptr = dupnode(file); - unref(tmp); + ret = fill_stat_array(name, array, & sbuf); - aptr = assoc_lookup(array, tmp = make_string("dev", 3), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_dev); - unref(tmp); + return make_number(ret, result); +} - aptr = assoc_lookup(array, tmp = make_string("ino", 3), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_ino); - unref(tmp); +/* init_filefuncs --- initialization routine */ - aptr = assoc_lookup(array, tmp = make_string("mode", 4), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_mode); - unref(tmp); +static awk_bool_t +init_filefuncs(void) +{ + int errors = 0; + int i; + awk_value_t value; + + /* at least right now, only FTS needs initializing */ + 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++; + } + } + return errors == 0; +} - aptr = assoc_lookup(array, tmp = make_string("nlink", 5), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_nlink); - unref(tmp); +static int fts_errors = 0; - aptr = assoc_lookup(array, tmp = make_string("uid", 3), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_uid); - unref(tmp); +/* fill_stat_element --- fill in stat element of array */ - aptr = assoc_lookup(array, tmp = make_string("gid", 3), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_gid); - unref(tmp); +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++; + } +} - aptr = assoc_lookup(array, tmp = make_string("size", 4), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_size); - unref(tmp); +/* fill_path_element --- fill in path element of array */ - aptr = assoc_lookup(array, tmp = make_string("blocks", 6), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_blocks); - unref(tmp); +static void +fill_path_element(awk_array_t element_array, const char *path) +{ + awk_value_t index, value; - aptr = assoc_lookup(array, tmp = make_string("atime", 5), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_atime); - unref(tmp); + (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++; + } +} - aptr = assoc_lookup(array, tmp = make_string("mtime", 5), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_mtime); - unref(tmp); +/* fill_error_element --- fill in error element of array */ - aptr = assoc_lookup(array, tmp = make_string("ctime", 5), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_ctime); - unref(tmp); +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++; + } +} - /* for block and character devices, add rdev, major and minor numbers */ - if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) { - aptr = assoc_lookup(array, tmp = make_string("rdev", 4), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_rdev); - unref(tmp); - - aptr = assoc_lookup(array, tmp = make_string("major", 5), FALSE); - *aptr = make_number((AWKNUM) major(sbuf.st_rdev)); - unref(tmp); - - aptr = assoc_lookup(array, tmp = make_string("minor", 5), FALSE); - *aptr = make_number((AWKNUM) minor(sbuf.st_rdev)); - unref(tmp); +/* 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); } -#ifdef HAVE_ST_BLKSIZE - aptr = assoc_lookup(array, tmp = make_string("blksize", 7), FALSE); - *aptr = make_number((AWKNUM) sbuf.st_blksize); - unref(tmp); -#endif /* HAVE_ST_BLKSIZE */ + /* error info */ + if (bad_ret || fentry->fts_errno != 0) { + fill_error_element(element_array, fentry->fts_errno); + } +} - aptr = assoc_lookup(array, tmp = make_string("pmode", 5), FALSE); - pmode = format_mode(sbuf.st_mode); - *aptr = make_string(pmode, strlen(pmode)); - unref(tmp); +/* process --- process the heirarchy */ - /* for symbolic links, add a linkval field */ - if (S_ISLNK(sbuf.st_mode)) { - char *buf; - int linksize; +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; - emalloc(buf, char *, sbuf.st_size + 2, "do_stat"); - if (((linksize = readlink(file->stptr, buf, - sbuf.st_size + 2)) >= 0) && - (linksize <= sbuf.st_size)) { /* - * set the linkval field only if we are able to - * retrieve the entire link value successfully. + * File case. + * destarray is the directory we're reading. + * step 1: create new empty array */ - buf[linksize] = '\0'; - - aptr = assoc_lookup(array, tmp = make_string("linkval", 7), FALSE); - *aptr = make_str_node(buf, linksize, ALREADY_MALLOCED); - unref(tmp); + 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(); + } + + break; + + case FTS_DEFAULT: + /* nothing to do */ + break; } } +} - /* 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; -#endif +/* do_fts --- walk a heirarchy and fill in an array */ + +/* + * Usage from awk: + * flags = or(FTS_PHYSICAL, ...) + * result = fts(pathlist, flags, filedata) + */ + +static awk_value_t * +do_fts(int nargs, awk_value_t *result) +{ + 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; } - aptr = assoc_lookup(array, tmp = make_string("type", 4), FALSE); - *aptr = make_string(type, strlen(type)); - unref(tmp); + if (! get_argument(1, AWK_NUMBER, & flagval)) { + warning(ext_id, _("fts: bad second parameter")); + update_ERRNO_int(EINVAL); + goto out; + } - return make_number((AWKNUM) ret); -} + if (! get_argument(2, AWK_ARRAY, & dest)) { + warning(ext_id, _("fts: bad third parameter")); + update_ERRNO_int(EINVAL); + goto out; + } -/* dlload --- load new builtins in this library */ + /* flatten pathlist */ + if (! flatten_array(pathlist.array_cookie, & path_array)) { + warning(ext_id, _("fts: could not flatten array\n")); + goto out; + } -NODE * -dlload(NODE *tree, void *dl) -{ - make_builtin("chdir", do_chdir, 1); - make_builtin("stat", do_stat, 2); + /* 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); } + +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, "") |