diff options
Diffstat (limited to 'extension/filefuncs2.c')
-rw-r--r-- | extension/filefuncs2.c | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/extension/filefuncs2.c b/extension/filefuncs2.c new file mode 100644 index 00000000..79fae616 --- /dev/null +++ b/extension/filefuncs2.c @@ -0,0 +1,383 @@ +/* + * filefuncs.c - Builtin functions that provide initial minimal iterface + * to the file system. + * + * 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 + */ + +/* + * Copyright (C) 2001, 2004, 2005, 2010, 2011 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 + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include "gawkapi.h" + +static gawk_api_t *api; /* for convenience macros to work */ +static awk_ext_id_t *ext_id; + +int plugin_is_GPL_compatible; + +/* do_chdir --- provide dynamically loaded chdir() builtin for gawk */ + +int +do_chdir(int nargs) +{ + const awk_value_t *newdir; + awk_value_t result; + int ret = -1; + + if (do_lint && nargs != 1) + lintwarn(ext_id, "chdir: called with incorrect number of arguments"); + + newdir = get_curfunc_param(ext_id, 0, AWK_PARAM_STRING); + ret = chdir(newdir->str_value.str); + if (ret < 0) + update_ERRNO_int(ext_id, errno); + + result.val_type = AWK_NUMBER; + result.num_value = ret; + set_return_value(ext_id, & result); + return 1; +} + +/* format_mode --- turn a stat mode field into something readable */ + +static char * +format_mode(unsigned long fmode) +{ + static char outbuf[12]; + static struct mode_map { + 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' }, + }; + int i, j, k; + + strcpy(outbuf, "----------"); + /* first, get the file type */ + i = 0; + switch (fmode & S_IFMT) { +#ifdef S_IFSOCK + case S_IFSOCK: + outbuf[i] = 's'; + break; +#endif +#ifdef S_IFLNK + case S_IFLNK: + outbuf[i] = 'l'; + break; +#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; +#endif /* S_IFDOOR */ + case S_IFCHR: + outbuf[i] = 'c'; + break; +#ifdef S_IFIFO + case S_IFIFO: + outbuf[i] = 'p'; + break; +#endif + } + + for (j = 0, k = sizeof(map)/sizeof(map[0]); j < k; j++) { + i++; + if ((fmode & map[j].mask) != 0) + outbuf[i] = map[j].rep; + } + + i++; + outbuf[i] = '\0'; + + /* setuid bit */ + if ((fmode & S_ISUID) != 0) { + if (outbuf[3] == 'x') + outbuf[3] = 's'; + else + outbuf[3] = 'S'; + } + + /* setgid without execute == locking */ + if ((fmode & S_ISGID) != 0) { + if (outbuf[6] == 'x') + outbuf[6] = 's'; + else + outbuf[6] = 'l'; + } + + /* the so-called "sticky" bit */ + if ((fmode & S_ISVTX) != 0) { + if (outbuf[9] == 'x') + outbuf[9] = 't'; + else + outbuf[9] = 'T'; + } + + 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 + +#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 + return NULL; + } + return NULL; +} + +/* array_set --- set an array element */ + +static void +array_set(awk_array_t array, const char *sub, awk_value_t *value) +{ + awk_element_t element; + + memset(& element, 0, sizeof(element)); + + emalloc(element.index.str, char *, strlen(sub), "array_set"); + + strcpy(element.index.str, sub); + element.index.len = strlen(sub); + element.value = *value; + + set_array_element(ext_id, array, & element); +} + +/* do_stat --- provide a stat() function for gawk */ + +static int +do_stat(int nargs) +{ + awk_value_t *file_param, *array_param; + char *name; + awk_array_t array; + struct stat sbuf; + int ret; + char *pmode; /* printable mode */ + char *type = "unknown"; + + if (do_lint && nargs != 2) { + lintwarn(ext_id, "stat: called with wrong number of arguments"); + ret = 0; + goto out; + } + + /* file is first arg, array to hold results is second */ + file_param = get_curfunc_param(ext_id, 0, AWK_PARAM_STRING); + array_param = get_curfunc_param(ext_id, 0, AWK_PARAM_ARRAY); + + if (file_param == NULL || array_param == NULL) { + warning(ext_id, "stat: bad paramaters"); + ret = 0; + goto out; + } + + name = file_param->str_value.str; + array = array_param->array_cookie; + + /* empty out the array */ + clear_array(ext_id, array); + + /* lstat the file, if error, set ERRNO and return */ + ret = lstat(name, & sbuf); + if (ret < 0) { + update_ERRNO_int(ext_id, errno); + ret = 0; + goto out; + } + + /* fill in the array */ + array_set(array, "name", make_string(ext_id, name, file_param->str_value.len)); + array_set(array, "dev", make_number(ext_id, (double) sbuf.st_dev)); + array_set(array, "ino", make_number(ext_id, (double) sbuf.st_ino)); + array_set(array, "mode", make_number(ext_id, (double) sbuf.st_mode)); + array_set(array, "nlink", make_number(ext_id, (double) sbuf.st_nlink)); + array_set(array, "uid", make_number(ext_id, (double) sbuf.st_uid)); + array_set(array, "gid", make_number(ext_id, (double) sbuf.st_gid)); + array_set(array, "size", make_number(ext_id, (double) sbuf.st_size)); + array_set(array, "blocks", make_number(ext_id, (double) sbuf.st_blocks)); + array_set(array, "atime", make_number(ext_id, (double) sbuf.st_atime)); + array_set(array, "mtime", make_number(ext_id, (double) sbuf.st_mtime)); + array_set(array, "ctime", make_number(ext_id, (double) 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(ext_id, (double) sbuf.st_rdev)); + array_set(array, "major", make_number(ext_id, (double) major(sbuf.st_rdev))); + array_set(array, "minor", make_number(ext_id, (double) minor(sbuf.st_rdev))); + } + +#ifdef HAVE_ST_BLKSIZE + array_set(array, "blksize", make_number(ext_id, (double) sbuf.st_blksize)); +#endif /* HAVE_ST_BLKSIZE */ + + pmode = format_mode(sbuf.st_mode); + array_set(array, "pmode", make_string(ext_id, pmode, strlen(pmode))); + + /* 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_string(ext_id, buf, linksize)); + else + warning(ext_id, "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; +#endif + } + + array_set(array, "type", make_string(ext_id, type, strlen(type))); + + ret = 1; /* success */ + +out: + set_return_value(ext_id, make_number(ext_id, (double) ret)); +} + + +/* dl_load --- load new builtins in this library */ + +int dl_load(gawk_api_t *api_p, awk_ext_id_t id) +{ + static awk_ext_func_t func_table[] = { + { "chdir", do_chdir, 1 }, + { "stat", do_stat, 2 }, + }; + size_t i, j; + int errors = 0; + + api = api_p; + ext_id = id; + + /* load functions */ + for (i = 0, j = sizeof(func_table) / sizeof(func_table[0]); i < j; i++) { + if (! add_ext_func(ext_id, & func_table[i])) { + warning(ext_id, "filefuncs: could not add %s\n", func_table[i].name); + errors++; + } + } + + return (errors == 0); +} |