[alt.sources] DBM Toolkit Redux

jfh@rpp386.cactus.org (John F. Haugh II) (01/14/90)

[ The preceding version of this thing was cancelled after
  someone noticed it didn't compile.  This is what I get
  for adding "one last feature" at the last moment and then
  failing to recompile ... ]

This is the beta-test release of my DBM command line toolkit.

There are no man pages [ what do you expect, this is a beta
release ].  You are expected to read the source code for
answers ;-)

There are several command line interface programs in here.
They are:

dbmstore    - store a record under a key in a database
dbmfetch    - fetch a record with a key from a database
dbmedit     - fetch a record, call an editor, and write the
	      record back.
dbmdelete   - remove a record using a key from a database
dbmfirstkey - return the first key in the database, a logical
	      rewind.
dbmnextkey  - return the key following the supplied key.
dbmkeys     - list all of the keys in the database.

The general syntax is

	<command name> <database name> <database key>

All of the commands, except dbmfirstkey, expect a key to be
provided on the command line.  Data is either output on
standard output, or input from standard input.  There are
not options to read or write to or from files.

Both data and keys may contain octal-escapes for non-printing
characters.  The DBM editor will edit dbm files which contain
binary data.

And here is the source code ...
---- cut here ----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Makefile
#	dbmdelete.c
#	dbmstore.c
#	dbmfetch.c
#	dbmedit.sh
#	dbmkeys.c
#	dbmfrstkey.c
#	dbmnextkey.c
# This archive created: Sat Jan 13 11:45:47 1990
# By:	John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# @(#)Makefile	1.2	01:23:12	1/12/90
#
# Makefile for dbmtools
#
# Copyright John F. Haugh II
# All rights reserved.
#
# This software may be copied for non-commercial uses.
# Use at your own risk.
#
#CFLAGS = -g
#LDFLAGS = -g
CFLAGS = -O
LDFLAGS = -s
LIBS = -ldbm

# The default Make rules can't get a .sh file, and neglect to make
# the output read-only in the .c case
.sh~.sh:
	$(GET) $(GFLAGS) -p $< > $*.sh
	chmod a-w $*.sh

.c~.c:
	$(GET) $(GFLAGS) -p $< > $*.c
	chmod a-w $*.c

FILES = Makefile dbmdelete.c dbmstore.c dbmfetch.c dbmedit.sh \
	dbmkeys.c dbmfrstkey.c dbmnextkey.c

EXECS = dbmdelete dbmstore dbmfetch dbmedit dbmkeys dbmfirstkey dbmnextkey

all: $(EXECS)

dbmdelete: dbmdelete.o
	cc -o dbmdelete $(LDFLAGS) dbmdelete.o $(LIBS)

dbmstore: dbmstore.o
	cc -o dbmstore $(LDFLAGS) dbmstore.o $(LIBS)

dbmfetch: dbmfetch.o
	cc -o dbmfetch $(LDFLAGS) dbmfetch.o $(LIBS)

dbmedit: dbmedit.sh
	cp dbmedit.sh dbmedit
	chmod +rx dbmedit

dbmkeys: dbmkeys.o
	cc -o dbmkeys $(LDFLAGS) dbmkeys.o $(LIBS)

dbmfirstkey: dbmfrstkey.o
	cc -o dbmfirstkey $(LDFLAGS) dbmfrstkey.o $(LIBS)

dbmnextkey: dbmnextkey.o
	cc -o dbmnextkey $(LDFLAGS) dbmnextkey.o $(LIBS)

shar:	dbmtools.sh

dbmtools.sh: $(FILES)
	shar $(FILES) > dbmtools.sh

clean:
	rm -f *.o a.out core

clobber: clean
	rm -f $(EXECS)

SHAR_EOF
fi
if test -f 'dbmdelete.c'
then
	echo shar: "will not over-write existing file 'dbmdelete.c'"
else
cat << \SHAR_EOF > 'dbmdelete.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmdelete.c	1.1 09:36:19 1/8/90
 *
 * Name: dbmdelete
 *
 * Purpose: delete a record from a DBM file
 *
 * Description:
 *	dbmdelete is a command line interface to the DBM library
 *	and provides the ability to remove individual records.
 *
 * Return Values:
 *	0 if everything is OK, 1 for everything else
 */

#include <dbm.h>
#include <stdio.h>

