Next: , Previous: Internal File Description, Up: Sample Library



C.3.2.2 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;
     
         if (do_lint && get_curfunc_arg_count() != 1)
             lintwarn("chdir: called with incorrect number of arguments");
     
         newdir = get_scalar_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:

         (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;
         NODE **aptr;
         char *pmode;    /* printable mode */
         char *type = "unknown";
     
     
         if (do_lint && get_curfunc_arg_count() > 2)
             lintwarn("stat: called with too many arguments");

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_scalar_argument(tree, 0, FALSE);
         array = get_array_argument(tree, 1, FALSE);
     
         /* 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.