home *** CD-ROM | disk | FTP | other *** search
- Subject: v13i044: Rolodex-like filing system
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Larry Lippman <kitty!larry@UUNET.UU.NET>
- Posting-number: Volume 13, Issue 44
- Archive-name: rf
-
- The enclosed program is called rf(1L). It is a simple database
- program written in C which stores names, addresses, telephone numbers,
- and other data. It functions not unlike a "rotary file" for fast access,
- which is why it is called ``rf''.
- This program has been tested on several different System V ports
- with no problems. Some BSD ports may require some minor curses changes,
- but since I am not a BSD-person, I can't be specific. There should be no
- other processor/port dependencies beyond curses implementation.
-
- <> Larry Lippman @ Recognition Research Corp., Clarence, New York
- <> UUCP: {allegra|ames|boulder|decvax|rutgers|watmath}!sunybcs!kitty!larry
- <> VOICE: 716/688-1231 {hplabs|ihnp4|mtune|utzoo|uunet}!/
- <> FAX: 716/741-9635 {G1,G2,G3 modes} "Have you hugged your cat today?"
-
- # This is a shell archive. Remove anything before this line, then
- # unpack it by saving it in a file and typing "sh file". (Files
- # unpacked will be owned by you and have default permissions.)
- #
- # This archive contains:
- # Makefile README rf.1 rf.c
-
- echo x - Makefile
- sed -e 's/^X//' > "Makefile" << '//E*O*F Makefile//'
- X# Makefile for rf(1L)
- X# Last revision 3 Jun 1985
- X#
- X# define SYSDATA if system file other than /usr/local/lib/rf_data
- X#
- XSRC = rf.c
- XLIBS = -lcurses
- XCFLAGS = -O -s
- XDESTDIR = /usr/bin/
- X
- Xrf: $(SRC)
- X cc $(CFLAGS) $(SRC) -o rf $(LIBS)
- X
- Xinstall:
- X chmod 555 rf
- X chgrp bin rf
- X chown bin rf
- X mv rf $(DESTDIR)
- X
- Xlint:
- X lint rf.c
- //E*O*F Makefile//
-
- echo x - README
- sed -e 's/^X//' > "README" << '//E*O*F README//'
- X The enclosed program is called rf(1L). It is a simple database
- Xprogram written in C which stores names, addresses, telephone numbers,
- Xand other data. It functions not unlike a "rotary file" for fast access,
- Xwhich is why it is called ``rf''.
- X In its present form it will search for a record by individual
- Xname, or by organization name. Any size search string may be used as
- Xthe key; command line parsing is intelligent enough such that quotes are
- Xunnecessary if the search string contains embedded whitespace. The output
- Xis displayed in a neat page-oriented fashion using curses(3X), with a paging
- Xcontrol where multiple matches are encountered.
- X The database file is easily configured using any editor. The
- Xdatabase file format is also designed so that it may be easily accessed by
- Xawk or sed for mailing list or other special applications. By setting an
- Xoption flag, the program will access a system-wide database, or a private
- Xdatabase file in the user's home directory.
- X While the program could have been written using awk and tput, it
- Xis substantially faster using C. In keeping with the recent discussion
- Xabout writing "new" UNIX functions, the increased speed is THE reason
- Xfor writing it in C. Typical search and display (9,600 baud) time on
- Xa 3B2 for one record from a 30 kilobyte database file is < 2.0 seconds;
- Xthis includes curses(3X) overhead - so it's pretty fast (the entire database
- Xfile is always searched to detect multiple matches).
- X The program is reasonably well commented, is intentionally written
- Xto be easily modified, and is reasonably well protected against users doing
- X"dumb" things. The program runs on three different Sys V versions, and
- Xshould probably run under BSD since it contains its own string search
- Xfunction and does not use getopt; the curses(3X) use is not particularly
- Xexotic.
- X The actual fields and field lengths were chosen to provide a
- Xprogram that fit the needs of my organization - an industrial R&D laboratory
- Xwhich communicates a GREAT deal with various other organizations.
- X The program is intentionally written so that the field definitions
- Xand their lengths can be easily modified for a particular organization.
- XFor example, our organization often sends telex and facsimile machine
- Xmessages, but many organizations NEVER send such messages - so these
- Xfields could be removed, allowing character expansion of the telephone
- Xnumber and uucp address fields.
- X Some people may not like my choice of video attributes for the
- Xpage display; obviously, this is easy to change.
- X The program fully passes lint, except for some silliness about
- Xunused curses(3X) functions.
- X If you are willing to accept my file name definitions and
- Xlocations, all you have to do is type ``make''...
- X
- X<> Larry Lippman @ Recognition Research Corp., Clarence, New York
- X<> UUCP: {allegra|ames|boulder|decvax|rutgers|watmath}!sunybcs!kitty!larry
- X<> VOICE: 716/688-1231 {hplabs|ihnp4|mtune|utzoo|uunet}!/
- X<> FAX: 716/741-9635 {G1,G2,G3 modes} "Have you hugged your cat today?"
- //E*O*F README//
-
- echo x - rf.1
- sed -e 's/^X//' > "rf.1" << '//E*O*F rf.1//'
- X.TH RF 1 Local
- X.UC 4
- X.SH NAME
- Xrf \- "rotary file" database for names, addresses, etc.
- X.SH SYNOPSIS
- X.B rf
- X[
- X.B \-f
- X] [
- X.B \-l
- X] [
- X.B \-o
- X] [
- X.B search string
- X]
- X.SH DESCRIPTION
- X.B Rf
- Xis a simple "rotary file" database which stores names, addresses,
- Xtelephone numbers, comments, etc. for rapid retrieval in a formatted
- Xpage fashion.
- X.B Rf
- Xsearches its database file using a search string keyed to a
- X.I person's
- X.B name
- Xor
- X.I person's
- X.B organization.
- XMultiple matches are paginated using a single key command.
- X.PP
- XThe
- X.B \-f
- Xflag selects a private database file installed as
- X.B .rf_data
- Xin the user's home directory. Invoking
- X.B rf
- Xwithout this flag selects the database file
- X.B /usr/local/lib/rf_data
- Xavailable to all users.
- X.PP
- XThe
- X.B \-l
- Xflag lists only the
- X.B name
- Xor
- X.B organization
- Xfield matches without displaying the rest of the record,
- Xand is used for rapid
- Xscanning of the database file where multiple matches may occur.
- X.PP
- XThe
- X.B \-o
- Xflag searches for a match in the
- X.B organization
- Xfield, rather than the
- X.B name
- Xfield.
- X.PP
- X[
- X.B search string
- X] may be any string of 1 to 72 characters in length; the string may
- Xcontain one or more instances of whitespace without having the
- Xstring enclosed in quotes. There is no upper <--> lower case
- Xconversion; the case presented is matched as is, and may be mixed.
- XPunctuation and whitespace
- Xembedded in the string is also matched;
- Xeach occurrence of whitespace must be limited to a length of
- Xone space.
- X.SH DATABASE FILE FORMAT
- XEach record consists of a minimum of two fields,
- Xwith all fields containing a two\-character identifier
- Xin the form of a letter followed by a colon.
- XThe data portion of the field may contain whitespace or any
- Xpunctuation to a maximum character length as described below.
- XEach record must begin with a
- X.B name
- Xfield; if there is an
- X.B organization
- Xfield, it must immediately follow the
- X.B name
- Xfield.
- XIf the record pertains to an
- X.B organization
- Xonly having no
- X.I person's
- X.B name
- Xentry, the
- X.B name
- Xfield identifier is still necessary,
- Xwith the rest of the field blank.
- XAll other fields are optional and may be included in any order.
- XRecords are separated by one blank line.
- X.PP
- XEach field is limited to one entry per record,
- Xexcept that
- X.B telephone,
- X.B telex,
- X.B fax
- Xand
- X.B uucp
- Xmay each have a maximum of two entries,
- Xfor line-order display as presented within the record;
- Xa maximum of four
- X.B comment
- Xentries is also permitted within the same record.
- X.PP
- X.nf
- X\fBN:name\fR 30 chars max.
- X\fBO:organization\fR 72 chars max.
- X\fBT:title\fR 24 chars max.
- X\fBD:department\fR 24 chars max.
- X\fBA:address\fR 72 chars max.
- X\fBP:telephone\fR 15 chars max.
- X\fBF:fax\fR 15 chars max.
- X\fBX:telex\fR 24 chars max.
- X\fBU:uucp\fR 24 chars max.
- X\fBH:home_telephone\fR 15 chars max.
- X\fBR:home_address\fR 72 chars max.
- X.fi
- X.PP
- XFields which exceed the above maximum number of characters
- Xresult in no error, but are silently truncated at
- Xthe maximum permissible length when displayed.
- XFields which contain incorrect header characters are ignored.
- XFor the sake of uniformity, comments in the database file
- Xthat are not intended for display
- Xshould be prefaced by the field header \fB#:\fR.
- X.SH EXAMPLE DATABASE FILE RECORD
- X.nf
- X.sh
- XN:Public, John Q.
- XO:Any Industry, Inc.
- XT:Systems Programmer
- XD:Widget R&D
- XA:123 Any Road, Anytown, NY 12345
- XP:716/123-4567
- XP:Ext 234
- XF:716/123-4599
- XX:12-3456 ANYINDNY
- XU:jqp@any.UUCP
- XH:716/123-9876
- XR:456 Anybrook Lane, Anysuburb, NY 12354
- XC:Writes CAD software for widgets
- XC:Has extensive experience with XYZNIX
- X.SH BUGS
- Xnone
- X.SH FILES
- X.nf
- X\fB$HOME/.rf_data\fR user private database file
- X\fB/usr/local/lib/rf_data\fR system-wide database file
- X.fi
- X.SH AUTHOR
- XLawrence G. Lippman, larry@kitty.UUCP
- //E*O*F rf.1//
-
- echo x - rf.c
- sed -e 's/^X//' > "rf.c" << '//E*O*F rf.c//'
- X/*
- X * rf(1L) A "rotary file"-like database for names, addresses, telephone
- X * numbers and related information.
- X *
- X * Copyright (c) 1985
- X * by Lawrence Lippman, larry@kitty.UUCP
- X * Recognition Research Corp., Clarence, NY
- X *
- X * This program may be freely used and distributed, provided that
- X * it is not used for monetary or other commercial gain, and
- X * provided that this notice remains intact.
- X *
- X * Last revision: 3 Jun 1985
- X */
- X
- X#include <stdio.h>
- X#include <curses.h>
- X#include <signal.h>
- X#include <string.h>
- X
- X#ifndef SYSDATA
- X#define SYSDATA "/usr/local/lib/rf_data" /* system database file */
- X#endif
- X
- X/*
- X * Global variables
- X */
- X char name[31], title[25], org[73], dept[25], adr[73];
- X char phone[2][16], telex[2][25], fax[2][16], uucp[2][25];
- X char homephone[16], homeadr[65], comments[4][73];
- X char Key[73], buf[80];
- X int Org, File, List;
- X int hits, phlines, comlines;
- X int terminate(), more(), strsearch();
- X FILE *f1, *fopen();
- X
- Xmain(argc,argv)
- X char **argv;
- X{
- X char filename[65], *getenv();
- X int keywords;
- X
- X/*
- X * Set up defaults
- X */
- X File = 0; /* Use system-wide database file, not user file */
- X List = 0; /* Display full entry, not just hit list */
- X Org = 0; /* Search by name, not organization */
- X
- X/*
- X * Get options and search string
- X */
- X (void) strcpy(Key,"");
- X keywords = 0;
- X
- X while(argc-- > 1) {
- X if(*argv[1] == '-')
- X switch(argv[1][1]) {
- X case 'l':
- X List = 1;
- X break;
- X case 'f':
- X File = 1;
- X break;
- X case 'o':
- X Org = 1;
- X break;
- X default:
- X usage();
- X }
- X
- X else{
- X if(keywords > 0)
- X (void) strcat(Key," ");
- X (void) strcat(Key,argv[1]);
- X keywords++;
- X }
- X argv++;
- X }
- X
- X if(strlen(Key) == 0)
- X usage();
- X
- X/*
- X * Select and open database file
- X */
- X if(File)
- X (void) sprintf(filename, "%s/.rf_data", getenv("HOME"));
- X else
- X (void) strcpy(filename, SYSDATA);
- X
- X if((f1 = fopen(filename,"r")) == NULL){
- X (void) fprintf(stderr,"Cannot open data file %s\n",filename);
- X exit(-1);
- X }
- X
- X/*
- X * Catch signals and setup curses
- X */
- X (void) signal(SIGINT, terminate);
- X (void) signal(SIGQUIT, terminate);
- X
- X initscr();
- X if(List){
- X idlok(stdscr,1);
- X setscrreg(0,19);
- X scrollok(stdscr,1);
- X }
- X
- X/*
- X * Read file, search for records, and do it to it...
- X */
- X hits = 0;
- X
- X while(fgets(buf,80,f1) != NULL){
- X if(buf[0] == 'N' && buf[1] == ':'){
- X if(strlen(buf) == 2)
- X (void) strcpy(name,"");
- X else{
- X (void) strxcpy(name,buf,2,30);
- X if(!Org){
- X if(strsearch(name,Key)){
- X if(hits > 0 && !List)
- X (void) more();
- X (void) strcpy(org,"");
- X (void) clrrecord();
- X (void) rdrecord();
- X (void) display();
- X hits++;
- X }
- X }
- X }
- X }
- X if(buf[0] == 'O' && buf[1] == ':'){
- X (void) strxcpy(org,buf,2,72);
- X if(Org){
- X if(strsearch(org,Key)){
- X if(hits > 0 && !List)
- X (void) more();
- X (void) clrrecord();
- X (void) rdrecord();
- X (void) display();
- X hits++;
- X }
- X }
- X }
- X }
- X
- X (void) terminate();
- X return(0);
- X}
- X
- X/*
- X * clrrecord: clear all display strings
- X */
- Xclrrecord()
- X{
- X int i;
- X
- X (void) strcpy(title,"");
- X (void) strcpy(dept,"");
- X (void) strcpy(adr,"");
- X (void) strcpy(homephone,"");
- X (void) strcpy(homeadr,"");
- X (void) strcpy(comments[0],"");
- X for(i = 0; i <= 1; i++){
- X (void) strcpy(phone[i],"");
- X (void) strcpy(telex[i],"");
- X (void) strcpy(fax[i],"");
- X (void) strcpy(uucp[i],"");
- X }
- X}
- X
- X/*
- X * rdrecord: read the rest of a record following a "name" read
- X */
- Xrdrecord()
- X{
- X int phln, fxln, txln, uuln;
- X
- X phln = fxln = txln = uuln = 0;
- X phlines = 0;
- X comlines = 0;
- X
- X while(fgets(buf,80,f1) != NULL){
- X
- X if(buf[1] != ':' || strlen(buf) <= 1)
- X return;
- X
- X switch(buf[0]) {
- X case 'O':
- X (void) strxcpy(org,buf,2,72);
- X break;
- X case 'T':
- X (void) strxcpy(title,buf,2,24);
- X break;
- X case 'D':
- X (void) strxcpy(dept,buf,2,24);
- X break;
- X case 'A':
- X (void) strxcpy(adr,buf,2,72);
- X break;
- X case 'P':
- X if(phln > 1)
- X break;
- X (void) strxcpy(phone[phln],buf,2,15);
- X phln++;
- X break;
- X case 'X':
- X if(txln > 1)
- X break;
- X (void) strxcpy(telex[txln],buf,2,24);
- X txln++;
- X break;
- X case 'F':
- X if(fxln > 1)
- X break;
- X (void) strxcpy(fax[fxln],buf,2,15);
- X fxln++;
- X break;
- X case 'U':
- X if(uuln > 1)
- X break;
- X (void) strxcpy(uucp[uuln],buf,2,24);
- X uuln++;
- X break;
- X case 'H':
- X (void) strxcpy(homephone,buf,2,15);
- X break;
- X case 'R':
- X (void) strxcpy(homeadr,buf,2,64);
- X break;
- X case 'C':
- X if(comlines > 3)
- X break;
- X (void) strxcpy(comments[comlines],buf,2,72);
- X comlines++;
- X break;
- X }
- X if(phln > 1 || txln > 1 || fxln > 1 || uuln > 1)
- X phlines = 1;
- X }
- X}
- X
- X/*
- X * display: Display the results of a search
- X */
- Xdisplay()
- X{
- X int l;
- X
- X if(List){
- X if(hits == 0){
- X erase();
- X refresh();
- X if(Org)
- X mvaddstr(0, 0, org);
- X else
- X mvaddstr(0, 0, name);
- X }else{
- X if(Org)
- X printw("%s", org);
- X else
- X printw("%s", name);
- X
- X }
- X refresh();
- X return(0);
- X }
- X
- X erase();
- X refresh();
- X
- X attron(A_REVERSE);
- X mvaddstr(0, 0, "NAME");
- X mvaddstr(0, 32, "TITLE");
- X mvaddstr(0, 56, "DEPT");
- X mvaddstr(3, 0, "ORGANIZATION NAME & ADDRESS");
- X mvaddstr(7, 0, "TELEPHONE");
- X mvaddstr(7, 16, "TELECOPIER");
- X mvaddstr(7, 32, "TELEX");
- X mvaddstr(7, 56, "UUCP");
- X mvaddstr(10 + phlines, 0, "HOME TELEPHONE");
- X mvaddstr(10 + phlines, 16, "HOME ADDRESS");
- X mvaddstr(13 + phlines, 0, "COMMENTS");
- X attroff(A_REVERSE);
- X
- X mvaddstr(1, 0, name);
- X mvaddstr(1, 32, title);
- X mvaddstr(1, 56, dept);
- X mvaddstr(4, 0, org);
- X mvaddstr(5, 0, adr);
- X for(l = 0; l <= phlines; l++){
- X mvaddstr(8 + l, 0, phone[l]);
- X mvaddstr(8 + l, 16, fax[l]);
- X mvaddstr(8 + l, 32, telex[l]);
- X mvaddstr(8 + l, 56, uucp[l]);
- X }
- X mvaddstr(11 + phlines, 0, homephone);
- X mvaddstr(11 + phlines, 16, homeadr);
- X for(l = 0; l <= comlines; l++)
- X mvaddstr(14 + phlines + l, 0, comments[l]);
- X
- X refresh();
- X return(0);
- X}
- X
- X/*
- X * more: Prompt for display of entries when multiple hits occur
- X */
- Xmore()
- X{
- Xagain:
- X attron(A_REVERSE);
- X mvaddstr(20, 0, "MORE HITS: CONTINUE?");
- X attroff(A_REVERSE);
- X mvaddstr(20, 22, "[y] [n] ");
- X refresh();
- X switch(getch()){
- X case 'n':
- X case 'N':
- X (void) terminate();
- X case 'y':
- X case 'Y':
- X return(0);
- X }
- X mvaddstr(20, 31, " ");
- X goto again;
- X}
- X
- X/*
- X * terminate: Exit gracefully
- X */
- Xterminate()
- X{
- X (void) fclose(f1);
- X
- X move(LINES-1, 0);
- X refresh();
- X endwin();
- X exit(0);
- X}
- X
- X/*
- X * strsearch: Search for any occurence of string "t" in string "s";
- X * return 1 if a match found, otherwise return 0
- X */
- Xstrsearch(s,t)
- X char s[80], t[80];
- X{
- X register i, n, nn;
- X int slength, tlength;
- X char st[80];
- X
- X slength = strlen(s);
- X tlength = strlen(t);
- X if(slength == 0 || tlength == 0)
- X return(0);
- X
- X for (i = 0; i < slength; i++){
- X n=0;
- X for(nn = i; nn < i + tlength; nn++){
- X if(nn > slength)
- X return(0);
- X st[n] = s[nn];
- X n++;
- X }
- X st[n] = '\0';
- X if (strcmp(st,t) == 0)
- X return(1);
- X }
- X return(0);
- X}
- X
- X/*
- X * strxcpy: Copy string "t" to string "s", with "offset" characters in
- X * string "t" skipped before the copy, and with a maximum of
- X * "maxs" characters [not including NULL] copied to string "s"
- X */
- Xstrxcpy(s,t,offset,maxs)
- X char s[80], t[80];
- X int offset, maxs;
- X{
- X register n, nn;
- X int tlength;
- X
- X nn = 0;
- X tlength = strlen(t);
- X
- X for(n = offset; n <= tlength + 1; n++){
- X s[nn] = t[n];
- X if(t[n] == '\0')
- X return(0);
- X nn++;
- X if(nn == maxs){
- X s[nn + 1] = '\0';
- X return(0);
- X }
- X }
- X return(0);
- X}
- X
- X/*
- X * usage: Display usage error message and exit
- X */
- Xusage()
- X{
- X (void) fprintf(stderr,"usage: rf [-f] [-l] [-o] searchstring\n");
- X exit(-1);
- X}
- //E*O*F rf.c//
-
- exit 0
-
-