|
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)
|
|