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