v20i057: Maintain multiple databases of textual data, Part02/02
Rich Salz
rsalz at uunet.uu.net
Wed Oct 25 02:12:06 AEST 1989
Submitted-by: Dave Curry <davy at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list