main (argc, argv)
int	argc;
char	**argv;
{
	datum	key;

	/*
	 * Check for proper number of arguments.  The first argument is
	 * the name of the database with no .dir or .pag.  The second
	 * argument is the literal key to search for, sorry, no binary
	 * keys just yet.
	 */

	if (argc != 3) {
		fprintf (stderr, "usage: dbmdelete database key\n");
		exit (1);
	}

	/*
	 * Open the named database.  It must already exist.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * All keys right now are character strings.  Point at the
	 * argument and provide its length.
	 */

	key.dptr = argv[2];
	key.dsize = strlen (argv[2]);

	/*
	 * Call the low-level DBM library routine to kill off
	 * the record.  Report an error if one occurs.
	 */

	if (delete (key)) {
		fprintf (stderr, "%s: can't delete\n", argv[2]);
		exit (1);
	}
	exit (0);
}
SHAR_EOF
fi
if test -f 'dbmstore.c'
then
	echo shar: "will not over-write existing file 'dbmstore.c'"
else
cat << \SHAR_EOF > 'dbmstore.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmstore.c	1.3 11:45:23 1/13/90
 *
 * Name: dbmstore
 *
 * Purpose: store a new record, or update an existing record
 *
 * Description:
 *	dbmstore reads a record full of data and stores it in the
 *	database under the supplied key.  The record can be either
 *	a completely new record, or a pre-existing record.
 *
 * Return Values:
 *	0 for success, 1 for all errors.
 */

#include <dbm.h>
#include <stdio.h>

main (argc, argv)
int	argc;
char	**argv;
{
	int	c;
	int	len;
	int	i, j;
	datum	value;
	datum	key;
	char	keybuf[BUFSIZ];
	char	buf[BUFSIZ];

	/*
	 * Check the argument.  The first argument is the name of the
	 * database, the second is the key to store the record under.
	 * The key may have octal values for characters.
	 */

	if (argc != 3) {
		fprintf (stderr, "usage: dbmstore database key\n");
		exit (1);
	}

	/*
	 * Open the database, reporting any errors that are found.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * Unquote the key.  The quoted text is in argv[2] and is
	 * copied one character at a time into keybuf[].  A quoted
	 * character begins with '\' and has three octal digits
	 * following it.
	 */

	for (i = j = 0;i < strlen (argv[2]);i++) {
		if (argv[2][i] == '\\') {
			c = (((argv[2][i + 1] - '0') << 3) +
			     ((argv[2][i + 2] - '0')) << 3) +
			     (argv[2][i + 3] - '0');
			i += 3;
		} else {
			c = argv[2][i];
		}
		keybuf[j++] = c;
	}
	key.dptr = keybuf;
	key.dsize = j;

	/*
	 * Unquotify the input stream.  Read from stdin until
	 * EOF is reached, converting any quoted characters as
	 * we go ...
	 */

	for (len = 0;len < BUFSIZ && (c = getchar ()) != EOF;len++) {
		if (c == '\\') {
			c = ((getchar () - '0') << 6);
			c += ((getchar () - '0') << 3);
			c += (getchar () - '0');
		}
		buf[len] = c;
	}
	value.dptr = buf;
	value.dsize = len;

	/*
	 * Store the new record under the key.  If there is an error
	 * report it using the quotified key value to avoid confusion.
	 */

	if (store (key, value)) {
		fprintf (stderr, "%s: can't store\n", argv[2]);
		exit (1);
	}
	exit (0);
}
SHAR_EOF
fi
if test -f 'dbmfetch.c'
then
	echo shar: "will not over-write existing file 'dbmfetch.c'"
else
cat << \SHAR_EOF > 'dbmfetch.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmfetch.c	1.2 01:23:20 1/12/90
 *
 * Name: dbmfetch
 *
 * Purpose: Retrieving a single record from a DBM database
 *
 * Description:
 *	dbmfetch retrieves a single record from a DBM database
 *	using the user supplied command line key.
 *
 * Return Values:
 *	0 if the key is found, 1 for all errors.
 */

#include <dbm.h>
#include <stdio.h>
#include <ctype.h>

