[alt.sources] cardfile part 1 of 2

djl@dplace.UUCP (Dave Lampe) (04/17/89)

This is the first release of my index card database program. It is only
usable on Sys V since it requires terminfo, not termcap.

Dave Lampe
{ames | lll-tis | sun | pyramid}!pacbell!dplace!djl
(415) 455-1571 (H)
(408) 986-1820 (W)

---- Cut Here and unpack ----
#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
#	Run the following text with /bin/sh to create:
#	  README
#	  cardfile.1
#	  Makefile
#	  cardfile.c
#	  add.c
#	  change.c
#	  common.c
#	  compress.c
#	  define.c
#	  delete.c
#	  dumpdb.c
#	  extract.c
#	  find.c
#	  findrcds.c
#	  getkey.c
#	  keymatch.c
#	  maint.c
#	  menu.c
#	  printdb.c
#	  putrcd.c
#	  rawio.c
#	  rbuildak.c
#	  screen.c
#	  setupkeys.c
#	  updak.c
#	  ascii.h
#	  cardfile.h
#	  library.def
#	  library.db
#	  library.ak0
#	  library.ak1
#	  library.ak2
#	  library.ak3
#
if test -r s2_seq_.tmp
then echo "Must unpack archives in sequence!"
     next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
     exit 1; fi
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
XThis program is a simple screen oriented database manager. It does have
Xsome rudimentary file formatting and printing capabilities. I use it as a
Xlibrary catalog. The metaphor is a stack of index cards with fields and
Xsubfields on them. To explain subfields, think of a book with multiple
Xauthors. Each author must be treated equally and there may be many authors.
XIf you search for any book written by "John Smith" you want to find it
Xwhether he is the only author or the fifteenth author.
X
XIt runs only on System V because it uses the terminfo capabilities. Some
Xof the source may look a little strange because I wrote it originally to
Xrun under CPM. I still have the CPM version if anyone wants it.
X
XThere are two things that need to be done yet. It needs to be converted
Xto run under BSD and the indexing needs to be converted from a sequential
Xsearch to something faster such as a B tree. Even with a sequential search,
Xthe speed is acceptable on a database of about a 500K.
X
XIncluded in the package is a piece of the catalog file for my library so
Xthat you can get the feeling for what it does. After you make it,
Xexecute "cardfile library" and then play.
X
XIf anyone finds any bugs (What? Bugs in MY code? :-) ), or makes any
Ximprovements, please let me know.
X
XDave Lampe
X{ames | lll-tis | sun | pyramid}!pacbell!dplace!djl
X(415) 455-1571 (H)
X(408) 986-1820 (W)
SHAR_EOF
chmod 0644 README || echo "restore of README fails"
echo "x - extracting cardfile.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > cardfile.1 &&
X.\ "@(#)cardfile.1	2.2 Delta Date 4/16/89  ExtrDate 4/16/89 ";
X.if t .po 1i
X.TH CARDFILE 1 djl
X.SH NAME
Xcardfile \- simple index card database
X.SH SYNOPSIS
X\fBcardfile [-r] file\fP
X.SH DESCRIPTION
X.I Cardfile
Xis a simple screen oriented index card database program.
XIf
X.I file
Xdoes not exist,
X.I cardfile
Xwill prompt for a definition of the fields to appear in each record.
XEach field has a name, a maximum length,
Xa flag to determine if it is required or not,
Xa flag to determine if it is an index field
X(whether or not it can be searched for),
Xand if multiple values may appear in the field,
Xwhat character will separate the values.
XFor example a book may have multiple authors separated by semicolons.
XWhen all fields have been defined,
Xreturn a blank screen and
X.I cardfile
Xwill create all necessary files and exit.
XReenter
X.I cardfile
Xwith the same filename and you will be able to start adding the data.
X.P
XThe \fB-r\fP flag tells \fIcardfile\fP to open thje database readonly.
XYou must use this flag to retrieve data from a database on which
Xyou do not have write permission.
XWhen \fIcardfile\fP starts, the main menu is the first screen presented.
XYou may use the cursor keys or the tab keys to move.
XWhen the cursor is next to the option desired the return key
Xwill select it.
XThe options on the main menu are:
X.SS Find
XThe \fIFIND\fP option is used to retrieve data from the file.
XYou will be presented with a screen containing all the fields flagged
Xas index fields.
XEnter the value for which you wish to search.
XIf the value is enclosed in quotes (\fB"\fP) it must match exactly.
XOtherwise, case is ignored and asterisk (\fB*\fP) can be used at the
Xend of a string to match anything,
Xi.e. \fIFarmer*\fP will match "Farmer,P.J." or "farmer,philip",
Xor "FARMER9999".
XSearch values within a field may be connected by "\fB&\fP" for
X\fBand\fP or "\fB|\fP" for \fBor\fP,
Xi.e. if you enter "1986|1987" any record with a value of 1986
Xor 1987 will be selected
X("and" only makes sense if multiple values are allowed in the field.)
XIf values are entered in multiple fields, a record must satisfy
Xboth criteria to be selected.
X.P
XWhen all values have been input,
Xhit return and the database will be searched.
XThe selected records will be displayed on the screen one at a time.
XReturn will display the next record,
XCtrl-B will move backwards through the list,
Xand Esc will abort the display.
X.SS Add
XThe \fIADD\fP option is used to add a new record to the database.
XYou will be presented with a screen with all the fields defined
Xon the record.
XThe maximum size of each field will appear after the field name.
XWhen all the data has been entered for a record,
Xreturn will save the data and blank the fields.
XThe data is not actually written to the file until
Xyou leave the \fIadd\fP screen.
XTo leave the \fIADD\fP screen simply return a blank screen.
X.SS Change
XThe \fICHANGE\fP option starts out like \fIFIND\fP but
Xwhen the selected records are displayed,
XCtrl-C will display an \fIADD\fP screen with the data from
Xthe selected record as the initial value of each field.
XChange the data as desired and then hit return to write the data
Xand display the next selected record.
X.SS Delete
XThe \fIDELETE\fP option starts out like \fIFIND\fP but
Xwhen the selected records are displayed,
XCtrl-D will delete the record from the database.
XThe record is not physically deleted from the file until
X\fICOMPRESS\fP is run, the record is only marked and ignored.
X.SS Print
XThe \fIPRINT\fP option is used to format and print the database
Xor a subset of the database.
XThe first screen asks for the output format, the extracted file
Xif any (see \fIExtract\fP on the \fIMaint\fP menu),
Xthe output file, and the output width.
X.if n .P
X.if t .bp
XAny character in the output format will be printed as entered
Xexcept for \fB%\fP sequences.
XThe recognized sequences are:
X.nf
X.ta 0.5i,1.5i
X	%N	The contents of field N
X	%N(form)	The contents of field N in \fIprintf\fP(3) 
X		"%form" format, i.e. "%1(%-20s)" will print
X		field 1 left justified in a 20 character field.
X	%n	Print a new-line.
X	%t	Output a tab character.
X	%f	Output a form feed.
X	%t(NN)	Advance to column NN.
X	%%	Print a %.
X.fi
XIf the extract name field is left blank, the entire database will be dumped.
XThe output file may be an ordinary file,
Xor it may be specified as "| \fIcommand\fP" in which case
X\fIcommand\fP will be started and the print piped to it.
XIf the output width is missing, it defaults to 80.
X.SS Exit
XThe \fIEXIT\fP option will return to UNIX.
X.SS Maint
XThe \fIMAINT\fP option will generate a submenu of infrequently used actions.
XThe actions available from the maintenance menu are:
X.br
X.po +0.5i
X.ll -0.5i
X.SS Dump
XThe \fIDUMP\fP option of the maintenance menu
Xis used to dump all records in the database and in the index files
Xto the printer.
XThe records are not formated, they are printed exactly as in the database.
X.SS Compress
XThe \fICOMPRESS\fP option of the maintenance menu
Xwill reclaim the space taken by records marked as deleted
Xand then will rebuild the index files.
XAfter \fICOMPRESS\fP has been run a record can no longer be recovered.
X.SS "Rebuild AK's"
XThe \fIREBUILD AK's\fP option of the maintenance menu
Xwill recreate the index files from the main database file.
XThis is necessary if the main database has been changed by any means
Xother than this program.
X.SS Extract
XThe \fIEXTRACT\fP option of the maintenance menu
Xis used to write selected records from the
Xdatabase to another file.
XYou are first asked for the name of the output file and then
Xpresented with a screen like \fIFIND\fP to select the records to
Xbe extracted.
X.SS Exit
XThe \fIEXIT\fP option of the maintenance menu will return to the main menu.
X.br
X.po -0.5i
X.ll +0.5i
X.SH "CONTROL KEYS"
XThe keys used to control the screens are defined in \fItermcap\fP(4).
X.sp
X.nf
X.ta 0.5i,1.5i,3i
X	TERMCAP	UNIX-PC	ACTION
X	CAP-NAME	KEY
X.sp
X	kcuf1	\(->	move right or to next field if at
X			end of field
X.if t .sp 0.5
X	kcub1	\(<-	move left or to previous field if
X			at start of field
X.if t .sp 0.5
X	kbs	Back	same as kcub1
X		Space
X.if t .sp 0.5
X	ht	Tab	move to start of next field
X.if t .sp 0.5
X	kcud1	Down	same as ht
X		Arrow
X.if t .sp 0.5
X	kcbt	shift-	move to start of this field or previous
X		Tab	field if at start
X.if t .sp 0.5
X	kcuu1	Up	same as kcbt
X		Arrow
X.if t .sp 0.5
X	kel	Clear	clear to end of field
X		Line
X.if t .sp 0.5
X	kf4	Funct	same as kel
X		Key 4
X.if t .sp 0.5
X	kdch1	Dlete	delete character under cursor
X		Char
X.if t .sp 0.5
X	kf3	Funct	same as kdch1
X		Key 3
X.if t .sp 0.5
X	kich1	Input	insert a blank under the cursor
X		Mode
X.if t .sp 0.5
X	kf2	Funct	same as kich1
X		Key 2
X.if t .sp 0.5
X	kf5	Funct	go to the next page of a multi-page form
X		Key 5
X.if t .sp 0.5
X	kf6	Funct	go to the previous page of a multi-page form
X		Key 6
X.fi
X.SH FILES
X.ta 1.5i
X\fIfile\fP.def	The file of field and file definitions
X.br
X\fIfile\fP.db	The database records
X.br
X\fIfile\fP.ak\fBN\fP	The index files for searches
X.SH NOTE
XNo field may contain a colon "\fB:\fP" as it is used as a field
Xseparator in the database.
X.br
XThe maximum size of a field is 255 characters
Xand of the total record is 1024 characters.
XThe maximum number of fields in a record is 11.
XThese are arbitrary numbers and are easy to change.
X.br
XAt least one field must be defined as an index field.
X.br
XThe termcap name
X.I kcbt
Xmay not be defined in some versions of UNIX.
XJust use the up arrow instead.
X.br
XThere is no concurrency control in \fIcardfile\fP.
SHAR_EOF
chmod 0444 cardfile.1 || echo "restore of cardfile.1 fails"
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# @(#)Makefile	2.3  DeltaDate 4/16/89   ExtrDate 4/16/89
X
X# These are for compiling on the Sun
X#CC	=	/usr/5bin/cc
X#LINT	=	/usr/5bin/lint
X#DEFINES =	-DBSD -DRE
X
X
X# -DRE get full regular expression matches for doing searches
XDEFINES	=	-DRE
XLINT	=	lint
X
XBINDIR	=	/usr/local/bin/
XMANDIR	=	/usr/man/man1/
X
X#
X# Nothing past here should need changing
X#
X
X.SUFFIXES:	.1 .1~
X.c~.c:
X		$(GET) $(GFLAGS) $<
X
X.1~.1:
X		$(GET) $(GFLAGS) $<
X
X
XSHAR	=	shar
XSHARFLAGS =	-v -c -l60 -o cardfile.shr
X
X#CFLAGS	=	-g -DDEBUG $(DEFINES)
XCFLAGS	=	-g $(DEFINES)
X
XDOCS	=	cardfile.1
X
XHDRS	=	ascii.h cardfile.h
X
XSRC	=	\
X		cardfile.c \
X		add.c \
X		change.c \
X		common.c \
X		compress.c \
X		define.c \
X		delete.c \
X		dumpdb.c \
X		extract.c \
X		find.c \
X		findrcds.c \
X		getkey.c \
X		keymatch.c \
X		maint.c \
X		menu.c \
X		printdb.c \
X		putrcd.c \
X		rawio.c \
X		rbuildak.c \
X		screen.c \
X		setupkeys.c \
X		updak.c \
X		$(NULL)
X
XOBJ	=	\
X		cardfile.o \
X		add.o \
X		change.o \
X		common.o \
X		compress.o \
X		define.o \
X		delete.o \
X		dumpdb.o \
X		extract.o \
X		find.o \
X		findrcds.o \
X		getkey.o \
X		keymatch.o \
X		maint.o \
X		menu.o \
X		printdb.o \
X		putrcd.o \
X		rawio.o \
X		rbuildak.o \
X		screen.o \
X		setupkeys.o \
X		updak.o \
X		$(NULL)
X
XTESTDB	=	\
X		library.def \
X		library.db \
X		library.ak0 \
X		library.ak1 \
X		library.ak2 \
X		library.ak3 \
X		$(NULL)
X
X###############################################################################
X#
X#	Make targets
X#
X###############################################################################
X
Xall:		cardfile cardfile.1
X
Xcardfile:	$(OBJ)
X		$(CC) -o cardfile $(OBJ) -lPW -lcurses
X
Xinstall:	all
X		cp cardfile $(BINDIR)cardfile
X		strip $(BINDIR)cardfile
X		cp cardfile.1 $(MANDIR)cardfile.1
X
Xclean:
X		rm -f *.o cardfile cardfile.1 $(SRC) $(HDRS) Makefile
X
Xprint:		prt_src prt_docs
X
Xprt_src:	$(HDRS) $(SRC) Makefile
X		cpr -w96 -l88 $(HDRS) $(SRC) Makefile |  \
X		lp -o-v8 -o-t -o-h12
X
Xprt_docs:	$(DOCS)
X		nroff -man cardfile.1 | lp -o-qc
X
Xshar:		$(HDRS) $(SRC) $(DOCS) Makefile $(TESTDB)
X		$(SHAR) $(SHARFLAGS) README $(DOCS) Makefile \
X			$(SRC) $(HDRS) $(TESTDB)
X
Xlint:		$(HDRS) $(SRC)
X		$(LINT) $(SRC)
X
X###############################################################################
X#
X#	Object dependencies
X#
X###############################################################################
X
Xadd.o:		cardfile.h ascii.h
Xcardfile.o:	cardfile.h ascii.h
Xchange.o:	cardfile.h ascii.h
Xcommon.o:	cardfile.h ascii.h
Xcompress.o:	cardfile.h
Xdefine.o:	cardfile.h ascii.h
Xdelete.o:	cardfile.h ascii.h
Xdumpdb.o:	cardfile.h ascii.h
Xextract.o:	cardfile.h ascii.h
Xfind.o:		cardfile.h ascii.h
Xfindrcds.o:	cardfile.h ascii.h
Xgetkey.o:	cardfile.h
Xkeymatch.o:	cardfile.h
Xmaint.o:	cardfile.h
Xmenu.o:		cardfile.h
Xprintdb.o:	cardfile.h ascii.h
Xputrcd.o:	cardfile.h
Xrbuildak.o:	cardfile.h
Xscreen.o:	cardfile.h ascii.h
Xsetupkeys.o:	cardfile.h ascii.h
Xupdak.o:	cardfile.h ascii.h
SHAR_EOF
chmod 0444 Makefile || echo "restore of Makefile fails"
echo "x - extracting cardfile.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > cardfile.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)cardfile.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      CARDFILE.C      */
X/*      This is the main routine for cardfile
X*/
X#include	"ascii.h"
X#include	"stdio.h"
X#include	"cardfile.h"
X#include	<curses.h>
X#include	<term.h>
X#include	<signal.h>
X
X#define FIND    0
X#define ADD     1
X#define CHANGE  2
X#define DELETE  3
X#define PRINT   4
X#define MAINT   5
X#define EXIT    6
X
XFILE    *def_fp;
Xint	readonly;
Xchar    fname[FNSIZE];
Xextern  char *getfield();
Xchar    *functs[]    = {"FIND       ",
X			"ADD        ",
X			"CHANGE     ",
X			"DELETE     ",
X			"PRINT      ",
X			"MAINTENANCE",
X			"EXIT       ",
X			0
X			};
X
Xchar	datadir[FNSIZE];
Xchar	*dbname;
Xstruct	termio	instty, outstty;
Xvoid	setupscr();
Xvoid	exit();
Xunsigned sleep();
X
Xmain(argc, argv)
Xint     argc;
Xchar    **argv;
X{
X    int 	num_flds, i;
X    char	line_buf[133];
X    struct      Fdata fields[MAXFLDS+1], *fp;
X    int 	num_ak;
X    struct      AKdata ak_data[MAXAK+1];
X    int 	func;
X    char	*cp;
X    extern char *optarg;
X    extern int optind, opterr;
X    
X    opterr = 0;
X    while ((i = getopt(argc, argv, "r")) != EOF) {
X	switch (i) {
X	case 'r':
X	    ++readonly;
X	    break;
X	case '?':
X	    usage(argv[0]);
X	    exit(1);
X	}
X    }
X    if (optind != argc-1) {
X	usage(argv[0]);
X	exit(1);
X    }
X    signal(SIGINT, getout);
X    signal(SIGQUIT, getout);
X    signal(SIGTERM, getout);
X    setupscr();		/* from here on use getout, not exit to reset screen */
X    if ((cp = strrchr(argv[optind], '/')) != NULL) {
X	*cp = '\0';
X	sprintf(datadir, "%s/", argv[optind]);
X	dbname = cp + 1;
X    } else {
X	*datadir = '\0';
X	dbname = argv[optind];
X    }
X    if (strlen(dbname) > 10) {
X	printf("Database name cannot be longer than 10 characters.\n");
X	sleep(5);
X	getout();
X    }
X    /* build definition file name */
X    sprintf(fname, "%s%s.def", datadir, dbname);
X    if ((def_fp = fopen(fname,"r")) == NULL) {
X	db_define(dbname);
X	getout();
X    }
X    if (fgets(line_buf, 132, def_fp) == NULL) {
X	printf("Unable to read DEF file\n");
X	sleep(5);
X	getout();
X    }
X    if (! readonly && access(fname, 06) != 0) {
X	printf("You can not write to the database, try\n%s -r %s%s\n",
X	    argv[0], datadir, dbname);
X	sleep(5);
X	getout();
X    }
X    strcpy(fname, getfield(line_buf, ":"));
X    num_flds = atoi(getfield(0, ":"));
X    for (i=0; i<num_flds; i++) {        /* get definition of each field */
X	fgets(line_buf, 132, def_fp);
X	fp = &fields[i];
X	strcpy(fp->F_title, getfield(line_buf, ":"));
X	fp->F_key = *getfield(0, ":");
X	strcpy(fp->F_seps, getfield(0, ":"));
X	fp->F_required = *getfield(0, ":");
X	fp->F_length = atoi(getfield(0, ":"));
X    }
X    fp = &fields[i];
X    fp->F_title[0] = '\0';
X    fgets(line_buf, 10, def_fp);
X    num_ak = atoi(line_buf);
X    for (i=0; i<num_ak; i++) {          /* get definition of each AK file */
X	fgets(line_buf, 132, def_fp);
X	line_buf[strlen(line_buf)-1] = '\0';
X	ak_data[i].A_fldnum = atoi(getfield(line_buf, ":"));
X	strcpy(ak_data[i].A_akname, getfield(0, ":"));
X    }
X    ak_data[i].A_fldnum = -1;
X    fclose(def_fp);
X    
X    /* get function to be performed */
X    while ((func = menu(dbname, functs)) != EXIT) {
X	switch (func) {
X	case FIND:
X	    find(fields, dbname);
X	    continue;
X	case ADD:
X	    add(fields, dbname);
X	    continue;
X	case CHANGE:
X	    change(fields, dbname);
X	    continue;
X	case DELETE:
X	    delete(fields, dbname);
X	    continue;
X	case PRINT:
X	    printdb(fields, dbname);
X	    continue;
X	case MAINT:
X	    maint(fields, dbname, ak_data);
X	    continue;
X	default:
X	    msg("Illegal function chosen");
X	    getout();
X	}
X    }
X    getout();
X/*NOTREACHED*/
X}
X
X
Xvoid
Xsetupscr()
X{
X    
X    setbuf(stdout, NULL);
X    setbuf(stdin, NULL);
X    ioctl(0, TCGETA, &outstty);
X    instty.c_iflag = outstty.c_iflag;
X    instty.c_oflag = outstty.c_oflag;
X    instty.c_cflag = outstty.c_cflag;
X    instty.c_lflag = 0;
X    instty.c_line = outstty.c_line;
X    instty.c_cc[VINTR] = outstty.c_cc[VINTR];
X    instty.c_cc[VQUIT] = outstty.c_cc[VQUIT];
X    instty.c_cc[VERASE] = outstty.c_cc[VERASE];
X    instty.c_cc[VKILL] = outstty.c_cc[VKILL];
X    instty.c_cc[VMIN] = 1;
X    instty.c_cc[VTIME] = 0;
X    instty.c_cc[6] = outstty.c_cc[6];
X    instty.c_cc[7] = outstty.c_cc[7];
X    ioctl(0, TCSETAW, &instty);
X    setupterm((char*)0, 1, (int*)0);
X    setupkeys();
X    putp(keypad_xmit);
X}
X
Xgetout()
X{
X
X    putp(keypad_local);
X    putp(clear_screen);
X    ioctl(0, TCSETAW, &outstty);
X    exit(0);
X}
X
Xstatic
Xusage(prog)
Xchar	*prog;
X{
X    printf("Usage: %s [-r] file\n", prog);
X}
SHAR_EOF
chmod 0644 cardfile.c || echo "restore of cardfile.c fails"
echo "x - extracting add.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > add.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)add.c	2.3    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      ADD.C   */
X/*      This subroutine is used to add a new record to the
X**	data base. It also requests the updating of the
X**	alternate key files.
X*/
X
X#include <stdio.h>
X#include <errno.h>
X#include "ascii.h"
X#include "cardfile.h"
X
Xlong    ftell();
Xchar    *malloc();
Xextern  int     errno;
Xextern	int	readonly;
X
Xadd(fields, dbname)
Xstruct  Fdata   fields[];
Xchar    *dbname;
X{
X    struct      Fdata   *fp;
X    struct      Sdata   add_screen[MAXFLDS+1], *sp;
X    int 	err;
X    char	first[SWIDTH+1];
X    char	out_line[DBSIZE+1];
X    FILE	*filep;
X    char	filename[FNSIZE];
X    long	offset;
X    char	*buffer;
X    
X    if (readonly) {
X	msg("Database is readonly");
X	return(1);
X    }
X    sprintf(first, "Add Records to %s Data Base", dbname);
X    buffer = malloc(BUFSIZE+1);
X    buffer[0] = '\0';
X    sprintf(filename, "%s%s.db", datadir, dbname);
X    if((filep = fopen(filename, "a")) == NULL) {
X	if (errno == EACCES) {
X	    strcpy(out_line, "You do not have permission to modify this database");
X	} else {
X	    sprintf(out_line, "Unable to open %s, errno=%d\n", filename, errno);
X	}
X	msg(out_line);
X	return(1);
X    }
X    fp = fields;
X    sp = add_screen;
X    while (fp->F_title[0] != 0) {
X	sp->S_title = fp->F_title;
X	sp->S_length = fp->F_length;
X	sp->S_result = malloc((unsigned)fp->F_length+1);
X	sp->S_dfault = 0;
X	++fp;
X	++sp;
X    }
X    sp->S_title = 0;
X    while (screen(first, add_screen, 0, 0) != 0) {
X	out_line[0] = '\0';
X	err = 0;
X	fp = fields;
X	sp = add_screen;
X	while (sp->S_title) {
X	    if (sp->S_result[0] == '\0' && fp->F_required == 'Y') {
X		sprintf(out_line, "Required field %s missing", fp->F_title);
X		msg(out_line);
X		++err;
X	    }
X	    if (strchr(sp->S_result, ':')) {
X		msg("A ':' is not allowed in any field");
X		++err;
X	    }
X	    if (strlen(out_line) + strlen(sp->S_result) >= DBSIZE) {
X		msg("Record too long");
X		++err;
X		break;
X	    }
X	    strcat(out_line, sp->S_result);
X	    strcat(out_line, ":");
X	    ++fp;
X	    ++sp;
X	}
X	if (err) {
X	    continue;
X	}
X	if (err > 0)
X	    break;
X	out_line[strlen(out_line)-1] = '\0';
X	offset = ftell(filep);
X	fprintf(filep, " :%s\n", out_line);
X	buildak(dbname, out_line, offset, fields, buffer);
X    }
X    sp = add_screen;
X    while (sp->S_title) {
X	free(sp->S_result);
X	++sp;
X    }
X    fclose(filep);
X    writeak(dbname, buffer);
X    free(buffer); 
X    return(0);
X}
SHAR_EOF
chmod 0444 add.c || echo "restore of add.c fails"
echo "x - extracting change.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > change.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)change.c	2.3    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      CHANGE.C		*/
X/*      This module is used to find and change records
X**	in the data base matching the selection criteria.
X**	The user may select multiple values of one field
X**	which will be or'd and/or multiple fields which will
X**	be and'd. In other words, if field 1 is specified
X**	as value1:value2 and field3 is specified as value3.
X**	Records with field1==(value1 || value2) && field3==value3
X**	will be selected for changing.
X**	It calls findrcds.c to do all the work of finding them.
X**	It does not actually change records, the flag(first)
X**	field is set to 'D' in the current record and a new
X**	record is added. To physically delete the old record,
X**	the data base must be compressed.
X*/
X
X#include <stdio.h>
X#include <errno.h>
X#include "ascii.h"
X#include "cardfile.h"
X#include <curses.h>
X#include <term.h>
X
Xchar	*malloc(), *getfield();
Xextern	int	readonly;
X
X
Xchange(fields, dbname)
Xstruct  Fdata   *fields;
Xchar    *dbname;
X{
X    char	first[SWIDTH];
X    int 	processc();
X
X    if (readonly) {
X	msg("Database is readonly");
X	return(1);
X    }
X    sprintf(first, "Select Records from the %s Data Base to be Changed",
X	dbname);
X    findrcds(fields, dbname, processc, first);
X    return(0);
X}
X
X
Xprocessc(fields, rcd, dbfile, dbname)
Xstruct  Fdata   *fields;
Xchar    *rcd;
XFILE    *dbfile;
Xchar    *dbname;
X{
X    char	c, save[DBSIZE+1];
X    long	offset, ftell();
X    char	*strchr();
X    static      char    fmt[256];
X    int 	fn;
X    struct      Fdata   *fp;
X    
X    strcpy(save, rcd);
X    putp(clear_screen);              /* clear screen */
X    if (*fmt == '\0') {         /* first time */
X	fp = fields;
X	fn = 1;
X	while (fp->F_title[0]) {
X	    sprintf(&fmt[strlen(fmt)], "%s: %%%d%%n", fp->F_title, fn);
X	    ++fn;
X	    ++fp;
X	}
X    }
X    putrcd("Record to be CHANGED", save, stdout, fmt, SWIDTH, 2);
X    putp(tparm(cursor_address, MSGLINE, 9));
X    fputs("RETURN for next, ESC to abort, Ctrl-C to Change, Ctrl-B to reverse",
X	  stdout);
X    noecho();
X    while ((c=rawgetchar()) != LF && c != CR) {
X	if (c == ETX) {         /* CTL-C entered */
X	    offset = ftell(dbfile) - strlen(rcd);
X	    *strchr(rcd, '\n') = '\0';          /* truncate \n */
X	    if (doadd(dbname, fields, rcd, dbfile) == 0) {
X		fseek(dbfile, offset, 0);
X		putc('D', dbfile);
X	    }
X	    break;
X	}
X	if (c == ESC) {
X	    echo();
X	    return(1);
X	}
X	if (c == STX) {			/* CTRL-B */
X	    echo();
X	    return(-1);
X	}
X	rawputchar(BEL);
X    }
X    echo();
X    return(0);
X}
X
Xdoadd(dbname, fields, rcd, filep)
Xchar    *dbname;
Xstruct  Fdata   *fields;
Xchar    *rcd;
XFILE    *filep;
X{
X    struct      Sdata   add_screen[MAXFLDS+1], *sp;
X    struct      Fdata   *fp;
X    int 	err;
X    char	buffer[BUFSIZE+1];
X    char	outline[DBSIZE+1];
X    long	offset, ftell();
X
X    fseek(filep, 0L, 2);     /* end of file */
X    fp = fields;
X    getfield(rcd, ":");         /* step past flag */
X    sp = add_screen;
X    while(fp->F_title[0]) {
X	sp->S_title = fp->F_title;
X	sp->S_length = fp->F_length;
X	sp->S_result = malloc((unsigned)fp->F_length);
X	sp->S_dfault = getfield(0, ":");
X	++fp;
X	++sp;
X    }
X    sp->S_title = 0;
X    outline[0] = '\0';
X    while (screen("Enter changes", add_screen, 0, 0) > 0) {
X	err = 0;
X	fp = fields;
X	sp = add_screen;
X	while(fp->F_title[0]) {
X	    if (sp->S_result[0] == '\0' && fp->F_required == 'Y') {
X		sprintf(outline, "Required field %s missing", fp->F_title);
X		msg(outline);
X		err++;
X	    }
X	    if (strchr(sp->S_result, ':')) {
X		msg("A : is not allowed in any field");
X		++err;
X	    }
X	    if (strlen(outline) + strlen(sp->S_result) >= DBSIZE) {
X		msg("Record too long");
X		++err;
X		break;
X	    }
X	    strcat(outline, sp->S_result);
X	    strcat(outline, ":");
X	    ++fp;
X	    ++sp;
X	}
X	if (err == 0)
X	    break;
X	outline[0] = '\0';
X    }
X    if (err > 0 || outline[0] == '\0') {
X	sp = add_screen;
X	while(sp->S_title)
X	    free((sp++)->S_result);
X	return(1);
X    }
X    outline[strlen(outline)-1] = '\0';  /* truncate trailing : */
X    offset = ftell(filep);
X    if (fprintf(filep, " :%s\n", outline) == EOF) {
X	msg("Error writing DB file");
X	getout();
X    }
X    buffer[0] = '\0';
X    buildak(dbname, outline, offset, fields, buffer);
X    writeak(dbname, buffer);
X    sp = add_screen;
X    while(sp->S_title)
X	free((sp++)->S_result);
X    return(0);
X}
SHAR_EOF
chmod 0444 change.c || echo "restore of change.c fails"
echo "x - extracting common.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > common.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)common.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*      COMMON.C        */
X#include "stdio.h"
X#include "ascii.h"
X#include "cardfile.h"
X#include <curses.h>
X#include <term.h>
X
Xmsg(str)        /* put a blinking message on the 23'th line of the crt */
Xchar    *str;
X{
X    putp(tparm(cursor_address, MSGLINE, 9));
X    putp(clr_eol);			/* erase line */
X    putp(enter_blink_mode);		/* blink */
X    fputs(str, stdout);
X    putp(exit_attribute_mode);		/* back to normal */
X    sleep(5);
X}
X
X
Xchar *
Xgetfield(string, sepset)        /* like strtok except contiguous seps */
Xchar    *string, *sepset;       /* result in a null field */
X{
X    register char *p, *r;
X    static char *savept;
X    char	*strpbrk();
X    
X    p = (string == NULL)? savept : string;
X    if (p == 0)
X        return(NULL);
X    if (*p == '\0')
X        return(NULL);
X    if ((r = strpbrk(p, sepset)) == NULL)       /* move past token */
X        savept = 0;     /* indicate this is the last token */
X    else {
X        *r = '\0';
X        savept = ++r;
X    }
X    return(p);
X}
X
X
Xhelp(help_msg)
Xchar    *help_msg;
X{
X    putp(clear_screen);              /* clear screen */
X    puts("\n");
X    fputs(help_msg, stdout);
X    putp(tparm(cursor_address, MSGLINE, 9));
X    fputs("Enter any character to continue.", stdout);
X    rawgetchar();
X    putp(clear_screen);              /* clear screen */
X}
SHAR_EOF
chmod 0644 common.c || echo "restore of common.c fails"
echo "x - extracting compress.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > compress.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)compress.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      COMPRESS.C      */
X/*	This function will remove all records with a flag
X**	byte of 'D' from the data base.  The alternate key
X**	files MUST be rebuilt after it finishes.
X*/
X
X#include "stdio.h"
X#include "cardfile.h"
X
Xcompress(dbname)
Xchar    *dbname;
X{
X    FILE	*in, *out;
X    char	fname[FNSIZE];
X    char	tempname[FNSIZE];
X    char	rcd[DBSIZE+1];
X    
X    sprintf(tempname, "%snewdb.$$$", datadir);
X    if ((out = fopen(tempname, "w")) == NULL) {
X	msg("Unable to create temporary file");
X	getout();
X    }
X    sprintf(fname, "%s%s.db", datadir, dbname);
X    if ((in = fopen(fname, "r")) == NULL) {
X	unlink(tempname);
X	msg("Unable to read DB file");
X	getout();
X    }
X    while (fgets(rcd, DBSIZE, in) != NULL) {
X	if (feof(in))
X	    break;
X	if (*rcd == 'D')        /* record to be deleted */
X	    continue;
X	if(fputs(rcd, out) == EOF) {
X	    unlink(tempname);
X	    msg("Unable to write to temporary file");
X	    getout();
X	}
X    }
X    fclose(in);
X    fclose(out);
X    unlink(fname);
X    link(tempname, fname);
X    unlink(tempname);
X}
SHAR_EOF
chmod 0644 compress.c || echo "restore of compress.c fails"
echo "x - extracting define.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > define.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)define.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      DEFINE.C	*/
X/*      This subroutine is used to define a new data-base.
X**	It expects as input the base name (8 characters or less)
X**	of all files.
X**
X**	It will create the definition file  -  name.def
X**	the primary file		    -  name.db
X**	and the alternate key files         -  name.akN
X*/
X
X#include "cardfile.h"
X#include "stdio.h"
X#include "ascii.h"
X#include <curses.h>
X#include <term.h>
X
Xstruct  Sdata   def_screen[] = {
X			{"FIELD NAME",TSIZE,"                    ",0},
X			{"MAXIMUM LENGTH",3,"   ",0},
X			{"REQUIRED?",1," ","N"},
X			{"SEARCH KEY",1," ","Y"},
X			{"SUBFIELD SEPARATORS",5,"     ",0},
X			{0,0,0,0}
X			};
X
Xstruct  Fdata   fields[MAXFLDS];
X
Xdb_define(basename)
Xchar    *basename;
X{
X    int 	i;
X    char	first[SWIDTH];
X    char	*last = "RETURN to exit";
X    struct      Fdata   *fp;
X    int 	num_fields, num_ak;
X    char	fname[FNSIZE];
X    FILE	*filep, *filep2;
X    int 	err;
X    int 	tsize;
X
X    sprintf(first,"DEFINING %s DATA BASE", basename);
X    num_fields = 0;
X    fp = fields;
X    tsize = 0;
X    if (access((*datadir ? datadir : "."), 06) != 0) {
X	printf("Cannot write into %s\n", (*datadir ? datadir : "."));
X	sleep(5);
X	return(1);
X    }
X    
X    /*
X     *	Read each field entered
X     */
X    while (screen(first, def_screen, last, 0)) {
X	err = 0;
X	strcpy(fp->F_title, def_screen[0].S_result);
X	if (strchr(fp->F_title, ':') != NULL) {
X	    msg("TITLE field must not include :");
X	    ++err;
X	}
X	fp->F_length = atoi(def_screen[1].S_result);
X	if (fp->F_length <= 0 || fp->F_length >= FLDLEN) {
X	    msg("LENGTH field must be less than 256");
X	    ++err;
X	}
X	if ((tsize += fp->F_length) > DBSIZE) {
X	    msg("Total record length too large");
X	    ++err;
X	}
X	fp->F_required = toupper(def_screen[2].S_result[0]);
X	if (fp->F_required != 'Y' && fp->F_required != 'N') {
X	    msg("REQUIRED must be Y or N");
X	    ++err;
X	}
X	fp->F_key = toupper(def_screen[3].S_result[0]);
X	if (fp->F_key != 'Y' && fp->F_key != 'N') {
X	    msg("KEY must be Y or N");
X	    ++err;
X	}
X	strcpy(fp->F_seps, def_screen[4].S_result);
X	if (strchr(fp->F_seps, ':') != NULL) {
X	    msg("SEPARATORS must not include :");
X	    ++err;
X	}
X	if (err) {
X	    continue;
X	}
X	for (i=0; i<num_fields; i++) {
X	    if (strcmp(fields[i].F_title, fp->F_title) == 0) {
X		msg("Field already defined");
X		err++;
X		break;
X	    }
X	}
X	if (err)
X	    continue;
X	++fp;
X	if (++num_fields > MAXFLDS) {
X	    msg("Too many fields defined, aborting");
X	    return(1);
X	}
X    }
X    
X    putp(clear_screen);              /* clear screen */
X    if (num_fields == 0) {
X	msg("No fields defined, aborting");
X	return(1);
X    }
X    sprintf(fname,"%s%s.def", datadir, basename);
X    if ((filep = fopen(fname,"w")) == NULL) {
X	msg("Unable to create DEF file");
X	return(1);
X    }
X    sprintf(fname,"%s%s.db", datadir, basename);
X    if ((filep2 = fopen(fname,"w")) == NULL) {
X	msg("Unable to create DB file");
X	sprintf(fname, "%s.def", basename);
X	unlink(fname);
X	return(1);
X    }
X    fclose(filep2);
X    fprintf(filep, "%s:%d\n", fname, num_fields);
X    num_ak = 0;
X    for (i=0; i<num_fields; i++) {
X	fp = &fields[i];
X	if (fp->F_key == 'Y')
X	    fp->F_key = '0' + num_ak++;
X	fprintf(filep, "%s:%c:%s:%c:%d\n", fp->F_title, fp->F_key,
X	    fp->F_seps, fp->F_required, fp->F_length);
X    }
X    if (num_ak == 0) {
X	msg("At least 1 key field must be defined");
X	unlink(fname);          /* DB file */
X	sprintf(fname, "%s.def", basename);
X	unlink(fname);
X	return(1);
X    }
X    fprintf(filep, "%d\n", num_ak);
X    num_ak = 0;
X    for (i=0; i<num_fields; i++) {
X	fp = &fields[i];
X	if (fp->F_key != 'N') {         /* Alternate key file required */
X	    sprintf(fname,"%s%s.ak%d", datadir, basename, num_ak++);
X	    if ((filep2 = fopen(fname,"w")) == NULL) {
X		msg("Unable to create AK file\n");
X		sprintf(fname, "%s.def", basename);
X		unlink(fname);
X		return(1);
X	    }
X	    fclose(filep2);
X	    fprintf(filep, "%d:%s\n", i, fname);
X	}
X    }
X    fclose(filep);
X    return(0);
X}
SHAR_EOF
chmod 0644 define.c || echo "restore of define.c fails"
echo "x - extracting delete.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > delete.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)delete.c	2.3    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*	DELETE.C		*/
X/*	This module is used to find and delete records
X**	in the data base matching the selection criteria.
X**	The user may select multiple values of one field
X**	which will be or'd and/or multiple fields which will
X**	be and'd. In other words, if field 1 is specified
X**	as value1:value2 and field3 is specified as value3.
X**	Records with field1==(value1 || value2) && field3==value3
X**	will be selected for deletion.
X**	It calls findrcds.c to do all the work.
X**	It does not actually delete records, the flag(first)
X**	field is set to 'D'. To physically delete them, the data
X**	base must be compressed.
X*/
X
X#include "stdio.h"
X#include "ascii.h"
X#include "cardfile.h"
X#include <curses.h>
X#include <term.h>
X
X
Xextern	int	readonly;
X
Xdelete(fields, dbname)
Xstruct  Fdata   *fields;
Xchar    *dbname;
X{
X    char	first[SWIDTH];
X    int 	processd();
X    
X    if (readonly) {
X	msg("Database is readonly");
X	return(1);
X    }
X    sprintf(first, "Select Records from the %s Data Base to be DELETED",
X	dbname);
X    findrcds(fields, dbname, processd, first);
X    return(0);
X}
X
X
X/*ARGSUSED*/
Xprocessd(fields, rcd, dbfile, dummy)
Xstruct  Fdata   *fields;
Xchar    *rcd;
XFILE    *dbfile;
Xchar	*dummy;
X{
X    char	c, save[DBSIZE+1];
X    static      char    fmt[256];
X    int 	fn;
X    struct      Fdata   *fp;
X    
X    strcpy(save, rcd);
X    putp(clear_screen);		/* clear screen */
X    if (*fmt == '\0') {		/* first time */
X	fp = fields;
X	fn = 1;
X	while (fp->F_title[0]) {
X	    sprintf(&fmt[strlen(fmt)], "%s: %%%d%%n", fp->F_title, fn);
X	    ++fn;
X	    ++fp;
X	}
X    }
X    putrcd("Record to be DELETED", save, stdout, fmt, SWIDTH, 2);
X    putp(tparm(cursor_address, MSGLINE, 9));
X    fputs("RETURN for next, ESC to abort, CTL-D to DELETE, CTL-B to reverse",
X	  stdout);
X    noecho();
X    while ((c=rawgetchar()) != LF && c != CR) {
X	if (c == EOT) {		/* CTL-D entered */
X	    fseek(dbfile, (long)(-strlen(rcd)), 1);
X	    putc('D', dbfile);
X	    break;
X	}
X	if (c == ESC) {
X	    echo();
X	    return(1);
X	}
X	if (c == STX) {
X	    echo();
X	    return(-1);
X	}
X	rawputchar(BEL);
X    }
X    echo();
X    return(0);
X}
SHAR_EOF
chmod 0444 delete.c || echo "restore of delete.c fails"
echo "x - extracting dumpdb.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dumpdb.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)dumpdb.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*      DUMPDB.C        */
X/*      This subroutine is used to dump all records from
X        the data base and the alternate key files, including
X        deleted records.
X*/
X
X#include "stdio.h"
X#include "ascii.h"
X#include "cardfile.h"
X#include <curses.h>
X#include <term.h>
X#include <signal.h>
X
Xstatic	FILE	*in, *out;
Xstatic	int	quit = 0;
Xstatic	int	abort();
Xstatic	int	(*old_sig)();
X
Xdumpdb(dbname, ak_data, fields)
Xchar    *dbname;
Xstruct  AKdata  *ak_data;
Xstruct  Fdata   *fields;
X{
X    char        ptitle[PWIDTH+1];
X    int         pnum, lnum;
X    char        fname[FNSIZE];
X    char        rcd[DBSIZE+1];
X    
X    if ((out = popen("lp", "w")) == NULL) {
X        msg("Unable to open printer");
X        return(1);
X    }
X    sprintf(fname, "%s%s.db", datadir, dbname);
X    if ((in = fopen(fname, "r")) == NULL) {
X	pclose(out);
X        msg("Unable to open DB file");
X        return(1);
X    }
X    putp(tparm(cursor_address, MSGLINE, 9));
X    putp(clr_eol);
X    fputs("DEL to abort dump", stdout);
X    old_sig = signal(SIGINT, abort);
X    lnum = PLENGTH + 1;                 /* force top-of-page */
X    pnum = 1;
X    sprintf(ptitle, "Records in %s.db", dbname);
X    while( !quit && (fgets(rcd, DBSIZE, in)) != NULL) {   /* print DB file */
X        rcd[strlen(rcd)-1] = '\0';      /* truncate \n */
X        if (lnum + 3 + (strlen(rcd)+PWIDTH-1)/PWIDTH >= PLENGTH) {
X            newpage(&pnum, ptitle);
X            lnum = 3;
X        }
X        fputs(rcd, out);
X        putc('\n', out);
X        lnum += (strlen(rcd)+PWIDTH-1)/PWIDTH;
X    }
X    if (quit)
X	return(1);
X    fclose(in);
X    while (ak_data->A_fldnum >= 0) {
X	strcpy(fname, datadir);
X        strcat(fname, ak_data->A_akname);
X        if ((in = fopen(fname, "r")) == NULL) {
X            sprintf(ptitle, "Unable to open %s", fname);
X            msg(ptitle);
X            return(1);
X        }
X        lnum = PLENGTH + 1;
X        pnum = 1;
X        sprintf(ptitle, "Records in %s sorted on %s",
X            fname, fields[ak_data->A_fldnum].F_title);
X	/* print AK file */
X        while( !quit && (fgets(rcd, DBSIZE, in)) != NULL) {
X            rcd[strlen(rcd)-1] = '\0';  /* truncate \n */
X            if (lnum + 3 + (strlen(rcd)+PWIDTH-1)/PWIDTH >= PLENGTH) {
X                newpage(&pnum, ptitle);
X                lnum = 3;
X            }
X            fputs(rcd, out);
X            putc('\n', out);
X            lnum += (strlen(rcd)+PWIDTH-1)/PWIDTH;
X        }
X	if (quit)
X	    return(1);
X        fclose(in);
X        ++ak_data;
X    }
X    pclose(out);
X    signal(SIGINT, old_sig);
X    return(0);
X}
X
Xnewpage(pnum, title)
Xint     *pnum;
Xchar    *title;
X{
X
X    putc(FF, out);
X    fprintf(out, "%-69sPAGE %4d\n\n\n", title, *pnum);
X    *pnum += 1;
X}
X
X
Xstatic
Xabort()
X{
X
X    signal(SIGINT, old_sig);
X    fputs("\n\nOUTPUT ABORTED!\n", out);
X    pclose(out);
X    fclose(in);
X    msg("Dump aborted");
X    quit = 1;
X}
SHAR_EOF
chmod 0644 dumpdb.c || echo "restore of dumpdb.c fails"
echo "x - extracting extract.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > extract.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)extract.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      EXTRACT.C       */
X/*      This subroutine is used to write selected records from the
X *      database on to another disk file.
X */
X
X#include "stdio.h"
X#include "cardfile.h"
X#include "ascii.h"
X#include <curses.h>
X#include <term.h>
X
XFILE    *ext_fp;
Xchar    ext_fname[FNSIZE];
Xint     rec_cnt;
Xstruct Sdata    fn_screen[] = {
X		    {"File Name", FNSIZE, ext_fname, NULL},
X		    {0, 0, 0, 0}
X		};
X
Xextract(fields, dbname)
Xstruct  Fdata   *fields;
Xchar    *dbname;
X{
X    char	first[SWIDTH+1];
X    int 	write_rcd();
X
X    sprintf(first, "Select records from the %s Data Base to be extracted",
X	dbname);
X    screen(first, fn_screen, "Enter File Name of Extract File", NULL);
X    if (ext_fname[0] == '\0') {
X	msg("No file name entered, using temp.ext");
X	strcpy(ext_fname, "temp.ext");
X    }
X    if ((ext_fp = fopen(ext_fname, "w")) == NULL) {
X	sprintf(first, "Unable to open file %s for output", ext_fname);
X	msg(first);
X	return(1);
X    }
X    rec_cnt = 0;
X    findrcds(fields, dbname, write_rcd, first);
X    fclose(ext_fp);
X    sprintf(first, "%d records written to %s", rec_cnt, ext_fname);
X    putp(tparm(cursor_address, MSGLINE, 9));
X    putp(clr_eol);
X    fputs(first, stdout);
X    sleep(5);
X    return(0);
X}
X
X
X/*ARGSUSED*/
Xint
Xwrite_rcd(dummy1, rcd, dummy2, dummy3)
Xchar    *rcd;
Xchar    *dummy1, *dummy2, *dummy3;
X{
X    ++rec_cnt;
X    return(fputs(rcd, ext_fp));
X}
SHAR_EOF
chmod 0444 extract.c || echo "restore of extract.c fails"
echo "x - extracting find.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > find.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)find.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      FIND.C  	*/
X/*      This module is used to find and display records
X**	in the data base matching the selection criteria.
X**	The user may select multiple values of one field
X**	which will be or'd and/or multiple fields which will
X**	be and'd. In other words, if field 1 is specified
X**	as value1:value2 and field3 is specified as value3.
X**	Records with field1==(value1 || value2) && field3==value3
X**	will be displayed.
X**	It calls findrcds.c to do all the work.
X*/
X
X#include "stdio.h"
X#include "ascii.h"
X#include "cardfile.h"
X#include <curses.h>
X#include <term.h>
X
X
Xfind(fields, dbname)
Xstruct  Fdata   *fields;
Xchar    *dbname;
X{
X    char	first[SWIDTH];
X    int 	display();
X    
X    sprintf(first, "Display Records from the %s Data Base", dbname);
X    findrcds(fields, dbname, display, first);
X    return(0);
X}
X
X
X/*ARGSUSED*/
Xdisplay(fields, rcd, dummy1, dummy2)
Xstruct  Fdata   *fields;
Xchar    *rcd;
XFILE	*dummy1;
Xchar    *dummy2;
X{
X    char	c, save[DBSIZE+1];
X    FILE	*filep;
X    char	*first = "Selected Records";
X    static      char    fmt[256];
X    int 	fn;
X    struct      Fdata   *fp;
X    
X    strcpy(save, rcd);
X    putp(clear_screen);		/* clear screen */
X    if (*fmt == '\0') {		/* first time */
X	fp = fields;
X	fn = 1;
X	while (fp->F_title[0]) {
X	    sprintf(&fmt[strlen(fmt)], "%s: %%%d%%n", fp->F_title, fn);
X	    ++fn;
X	    ++fp;
X	}
X    }
X    putrcd(first, rcd, stdout, fmt, SWIDTH, 2);
X    putp(tparm(cursor_address, MSGLINE, 9));
X    fputs("RETURN for next, ESC to abort, CTL-P to print, CTL-B to reverse",
X	  stdout);
X    noecho();
X    while ((c=rawgetchar()) != LF && c != CR) {
X	if (c == DLE) {		/* CTL-P entered */
X	    if ((filep=popen("lp", "w")) == NULL) {
X		msg("Unable to open printer");
X		continue;
X	    }
X	    strcpy(rcd, save);
X	    putrcd(first, rcd, filep, fmt, PWIDTH, 1);
X	    pclose(filep);
X	    continue;
X	}
X	if (c == ESC) {
X	    echo();
X	    return(1);
X	}
X	if (c == STX) {
X	    echo();
X	    return(-1);
X	}
X	rawputchar(BEL);
X    }
X    echo();
X    return(0);
X}
SHAR_EOF
chmod 0444 find.c || echo "restore of find.c fails"
echo "x - extracting findrcds.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > findrcds.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)findrcds.c	2.3    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      FINDRCDS.C      */
X/*      This module is used to find records in the data base
X**      matching the selection criteria.
X**      The user may select multiple values of one field
X**      connected by '&' or '|' which will be or'd and/or
X**      multiple fields which will be and'd.
X**      In other words, if field 1 is specified
X**      as value1|value2 and field3 is specified as value3.
X**      Records with field1==(value1 || value2) && field3==value3
X**      will be selected. An external routine 'display', passed
X**      as a parameter will be called to process the selected records.
X*/
X
X#include <stdio.h>
X#include <errno.h>
X#include "ascii.h"
X#include "cardfile.h"
X
Xextern	int	readonly;
X
Xstruct Keys {
X    char	K_flag;		/*     0 = good record
X				 *     1 = found on this search argument
X				 *    64 = physical end of key array
X				 *   128 = first free key block */
X    long	K_offset;
X};
X
X
Xfindrcds(fields, dbname, display, first)
Xstruct  Fdata   *fields;
Xchar    *dbname;
Xint     (*display)();
Xchar    *first;
X{
X    struct      Fdata   *fp;
X    struct      Sdata   find_screen[MAXFLDS+1], *sp;
X    int 	found;
X    FILE	*filep;
X    char	filename[FNSIZE];
X    diskptr     offset;
X    char	*last =
X		"Separate alternate values with a | for 'or' or a & for 'and'";
X    int		knum, firstflag;
X    long	getkey();
X    char	*keyval, *cp;
X    char	*rcd;
X    char	operator, nextop;
X    char	out_line[SWIDTH+1];
X    struct	Keys	keys[MAXKEYS+1], *kp;
X    int		i;
X    int 	rc;
X    char	*getslct(), *malloc();
X    
X    fp = fields;
X    sp = find_screen;
X    while (fp->F_title[0] != '\0') {
X	if (fp->F_key == 'N') {
X	    ++fp;
X	    continue;
X	}
X	sp->S_title = fp->F_title;
X	sp->S_length = 2 * fp->F_length;
X	sp->S_result = malloc((unsigned)sp->S_length+1);
X	sp->S_dfault = 0;
X	++fp;
X	++sp;
X    }
X    sp->S_title = 0;
X    while (screen(first, find_screen, last, 0) != 0) {
X	firstflag = 1;
X	knum = -1;
X	keys[0].K_flag = 128;
X	for (i=1; i<MAXKEYS; ++i)
X	    keys[i].K_flag = 0;
X	keys[MAXKEYS].K_flag = 64;
X	sp = find_screen;
X	while (sp->S_title) {           /* loop on key field */
X	    ++knum;
X	    if (*(sp->S_result) == '\0') {      /* was key entered */
X		++sp;
X		continue;
X	    }
X	    sprintf(filename, "%s%s.ak%d", datadir, dbname, knum);
X	    if ((filep = fopen(filename, "r")) == NULL) {
X		msg("Unable to open alternate key file");
X		getout();
X	    }
X	    operator = '&';
X	    if (firstflag) {
X		firstflag = 0;
X		operator = '|';
X	    }
X	    cp = sp->S_result;
X	    /* loop for each key for this field */
X	    while ((keyval = getslct(cp, "&|", &nextop)) != NULL) {
X		cp = NULL;
X		/* loop getting offsets for this value */
X		while ((offset = getkey(filep, keyval)) >= 0L) {
X		    keyval = 0;
X		    if (operator == '|') {
X			if (orkey(offset, keys) == -1)
X			    break;
X		    }
X		    if (operator == '&') {
X			andkey(offset, keys);
X		    }
X		}
X		if (operator == '&') {
X		    purgekey(keys);
X		}
X		operator = nextop;
X	    }
X	    fclose(filep);
X	    ++sp;
X	}
X	sprintf(filename, "%s%s.db", datadir, dbname);
X	if (readonly) {
X	    if ((filep = fopen(filename, "r")) == NULL) {
X		sprintf(out_line, "Unable to open %s, errno=%d\n", filename, errno);
X		msg(out_line);
X		return(1);
X	    }
X	} else {
X	    if ((filep = fopen(filename, "r+")) == NULL) {
X		if (errno == EACCES) {
X		    strcpy(out_line, "You do not have permission to modify this database");
X		} else {
X		    sprintf(out_line, "Unable to open %s, errno=%d\n", filename, errno);
X		}
X		msg(out_line);
X		return(1);
X	    }
X	}
X	found = 0;
X	rcd = malloc(DBSIZE);
X	kp = keys;
X	while (kp->K_flag == 0) {
X	    if (kp->K_offset <0L) {
X		++kp;
X		continue;
X	    }
X	    fseek(filep, kp->K_offset, 0);
X	    if (fgets(rcd, DBSIZE, filep) == NULL) {
X		msg("Error reading DB file");
X		getout();
X	    }
X	    if (*rcd == 'D') {
X		kp->K_offset = -1L;
X		++kp;
X		continue;
X	    }
X	    ++found;
X	    if ((rc = display(fields, rcd, filep, dbname)) == 1)
X		break;
X	    if (rc == -1) {	/* reverse */
X		while (kp > &keys[0]) {
X		    --kp;
X		    if (kp->K_offset >= 0L)
X			break;
X		}
X	    } else {
X		++kp;
X	    }
X	}
X	fclose(filep);
X	free(rcd);
X	if (found == 0) {
X	    msg("No records found");
X	}
X    }
X    sp = find_screen;
X    while (sp->S_title) {
X	free(sp->S_result);
X	++sp;
X    }
X    return(0);
X}
X
X
Xchar *
Xgetslct(string, seps, sepval)   /* identical to getfld except separator is */
Xchar    *string, *seps, *sepval;        /* returned thru sepval */
X{
X    register char       *p, *r;
X    static   char       *savept;
X    
X    p = (string == NULL)? savept : string;
X    if (p == 0)
X	return(NULL);
X    if (*p == '\0')
X	return(NULL);
X    if ((r = strpbrk(p, seps)) == NULL) {       /* move past token */
X	*sepval = '\0';
X	savept = 0;             /* indicate this is the last token */
X    } else {
X	*sepval = *r;
X	*r = '\0';
X	savept = ++r;
X    }
X    return(p);
X}
X
X
Xorkey(offset, keys)
Xdiskptr	offset;
Xstruct	Keys	*keys;
X{
X    struct Keys	*save;
X
X    save = NULL;
X    while (keys->K_flag == 0) {
X	if (offset == keys->K_offset) {
X	    return(0);
X	}
X	if (keys->K_offset < 0L && save == NULL) {
X	    save = keys;	/* found free point for insertion */
X	}
X	++keys;
X    }
X    if (save == NULL) {
X	if (keys->K_flag == 64) {
X	    msg("Too many records selected");
X	    return(-1);
X	}
X	save = keys;
X	if ((++keys)->K_flag != 64)
X	    keys->K_flag = 128;
X    }
X    save->K_flag = 0;
X    save->K_offset = offset;
X    return(0);
X}
X
X#define MATCH	1
X
Xandkey(offset, keys)
Xdiskptr	offset;
Xstruct Keys	*keys;
X{
X
X    while(keys->K_flag < 64) {
X	if (offset == keys->K_offset) {
X	    keys->K_flag = MATCH;
X	    break;
X	}
X	++keys;
X    }
X}
X
X
Xpurgekey(keys)
Xstruct Keys	*keys;
X{
X
X    while(keys->K_flag < 64) {
X	if (keys->K_flag != MATCH) {
X	    keys->K_offset = -1L;
X	} else {
X	    keys->K_flag = 0;
X	}
X	++keys;
X    }
X}
SHAR_EOF
chmod 0444 findrcds.c || echo "restore of findrcds.c fails"
echo "x - extracting getkey.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > getkey.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)getkey.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*      GETKEY.C        */
X/*      This subroutine is used to search an alternate key file
X        to find records matching the input value. It returns the
X        offset of the record in the DB file if found, or -1 if
X        no match was found.
X*/
X#include "stdio.h"
X#include "cardfile.h"
X#include "ascii.h"
X
Xdiskptr
Xgetkey(file, val)
XFILE    *file;
Xchar    *val;
X{
X    static char *match;
X    char        rcd[AKSIZE];
X    long        atol();
X    char        *fgets();
X    int	rc;
X    
X    if (val != 0) {     /* first time */
X        match = val;
X        fseek(file, 0L, 0);
X    }
X    while (fgets(rcd, AKSIZE, file) != NULL) {
X        if ((rc = keymatch(rcd, match)) == -1) {
X	    break;
X	} else if (rc == 1) {
X            return (atol(strchr(rcd, ':')+1));
X        }
X    }
X    return (-1L);
X}
SHAR_EOF
chmod 0644 getkey.c || echo "restore of getkey.c fails"
echo "x - extracting keymatch.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > keymatch.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)keymatch.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*	KEYMATCH.C      */
X/*	This subroutine is used to test if the key in the AK file
X**	record matches the input value. Two options are supported,
X**	UNIX style regular expressions or simple matches. If 'RE'
X**	is defined regular expressions are allowed.
X**	If the input value is enclosed in quotes an exact match is
X**	required, otherwise upper/lower case is ignored. If simple
X**	matching is being done, an '*' at the end of an unquoted
X**	string matches everything.
X**/
X#include "cardfile.h"
X
X#ifdef RE
X# ifdef BSD
Xextern	char	*re_comp();
Xextern	int	re_exec();
X# else
Xextern	char	*regcmp(), *regex();
X# endif
X#endif
X
Xkeymatch(akrcd, val)
Xchar    *akrcd, *val;
X{
X    register int vlen;
X    char	convert[256];
X    char	key[256];
X    static int	lower;
X#ifdef RE
X    static char	lastval[256];
X# ifndef BSD
X    static char	*cval;
X# endif
X#else
X    register char *cp = convert,
X		  *kp = key;
X#endif
X    
X#ifdef RE
X    if (strcmp(val, lastval) != 0) {		/* new pattern */
X	vlen = strlen(val);
X# ifndef BSD
X	if (cval)		/* free last pattern if any */
X	    free (cval);
X# endif
X	strcpy(lastval, val);
X#else
X	vlen = strlen(val);
X#endif
X	strcpy(convert, val);
X	if ((*convert == '"' && convert[vlen-1] == '"')
X	    || (*convert == '\'' && convert[vlen-1] == '\'')) {
X	    strcpy(convert, convert+1);
X	    convert[vlen-2] = '\0';
X	    lower = 0;
X	} else {
X	    strlower(convert);
X	    lower = 1;
X	}
X#ifdef RE
X# ifdef BSD
X	if (re_comp(convert) != 0) {
X# else
X	if ((cval = regcmp(convert, 0)) == 0) {
X# endif
X	    msg("Invalid search pattern");
X	    return(-1);
X	}
X    }
X#endif
X    strcpy(key, akrcd);
X    *strchr(key, ':') = '\0';
X    if (lower) {
X	strlower(key);
X    }
X#ifdef RE
X# ifdef BSD
X    if (re_exec(key) != 0)
X        return(1);
X# else
X    if (regex(cval, key) != 0)
X        return(1);
X# endif
X    return(0);
X#else
X    while (*cp != '\0' && (lower == 0 || *cp != '*')) {
X	if (*cp != *kp)
X	    return(0);
X	++cp;
X	++kp;
X    }
X    if (*cp == '\0' && *kp != '\0')
X	return(0);
X    return(1);
X#endif
X}
X
X
Xstatic
Xstrlower(str)
Xregister char	*str;
X{
X
X    while (*str) {
X	if (*str >= 'A' && *str <= 'Z')
X	    *str = *str - 'A' + 'a';
X	++str;
X    }
X}
SHAR_EOF
chmod 0644 keymatch.c || echo "restore of keymatch.c fails"
echo "x - extracting maint.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > maint.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)maint.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*      MAINT.C         */
X/*      This module is displays the maintenance menu and
X        controls the execution of the associated routines
X*/
X#include "stdio.h"
X#include "cardfile.h"
X
Xchar    *mfuncts[]   = {"EXIT        ",
X                        "DUMP        ",
X                        "COMPRESS    ",
X                        "REBUILD AK's",
X                        "EXTRACT     ",
X                        0
X                        };
X#define DUMP    1
X#define COMPRES 2
X#define RBUILD  3
X#define EXTRACT 4
X#define EXIT    0
X
X
Xmaint(fields, dbname, ak_data)
Xstruct  Fdata   *fields;
Xchar    *dbname;
Xstruct  AKdata  *ak_data;
X{
X    char        first[SWIDTH];
X    int         func;
X    
X    sprintf(first, "Maintenance functions for %s", dbname);
X    while ((func = menu(first, mfuncts)) != EXIT) {
X        switch (func) {
X        case DUMP:
X            dumpdb(dbname, ak_data, fields);
X            continue;
X        case COMPRES:
X            compress(dbname);
X            rbuildak(dbname, ak_data, fields);
X            continue;
X        case RBUILD:
X            rbuildak(dbname, ak_data, fields);
X            continue;
X        case EXTRACT:
X            extract(fields, dbname);
X            continue;
X        default:
X            msg("Illegal function chosen");
X        }
X    }
X}
SHAR_EOF
chmod 0644 maint.c || echo "restore of maint.c fails"
echo "x - extracting menu.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > menu.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)menu.c	2.1    DeltaDate 2/13/89    ExtrDate 2/13/89";
X#endif
X
X/*      MENU.C          */
X/*      This subroutine is used to display a menu of commands
X        and wait for the user to select one.
X        It returns the number of the command selected.
X*/
X#include "cardfile.h"
X
Xmenu(dbname, functs)
Xchar    *dbname;
Xchar    **functs;
X{
X    int         func;
X    char        dummy[2];
X    struct      Sdata   *sp, *mscreen;
X    char	*malloc();
X
X    mscreen = (struct Sdata*)malloc((MAXFLDS+1) * sizeof(struct Sdata));
X    sp = mscreen;
X    do {
X        sp->S_title = *functs;
X        sp->S_length = 1;
X        sp->S_result = dummy;
X        sp->S_dfault = 0;
X        ++sp;
X    } while (*(functs++));
X    screen(dbname, mscreen, 0, &func);
X    free((char*)mscreen);
X    return (func);
X}
SHAR_EOF
chmod 0644 menu.c || echo "restore of menu.c fails"
echo "x - extracting printdb.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > printdb.c &&
X#ifndef lint
Xstatic char Sccsid[] = "@(#)printdb.c	2.2    DeltaDate 4/16/89    ExtrDate 4/16/89";
X#endif
X
X/*      PRINTDB.C       */
X/*      This module format prints the data base in the order requested.
X**      NOTE: If a record contains multiple values for the key selected,
X**      it will be printed multiple times.
X**      Alternativly it will format print an extracted file (see extract.c)
X**      The formats allowed are:
X**		%NN     - contents of field NN (1-maxfield)
X**		%NN(form) - contents of field NN in printf '%form' format
X**		%n      - new-line
X**		%t      - tab
X**		%f      - form feed
X**		%t(NN)  - tab to column NN
X**		%%      - %
X**      NOTE: '.' ends a format specification %1.1 prints the contents of
X**      field 1 concatenated with a '1'.
X**/
X#include "stdio.h"
X#include "cardfile.h"
X#include "ascii.h"
X#include <curses.h>
X#include <term.h>
X#include <signal.h>
X
Xchar    out_format[128], ext_file[15];
Xchar    out_file[15], out_width[4];
Xstruct  Sdata   first_screen[] = {
X			{"Format", 127, out_format, 0},
X			{"Extracted File", 14, ext_file, 0},
X			{"Output File", 14, out_file, 0},
X			{"Output Width", 3, out_width, 0},
X			{0, 0, 0, 0}
X		};
X
Xstatic	int	quit = 0;
Xstatic	int	(*old_sig)();
Xstatic	FILE	*out;
Xstatic		abort();
X
X
Xprintdb(fields, dbname)
Xstruct  Fdata *fields;
Xchar    *dbname;
X{
X    char	*keys[MAXAK+1];
X    char	keyv[MAXAK][TSIZE+1];
X    int 	nkeys, i;
X    struct      Fdata *fp;
X    char	aknum[MAXAK];
X    int 	keynum;
X    char	title[SWIDTH];
X    char	fname[FNSIZE];
X    FILE	*akfile, *dbfile;
X    char	akrec[AKSIZE+1], dbrec[DBSIZE+1];
X    long	offset, atol();
X    int 	width;
X    char	*last;
X    char	hline[1024];
X
X    sprintf(title, "Print %s data base", dbname);
X    while (1) {
X	last = "Enter ? for help.";
X	    screen(title, first_screen, last, 0);
X	    if (strcmp(ext_file, "?") == 0) {
X		strcpy(hline, "Enter the name of the extract file generated");
X		strcat(hline, " from\nthe maintenance menu. If you leave it");
X		strcat(hline, " blank, the entire\ndata base will be dumped.");
X		help(hline);
X		continue;
X	    }
X	    if (strcmp(out_width, "?") == 0) {
X		help("Enter the width of the output device, defaults to 80.");
X		continue;
X	    }
X	    if (*out_width == '\0') {
X		width = PWIDTH;
X	    } else {
X		width = atoi(out_width);
X	    }
X	    if (strcmp(out_file, "?") == 0) {
X		strcpy(hline, "Enter the name of the output file,");
X		strcat(hline, " defaults to '|lp', the printer.");
X		help(hline);
X		continue;
X	    }
X	    if (*out_file == '\0') {
X		strcpy(out_file, "|lp");
X	    }
X	    if (strcmp(out_format, "?") == 0) {
X		strcpy(hline, "Enter the output format. All characters will");
X		strcat(hline, " be\nprinted as entered except for % sequences.");
X		strcat(hline, "\n\n\t%NN\t - contents of field NN (1-maxfield)");
X		strcat(hline, "\n\t%NN(form) - contents of field NN in");
X		    strcat(hline, " printf '%form' format\n");
X		strcat(hline, "\t%n\t- new-line\n");
X		strcat(hline, "\t%t\t- tab\n");
X		strcat(hline, "\t%t(NN)\t- tab to column NN\n");
X		strcat(hline, "\t%f\t- form-feed\n");
X		strcat(hline, "\t%%\t- %");
X		help(hline);
X		continue;
X	    }
X	    if (*out_format == '\0') {  /* not specified, use default */
X	    fp = fields;
X	    i = 1;
X	    while (fp->F_title[0]) {
X		sprintf(&out_format[strlen(out_format)], "%s: %%%d%%n",
X		    fp->F_title, i);
X		++i;
X		++fp;
X	    }
X	    strcat(out_format, "%n%n%n");
X	}
X	break;
X    }
X    if (ext_file[0] == '\0') {
X	msg("No extract specified, entire database will be dumped");
X    }
X    fp = fields;
X    nkeys = 0;
X    if (ext_file[0] == '\0') {
X	while (fp->F_title[0] != '\0') {
X	    if (fp->F_key != 'N') {
X		sprintf(keyv[nkeys], "%-20s", fp->F_title);
X		keys[nkeys] = keyv[nkeys];
X		aknum[nkeys] = fp->F_key;
X		++nkeys;
X	    }
X	    ++fp;
X	}
X	keys[nkeys] = "Cancel              ";
X	aknum[nkeys] = '\0';
X	++nkeys;
X	keys[nkeys] = 0;
X	sprintf(title, "Print %s data base sorted by", dbname);
X	keynum = menu(title, keys);
X	if (aknum[keynum] == '\0') {    /* cancel */
X	    return(1);
X	}
X	if (*out_file == '|')
X	    out = popen(out_file+1, "w");
X	else
X	    out = fopen(out_file, "w");
X	if (out == NULL) {
X	    msg("Unable to open output file");
X	    return(1);
X	}
X
X	sprintf(fname, "%s%s.ak%c", datadir, dbname, aknum[keynum]);
X	if ((akfile = fopen(fname, "r")) == NULL) {
X	    msg("Unable to open alternate key file");
X	    getout();
X	}
X	sprintf(fname, "%s%s.db", datadir, dbname);
X	if ((dbfile = fopen(fname, "r")) == NULL) {
X	    msg("Unable to open db file");
X	    getout();
X	}
X	putp(tparm(cursor_address, MSGLINE, 9));
X	putp(clr_eol);		/* clear line */
X	fputs("DEL to abort printing", stdout);
X	old_sig = signal(SIGINT, abort);
X
X	while(!quit && fgets(akrec, AKSIZE, akfile) != NULL) {
X	    offset = atol(strchr(akrec, ':')+1);
X	    if (offset < 0L)
X		continue;
X	    fseek(dbfile, offset, 0);
X	    if (fgets(dbrec, DBSIZE, dbfile) == NULL) {
X		msg("Bad offset found");
X		continue;
X	    }
X	    if (*dbrec == 'D')  /* check for delete flag */
X		continue;
X	    putrcd(0, dbrec, out, out_format, width, 1);
X	}
X	fclose(akfile);
X    } else {
X	if ((dbfile = fopen(ext_file, "r")) == NULL) {
SHAR_EOF
echo "End of part 1"
echo "File printdb.c is continued in part 2"
echo "2" > s2_seq_.tmp
exit 0