Autoconf, Automake, and Libtool: sic_builtin.c

| ### 9.3.3 ``sic_builtin.c'` In addition to the syntax handlers I have just added to the Sic shell, the language of this shell is also defined by the builtin commands it provides. The infrastructure for this file is built from a table of functions which is fed into various C preprocessor macros, just as I did for the syntax handlers. One builtin handler function has special status, `builtin_unknown`. This is the builtin that is called, if the Sic library cannot find a suitable builtin function to handle the current input command. At first this doesn't sound especially important -- but it is the key to any shell implementation. When there is no builtin handler for the command, the shell will search the users command path, ``$PATH'`, to find a suitable executable. And this is the job of `builtin_unknown`:
    int
    builtin_unknown (Sic *sic, int argc, char *const argv[])
    {
      char *path = path_find (argv[0]);
      int status = SIC_ERROR;

      if (!path)
        {
          sic_result_append (sic, "command \"");
          sic_result_append (sic, argv[0]);
          sic_result_append (sic, "\" not found");
        }
      else if (path_execute (sic, path, argv) != SIC_OKAY)
        {
          sic_result_append (sic, "command \"");
          sic_result_append (sic, argv[0]);
          sic_result_append (sic, "\" failed: ");
          sic_result_append (sic, strerror (errno));
        }
      else
        status = SIC_OKAY;

      return status;
    }

    static char *
    path_find (const char *command)
    {
      char *path = xstrdup (command);

      if (*command == '/')
        {
          if (access (command, X_OK) < 0)
            goto notfound;
        }
      else
        {
          char *PATH = getenv ("PATH");
          char *pbeg, *pend;
          size_t len;

          for (pbeg = PATH; *pbeg != '\0'; pbeg = pend)
            {
              pbeg += strspn (pbeg, ":");
              len = strcspn (pbeg, ":");
              pend = pbeg + len;
              path = XREALLOC (char, path, 2 + len + strlen(command));
              *path = '\0';
              strncat (path, pbeg, len);
              if (path[len -1] != '/') strcat (path, "/");
              strcat (path, command);

              if (access (path, X_OK) == 0)
                  break;
            }

          if (*pbeg == '\0')
              goto notfound;
        }

      return path;

     notfound:
      XFREE (path);
      return NULL;
    }
Running ``autoscan'` again at this point adds `AC_CHECK_FUNCS(strcspn strspn)` to ``configure.scan'`. This tells me that these functions are not truly portable. As before I provide fallback implementations for these functions in case they are missing from the target host -- and as it turns out, they are easy to write:
    /* strcspn.c -- implement strcspn() for architectures without it */

    #if HAVE_CONFIG_H
    #  include <config.h>
    #endif

    #include <sys/types.h>

    #if STDC_HEADERS
    #  include <string.h>
    #elif HAVE_STRINGS_H
    #  include <strings.h>
    #endif

    #if !HAVE_STRCHR
    #  ifndef strchr
    #    define strchr index
    #  endif
    #endif

    size_t
    strcspn (const char *string, const char *reject)
    {
      size_t count = 0;
      while (strchr (reject, *string) == 0)
        ++count, ++string;

      return count;
    }
There is no need to add any code to ``Makefile.am'`, because the `configure` script will automatically add the names of the missing function sources to ``@LIBOBJS@'`. This implementation uses the `autoconf` generated ``config.h'` to get information about the availability of headers and type definitions. It is interesting that `autoscan` reports that `strchr` and `strrchr`, which are used in the fallback implementations of `strcspn` and `strspn` respectively, are themselves not portable! Luckily, the Autoconf manual tells me exactly how to deal with this: by adding some code to my ``common.h'` (paraphrased from the literal code in the manual):
    #if !STDC_HEADERS
    #  if !HAVE_STRCHR
    #    define strchr index
    #    define strrchr rindex
    #  endif
    #endif
And another macro in ``configure.in'`:
    AC_CHECK_FUNCS(strchr strrchr)

This document was generated by Gary V. Vaughan on February, 8 2006 using texi2html