main (argc, argv)
int	argc;
char	**argv;
{
	int	i;
	datum	value;
	datum	key;

	/*
	 * Count the arguments.  First argument is a database name
	 * without .dir or .pag.  Second argument is the literal
	 * key used to look up the record.
	 */

	if (argc != 3) {
		fprintf (stderr, "usage: dbmfetch database key\n");
		exit (1);
	}

	/*
	 * Open the database.  It must already exist or an error
	 * is reported.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * Initialize the key datum structure and use it to lookup
	 * the desired record in the database.
	 */

	key.dptr = argv[2];
	key.dsize = strlen (argv[2]);

	value = fetch (key);

	/*
	 * See if a value was actually returned, if so, print out
	 * the string itself.  It is assumed the string is terminated
	 * with a newline, so one isn't printed.
	 */

	if (value.dptr == 0) {
		fprintf (stderr, "%s: not found\n", argv[2]);
		exit (1);
	}
	for (i = 0;i < value.dsize;i++) {
		if (! isascii (value.dptr[i]) ||
				(! isprint (value.dptr[i]) &&
					! isspace (value.dptr[i]))
				|| value.dptr[i] == '\\')
			printf ("\\%03.3o", value.dptr[i] & 0377);
		else
			putchar (value.dptr[i]);
	}
	exit (0);
}
SHAR_EOF
fi
if test -f 'dbmedit.sh'
then
	echo shar: "will not over-write existing file 'dbmedit.sh'"
else
cat << \SHAR_EOF > 'dbmedit.sh'
#
# @(#)dbmedit.sh	1.2	01:23:18	1/12/90
#
# Edit a DBM entry
#
# Copyright 1990 John F. Haugh II
# All rights reserved.
#
# This software may be copied for non-commercial uses.
# Use at your own risk.
#
TEMP=/tmp/dbmedit$$
trap 'rm -f $TEMP ; exit $rc' 0 1 2 3 15

if [ "$EDITOR" = "" ]
then
	EDITOR=/usr/bin/vi
fi

