[comp.sources.unix] v20i057: Maintain multiple databases of textual data, Part02/02

rsalz@uunet.uu.net (Rich Salz) (10/24/89)

Submitted-by: Dave Curry <davy@riacs.edu>
Posting-number: Volume 20, Issue 57
Archive-name: index-db/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  screen.c
# Wrapped by rsalz@papaya.bbn.com on Tue Oct 24 12:09:02 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(14727 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char *RCSid = "$Header: /u5/davy/progs/index/RCS/screen.c,v 1.1 89/08/09 11:06:53 davy Exp $";
X#endif
X/*
X * screen.c - screen management routines.
X *
X * David A. Curry
X * Research Institute for Advanced Computer Science
X * Mail Stop 230-5
X * NASA Ames Research Center
X * Moffett Field, CA 94035
X * davy@riacs.edu
X *
X * $Log:	screen.c,v $
X * Revision 1.1  89/08/09  11:06:53  davy
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <signal.h>
X#include <curses.h>
X#include <stdio.h>
X#include "defs.h"
X
Xstatic int	screen_inited = 0;	/* for use with set/reset modes	*/
X
X/*
X * Menu used by disp_entries routine.
X */
Xstatic char	*disp_menu[] = {
X	"<RET> = next entry                   \"d\" = delete this entry\n",
X	"\"-\" = previous entry                 \"e\" = edit this entry\n",
X	"\"q\" = return to main menu",
X	0
X};
X
X/*
X * prompt_str - prompt the user for a string.
X */
Xprompt_str(row, col, promptstr, answer)
Xchar *promptstr, *answer;
Xint row, col;
X{
X	char *line;
X	int len, col0;
X	register int c, i, j;
X
X	/*
X	 * Print the prompt at (row,col).
X	 */
X	mvaddstr(row, col, promptstr);
X	refresh();
X
X	/*
X	 * Calculate "column zero", which is at the right
X	 * end of the prompt.
X	 */
X	col += strlen(promptstr);
X	line = answer;
X	col0 = col;
X	len = 0;
X
X	/*
X	 * Read characters till we get what we want.  The user
X	 * is allowed basic EMACS-style line editing.
X	 */
X	while ((c = getchar()) != EOF) {
X		switch (c) {
X		case CTRL(a):			/* beginning of line	*/
X			col = col0;
X			break;
X		case CTRL(b):			/* back character	*/
X			if (col > col0)
X				col--;
X			break;
X		case CTRL(d):			/* delete character	*/
X			/*
X			 * If there's stuff in the string,
X			 * delete this character.
X			 */
X			if (len) {
X				/*
X				 * Calculate string position of
X				 * character to delete.
X				 */
X				i = col - col0;
X
X				/*
X				 * Shuffle the string "left" one
X				 * place.
X				 */
X				while (i < len) {
X					line[i] = line[i+1];
X					i++;
X				}
X
X				/*
X				 * Delete the character on the screen.
X				 */
X				len -= 1;
X				delch();
X			}
X
X			break;
X		case CTRL(e):			/* end of line		*/
X			col = col0 + len;
X			break;
X		case CTRL(f):			/* forward character	*/
X			if ((col - col0) < len)
X				col++;
X			break;
X		case CTRL(h):			/* backspace delete	*/
X		case '\177':
X			/*
X			 * If there's stuff in the string,
X			 * delete the character.
X			 */
X			if (len) {
X				/*
X				 * Calculate position in string of
X				 * character to be deleted.
X				 */
X				i = col - col0 - 1;
X
X				/*
X				 * Shuffle the string "left" one place.
X				 */
X				while (i < len) {
X					line[i] = line[i+1];
X					i++;
X				}
X
X				len -= 1;
X
X				/*
X				 * Delete the character on the screen.
X				 */
X				move(row, --col);
X				delch();
X			}
X			break;
X		case CTRL(k):			/* kill line		*/
X			/*
X			 * Clear the string.
X			 */
X			if (len) {
X				i = col - col0;
X
X				line[i] = '\0';
X				len = i;
X
X				clrtoeol();
X			}
X			break;
X		case CTRL(l):			/* redraw screen	*/
X			wrefresh(curscr);
X			break;
X		case '\n':			/* return the string	*/
X			line[len] = '\0';
X			return;
X		default:			/* regular character	*/
X			/*
X			 * If it's the user's line-kill character,
X			 * we'll accept that to kill the line too.
X			 */
X			if (c == _tty.sg_kill) {
X				move(row, col0);
X				clrtoeol();
X				col = col0;
X
X				*line = '\0';
X				len = 0;
X			}
X			else {
X				/*
X				 * If it's a printable character,
X				 * insert it into the string.
X				 */
X				if ((c >= ' ') && (c < '\177')) {
X					/*
X					 * Calculate position of character
X					 * in string.
X					 */
X					i = col - col0;
X
X					/*
X					 * If we have to, move the
X					 * string "right" one place
X					 * to insert the character.
X					 */
X					if (i < len) {
X						for (j = len; j >= i; j--)
X							line[j+1] = line[j];
X					}
X
X					line[i] = c;
X					len += 1;
X					col++;
X
X					/*
X					 * Insert the character on the
X					 * screen.
X					 */
X					insch(c);
X
X				}
X			}
X			break;
X		}
X
X		/*
X		 * Move the cursor.
X		 */
X		move(row, col);
X		refresh();
X	}
X}
X
X/*
X * prompt_char - prompt the user for a single character, which must
X *		 be one of the ones in the "valid" string.
X */
Xprompt_char(row, col, promptstr, valid)
Xchar *promptstr, *valid;
Xint row, col;
X{
X	char *index();
X	register int c;
X
X	/*
X	 * Print the prompt.
X	 */
X	mvaddstr(row, col, promptstr);
X	clrtoeol();
X	refresh();
X
X	/*
X	 * Read characters...
X	 */
X	while ((c = getchar()) != EOF) {
X		/*
X		 * If it's not a valid one, beep
X		 * and get another one.
X		 */
X		if (index(valid, c) == NULL) {
X			putc('\007', stdout);
X			fflush(stdout);
X			continue;
X		}
X
X		/*
X		 * Add the character to the
X		 * screen, and return it.
X		 */
X		addch(c);
X		refresh();
X
X		return(c);
X	}
X}
X
X/*
X * main_menu - we dispatch to database functions from here.
X */
Xmain_menu(dbname)
Xchar *dbname;
X{
X	register char c;
X
X	/*
X	 * Set tty modes.
X	 */
X	set_modes();
X
X	/*
X	 * Forever...
X	 */
X	for (;;) {
X		/*
X		 * Clear the screen.
X		 */
X		clear();
X
X		/*
X		 * Print the name of the database and number of
X		 * entries.
X		 */
X		mvprintw(0, 0, "Database: %s  (%d entries)", dbname,
X			 dbentries);
X
X		/*
X		 * Mention whether or not it's modified.
X		 */
X		if (dbmodified)
X			addstr("  [modified]");
X
X		/*
X		 * Print the choices.
X		 */
X		mvaddstr(3, 8, "a - Add new entry to database");
X		mvaddstr(4, 8, "f - Find entry in database");
X		mvaddstr(5, 8, "r - Read database entry by entry");
X		mvaddstr(6, 8, "s - Save modifications, do not exit");
X		mvaddstr(7, 8, "q - Save modifications, exit");
X		mvaddstr(8, 8, "x - Exit");
X
X		/*
X		 * Get one of the choices.
X		 */
X		c = prompt_char(10, 0, "Command: ", "afqrsx\014");
X
X		/*
X		 * Dispatch the choice to the proper
X		 * database function.
X		 */
X		switch (c) {
X		case 'a':
X			add_entry();
X			break;
X		case 'f':
X			find_entry();
X			break;
X		case 'q':
X			save_bye(dbname);
X			break;
X		case 'r':
X			read_db();
X			break;
X		case 's':
X			save_db(dbname);
X			break;
X		case 'x':
X			byebye();
X			break;
X		case CTRL(l):			/* redraw screen	*/
X			wrefresh(curscr);
X			break;
X		}
X	}
X}
X
X/*
X * disp_entries - display all database entries with DB_PRINT set, and
X *		  let the user choose what to do with the entry.
X */
Xdisp_entries()
X{
X	int first, save = -1;
X	register int c, i, j;
X
X	/*
X	 * Clear the screen.
X	 */
Xtop:	clear();
X
X	/*
X	 * Print the names of the fields.
X	 */
X	for (i = 0; i < idx.idx_nlines; i++) {
X		mvprintw(i, 0, "%s%s", idx.idx_lines[i],
X			 idx.idx_lines[i][0] ? ":" : "");
X	}
X
X	/*
X	 * Print the menu.
X	 */
X	for (i=0; disp_menu[i] != NULL; i++)
X		mvaddstr(idx.idx_nlines+i+2, 10, disp_menu[i]);
X
X	/*
X	 * Find the first printable entry, and save its
X	 * index in first.
X	 */
X	for (first=0; first < dbentries; first++) {
X		if (db[first].db_flag & DB_PRINT)
X			break;
X	}
X
X	/*
X	 * Set current entry to either the first one
X	 * or the saved one.
X	 */
X	if (save < 0)
X		i = first;
X	else
X		i = save;
X
X	/*
X	 * Until we run out of entries...
X	 */
X	while (i < dbentries) {
X		/*
X		 * Print the entry.
X		 */
X		for (j=0; j < idx.idx_nlines; j++) {
X			move(j, idx.idx_maxlen + 2);
X			clrtoeol();
X			addstr(db[i].db_lines[j]);
X		}
X
X		/*
X		 * Print current/total entry numbers.
X		 */
X		mvprintw(idx.idx_nlines+7, COLS-15, "%5d/%-5d", i+1,
X			 dbentries);
X
X		/*
X		 * See what they want to do with this entry.
X		 */
X		c = prompt_char(idx.idx_nlines+6, 0, "Command: ",
X				"-deq\n\014");
X
X		/*
X		 * Dispatch the command...
X		 */
X		switch (c) {
X		case '\n':			/* go to next entry	*/
X			/*
X			 * Save this one.
X			 */
X			save = i;
X
X			/*
X			 * Advance to next printable one.
X			 */
X			for (i++; i < dbentries; i++) {
X				if (db[i].db_flag & DB_PRINT)
X					break;
X			}
X
X			break;
X		case '-':			/* go to previous entry	*/
X			/*
X			 * Save this one.
X			 */
X			save = i;
X
X			/*
X			 * Hunt for the previous printable one.
X			 */
X			for (i--; i >= 0; i--) {
X				if (db[i].db_flag & DB_PRINT)
X					break;
X			}
X
X			if (i < 0)
X				i = first;
X
X			break;
X		case 'd':			/* delete entry		*/
X			/*
X			 * See if they really want to delete it.
X			 * If so, mark it as not valid, and
X			 * re-sort the database.
X			 *
X			 * Because of the save variable, next
X			 * time through we'll print the last
X			 * one they looked at.
X			 */
X			if (del_entry(&db[i])) {
X				db[i].db_flag &= ~(DB_VALID | DB_PRINT);
X				qsort(db, dbentries, sizeof(struct dbfile),
X				      dbsort);
X				dbmodified = 1;
X				dbentries--;
X				goto top;
X			}
X
X			save = i;
X			goto top;
X		case 'e':			/* edit entry		*/
X			/*
X			 * Let them edit the entry.
X			 */
X			if (edit_entry(&db[i], "modified"))
X				dbmodified = 1;
X
X			save = i;
X			goto top;
X		case 'q':			/* return to main menu	*/
X			return;
X		case '\014':			/* redraw screen	*/
X			break;
X		}
X	}
X}
X
X/*
X * edit_entry - allow the user to edit a database entry.
X */
Xedit_entry(entry, word)
Xstruct dbfile *entry;
Xchar *word;
X{
X	int *len;
X	int col0;
X	char *line;
X	char tbuf[64];
X	char *malloc();
X	struct dbfile tmp;
X	register int c, i, j, row, col;
X
X	/*
X	 * Figure out where "column zero" is, to the
X	 * right of the longest field name.
X	 */
X	col0 = idx.idx_maxlen + 2;
X
X	/*
X	 * Clear the screen.
X	 */
X	clear();
X
X	/*
X	 * Print the field names.
X	 */
X	for (row = 0; row < idx.idx_nlines; row++) {
X		mvprintw(row, 0, "%s%s", idx.idx_lines[row],
X			 idx.idx_lines[row][0] ? ":" : "");
X	}
X
X	/*
X	 * Allocate some space in a temporary entry, and copy
X	 * the entry to be edited into it.  This way they can
X	 * abort the edit.
X	 */
X	for (i=0; i < idx.idx_nlines; i++) {
X		/*
X		 * Allocate memory for this line.
X		 */
X		if ((tmp.db_lines[i] = malloc(BUFSIZ)) == NULL) {
X			error("%s: out of memory.\n", pname, 0, 0, 0);
X			exit(1);
X		}
X
X		/*
X		 * Save the length of the entry, zero the
X		 * memory.
X		 */
X		tmp.db_lens[i] = entry->db_lens[i];
X		bzero(tmp.db_lines[i], BUFSIZ);
X
X		/*
X		 * Copy and print the line from the entry.
X		 */
X		if (entry->db_lines[i]) {
X			strcpy(tmp.db_lines[i], entry->db_lines[i]);
X			mvaddstr(i, col0, entry->db_lines[i]);
X		}
X	}
X
X	col = col0;
X	row = 0;
X
X	move(row, col);
X	refresh();
X
X	/*
X	 * Now let them edit the entry.  We provide the basic EMACS-style
X	 * control characters.
X	 */
X	while ((c = getchar()) != EOF) {
X		/*
X		 * Get the current line and line length.
X		 */
X		line = tmp.db_lines[row];
X		len = &tmp.db_lens[row];
X
X		switch (c) {
X		case CTRL(a):			/* beginning of line	*/
X			col = col0;
X			break;
X		case CTRL(b):			/* back character	*/
X			if (col > col0)
X				col--;
X			break;
X		case CTRL(d):			/* delete character	*/
X			if (*len) {
X				/*
X				 * Calculate position of character
X				 * in string.
X				 */
X				i = col - col0;
X
X				/*
X				 * Shuffle the string to the "left".
X				 */
X				while (i < *len) {
X					line[i] = line[i+1];
X					i++;
X				}
X
X				*len -= 1;
X
X				/*
X				 * Delete the character on the screen.
X				 */
X				delch();
X			}
X
X			break;
X		case CTRL(e):			/* end of line		*/
X			col = col0 + *len;
X			break;
X		case CTRL(f):			/* forward character	*/
X			if ((col - col0) < *len)
X				col++;
X			break;
X		case CTRL(h):			/* backspace delete	*/
X		case '\177':
X			if (*len) {
X				/*
X				 * Calculate position of character to
X				 * delete.
X				 */
X				i = col - col0 - 1;
X
X				/*
X				 * Shuffle string "left".
X				 */
X				while (i < *len) {
X					line[i] = line[i+1];
X					i++;
X				}
X
X				*len -= 1;
X
X				/*
X				 * Delete the character from the screen.
X				 */
X				move(row, --col);
X				delch();
X			}
X			break;
X		case CTRL(k):			/* kill line		*/
X			/*
X			 * Kill the line.
X			 */
X			if (*len) {
X				i = col - col0;
X
X				line[i] = '\0';
X				*len = i;
X
X				clrtoeol();
X			}
X			break;
X		case CTRL(l):			/* redraw screen	*/
X			wrefresh(curscr);
X			break;
X		case CTRL(n):			/* next line		*/
X			/*
X			 * Wrap around to the top if necessary.
X			 */
X			if (++row >= idx.idx_nlines)
X				row = 0;
X
X			/*
X			 * If nothing in this column, move to
X			 * nearest non-empty column.
X			 */
X			if ((col - col0) > tmp.db_lens[row])
X				col = col0 + tmp.db_lens[row];
X			break;
X		case CTRL(p):			/* previous line	*/
X			/*
X			 * Wrap around if necessary.
X			 */
X			if (--row < 0)
X				row = idx.idx_nlines - 1;
X
X			/*
X			 * If nothing in this column, move to
X			 * nearest non-empty column.
X			 */
X			if ((col - col0) > tmp.db_lens[row])
X				col = col0 + tmp.db_lens[row];
X			break;
X		case CTRL([):			/* save entry		*/
X			/*
X			 * Prompt for whether to save the entry.
X			 */
X			sprintf(tbuf, "Save %s entry in database? ", word);
X			c = prompt_char(idx.idx_nlines+2, 0, tbuf, "YyNn\n");
X
X			/*
X			 * See what they said.
X			 */
X			switch (c) {
X			case '\n':		/* never mind		*/
X				move(idx.idx_nlines+2, 0);
X				clrtoeol();
X				break;
X			case 'Y':		/* save entry		*/
X			case 'y':
X				/*
X				 * Copy the temporary entry into the real
X				 * entry.
X				 */
X				for (i=0; i < idx.idx_nlines; i++) {
X					if (entry->db_lines[i])
X						free(entry->db_lines[i]);
X
X					entry->db_lines[i] = savestr(tmp.db_lines[i]);
X					entry->db_lens[i] = tmp.db_lens[i];
X					free(tmp.db_lines[i]);
X				}
X
X				return(1);
X			case 'N':		/* don't save entry	*/
X			case 'n':
X				/*
X				 * Free temporary memory.
X				 */
X				for (i=0; i < idx.idx_nlines; i++)
X					free(tmp.db_lines[i]);
X				return(0);
X			}
X			break;
X		case '\n':			/* go to next line	*/
X			/*
X			 * Wrap around if necessary.
X			 */
X			if (++row >= idx.idx_nlines)
X				row = 0;
X			col = col0;
X			break;
X		default:			/* something else	*/
X			/*
X			 * If it's the user's kill character, we'll
X			 * accept that to delete the line too.
X			 */
X			if (c == _tty.sg_kill) {
X				move(row, col0);
X				clrtoeol();
X				col = col0;
X
X				*line = '\0';
X				*len = 0;
X			}
X			else {
X				/*
X				 * If it's a printable character, insert
X				 * it into the string.
X				 */
X				if ((c >= ' ') && (c < '\177')) {
X					/*
X					 * Calculate character position.
X					 */
X					i = col - col0;
X
X					/*
X					 * If necessary, move the string
X					 * to the "right" to insert the
X					 * character.
X					 */
X					if (i < *len) {
X						for (j = *len; j >= i; j--)
X							line[j+1] = line[j];
X					}
X
X					line[i] = c;
X					*len += 1;
X					col++;
X
X					/*
X					 * Insert the character on the
X					 * screen.
X					 */
X					insch(c);
X				}
X			}
X			break;
X		}
X
X		/*
X		 * Move to the current row/column.
X		 */
X		move(row, col);
X		refresh();
X	}
X}
X
X/*
X * reset_modes - restore tty modes to their original state.
X */
Xreset_modes()
X{
X	/*
X	 * No need.
X	 */
X	if (!screen_inited)
X		return;
X
X	screen_inited = 0;
X
X	/*
X	 * Move to bottom of screen.
X	 */
X	move(LINES-1, 0);
X	refresh();
X
X	/*
X	 * Restore modes.
X	 */
X	nocbreak();
X	echo();
X
X	/*
X	 * Turn curses "off".
X	 */
X	endwin();
X
X	signal(SIGQUIT, SIG_DFL);
X	signal(SIGINT, SIG_DFL);
X}
X
X/*
X * set_modes - set tty modes to what we want.
X */
Xset_modes()
X{
X	/*
X	 * Already done.
X	 */
X	if (screen_inited)
X		return;
X
X	screen_inited = 1;
X
X	/*
X	 * Ignore signals.
X	 */
X	signal(SIGQUIT, SIG_IGN);
X	signal(SIGINT, SIG_IGN);
X
X	/*
X	 * Turn "curses" on, turn echo off, turn cbreak on.
X	 */
X	initscr();
X	noecho();
X	cbreak();
X}
X
END_OF_FILE
if test 14727 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.