/* testgdbm.c - Driver program to test the database routines and to
help debug gdbm. Uses inside information to show "system" information */
/* This file is part of GDBM, the GNU data base manager.
Copyright (C) 1990, 1991, 1993, 2007, 2011 Free Software Foundation,
Inc.
GDBM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GDBM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GDBM. If not, see . */
/* Include system configuration before all else. */
#include "autoconf.h"
#include "gdbmdefs.h"
#include
#include
#include
#include
#ifdef HAVE_SYS_TERMIOS_H
#include
#endif
#include
extern const char *gdbm_version;
extern const char *gdbm_strerror (gdbm_error);
char *progname; /* Program name */
char *file_name = NULL; /* Database file name */
GDBM_FILE gdbm_file = NULL; /* Database to operate upon */
int interactive; /* Are we running in interactive mode? */
datum key_data; /* Current key */
datum return_data; /* Current data */
int key_z = 1; /* Keys are nul-terminated strings */
int data_z = 1; /* Data are nul-terminated strings */
/* Debug procedure to print the contents of the current hash bucket. */
void
print_bucket (hash_bucket * bucket, char *mesg)
{
int index;
printf ("******* %s **********\n\nbits = %d\ncount= %d\nHash Table:\n",
mesg, bucket->bucket_bits, bucket->count);
printf
(" # hash value key size data size data adr home\n");
for (index = 0; index < gdbm_file->header->bucket_elems; index++)
printf (" %4d %12x %11d %11d %11lu %5d\n", index,
bucket->h_table[index].hash_value,
bucket->h_table[index].key_size,
bucket->h_table[index].data_size,
(unsigned long) bucket->h_table[index].data_pointer,
bucket->h_table[index].hash_value %
gdbm_file->header->bucket_elems);
printf ("\nAvail count = %1d\n", bucket->av_count);
printf ("Avail adr size\n");
for (index = 0; index < bucket->av_count; index++)
printf ("%9lu%9d\n",
(unsigned long) bucket->bucket_avail[index].av_adr,
bucket->bucket_avail[index].av_size);
}
void
_gdbm_print_avail_list (GDBM_FILE dbf)
{
int temp;
int size;
avail_block *av_stk;
/* Print the the header avail block. */
printf ("\nheader block\nsize = %d\ncount = %d\n",
dbf->header->avail.size, dbf->header->avail.count);
for (temp = 0; temp < dbf->header->avail.count; temp++)
{
printf (" %15d %10lu \n", dbf->header->avail.av_table[temp].av_size,
(unsigned long) dbf->header->avail.av_table[temp].av_adr);
}
/* Initialize the variables for a pass throught the avail stack. */
temp = dbf->header->avail.next_block;
size = (((dbf->header->avail.size * sizeof (avail_elem)) >> 1)
+ sizeof (avail_block));
av_stk = (avail_block *) malloc (size);
if (av_stk == NULL)
{
printf ("Out of memory\n");
exit (2);
}
/* Print the stack. */
while (FALSE)
{
__lseek (dbf, temp, L_SET);
__read (dbf, av_stk, size);
/* Print the block! */
printf ("\nblock = %d\nsize = %d\ncount = %d\n", temp,
av_stk->size, av_stk->count);
for (temp = 0; temp < av_stk->count; temp++)
{
printf (" %15d %10lu \n", av_stk->av_table[temp].av_size,
(unsigned long) av_stk->av_table[temp].av_adr);
}
temp = av_stk->next_block;
}
}
void
_gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf)
{
int index;
char changed;
if (dbf->bucket_cache != NULL)
{
fprintf
(fp,
"Bucket Cache (size %d):\n Index: Address Changed Data_Hash \n",
dbf->cache_size);
for (index = 0; index < dbf->cache_size; index++)
{
changed = dbf->bucket_cache[index].ca_changed;
fprintf (fp, " %5d: %7lu %7s %x\n",
index,
(unsigned long) dbf->bucket_cache[index].ca_adr,
(changed ? "True" : "False"),
dbf->bucket_cache[index].ca_data.hash_val);
}
}
else
fprintf (fp, "Bucket cache has not been initialized.\n");
}
void
usage ()
{
printf ("Usage: %s OPTIONS\n", progname);
printf ("Test and modify a GDBM database.\n");
printf ("\n");
printf ("OPTIONS are:\n\n");
printf (" -b SIZE set block size\n");
printf (" -c SIZE set cache size\n");
printf (" -g FILE operate on FILE instead of `junk.gdbm'\n");
printf (" -h print this help summary\n");
printf (" -l disable file locking\n");
printf (" -m disable file mmap\n");
printf (" -n create database\n");
printf (" -r open database in read-only mode\n");
printf (" -s synchronize to the disk after each write\n");
printf (" -v print program version\n");
printf ("\n");
printf ("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
}
void
version ()
{
printf ("testgdbm (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
printf ("Copyright (C) 2007 Free Software Foundation, Inc.\n");
printf ("License GPLv3+: GNU GPL version 3 or later \n");
printf ("This is free software: you are free to change and redistribute it.\n");
printf ("There is NO WARRANTY, to the extent permitted by law.\n");
}
void
error (int code, char *fmt, ...)
{
va_list ap;
fprintf (stderr, "%s: ", progname);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
if (code)
exit (code);
}
int
trimnl (char *str)
{
int len = strlen (str);
if (str[len - 1] == '\n')
{
str[--len] = 0;
return 1;
}
return 0;
}
void
read_from_file (const char *name, int replace)
{
int line = 0;
char buf[1024];
datum key;
datum data;
FILE *fp;
int flag = replace ? GDBM_REPLACE : 0;
fp = fopen (name, "r");
if (!fp)
{
fprintf (stderr, "cannot open file `%s' for reading: %s\n",
name, strerror (errno));
return;
}
while (fgets (buf, sizeof buf, fp))
{
char *p;
if (!trimnl (buf))
{
fprintf (stderr, "%s:%d: line too long\n", name, line);
continue;
}
line++;
p = strchr (buf, ' ');
if (!p)
{
fprintf (stderr, "%s:%d: malformed line\n", name, line);
continue;
}
for (*p++ = 0; *p && isspace (*p); p++)
;
key.dptr = buf;
key.dsize = strlen (buf) + key_z;
data.dptr = p;
data.dsize = strlen (p) + data_z;
if (gdbm_store (gdbm_file, key, data, flag) != 0)
fprintf (stderr, "%d: item not inserted\n", line);
}
fclose (fp);
}
int
get_screen_lines ()
{
#ifdef TIOCGWINSZ
if (isatty (1))
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
{
const char *lines = getenv ("LINES");
if (lines)
ws.ws_row = strtol (lines, NULL, 10);
}
return ws.ws_row;
}
#else
const char *lines = getenv ("LINES");
if (lines)
return strtol (lines, NULL, 10);
#endif
return -1;
}
void
page_data (int count, void (*func) (FILE *, void *), void *data)
{
FILE *out = stdout;
if (interactive && (count < 0 || count > get_screen_lines ()))
{
char *pager = getenv ("PAGER");
if (pager)
{
out = popen (getenv ("PAGER"), "w");
if (!out)
{
fprintf (stderr, "cannot run pager `%s': %s\n", pager,
strerror (errno));
out = stdout;
}
}
}
func (out, data);
if (out != stdout)
pclose (out);
}
int
get_record_count ()
{
datum key, data;
int count = 0;
data = gdbm_firstkey (gdbm_file);
while (data.dptr != NULL)
{
count++;
key = data;
data = gdbm_nextkey (gdbm_file, key);
free (key.dptr);
}
return count;
}
#define ARG_UNUSED __attribute__ ((__unused__))
#define NARGS 2
void
count_handler (char *arg[NARGS] ARG_UNUSED)
{
printf ("There are %d items in the database.\n", get_record_count ());
}
void
delete_handler (char *arg[NARGS])
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (arg[0]);
key_data.dsize = strlen (arg[0]) + key_z;
if (gdbm_delete (gdbm_file, key_data) != 0)
printf ("Item not found or deleted\n");
}
void
fetch_handler (char *arg[NARGS])
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (arg[0]);
key_data.dsize = strlen (arg[0]) + key_z;
return_data = gdbm_fetch (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
printf ("data is ->%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
printf ("No such item found.\n");
}
void
nextkey_handler (char *arg[NARGS])
{
if (arg[0])
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (arg[0]);
key_data.dsize = strlen (arg[0]) + key_z;
}
return_data = gdbm_nextkey (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
key_data = return_data;
printf ("key is ->%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
printf ("data is ->%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
{
printf ("No such item found.\n");
free (key_data.dptr);
key_data.dptr = NULL;
}
}
void
store_handler (char *arg[NARGS])
{
datum key;
datum data;
key.dptr = arg[0];
key.dsize = strlen (arg[0]) + key_z;
data.dptr = arg[1];
data.dsize = strlen (arg[1]) + data_z;
if (gdbm_store (gdbm_file, key, data, GDBM_REPLACE) != 0)
printf ("Item not inserted. \n");
}
void
firstkey_handler (char *arg[NARGS])
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data = gdbm_firstkey (gdbm_file);
if (key_data.dptr != NULL)
{
printf ("key is ->%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
printf ("data is ->%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
printf ("No such item found.\n");
}
void
next_on_last_handler (char *arg[NARGS] ARG_UNUSED)
{
return_data = gdbm_nextkey (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
free (key_data.dptr);
key_data = return_data;
printf ("key is ->%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
printf ("data is ->%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
printf ("No such item found.\n");
}
void
reorganize_handler (char *arg[NARGS] ARG_UNUSED)
{
if (gdbm_reorganize (gdbm_file))
printf ("Reorganization failed.\n");
else
printf ("Reorganization succeeded.\n");
}
void
avail_handler (char *arg[NARGS] ARG_UNUSED)
{
_gdbm_print_avail_list (gdbm_file);
}
void
print_current_bucket_handler (char *arg[NARGS] ARG_UNUSED)
{
print_bucket (gdbm_file->bucket, "Current bucket");
printf ("\n current directory entry = %d.\n",
gdbm_file->bucket_dir);
printf (" current bucket address = %lu.\n",
(unsigned long) gdbm_file->cache_entry->ca_adr);
}
int
getnum (int *pnum, char *arg, char **endp)
{
char *p;
unsigned long x = strtoul (arg, &p, 10);
if (*p && !isspace (*p))
{
printf ("not a number (stopped near %s)\n", p);
return 1;
}
while (*p && isspace (*p))
p++;
if (endp)
*endp = p;
else
{
printf ("not a number (stopped near %s)\n", p);
return 1;
}
*pnum = x;
return 0;
}
void
print_bucket_handler (char *arg[NARGS])
{
int temp;
if (getnum (&temp, arg[0], NULL))
return;
if (temp >= gdbm_file->header->dir_size / 4)
{
printf ("Not a bucket. \n");
}
_gdbm_get_bucket (gdbm_file, temp);
printf ("Your bucket is now ");
print_current_bucket_handler (NULL);
}
void
_print_dir (FILE *out, void *data)
{
int i;
fprintf (out, "Hash table directory.\n");
fprintf (out, " Size = %d. Bits = %d. \n\n",
gdbm_file->header->dir_size, gdbm_file->header->dir_bits);
for (i = 0; i < gdbm_file->header->dir_size / 4; i++)
fprintf (out, " %10d: %12lu\n", i, (unsigned long) gdbm_file->dir[i]);
}
void
print_dir_handler (char *arg[NARGS] ARG_UNUSED)
{
page_data (gdbm_file->header->dir_size / 4 + 3, _print_dir, NULL);
}
void
print_header_handler (char *arg[NARGS] ARG_UNUSED)
{
printf ("\nFile Header: \n\n");
printf (" table = %lu\n", (unsigned long) gdbm_file->header->dir);
printf (" table size = %d\n", gdbm_file->header->dir_size);
printf (" table bits = %d\n", gdbm_file->header->dir_bits);
printf (" block size = %d\n", gdbm_file->header->block_size);
printf (" bucket elems = %d\n", gdbm_file->header->bucket_elems);
printf (" bucket size = %d\n", gdbm_file->header->bucket_size);
printf (" header magic = %x\n", gdbm_file->header->header_magic);
printf (" next block = %lu\n",
(unsigned long) gdbm_file->header->next_block);
printf (" avail size = %d\n", gdbm_file->header->avail.size);
printf (" avail count = %d\n", gdbm_file->header->avail.count);
printf (" avail nx blk = %lu\n",
(unsigned long) gdbm_file->header->avail.next_block);
}
void
hash_handler (char *arg[NARGS])
{
datum key;
key.dptr = arg[0];
key.dsize = strlen (arg[0]) + key_z;
printf ("hash value = %x. \n", _gdbm_hash (key));
}
static void
print_bucket_cache (FILE *out, void *data)
{
_gdbm_print_bucket_cache (out, data);
}
void
print_cache_handler (char *arg[NARGS] ARG_UNUSED)
{
page_data (gdbm_file->bucket_cache ? gdbm_file->cache_size + 1 : 1,
print_bucket_cache, gdbm_file);
}
void
print_version_handler (char *arg[NARGS] ARG_UNUSED)
{
printf ("%s\n", gdbm_version);
}
void
read_handler (char *arg[NARGS])
{
read_from_file (arg[0], arg[1] && strcmp (arg[1], "replace") == 0);
}
void
list_all (FILE *out, void *x)
{
datum key;
datum data;
key = gdbm_firstkey (gdbm_file);
while (key.dptr)
{
datum nextkey = gdbm_nextkey (gdbm_file, key);
data = gdbm_fetch (gdbm_file, key);
if (!data.dptr)
fprintf (out, "cannot fetch data (key %.*s)\n", key.dsize, key.dptr);
else
{
fprintf (out, "%.*s %.*s\n", key.dsize, key.dptr, data.dsize,
data.dptr);
free (data.dptr);
}
free (key.dptr);
key = nextkey;
}
}
void
list_handler (char *arg[NARGS] ARG_UNUSED)
{
page_data (get_record_count (), list_all, NULL);
}
void
quit_handler (char *arg[NARGS] ARG_UNUSED)
{
if (gdbm_file != NULL)
gdbm_close (gdbm_file);
exit (0);
}
void
export_handler (char *arg[NARGS])
{
int flags = GDBM_WRCREAT;
if (arg[1] != NULL && strcmp(arg[1], "truncate") == 0)
flags = GDBM_NEWDB;
if (gdbm_export (gdbm_file, arg[0], flags, 0600) == -1)
printf ("gdbm_export failed, %s\n", gdbm_strerror (gdbm_errno));
}
void
import_handler (char *arg[NARGS])
{
int flag = GDBM_INSERT;
if (arg[1] != NULL && strcmp(arg[1], "replace") == 0)
flag = GDBM_REPLACE;
if (gdbm_import (gdbm_file, arg[0], flag) == -1)
printf ("gdbm_import failed, %s\n", gdbm_strerror (gdbm_errno));
}
void
status_handler (char *arg[NARGS] ARG_UNUSED)
{
printf ("Database file: %s\n", file_name);
printf ("Zero terminated keys: %s\n", key_z ? "yes" : "no");
printf ("Zero terminated data: %s\n", data_z ? "yes" : "no");
}
void
key_z_handler (char *arg[NARGS] ARG_UNUSED)
{
key_z = !key_z;
printf ("Zero terminated keys: %s\n", key_z ? "yes" : "no");
}
void
data_z_handler (char *arg[NARGS] ARG_UNUSED)
{
data_z = !data_z;
printf ("Zero terminated data: %s\n", data_z ? "yes" : "no");
}
void help_handler (char *arg[NARGS]);
struct command
{
int abbrev;
void (*handler) (char *[NARGS]);
char *args[NARGS];
char *doc;
};
struct command command_tab[] = {
{ 'c', count_handler, { NULL, NULL, }, "count (number of entries)" },
{ 'd', delete_handler, { "key", NULL, }, "delete" },
{ 'e', export_handler, { "file", "[truncate]", }, "export" },
{ 'f', fetch_handler, { "key", NULL }, "fetch" },
{ 'i', import_handler, { "file", "[replace]", }, "import" },
{ 'l', list_handler, { NULL, NULL }, "list" },
{ 'n', nextkey_handler, { "[key]", NULL }, "nextkey" },
{ 'q', quit_handler, { NULL, NULL }, "quit" },
{ 's', store_handler, { "key", "data" }, "store" },
{ '1', firstkey_handler, { NULL, NULL }, "firstkey" },
{ '2', next_on_last_handler, { NULL, NULL, },
"nextkey on last key (from n, 1 or 2)" },
{ '<', read_handler, { "file", "[replace]" },
"read entries from file and store" },
{ 'r', reorganize_handler, { NULL, NULL, }, "reorganize" },
{ 'z', key_z_handler, { NULL, NULL }, "toggle key nul-termination" },
{ 'A', avail_handler, { NULL, NULL, }, "print avail list" },
{ 'B', print_bucket_handler, { "bucket-number", NULL, }, "print a bucket" },
{ 'C', print_current_bucket_handler, { NULL, NULL, },
"print current bucket" },
{ 'D', print_dir_handler, { NULL, NULL, }, "print hash directory" },
{ 'F', print_header_handler, { NULL, NULL, }, "print file header" },
{ 'H', hash_handler, { "key", NULL, }, "hash value of key" },
{ 'K', print_cache_handler, { NULL, NULL, }, "print the bucket cache" },
{ 'S', status_handler, { NULL, NULL }, "print current program status" },
{ 'V', print_version_handler, { NULL, NULL, }, "print version of gdbm" },
{ 'Z', data_z_handler, { NULL, NULL }, "toggle data nul-termination" },
{ '?', help_handler, { NULL, NULL, }, "print this help list" },
{ 'q', quit_handler, { NULL, NULL, }, "quit the program" },
{ 0 }
};
#define CMDCOLS 30
void
format_help_out (FILE *out, void *data)
{
struct command *cmd;
for (cmd = command_tab; cmd->abbrev; cmd++)
{
int i;
int n = fprintf (out, " %c", cmd->abbrev);
for (i = 0; i < NARGS && cmd->args[i]; i++)
n += fprintf (out, " %s", cmd->args[i]);
if (n < CMDCOLS)
fprintf (out, "%*.s", CMDCOLS-n, "");
fprintf (out, " %s", cmd->doc);
fputc ('\n', out);
}
}
void
help_handler (char *arg[NARGS])
{
page_data (sizeof (command_tab) / sizeof (command_tab[0]) + 1,
format_help_out, NULL);
}
struct command *
find_command (char *p)
{
struct command *cmd;
if (p[1])
{
printf ("Multicharacter commands are not yet implemented.\n");
return NULL;
}
for (cmd = command_tab; cmd->abbrev; cmd++)
if (cmd->abbrev == *p)
return cmd;
return NULL;
}
#define SKIPWS(p) while (*(p) && isspace (*(p))) (p)++
#define SKIPWORD(p) while (*(p) && !isspace (*(p))) (p)++
char *
getword (char *s, char **endp)
{
char *p;
SKIPWS (s);
p = s;
SKIPWORD (s);
if (*s)
{
*s++ = 0;
SKIPWS (s);
}
*endp = s;
return p;
}
/* The test program allows one to call all the routines plus the hash function.
The commands are single letter commands. The user is prompted for all other
information. See the help command (?) for a list of all commands. */
int
main (int argc, char *argv[])
{
char cmdbuf[1000];
int cache_size = DEFAULT_CACHESIZE;
int block_size = 0;
int opt;
char reader = FALSE;
char newdb = FALSE;
int flags = 0;
progname = strrchr (argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
/* Argument checking. */
if (argc == 2)
{
if (strcmp (argv[1], "--help") == 0)
{
usage ();
exit (0);
}
else if (strcmp (argv[1], "--version") == 0)
{
version ();
exit (0);
}
}
opterr = 0;
while ((opt = getopt (argc, argv, "lmsrnc:b:g:hv")) != -1)
switch (opt)
{
case 'h':
usage ();
exit (0);
case 'l':
flags = flags | GDBM_NOLOCK;
break;
case 'm':
flags = flags | GDBM_NOMMAP;
break;
case 's':
if (reader)
error (2, "-s is incompatible with -r");
flags = flags | GDBM_SYNC;
break;
case 'r':
if (newdb)
error (2, "-r is incompatible with -n");
reader = TRUE;
break;
case 'n':
if (reader)
error (2, "-n is incompatible with -r");
newdb = TRUE;
break;
case 'c':
cache_size = atoi (optarg);
break;
case 'b':
block_size = atoi (optarg);
break;
case 'g':
file_name = optarg;
break;
case 'v':
version ();
exit (0);
default:
error (2, "unknown option; try `%s -h' for more info\n", progname);
}
if (file_name == NULL)
file_name = "junk.gdbm";
/* Initialize variables. */
interactive = isatty (0);
if (reader)
{
gdbm_file = gdbm_open (file_name, block_size, GDBM_READER, 00664, NULL);
}
else if (newdb)
{
gdbm_file =
gdbm_open (file_name, block_size, GDBM_NEWDB | flags, 00664, NULL);
}
else
{
gdbm_file =
gdbm_open (file_name, block_size, GDBM_WRCREAT | flags, 00664, NULL);
}
if (gdbm_file == NULL)
error (2, "gdbm_open failed: %s", gdbm_strerror (gdbm_errno));
if (gdbm_setopt (gdbm_file, GDBM_CACHESIZE, &cache_size, sizeof (int)) ==
-1)
error (2, "gdbm_setopt failed: %s", gdbm_strerror (gdbm_errno));
signal (SIGPIPE, SIG_IGN);
/* Welcome message. */
if (interactive)
printf ("\nWelcome to the gdbm test program. Type ? for help.\n\n");
while (1)
{
int i;
char *p, *sp;
char argbuf[NARGS][128];
char *args[NARGS];
struct command *cmd;
if (interactive)
printf ("com -> ");
if (fgets (cmdbuf, sizeof cmdbuf, stdin) == NULL)
{
putchar ('\n');
break;
}
trimnl (cmdbuf);
p = getword (cmdbuf, &sp);
if (!*p)
continue;
cmd = find_command (p);
if (!cmd)
{
printf ("Invalid command. Try ? for help.\n");
continue;
}
memset (args, 0, sizeof (args));
for (i = 0; i < NARGS && cmd->args[i]; i++)
{
p = i < NARGS-1 ? getword (sp, &sp) : sp;
if (!*p)
{
char *prompt = cmd->args[i];
if (*prompt == '[')
/* Optional argument */
break;
if (!interactive)
{
printf ("%c: not enough arguments\n", cmd->abbrev);
exit (1);
}
printf ("%s -> ", prompt);
if (fgets (argbuf[i], sizeof argbuf[i], stdin) == NULL)
{
printf ("unexpected eof\n");
exit (0);
}
trimnl (argbuf[i]);
args[i] = argbuf[i];
}
else
args[i] = p;
}
cmd->handler (args);
}
/* Quit normally. */
quit_handler (NULL);
return 0;
}