#
# Check for proper number of arguments
#
if [ $# != 2 ]
then
	echo usage: $0 database key 1>&2
	rc=1
	exit
fi

#
# Check for the database existing
#
if [ ! -f $1.dir -o ! -f $1.pag ]
then
	echo $0: The database $1 does not exist 1>&2
	rc=1
	exit
fi

#
# Get the value from the database and shove the
# user into their favorite editor.  Ignore the
# error return since the key may not exist just
# yet and the user wants to create it.
#
dbmfetch $1 "$2" > $TEMP 2>&1

$EDITOR $TEMP
rc=$?

if [ $rc != 0 ]
then
	echo $0: Error editing $1 record "$2" 1>&2
	exit
fi

#
# Store the value back into the database
#
dbmstore $1 "$2" < $TEMP
rc=$?
exit
SHAR_EOF
fi
if test -f 'dbmkeys.c'
then
	echo shar: "will not over-write existing file 'dbmkeys.c'"
else
cat << \SHAR_EOF > 'dbmkeys.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmkeys.c	1.2 01:23:22 1/12/90
 *
 * Name: dbmkeys
 *
 * Purpose: list all database keys
 *
 * Description:
 *	dbmkeys outputs all of the keys in the database.  This
 *	can be used to select keys for some operation.
 *
 * Return Values:
 *	0 if no errors are detected, 1 otherwise.
 */

#include <dbm.h>
#include <stdio.h>
#include <ctype.h>

main (argc, argv)
int	argc;
char	**argv;
{
	int	i;
	datum	key;

	/*
	 * Check the arguments.  The name of the database must be
	 * given and the database must already exist.
	 */

	if (argc != 2) {
		fprintf (stderr, "usage: dbmkeys database\n");
		exit (1);
	}

	/*
	 * Open the database and report and error if one occurs.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * Walk through the entire database printing out all of the
	 * keys, quoting any special characters as we go.
	 */

	for (key = firstkey ();key.dptr != 0;key = nextkey (key)) {
		for (i = 0;i < key.dsize;i++) {
			if (! isascii (key.dptr[i]) ||
					(! isprint (key.dptr[i]) &&
						! isspace (key.dptr[i]))
					|| key.dptr[i] == '\\')
				printf ("\\%03.3o", key.dptr[i] & 0377);
			else
				putchar (key.dptr[i]);
		}
		putchar ('\n');
	}
	exit (0);
}
SHAR_EOF
fi
if test -f 'dbmfrstkey.c'
then
	echo shar: "will not over-write existing file 'dbmfrstkey.c'"
else
cat << \SHAR_EOF > 'dbmfrstkey.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmfrstkey.c	1.2 01:23:21 1/12/90
 *
 * Name: dbmfirstkey
 *
 * Purpose: return the first key in the database
 *
 * Description:
 *	Returns the first key in a DBM database.  This key may
 *	later be used as input to dbmnextkey to return successive
 *	keys from the database.
 *
 * Return Values:
 *	0 if the first key was found, 1 for all other errors.
 */

#include <dbm.h>
#include <stdio.h>
#include <ctype.h>

main (argc, argv)
int	argc;
char	**argv;
{
	int	i;
	datum	key;

	/*
	 * Check the arguments.  Must include the name of the database,
	 * which must already exist.
	 */

	if (argc != 2) {
		fprintf (stderr, "usage: dbmfirstkey database\n");
		exit (1);
	}

	/*
	 * Initialize the database and return an error message if there
	 * is some problem opening the database.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * Fetch the first key from the database and return it to the
	 * user.  Quote all non-printable characters while you're at it.
	 */

	key = firstkey ();

	for (i = 0;i < key.dsize;i++) {
		if (! isascii (key.dptr[i]) ||
				(! isprint (key.dptr[i]) &&
					! isspace (key.dptr[i]))
				|| key.dptr[i] == '\\')
			printf ("\\%03.3o", key.dptr[i] & 0377);
		else
			putchar (key.dptr[i]);
	}
	putchar ('\n');
	exit (0);
}
SHAR_EOF
fi
if test -f 'dbmnextkey.c'
then
	echo shar: "will not over-write existing file 'dbmnextkey.c'"
else
cat << \SHAR_EOF > 'dbmnextkey.c'
/*
 * Copyright 1990 John F. Haugh II
 * All rights reserved.
 *
 * This software may be reproduced for non-commercial uses.
 * Use at your own risk.
 */

/*
 * @(#)dbmnextkey.c	1.3 11:45:21 1/13/90
 *
 * Name: dbmnextkey
 *
 * Purpose: return the next key after the supplied argument
 *
 * Description:
 *	dbmnextkey returns the next key in the database after the
 *	supplied argument, or nothing if no key was found.  This
 *	can be used to walk through the database one key at a time.
 *
 * Return Values:
 *	0 for success, 1 for all errors
 */

#include <dbm.h>
#include <stdio.h>
#include <ctype.h>

main (argc, argv)
int	argc;
char	**argv;
{
	int	i;
	int	j;
	int	c;
	datum	next;
	datum	key;
	char	buf[BUFSIZ];

	/*
	 * Check the arguments.  The first is the name of a database,
	 * which must already exist.  The second is the key to search
	 * for.  It may contain quoted characters.
	 */

	if (argc != 3) {
		fprintf (stderr, "usage: dbmfetch database key\n");
		exit (1);
	}

	/*
	 * Open the database and report any errors which may occur.
	 */

	if (dbminit (argv[1])) {
		perror (argv[1]);
		exit (1);
	}

	/*
	 * Unquote the key.  The quoted text is in argv[2] and is
	 * copied one character at a time into buf[].  A quoted
	 * character begins with '\' and has three octal digits
	 * following it.
	 */

	for (i = j = 0;i < strlen (argv[2]);i++) {
		if (argv[2][i] == '\\') {
			c = (((argv[2][i + 1] - '0') << 3) +
			     ((argv[2][i + 2] - '0')) << 3) +
			     (argv[2][i + 3] - '0');
			i += 3;
		} else {
			c = argv[2][i];
		}
		buf[j++] = c;
	}

	/* 
	 * Initialize the key structure.  The unquotified key is
	 * now in buf[] and the length is in j.
	 */

	key.dptr = buf;
	key.dsize = j;

	/*
	 * Get the next key from the database.  If the data pointer
	 * is null there are no more keys.
	 */

	next = nextkey (key);

	if (next.dptr == 0)
		exit (0);

	/*
	 * Output the new quotified key.
	 */

	for (i = 0;i < next.dsize;i++) {
		if (! isascii (next.dptr[i]) ||
				(! isprint (next.dptr[i]) &&
					! isspace (next.dptr[i]))
				|| next.dptr[i] == '\\')
			printf ("\\%03.3o", next.dptr[i] & 0377);
		else
			putchar (next.dptr[i]);
	}
	exit (0);
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                             UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832                           Domain: jfh@rpp386.cactus.org