C Code for `chdir' and `stat'
.............................
Here is the C code for these extensions. They were written for
GNU/Linux. The code needs some more work for complete portability to
other POSIX-compliant systems:(1)
#include "awk.h"
#include <sys/sysmacros.h>
/* do_chdir --- provide dynamically loaded
chdir() builtin for gawk */
static NODE *
do_chdir(tree)
NODE *tree;
{
NODE *newdir;
int ret = -1;
newdir = get_argument(tree, 0);
The file includes the `"awk.h"' header file for definitions for the
`gawk' internals. It includes `<sys/sysmacros.h>' for access to the
`major' and `minor' macros.
By convention, for an `awk' function `foo', the function that
implements it is called `do_foo'. The function should take a `NODE *'
argument, usually called `tree', that represents the argument list to
the function. The `newdir' variable represents the new directory to
change to, retrieved with `get_argument'. Note that the first argument
is numbered zero.
This code actually accomplishes the `chdir'. It first forces the
argument to be a string and passes the string value to the `chdir'
system call. If the `chdir' fails, `ERRNO' is updated. The result of
`force_string' has to be freed with `free_temp':
if (newdir != NULL) {
(void) force_string(newdir);
ret = chdir(newdir->stptr);
if (ret < 0)
update_ERRNO();
free_temp(newdir);
}
Finally, the function returns the return value to the `awk' level,
using `set_value'. Then it must return a value from the call to the new
built-in (this value ignored by the interpreter):
/* Set the return value */
set_value(tmp_number((AWKNUM) ret));
/* Just to make the interpreter happy */
return tmp_number((AWKNUM) 0);
}
The `stat' built-in is more involved. First comes a function that
turns a numeric mode into a printable representation (e.g., 644 becomes
`-rw-r--r--'). This is omitted here for brevity:
/* format_mode --- turn a stat mode field
into something readable */
static char *
format_mode(fmode)
unsigned long fmode;
{
...
}
Next comes the actual `do_stat' function itself. First come the
variable declarations and argument checking:
/* do_stat --- provide a stat() function for gawk */
static NODE *
do_stat(tree)
NODE *tree;
{
NODE *file, *array;
struct stat sbuf;
int ret;
char *msg;
NODE **aptr;
char *pmode; /* printable mode */
char *type = "unknown";
/* check arg count */
if (tree->param_cnt != 2)
fatal(
"stat: called with %d arguments, should be 2",
tree->param_cnt);
Then comes the actual work. First, we get the arguments. Then, we
always clear the array. To get the file information, we use `lstat',
in case the file is a symbolic link. If there's an error, we set
`ERRNO' and return:
/*
* directory is first arg,
* array to hold results is second
*/
file = get_argument(tree, 0);
array = get_argument(tree, 1);
/* empty out the array */
assoc_clear(array);
/* lstat the file, if error, set ERRNO and return */
(void) force_string(file);
ret = lstat(file->stptr, & sbuf);
if (ret < 0) {
update_ERRNO();
set_value(tmp_number((AWKNUM) ret));
free_temp(file);
return tmp_number((AWKNUM) 0);
}
Now comes the tedious part: filling in the array. Only a few of the
calls are shown here, since they all follow the same pattern:
/* fill in the array */
aptr = assoc_lookup(array, tmp_string("name", 4), FALSE);
*aptr = dupnode(file);
aptr = assoc_lookup(array, tmp_string("mode", 4), FALSE);
*aptr = make_number((AWKNUM) sbuf.st_mode);
aptr = assoc_lookup(array, tmp_string("pmode", 5), FALSE);
pmode = format_mode(sbuf.st_mode);
*aptr = make_string(pmode, strlen(pmode));
When done, we free the temporary value containing the file name, set
the return value, and return:
free_temp(file);
/* Set the return value */
set_value(tmp_number((AWKNUM) ret));
/* Just to make the interpreter happy */
return tmp_number((AWKNUM) 0);
}
Finally, it's necessary to provide the "glue" that loads the new
function(s) into `gawk'. By convention, each library has a routine
named `dlload' that does the job:
/* dlload --- load new builtins in this library */
NODE *
dlload(tree, dl)
NODE *tree;
void *dl;
{
make_builtin("chdir", do_chdir, 1);
make_builtin("stat", do_stat, 2);
return tmp_number((AWKNUM) 0);
}
And that's it! As an exercise, consider adding functions to
implement system calls such as `chown', `chmod', and `umask'.
---------- Footnotes ----------
(1) This version is edited slightly for presentation. The complete
version can be found in `extension/filefuncs.c' in the `gawk'
distribution.