[mod.sources] rolodex database program -- part 2 of 3

sources-request@panda.UUCP (03/14/86)

Mod.sources:  Volume 4, Issue 28
Submitted by: ihnp4!think!massar (JP Massar)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	choices.h
#	clear.c
#	datadef.h
#	io.c
#	menuaux.c
#	operations.c
#	options.c
#	rlist.c
#	rolo.c
#	rolodefs.h
#	rolofiles.h
#	search.c
#	update.c
# This archive created: Thu Mar 13 18:29:52 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'choices.h'" '(685 characters)'
if test -f 'choices.h'
then
	echo shar: will not over-write existing file "'choices.h'"
else
cat << \SHAR_EOF > 'choices.h'
typedef enum { P_CONTINUE, P_NEXT_PERSON, P_ABORT, P_HELP } P_Choices;

typedef enum { 
        
        M_SEARCH_BY_OTHER, M_SEARCH_BY_NAME, M_PRINT_TO_LASER_PRINTER,
        M_HELP, M_EXIT, M_PERUSE, M_SAVE, M_ADD

      } Main_Choices;

typedef enum { 

        A_ABORT_ADD, A_BACKUP, A_FILL_IN_REST,
        A_ABORT_ROLO, A_HELP, A_NO_DATA

      } Add_Choices;

typedef enum { 
        
        E_ABORT, E_UPDATE, E_DELETE, E_CONTINUE, E_PREV, E_HELP, E_SCAN
        
      } E_Choices;

typedef enum { U_ABORT, U_HELP, U_END_UPDATE } U_Choices;

typedef enum { S_ABORT, S_HELP, S_SCAN_ONE_BY_ONE } S_Choices;

typedef enum { O_ABORT, O_HELP, O_BACKUP, O_DONE_OTHERS } O_Choices;
SHAR_EOF
if test 685 -ne "`wc -c < 'choices.h'`"
then
	echo shar: error transmitting "'choices.h'" '(should have been 685 characters)'
fi
fi
echo shar: extracting "'clear.c'" '(930 characters)'
if test -f 'clear.c'
then
	echo shar: will not over-write existing file "'clear.c'"
else
cat << \SHAR_EOF > 'clear.c'
#include <stdio.h>
#ifdef TERMINFO
#include <term.h>
#endif
#undef putchar

int putchar();
static int ok_to_clear;

#ifdef TERMCAP
static char clear_screen[128] = 0;
static int lines;
#endif

clearinit ()
{
#ifdef TERMINFO
  int i;        
  setupterm(getenv("TERM"),1,&i);
  ok_to_clear = (i == 1) ? 1 : 0;
  if (i != 1) {
     fprintf(stderr,"Warning: Terminal type unknown\n");
  }
  return (i == 1) ? 0 : -1;
#endif
#ifdef TERMCAP
  char tc[1024];
  char *ptr = clear_screen;

  if (tgetent(tc, getenv("TERM")) < 1) {
    ok_to_clear = 0;
    return;
  }
  tgetstr("cl", &ptr);
  lines = tgetnum("li");
  ok_to_clear = (clear_screen[0] != 0 && lines > 0);

#endif
}        
        
clear_the_screen ()
{
#ifdef TERMINFO
  if (!ok_to_clear) return;        
  tputs(clear_screen,lines,putchar);
  fflush(stdout);
#endif
#ifdef TERMCAP
  if (!ok_to_clear) return;
  tputs(clear_screen,lines,putchar);
  fflush(stdout);
#endif
}
SHAR_EOF
if test 930 -ne "`wc -c < 'clear.c'`"
then
	echo shar: error transmitting "'clear.c'" '(should have been 930 characters)'
fi
fi
echo shar: extracting "'datadef.h'" '(1911 characters)'
if test -f 'datadef.h'
then
	echo shar: will not over-write existing file "'datadef.h'"
else
cat << \SHAR_EOF > 'datadef.h'
#define ABORTSTRING "\\"
#define ABORTCHAR '\\'

#define MAXMATCHES 17

#define N_BASIC_FIELDS 8
#define OTHER -1

typedef enum Basic_Field {

    R_NAME = 0, R_WORK_PHONE, R_HOME_PHONE, R_COMPANY, R_WORK_ADDRESS,
    R_HOME_ADDRESS, R_REMARKS, R_UPDATED

  };    
    
extern char *Field_Names[];  
  
/* A Rolodex entry */

typedef struct {

    char *basicfields[N_BASIC_FIELDS];
    int n_others;
    char **other_fields;

  } Rolo_Entry, *Ptr_Rolo_Entry;

  
#define get_basic_rolo_field(n,x) (((x) -> basicfields)[(n)])
#define get_n_others(x) ((x) -> n_others)  
#define get_other_field(n,x) (((x) -> other_fields)[n])
  
#define set_basic_rolo_field(n,x,s) (((x) -> basicfields[(n)]) = (s))
#define set_n_others(x,n) (((x) -> n_others) = (n))
#define incr_n_others(x) (((x) -> n_others)++)
#define set_other_field(n,x,s) ((((x) -> other_fields)[n]) = (s))

typedef struct link {

    Ptr_Rolo_Entry entry;
    int matched;
    struct link *prev;
    struct link *next;

  } Rolo_List, *Ptr_Rolo_List;


#define get_next_link(x) ((x) -> next)
#define get_prev_link(x) ((x) -> prev)
#define get_entry(x)     ((x) -> entry)
#define get_matched(x) ((x) -> matched)

#define set_next_link(x,y) (((x) -> next) = (y))
#define set_prev_link(x,y) (((x) -> prev) = (y))
#define set_entry(x,y) (((x) -> entry) = (y))
#define set_matched(x) (((x) -> matched) = 1)
#define unset_matched(x) (((x) -> matched) = 0);

extern Ptr_Rolo_List Begin_Rlist;
extern Ptr_Rolo_List End_Rlist;

#define MAXLINELEN 80
#define DIRPATHLEN 100

extern int changed;
extern int reorder_file;
extern int rololocked;

extern char *rolo_emalloc();
extern char *malloc();
extern Ptr_Rolo_List new_link_with_entry();
extern char *copystr();
extern int compare_links();
extern char *timestring();
extern char *homedir(), *libdir();
extern char *getenv();
extern char *ctime();
extern char *select_search_string();
extern int in_search_mode;
SHAR_EOF
if test 1911 -ne "`wc -c < 'datadef.h'`"
then
	echo shar: error transmitting "'datadef.h'" '(should have been 1911 characters)'
fi
fi
echo shar: extracting "'io.c'" '(8455 characters)'
if test -f 'io.c'
then
	echo shar: will not over-write existing file "'io.c'"
else
cat << \SHAR_EOF > 'io.c'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <ctype.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif

#include "datadef.h"
#include "rolofiles.h"


char *Field_Names[N_BASIC_FIELDS] = {
        
        "Name: ", "Work Phone: ", "Home Phone: ", "Company: ",
        "Work Address: ", "Home Address: ", "Remarks: ", "Date Updated: "
        
     };

Ptr_Rolo_List Begin_Rlist = 0;
Ptr_Rolo_List End_Rlist = 0;
Ptr_Rolo_List Current_Entry = 0;

static char *rolofiledata;

read_rolodex (fd) int fd;

{
  struct stat statdata;
  int filesize,i,j,k,start_of_others,warning_given;
  Ptr_Rolo_Entry newentry,oldentry,currententry;
  Ptr_Rolo_List newlink,rptr;
  char *next_field,*next_other,*oldname,*currentname;
  char **other_pointers;
  int n_entries = 0;
  
  /* find out how many bytes are in the file */
  
  fstat(fd,&statdata);
  if ((filesize = statdata.st_size) == 0) {
     return(0);
  }

  /* create an array of characters that big */
  
  rolofiledata = rolo_emalloc(filesize);

  /* read them all in at once for efficiency */
  
  if (filesize != read(fd,rolofiledata,filesize)) {
     fprintf(stderr,"rolodex read failed\n");
     exit(-1);
  }

  j = 0;
  
  /* for each entry in the rolodex file */
  
  while (j < filesize) {

      n_entries++;
        
      /* create the link and space for the data entry */
        
      newlink = new_link_with_entry();
      newentry = get_entry(newlink);
      if (j == 0) {
         Begin_Rlist = newlink;
         set_prev_link(newlink,0);
         set_next_link(newlink,0);
      }
      else {
          set_next_link(End_Rlist,newlink);
          set_prev_link(newlink,End_Rlist);
          set_next_link(newlink,0);
      }
      End_Rlist = newlink;

      /* locate each required field in the character array and change */
      /* the ending line feed to a null.  Insert a pointer to the */
      /* beginning of the field into the data entry */

      for (i = 0; i < N_BASIC_FIELDS; i++) {
          next_field = rolofiledata + j;
          while (rolofiledata[j] != '\n') {
            j++;
          }
          rolofiledata[j] = '\0';
          j++;
          set_basic_rolo_field(i,newentry,next_field);
      }

      /* the end of an entry is indicated by two adjacent newlines */

      if (rolofiledata[j] == '\n') {
         j++;
         newentry -> other_fields = 0;
         continue;
      }

      /* there must be additional, user-inserted fields. Find out how many. */

      start_of_others = j;
      while (1) {
        while (rolofiledata[j] != '\n') {
          j++;
        }
        incr_n_others(newentry);
        j++;
        if (rolofiledata[j] == '\n') {
           j++;
           break;
        }
     }

     /* allocate an array of character pointers to hold these fields */

     other_pointers = (char **)rolo_emalloc(get_n_others(newentry)*sizeof(char *));

     /* separate each field and insert a pointer to it in the char array */

     k = start_of_others;
     for (i = 0; i < get_n_others(newentry); i++) {
         next_other = rolofiledata + k;
         while (rolofiledata[k] != '\n') {
           k++;
         }
         rolofiledata[k] = '\0';
         other_pointers[i] = next_other;
         k++;
     }

     /* insert the pointer to this character array into the data entry */

     newentry -> other_fields = other_pointers;

  }

  /* check that all the entries are in alphabetical order by name */
  
  warning_given = 0;
  rptr = get_next_link(Begin_Rlist);
  while (rptr != 0) {
    if (1 == compare_links(get_prev_link(rptr),rptr)) {
       if (!warning_given) fprintf(stderr,"Warning, rolodex out of order\n");
       warning_given = 1;
       reorder_file = 1;
    }
    rptr = get_next_link(rptr);
  }    
    
  return(n_entries);
  
}


write_rolo_list (fp) FILE *fp; 

/* write the entire in-core rolodex to a file */

{

  Ptr_Rolo_List rptr;
  Ptr_Rolo_Entry entry;
  int j;

  rptr = Begin_Rlist;

  while (rptr != 0) {
    entry = get_entry(rptr);
    for (j = 0; j < N_BASIC_FIELDS; j++) {
        fprintf(fp,"%s\n",get_basic_rolo_field(j,entry));
    }
    for (j = 0; j < get_n_others(entry); j++) {
        fprintf(fp,"%s\n",get_other_field(j,entry));
    }
    fprintf(fp,"\n");
    rptr = get_next_link(rptr);
  }

}


write_rolo (fp1,fp2) FILE *fp1; FILE *fp2;

{
  write_rolo_list(fp1);
  write_rolo_list(fp2);
}


display_basic_field (name,value,show,up) char *name; char *value; int show,up;
{
  int semi = 0;        
  int i;
  if (all_whitespace(value) && !show) return;
  printf("%-25s",name);
  while (*value != '\0') {
    if (*value == ';') {
       while (*++value == ' '); 
       putchar('\n');
       for (i = 0; i < (up ? 28 : 25); i++) putchar(' ');
       semi = 1;
    }
    else {
       semi = 0;
       putchar(*value++);
    }
  }
  putchar('\n');
}


display_other_field (fieldstring) char *fieldstring;
{
  int already_put_sep = 0;        
  int count = 0;
  int i;
  while (*fieldstring != '\0') {
    if (*fieldstring == ';' && already_put_sep) {
       while (*++fieldstring == ' ');
       putchar('\n');
       for (i = 0; i < 25; i++) putchar(' ');
       continue;
    }
    putchar(*fieldstring);
    count++;
    if (*fieldstring == ':' && !already_put_sep) {
       for (i = count; i < 24; i++) putchar(' ');
       already_put_sep = 1;
    }
    fieldstring++;
  }
  putchar('\n');
}


summarize_entry_list (rlist,ss) Ptr_Rolo_List rlist; char *ss;

/* print out the Name field for each entry that is tagged as matched */
/* and number each entry. */

{
  int count = 1;
  clear_the_screen();
  printf("Entries that match '%s' :\n\n",ss);
  while (rlist != 0) {
    if (get_matched(rlist)) {
       printf (
          "%d. \t%s\n",
          count++,
          get_basic_rolo_field((int) R_NAME,get_entry(rlist))
       );
    }
    rlist = get_next_link(rlist);    
  }
  putchar('\n');
}


display_field_names ()

/* display and number each standard field name. */

{
  int j;
  char *name;
  clear_the_screen();
  for (j = 0; j < N_BASIC_FIELDS - 1; j++) {        
      name = Field_Names[j];        
      printf("%d. ",j+1);
      while (*name != ':') putchar(*name++);
      putchar('\n');
  }
  printf("%d. ",N_BASIC_FIELDS);
  printf("A user created item name\n\n");
}  
  

display_entry (entry) Ptr_Rolo_Entry entry;

{
  int j,n_others;
  char *string;
  
  clear_the_screen();
  
  /* display the standard fields other than Date Updated */
  
  for (j = 0; j < N_BASIC_FIELDS - 1; j++) {
      string = get_basic_rolo_field(j,entry);
      display_basic_field(Field_Names[j],string,0,0);
  }        
      
  /* display any additional fields the user has defined for this entry */
  
  n_others = get_n_others(entry);
  for (j = 0; j < n_others; j++) {
      string = get_other_field(j,entry);
      display_other_field(string);
   }

   /* display the Date Updated field */
   
   j = N_BASIC_FIELDS - 1;
   display_basic_field(Field_Names[j],get_basic_rolo_field(j,entry),0,0);
   fprintf(stdout,"\n");

}


display_entry_for_update (entry) Ptr_Rolo_Entry entry;

/* same as display_entry, except each item is numbered and the Date Updated */
/* item is not displayed */

{
  int j,n_others;
  char *string;
  int count = 1;
  
  clear_the_screen();
  
  for (j = 0; j < N_BASIC_FIELDS - 1; j++) {
      string = get_basic_rolo_field(j,entry);
      printf("%d. ",count++);
      display_basic_field(Field_Names[j],string,1,1);
  }        
      
  n_others = get_n_others(entry);
  for (j = 0; j < n_others; j++) {
      string = get_other_field(j,entry);
      printf("%d. ",count++);
      display_other_field(string);
  }
  
  printf("%d. Add a new user defined field\n",count);

  fprintf(stdout,"\n");

}


int cathelpfile (filepath,helptopic,clear) 

  char *filepath, *helptopic; 
  int clear;

{
  FILE *fp;          
  char buffer[MAXLINELEN];
  if (clear) clear_the_screen();
  if (NULL == (fp = fopen(filepath,"r"))) {
     if (helptopic) {
        printf("No help available on %s, sorry.\n\n",helptopic); 
     }
     else {
        fprintf(stderr,"Fatal error, can't open %s\n",filepath);
        exit(-1);
     }
     return;
  }
  while (NULL != fgets(buffer,MAXLINELEN,fp)) printf("%s",buffer);  
  printf("\n");
  fclose(fp);
  return;
}


any_char_to_continue ()
{
  char buffer[80];
  printf("RETURN to continue: ");
  fgets(buffer,80,stdin);
  return;
}
SHAR_EOF
if test 8455 -ne "`wc -c < 'io.c'`"
then
	echo shar: error transmitting "'io.c'" '(should have been 8455 characters)'
fi
fi
echo shar: extracting "'menuaux.c'" '(1433 characters)'
if test -f 'menuaux.c'
then
	echo shar: will not over-write existing file "'menuaux.c'"
else
cat << \SHAR_EOF > 'menuaux.c'
#include <stdio.h>
#include <ctype.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"

#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"


rolo_menu_yes_no (prompt,rtn_default,help_allowed,helpfile,subject)

  char *prompt;
  int rtn_default;
  int help_allowed;
  char *helpfile, *subject;
  
{
  int rval;
  reask :
  rval = menu_yes_no_abort_or_help (
              prompt,ABORTSTRING,help_allowed,rtn_default
           );
  switch (rval) {
    case MENU_EOF :
      user_eof();
      break;
    case MENU_HELP : 
      cathelpfile(libdir(helpfile),subject,1);
      goto reask;
      break;
    default :
      return(rval);
      break;
  }
}
  

rolo_menu_data_help_or_abort (prompt,helpfile,subject,ptr_response)

  char *prompt, *helpfile, *subject;
  char **ptr_response;
  
{ 
  int rval;
  reask :
  rval = menu_data_help_or_abort(prompt,ABORTSTRING,ptr_response);
  if (rval == MENU_EOF) user_eof();
  if (rval == MENU_HELP) {
     cathelpfile(libdir(helpfile),subject,1);
     goto reask;
  }     
  return(rval);
}
     

rolo_menu_number_help_or_abort (prompt,low,high,ptr_ival)

  char *prompt;
  int low,high,*ptr_ival;
  
{  
  int rval;
  if (MENU_EOF == (rval = menu_number_help_or_abort (
                               prompt,ABORTSTRING,low,high,ptr_ival
                           )))
     user_eof();
  return(rval);
}
SHAR_EOF
if test 1433 -ne "`wc -c < 'menuaux.c'`"
then
	echo shar: error transmitting "'menuaux.c'" '(should have been 1433 characters)'
fi
fi
echo shar: extracting "'operations.c'" '(8950 characters)'
if test -f 'operations.c'
then
	echo shar: will not over-write existing file "'operations.c'"
else
cat << \SHAR_EOF > 'operations.c'
#include <stdio.h>
#include <ctype.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"
#include "mem.h"


#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"
#include "choices.h"

extern char *ctime();

Ptr_Rolo_List create_entry (basicdata,otherdata) char **basicdata, **otherdata;
{
  Ptr_Rolo_List newlink;        
  Ptr_Rolo_Entry newentry;
  int i,j;
  newlink = new_link_with_entry();        
  newentry = get_entry(newlink);
  for (j = 0; j < N_BASIC_FIELDS; j++) {
      set_basic_rolo_field(j,newentry,basicdata[j]);
  }
  j = 0;
  while (otherdata[j] != 0) j++;
  set_n_others(newentry,j);
  if (j > 0) {
     newentry -> other_fields = (char **) rolo_emalloc(j*sizeof(char *));
     for (i = 0; i < j; i++) {
         set_other_field(i,newentry,otherdata[i]);
     }
  }
  else newentry -> other_fields = 0;
  return(newlink);
}  
  

other_fields () 
{
  int rval;        
  rval = rolo_menu_yes_no (
             "Additional fields? ",DEFAULT_NO,1,
             "morefieldshelp","additional fields"
          );
  return(rval == MENU_YES);
}


add_the_entry ()
{
  return(MENU_YES == rolo_menu_yes_no (
              "Add new entry to rolodex? ",DEFAULT_YES,1,
              "newaddhelp","adding newly entered entry"
           ));
}


rolo_add () 

{
  int i,j,k,rval,menuval;
  long timeval;
  char *response;
  char *basicdata[N_BASIC_FIELDS], *otherdata[100], *datum;
  Ptr_Rolo_List rlink;
        
  for (j = 0; j < 100; j++) otherdata[j] = 0;
  for (j = 0; j < N_BASIC_FIELDS; j++) basicdata[j] = 0;
  cathelpfile(libdir("addinfo"),0,1);
  
  /* 'k' and 'kludge' are are kludge to allow us to back up from entering */
  /* user defined fields to go an correct wrong basic field information. */
  
  k = 0;
  
  kludge :
  
  for (j = k; j < N_BASIC_FIELDS - 1; j++) {
        
      redo :
        
      rval = menu_match (
           &menuval,&response,
           Field_Names[j],
           0,0,0,1,5,
           "\\",A_ABORT_ADD,
           "^",A_BACKUP,
           "!",A_FILL_IN_REST,
           "?",A_HELP,
           "",A_NO_DATA
        );
        
      switch (rval) {
        
        case MENU_NO_MATCH :
          basicdata[j] = copystr(response);
          if (j == 0 && strlen(basicdata[j]) == 0) {
             printf("Each entry must have a name!\n");
             goto redo;
          }
          break;

        case MENU_MATCH :
          switch (menuval) {
            case A_BACKUP :
              if (j == 0) return;
              j--;
              goto redo;
              break;
            case A_ABORT_ADD :
              return;
              break;
            case A_FILL_IN_REST :
              if (j == 0) {
                 fprintf(stderr,"You must enter at least a name...\n");
                 goto redo;
              }
              goto add_entry;
              break;
            case A_HELP :
              cathelpfile(libdir("addhelp"),"adding entries",1);
              any_char_to_continue();
              clear_the_screen();
              cathelpfile(libdir("addinfo"),0,0);
              for (i = 0; i < j; i++) {
                  printf("%s%s\n",Field_Names[i],basicdata[i]);
              }
              goto redo;
              break;
            case A_NO_DATA :
              if (basicdata[j] != 0) basicdata[j][0] = '\0';
              break;
            default :
              fprintf(stderr,"Impossible rval from rolo_add menu_match\n");
              save_and_exit(-1);
              break;
          }
          break;

        case MENU_EOF :
          user_eof();
          break;
          
        case MENU_ERROR :
        case MENU_AMBIGUOUS :
        default :
          fprintf(stderr,"Impossible return from rolo_add menu_match\n");
          save_and_exit(-1);
          break;

      }

  }

  if (other_fields()) {
     for (j = 0; j < 100; j++) {
         redo_other :
         rval = menu_match (
              &menuval,&response,
              "Enter <name>: <data> (type RETURN to quit) : ",
              0,0,0,0,5,
              "\\",O_ABORT,
              "?",O_HELP,
              "Help",O_HELP,
              "^",O_BACKUP,
              "",O_DONE_OTHERS
           );
         switch (rval) {
           case MENU_MATCH :
             switch (menuval) {
               case O_DONE_OTHERS :
                 goto add_entry;
                 break;
               case O_BACKUP :
                 if (j == 0) {
                    k = N_BASIC_FIELDS - 2;
                    goto kludge;
                 }
                 else {
                    j--;
                    printf("Deleting %s\n",otherdata[j]);
                    goto redo_other;
                 }
                 break;
               case O_ABORT :
                 return;
                 break;
               case O_HELP :
                 cathelpfile(libdir("otherformathelp"),"user-item format",1);
                 any_char_to_continue();
                 goto redo_other;
              }   
             break;
           case MENU_NO_MATCH :
             if (0 == index(response,':')) {
                printf("No field name provided -- separate with a ':'.\n");
                goto redo_other;
             }
             otherdata[j] = copystr(response);
             break;
           case MENU_EOF :
             user_eof();
             break;
           case MENU_AMBIGUOUS :
           case MENU_ERROR :
           default :
             fprintf(stderr,"Impossible rval from add_other menu_match\n");
             save_and_exit(-1);
         }
     }
  }

  add_entry :   

  basicdata[N_BASIC_FIELDS - 1] = timestring();
  
  rlink = create_entry(basicdata,otherdata);
  clear_the_screen();
  display_entry(get_entry(rlink));
  if (add_the_entry()) {
     printf (
         "Adding entry for %s to rolodex\n",
         get_basic_rolo_field((int) R_NAME,get_entry(rlink))
      );
     rolo_insert(rlink,compare_links);
     changed = 1;
     sleep(2);
  }
  else {
     return;
  }

}


entry_action (rlink) Ptr_Rolo_List rlink;

{
  static entry_menu_displayed = 0;
  int rval,menuval;
  char *response;
  
  if (!entry_menu_displayed) cathelpfile(libdir("entrymenu"),0,0);
  entry_menu_displayed = 1;
  
  redo :
  
  rval = menu_match (
       &menuval, &response,
       "Action (? for help) : ",
       0,1,1,1,7,
       "\\",E_ABORT,
       "?",E_HELP,
       "",E_CONTINUE,
       "-",E_DELETE,
       "+",E_UPDATE,
       "<",E_PREV,
       "%",E_SCAN
    );
    
  if (rval != MENU_MATCH) {
     if (rval == MENU_EOF) user_eof();
     fprintf(stderr,"Impossible return from entry_action menu_match\n");
     save_and_exit(-1);
  }

  switch (menuval) {
    case E_ABORT :
    case E_CONTINUE :
    case E_PREV :
      break;
    case E_SCAN :
      rolo_peruse_mode(get_next_link(rlink));
      break;
    case E_UPDATE :
      rolo_update_mode(rlink);
      break;
    case E_DELETE :
      rolo_delete(rlink);
      printf("Entry deleted\n");
      sleep(1);
      changed = 1;
      break;
    case E_HELP :
      cathelpfile (
          libdir(in_search_mode ? "esearchhelp" : "escanhelp"),
          "entry actions",
          1
       );
      any_char_to_continue();
      clear_the_screen();
      display_entry(get_entry(rlink));
      goto redo;
      break;
    default :
      fprintf(stderr,"Impossible menuval in entry_action\n");
      save_and_exit(-1);
  }

  return(menuval);
  
}


display_list_of_entries (rlist) Ptr_Rolo_List rlist;

{
  Ptr_Rolo_List old;
        
  while (rlist != 0) {        
    
    if (!get_matched(rlist)) goto next;
        
    loop :    
    
    display_entry(get_entry(rlist));
    
    switch (entry_action(rlist)) {
      case E_CONTINUE :
        break;
      case E_ABORT :
        return;
        break;
      case E_PREV :
        old = rlist;
        find_prev_match :
        if (get_prev_link(rlist) == 0) {
           rlist = old;
           printf("No previous entry in scan list\n");
           sleep(2);
        }
        else {
           rlist = get_prev_link(rlist);
           if (!get_matched(rlist)) goto find_prev_match;
        }
        goto loop;
        break;
      default :
        printf("Displaying next entry in scan list...\n");
        sleep(1);
        break;
    }
    
    next :
    
    rlist = get_next_link(rlist);
    
  }
  
  printf("No further entries to scan...\n");
  sleep(2);     
  
}
  

rolo_peruse_mode (first_rlink) Ptr_Rolo_List first_rlink;

{
  int rval;
  Ptr_Rolo_List rlist = first_rlink;
  if (0 == Begin_Rlist) {
     fprintf(stderr,"No further entries in rolodex...\n");
     sleep(2);
     return;
  }
  while (rlist != 0) {  
    set_matched(rlist);
    rlist = get_next_link(rlist);
  }    
  display_list_of_entries(first_rlink);
  rlist = first_rlink;
  while (rlist != 0) {  
    unset_matched(rlist);
    rlist = get_next_link(rlist);
  }    
}
SHAR_EOF
if test 8950 -ne "`wc -c < 'operations.c'`"
then
	echo shar: error transmitting "'operations.c'" '(should have been 8950 characters)'
fi
fi
echo shar: extracting "'options.c'" '(6785 characters)'
if test -f 'options.c'
then
	echo shar: will not over-write existing file "'options.c'"
else
cat << \SHAR_EOF > 'options.c'
#include <sys/file.h>
#include <stdio.h>
#include <ctype.h>
#include <sgtty.h>
#include <sys/time.h>
#include <signal.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"
#include "mem.h"

#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"
#include "choices.h"


print_short ()

/* print the names and phone numbers of everyone in the rolodex. */

{ 

  Ptr_Rolo_List rptr;
  Ptr_Rolo_Entry entry;
        
  rptr = Begin_Rlist;
  if (rptr == 0) {
     fprintf(stderr,"No entries to print out...\n");
     return;
  }
   
  fprintf (
     stdout,
     "\nNAME                      WORK PHONE                HOME PHONE"
   );
  fprintf (
     stdout,
     "\n----                      ----------                ----------\n\n\n"
   );

   while (rptr != 0) {
        entry = get_entry(rptr);
        fprintf (
            stdout,
            "%-25s %-25s %-25s\n",
            get_basic_rolo_field((int) R_NAME,entry),
            get_basic_rolo_field((int) R_WORK_PHONE,entry),
            get_basic_rolo_field((int) R_HOME_PHONE,entry)
         );   
         rptr = get_next_link(rptr);
   }       
   
 }  
   

person_match (person,entry) char *person; Ptr_Rolo_Entry entry; 
 
/* Match against a rolodex entry's Name and Company fields. */
/* Thus if I say 'rolo CCA' I will find people who work at CCA. */
/* This is good because sometimes you will forget a name but remember */
/* the company the person works for. */

{ 
  char *name, *company;        
  int len;
  len = strlen(person);
  name = get_basic_rolo_field((int) R_NAME,entry);
  company = get_basic_rolo_field((int) R_COMPANY,entry);
  if (strncsearch(name,strlen(name),person,len)) return(1);
  if (strncsearch(company,strlen(company),person,len)) return(1);
  return(0);
}
 

int find_all_person_matches (person) char *person;

{
  Ptr_Rolo_List rptr = Begin_Rlist;        
  int count = 0;
  while (rptr != 0) {
    unset_matched(rptr);
    if (person_match(person,get_entry(rptr))) {
       set_matched(rptr);
       count++;
    }
    rptr = get_next_link(rptr);
  }
  return(count);
}


look_for_person (person) char *person; 

/* search against Name and Company over the rolodex.  If a match is found */
/* display the entry and give the user a choice of what to do next. */

{
  Ptr_Rolo_List rptr;        
  int found = 0,result,nmatches;
  static displayed_menu = 0;
  char *response;
  
  rptr = Begin_Rlist;        
  while (rptr != 0) {        
    if (person_match(person,get_entry(rptr))) {
       clear_the_screen();
       display_entry(get_entry(rptr));
       if (!found) {
          nmatches = find_all_person_matches(person);
          if (nmatches > 1) {
             printf (
                "There are %d other entries which match '%s'\n\n",
                nmatches - 1, person
              );
          }
       }
       found = 1;
       try_again :
       if (!displayed_menu) cathelpfile(libdir("poptionmenu"),0,0);
       displayed_menu = 1;
       menu_match (
            &result,
            &response,
            "Select option (? for help): ",
            0,1,1,1,6,
            "Continue",P_CONTINUE,
            "",P_CONTINUE,
            "Next",P_NEXT_PERSON,
            "\\",P_ABORT,
            "Help",P_HELP,
            "?",P_HELP
         );
       switch (result) {
         case P_CONTINUE :
           break;
         case P_NEXT_PERSON :
           return;
           break;
         case P_ABORT :
           roloexit(0);
           break;
         case P_HELP :
           cathelpfile(libdir("poptionshelp"),"person search options",1);
           goto try_again;
         default :
           fprintf(stderr,"Impossible return from menu_match\n");
           exit(-1);
       }
    }
    rptr = get_next_link(rptr);
  }
  if (!found) {
     fprintf(stderr,"\nNo entry found for '%s'\n\n",person);
     sleep(2);
  }
  else {
     printf("No further matches for '%s'\n",person);
     sleep(2);
  }   
}
 
 
print_people ()

{
  int index;
  char *person;
  index = 1;
  while (T) {
     if (0 == (person = non_option_arg(index++))) break;
     look_for_person(person);
  }
}


interactive_rolo ()

/* Top level of the iteractive rolodex.  This code is just a big switch */
/* which passes responsibility off to various routines in 'operations.c' */
/* and 'update.c' */

{

  int result,rval,field_index;
  char *response,*field_name,*search_string;

  fprintf(stdout,"\n\nTMC ROLODEX, Version %s\n\n\n",VERSION);        
  
  while (1) {        
        
    cathelpfile(libdir("mainmenu"),0,0);
    rval = menu_match (
         &result,
         &response,
         "Select option (? for help): ",
         0,1,0,1,7,
         "+",M_ADD,
         "%",M_PERUSE,
         "\\",M_EXIT,
         "?",M_HELP,
         "*",M_SAVE,
         "$",M_SEARCH_BY_OTHER,
         "!",M_PRINT_TO_LASER_PRINTER
      );
      
    switch (rval) {
        
      case MENU_AMBIGUOUS :
      case MENU_ERROR :
        fprintf(stderr,"Impossible return 1 from main menu_match\n");
        exit(-1);
        break;
        
      case MENU_NO_MATCH :
        response = copystr(response);
        rolo_search_mode((int) R_NAME,Field_Names[(int) R_NAME],response);
        break;
        
      case MENU_MATCH :
        
        switch (result) {
          case M_ADD :
            rolo_add();
            break;
          case M_SEARCH_BY_OTHER :
            if ((-1 == select_field_to_search_by(&field_index,&field_name)) ||
                (0 == (search_string = select_search_string()))) {
               break;
            }
            rolo_search_mode(field_index,field_name,search_string);
            break;
          case M_PRINT_TO_LASER_PRINTER :
            fprintf(stderr,"Not implemented\n");
            sleep(1);
            break;
          case M_PERUSE :
            rolo_peruse_mode(Begin_Rlist);
            break;
          case M_EXIT :
            save_and_exit(0);
            break;
          case M_SAVE :
            if (changed) {
               save_to_disk();
               sleep(1);
            }
            else {
               printf("No changes to be saved...\n");
               sleep(2);
            }
            break;
          case M_HELP :
            cathelpfile(libdir("moptionhelp"),"top level menu",1);
            any_char_to_continue();
            break;
          default :
            fprintf(stderr,"Impossible result from menu_match...\n");
            save_and_exit(-1);
        }
        break;
            
      case MENU_EOF :
        user_eof();
        break;
        
      default :
        fprintf(stderr,"Impossible return 2 from menu_match\n");
        save_and_exit(-1);
        
    }
    
    clear_the_screen();
    
  }

}
SHAR_EOF
if test 6785 -ne "`wc -c < 'options.c'`"
then
	echo shar: error transmitting "'options.c'" '(should have been 6785 characters)'
fi
fi
echo shar: extracting "'rlist.c'" '(2445 characters)'
if test -f 'rlist.c'
then
	echo shar: will not over-write existing file "'rlist.c'"
else
cat << \SHAR_EOF > 'rlist.c'
#include "datadef.h"


int rlength (rlist) Ptr_Rolo_List rlist;
{
  return((rlist == 0) ? 0 : 1 + rlength(get_next_link(rlist)));
}  
  

Ptr_Rolo_List new_link_with_entry ()
{
  Ptr_Rolo_List newlink;        
  Ptr_Rolo_Entry newentry;
  newlink = (Ptr_Rolo_List) rolo_emalloc(sizeof(Rolo_List));
  unset_matched(newlink);
  newentry = (Ptr_Rolo_Entry) rolo_emalloc(sizeof(Rolo_Entry));
  set_n_others(newentry,0);        
  newentry -> other_fields = 0;
  set_entry(newlink,newentry);
  return(newlink);
}


rolo_insert (link,compare) Ptr_Rolo_List link; int (*compare)();

{
  Ptr_Rolo_List rptr;        
        
  if (Begin_Rlist == 0) {
     Begin_Rlist = link;
     End_Rlist = link;
     set_prev_link(link,0);
     set_next_link(link,0);
     return;
  }
  
  /* find the element it goes before, alphabetically, and insert it */

  rptr = Begin_Rlist;
  while (rptr != 0) {
    if (1 == (*compare)(rptr,link)) {
       set_prev_link(link,get_prev_link(rptr));
       set_next_link(link,rptr);
       if (get_prev_link(rptr) != 0) 
          set_next_link(get_prev_link(rptr),link);
       else
          Begin_Rlist = link;
       set_prev_link(rptr,link);
       return;
    }
    rptr = get_next_link(rptr);
  }

  /* it goes at the end */
  
  set_next_link(End_Rlist,link);
  set_prev_link(link,End_Rlist);
  set_next_link(link,0);
  End_Rlist = link;
  return;

}


rolo_delete (link) Ptr_Rolo_List link;

{
  if (get_next_link(link) == 0 && get_prev_link(link) == 0) {
     Begin_Rlist = 0;
     End_Rlist = 0;
     return;
  }

  if (get_prev_link(link) == 0) {
     Begin_Rlist = get_next_link(link);
     set_prev_link(Begin_Rlist,0);
     return;
  }

  if (get_next_link(link) == 0) {
     End_Rlist = get_prev_link(link);
     set_next_link(End_Rlist,0);
     return;
  }

  set_next_link(get_prev_link(link),get_next_link(link));
  set_prev_link(get_next_link(link),get_prev_link(link));
  return;

}


compare_links (l1,l2) Ptr_Rolo_List l1,l2;

{
  Ptr_Rolo_Entry e1,e2;        
  char *n1,*n2;
  e1 = get_entry(l1);        
  e2 = get_entry(l2);        
  n1 = get_basic_rolo_field((int) R_NAME,e1);
  n2 = get_basic_rolo_field((int) R_NAME,e2);
  return(nocase_compare(n1,strlen(n1),n2,strlen(n2)));
}  
  

rolo_reorder ()
{
  Ptr_Rolo_List rptr,oldlink;
  rptr = Begin_Rlist;
  Begin_Rlist = 0;
  while (rptr != 0) {
    oldlink = get_next_link(rptr);
    rolo_insert(rptr,compare_links);
    rptr = oldlink;
  }
}


SHAR_EOF
if test 2445 -ne "`wc -c < 'rlist.c'`"
then
	echo shar: error transmitting "'rlist.c'" '(should have been 2445 characters)'
fi
fi
echo shar: extracting "'rolo.c'" '(8738 characters)'
if test -f 'rolo.c'
then
	echo shar: will not over-write existing file "'rolo.c'"
else
cat << \SHAR_EOF > 'rolo.c'
#include <sys/file.h>
#include <stdio.h>
#include <ctype.h>
#include <sgtty.h>
#include <signal.h>
#include <curses.h>
#include <pwd.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"
#include "mem.h"

#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"


static char rolodir[DIRPATHLEN];        /* directory where rolo data is */
static char filebuf[DIRPATHLEN];        /* stores result of homedir() */

int changed = 0;
int reorder_file = 0;
int rololocked = 0;
int in_search_mode = 0;


char *rolo_emalloc (size) int size;

/* error handling memory allocator */

{
  char *rval;        
  if (0 == (rval = malloc(size))) {        
     fprintf(stderr,"Fatal error:  out of memory\n");
     save_and_exit(-1);
  }
  return(rval);
}  

        
char *copystr (s) char *s;

/* memory allocating string copy routine */


{
 char *copy;        
 if (s == 0) return(0);
 copy = rolo_emalloc(strlen(s) + 1);
 strcpy(copy,s);
 return(copy);
}

 
char *timestring ()

/* returns a string timestamp */

{
  char *s;        
  long timeval;
  time(&timeval);
  s = ctime(&timeval);  
  s[strlen(s) - 1] = '\0';
  return(copystr(s));
}  
  

user_interrupt ()

/* if the user hits C-C (we assume he does it deliberately) */

{
  unlink(homedir(ROLOLOCK));
  fprintf(stderr,"\nAborting rolodex, no changes since last save recorded\n");
  exit(-1);  
}  


user_eof ()

/* if the user hits C-D */

{
  unlink(homedir(ROLOLOCK));        
  fprintf(stderr,"\nUnexpected EOF on terminal. Saving rolodex and exiting\n");
  save_and_exit(-1);        
}


roloexit (rval) int rval;
{
  if (rololocked) unlink(homedir(ROLOLOCK));        
  exit(rval);
}  


save_to_disk ()

/* move the old rolodex to a backup, and write out the new rolodex and */
/* a copy of the new rolodex (just for safety) */

{
  FILE *tempfp,*copyfp;        
  char d1[DIRPATHLEN], d2[DIRPATHLEN];
  int r;
        
  tempfp = fopen(homedir(ROLOTEMP),"w");
  copyfp = fopen(homedir(ROLOCOPY),"w");
  if (tempfp == NULL || copyfp == NULL) {
     fprintf(stderr,"Unable to write rolodex...\n");
     fprintf(stderr,"Any changes made have not been recorded\n");
     roloexit(-1);
  }
  write_rolo(tempfp,copyfp);
  fclose(tempfp);
  fclose(copyfp);
  if (rename(strcpy(d1,homedir(ROLODATA)),strcpy(d2,homedir(ROLOBAK))) ||
      rename(strcpy(d1,homedir(ROLOTEMP)),strcpy(d2,homedir(ROLODATA)))) {
     fprintf(stderr,"Rename failed.  Revised rolodex is in %s\n",ROLOCOPY);
     roloexit(-1);
  }
  printf("Rolodex saved\n");
  sleep(1);
  changed = 0;
}
  

save_and_exit (rval) int rval;
{
  if (changed) save_to_disk();        
  roloexit(rval);        
}


extern struct passwd *getpwnam();

char *home_directory (name) char *name;
{
  struct passwd *pwentry;
  if (0 == (pwentry = getpwnam(name))) return("");
  return(pwentry -> pw_dir);
}        


char *homedir (filename) char *filename;

/* e.g., given "rolodex.dat", create "/u/massar/rolodex.dat" */
/* rolodir generally the user's home directory but could be someone else's */
/* home directory if the -u option is used. */

{
  nbuffconcat(filebuf,3,rolodir,"/",filename);
  return(filebuf);
}


char *libdir (filename) char *filename;

/* return a full pathname into the rolodex library directory */
/* the string must be copied if it is to be saved! */

{
  nbuffconcat(filebuf,3,ROLOLIB,"/",filename);        
  return(filebuf);        
}


rolo_only_to_read () 
{
  return(option_present(SUMMARYFLAG) || n_non_option_args() > 0);
}


locked_action () 
{
  if (option_present(OTHERUSERFLAG)) {        
     fprintf(stderr,"Someone else is modifying that rolodex, sorry\n");
     exit(-1);
  }
  else {
     cathelpfile(libdir("lockinfo"),"locked rolodex",0);
     exit(-1);
  }
}  
  

main (argc,argv) int argc; char *argv[];

{
    int fd,in_use,read_only,rolofd;
    Bool not_own_rolodex;        
    char *user;
    FILE *tempfp;
    
    clearinit();
    clear_the_screen();
    
    /* parse the options and arguments, if any */
    
    switch (get_args(argc,argv,T,T)) {
        case ARG_ERROR : 
          roloexit(-1);
        case NO_ARGS :
          break;
        case ARGS_PRESENT :
          if (ALL_LEGAL != legal_options(LEGAL_OPTIONS)) {
                fprintf(stderr,"illegal option\nusage: %s\n",USAGE);
                roloexit(-1);
          }
    }
    
    /* find the directory in which the rolodex file we want to use is */
    
    not_own_rolodex = option_present(OTHERUSERFLAG);        
    if (not_own_rolodex) {
       if (NIL == (user = option_arg(OTHERUSERFLAG,1)) || 
           n_option_args(OTHERUSERFLAG) != 1) {
          fprintf(stderr,"Illegal syntax using -u option\nusage: %s\n",USAGE);
          roloexit(-1);
       }
    }        
    else {
       if (0 == (user = getenv("HOME"))) {
          fprintf(stderr,"Cant find your home directory, no HOME\n");
          roloexit(-1);
       }
    }
    
    if (not_own_rolodex) {
       strcpy(rolodir,home_directory(user));
       if (*rolodir == '\0') {
          fprintf(stderr,"No user %s is known to the system\n",user);
          roloexit(-1);
       }
    }
    else strcpy(rolodir,user);
    
    /* is the rolodex readable? */
    
    if (0 != access(homedir(ROLODATA),R_OK)) {
        
       /* No.  if it exists and we cant read it, that's an error */
        
       if (0 == access(homedir(ROLODATA),F_OK)) { 
          fprintf(stderr,"Cant access rolodex data file to read\n");
          roloexit(-1);
       }
       
       /* if it doesn't exist, should we create one? */
       
       if (option_present(OTHERUSERFLAG)) {
          fprintf(stderr,"No rolodex file belonging to %s found\n",user);
          roloexit(-1);
       }
       
       /* try to create it */
       
       if (-1 == (fd = creat(homedir(ROLODATA),0644))) {
          fprintf(stderr,"couldnt create rolodex in your home directory\n");
          roloexit(-1);
       }
       
       else {
          close(fd);
          fprintf(stderr,"Creating empty rolodex...\n");
       }

    }
    
    /* see if someone else is using it */
    
    in_use = (0 == access(homedir(ROLOLOCK),F_OK));
    
    /* are we going to access the rolodex only for reading? */
    
    if (!(read_only = rolo_only_to_read())) {
    
       /* No.  Make sure no one else has it locked. */
        
       if (in_use) {
          locked_action();
       }
        
       /* create a lock file.  Catch interrupts so that we can remove */
       /* the lock file if the user decides to abort */
       
       if (!option_present(NOLOCKFLAG)) {
          if ((fd = open(homedir(ROLOLOCK),O_EXCL|O_CREAT,00200|00400)) < 0) {
             fprintf(stderr,"unable to create lock file...\n");
	     exit(1);
	  }
          rololocked = 1;
          close(fd);
          signal(SIGINT,user_interrupt);        
       }
        
       /* open a temporary file for writing changes to make sure we can */
       /* write into the directory */
       
       /* when the rolodex is saved, the old rolodex is moved to */
       /* a '~' file, the temporary is made to be the new rolodex, */
       /* and a copy of the new rolodex is made */
       
       if (NULL == (tempfp = fopen(homedir(ROLOTEMP),"w"))) {
           fprintf(stderr,"Can't open temporary file to write to\n");
           roloexit(-1);
       }        
       fclose(tempfp);
    
    }
       
    allocate_memory_chunk(CHUNKSIZE);
    
    if (NULL == (rolofd = open(homedir(ROLODATA),O_RDONLY))) {
        fprintf(stderr,"Can't open rolodex data file to read\n");
        roloexit(-1);
    }
    
    /* read in the rolodex from disk */
    /* It should never be out of order since it is written to disk ordered */
    /* but just in case... */
    
    if (!read_only) printf("Reading in rolodex from %s\n",homedir(ROLODATA));
    read_rolodex(rolofd);
    close(rolofd);
    if (!read_only) printf("%d entries listed\n",rlength(Begin_Rlist));
    if (reorder_file && !read_only) {
       fprintf(stderr,"Reordering rolodex...\n");
       rolo_reorder();
       fprintf(stderr,"Saving reordered rolodex to disk...\n");
       save_to_disk();
    }
       
    /* the following routines live in 'options.c' */
    
    /* -s option.  Prints a short listing of people and phone numbers to */
    /* standard output */
    
    if (option_present(SUMMARYFLAG)) {
        print_short();
        exit(0);
    }
    
    /* rolo <name1> <name2> ... */
    /* print out info about people whose names contain any of the arguments */
    
    if (n_non_option_args() > 0) {
       print_people();
       exit(0);
    }
    
    /* regular rolodex program */
    
    interactive_rolo();
    exit(0);
    
}    
SHAR_EOF
if test 8738 -ne "`wc -c < 'rolo.c'`"
then
	echo shar: error transmitting "'rolo.c'" '(should have been 8738 characters)'
fi
fi
echo shar: extracting "'rolodefs.h'" '(262 characters)'
if test -f 'rolodefs.h'
then
	echo shar: will not over-write existing file "'rolodefs.h'"
else
cat << \SHAR_EOF > 'rolodefs.h'
#define VERSION "2.0"

#define CHUNKSIZE 100000        /* storage size for holding data file */

#define NOLOCKFLAG 'l'
#define SUMMARYFLAG 's'
#define OTHERUSERFLAG 'u'

#define LEGAL_OPTIONS "lsu"
#define USAGE "rolo [ person1 person2 ...] [ -l -s -u user ] "
SHAR_EOF
if test 262 -ne "`wc -c < 'rolodefs.h'`"
then
	echo shar: error transmitting "'rolodefs.h'" '(should have been 262 characters)'
fi
fi
echo shar: extracting "'rolofiles.h'" '(280 characters)'
if test -f 'rolofiles.h'
then
	echo shar: will not over-write existing file "'rolofiles.h'"
else
cat << \SHAR_EOF > 'rolofiles.h'
#define ROLOBAK ".rolodex~"
#define ROLODATA ".rolodex.dat"
#define ROLOPRINT "roloprint.text"
#define ROLOLOCK ".rolodexdata.lock"
#define ROLOTEMP ".rolotemp"
#define ROLOCOPY ".rolocopy"

/* This is now defined in the Makefile */

/*  #define ROLOLIB "/usr/local/lib/rolo"  */
SHAR_EOF
if test 280 -ne "`wc -c < 'rolofiles.h'`"
then
	echo shar: error transmitting "'rolofiles.h'" '(should have been 280 characters)'
fi
fi
echo shar: extracting "'search.c'" '(9108 characters)'
if test -f 'search.c'
then
	echo shar: will not over-write existing file "'search.c'"
else
cat << \SHAR_EOF > 'search.c'
#include <stdio.h>
#include <ctype.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"
#include "mem.h"

#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"
#include "choices.h"


char *select_search_string ()

/* returns 0 if user wants to quit, otherwise returns a user-provided string */

{
  int rval;        
  char *response;
  rval = rolo_menu_data_help_or_abort (        
              "Enter string to search for: ",
              "searchstringhelp",
              "string to search for",
              &response
          );
  switch (rval) {          
    case MENU_ABORT :
      return(0);
      break;
    case MENU_DATA :
      return(copystr(response));
      break;
  }
}  
  

select_field_to_search_by (ptr_index,ptr_name) int *ptr_index; char **ptr_name;

/* returns -1 if the user wishes to abort, otherwise returns 0. */
/* if the user wishes to search by a user-defined field, *ptr_index is OTHER */
/* and *ptr_name is the user-provided name of the field. */

{
  char *response;
  int nchoices = N_BASIC_FIELDS;
  int field_index,rval;
  
  redo :
  
  /* list out each basic field that the user can search by.   The user is */
  /* also given an option to search by a user-provided field.   At the */
  /* moment you cannot search by 'Date Updated' */
  
  display_field_names();  
  
  reask :
  
  rval = rolo_menu_number_help_or_abort (
       "Number of item to search by? ",
       1,nchoices,&field_index
    );
    
  switch (rval) {
        
    case MENU_ABORT :
      return(-1);
      break;
      
    case MENU_HELP :
      cathelpfile(libdir("fieldsearchhelp"),"entering search field",1);
      any_char_to_continue();
      goto redo;
      break;
      
    case MENU_DATA :
    
      if (field_index != nchoices) {
         *ptr_index = field_index - 1;
         *ptr_name = copystr(Field_Names[*ptr_index]);
         return(0);
      }
      
      /* the user wants to search by a user-specified field */
      
      else {
        
         reask2 :
        
         rval = rolo_menu_data_help_or_abort (
                    "Name of user-defined field? ",
                    "userfieldhelp",
                    "name of user field to search by",
                    &response
                 );
         switch (rval) {
           case MENU_ABORT :
             return(-1);
             break;
           case MENU_DATA :
             *ptr_index = OTHER;
             *ptr_name = copystr(response);           
             return(0);         
             break;
         }
      }
      break;
  }
  
}  
      

match_by_name_or_company (search_string,sslen) char *search_string; int sslen;

{
  char *name,*company;
  Ptr_Rolo_Entry entry;        
  Ptr_Rolo_List rlist;
  int count = 0;
  
  rlist = Begin_Rlist;
  while (rlist != 0) {  
    entry = get_entry(rlist);
    name = get_basic_rolo_field((int) R_NAME,entry);
    company = get_basic_rolo_field((int) R_COMPANY,entry);
    if (strncsearch(name,strlen(name),search_string,sslen) ||
        strncsearch(company,strlen(company),search_string,sslen)) {
       set_matched(rlist);
       count++;
    }
  }
  return(count);
  
}


match_link (rlink,field_index,field_name,fnlen,search_string,sslen)

  /* if a match is present, sets the 'matched' field in the link, and */
  /* returns 1, otherwise returns 0. */

  Ptr_Rolo_List rlink;
  int field_index;
  char *field_name;
  int fnlen;
  char *search_string;
  int sslen;
  
{
  Ptr_Rolo_Entry entry;        
  char *field;
  char name[100];
  int j;
        
  entry = get_entry(rlink);
  
  if (field_index == OTHER) {
     for (j = 0; j < get_n_others(entry); j++) {
         field = get_other_field(j,entry);
         while (*field != ':') *field++;
         *field = '\0';
         remove_excess_blanks(name,get_other_field(j,entry));
         *field++ = ':';
         if (0 != nocase_compare(name,strlen(name),field_name,fnlen)) {
            continue;
         }
         if (strncsearch(field,strlen(field),search_string,sslen)) {
            set_matched(rlink);
            return(1);
         }
     }
     return(0);
  }
  else {
     field = get_basic_rolo_field(field_index,entry);
     if (strncsearch(field,strlen(field),search_string,sslen)) {
        set_matched(rlink);
        return(1);
     }
     return(0);
  }

}


find_all_matches (field_index,field_name,search_string,ptr_first_match) 

  /* mark every entry in the rolodex which matches against the search_string */
  /* If the search_string is a substring of the data in the given field then */
  /* that is a match.  Return the number of matches.  If there are any */
  /* matches *ptr_first_match will contain the first matching link. */

  int field_index;
  char *field_name, *search_string;
  Ptr_Rolo_List *ptr_first_match;

{  
  char buffer[100];    
  int fnlen,sslen;
  int count = 0;
  Ptr_Rolo_List rlist = Begin_Rlist;
  
  remove_excess_blanks(buffer,field_name);
  fnlen = strlen(buffer);
  sslen = strlen(search_string);
  
  while (rlist != 0) {  
    unset_matched(rlist);
    if (match_link(rlist,field_index,buffer,fnlen,search_string,sslen)) {
       if (count++ == 0) *ptr_first_match = rlist;
    }
    rlist = get_next_link(rlist);
  }    
  
  return(count);
  
}


rolo_search_mode (field_index,field_name,search_string)

  int field_index;
  char *field_name;
  char *search_string;

{
  int rval,n,j,menuval,ival;
  char *response;
  Ptr_Rolo_List first_match,rmatch,rlist;

  /* mark every entry in the rolodex that satisfies the search criteria */
  /* and return the number of items so marked. */
  
  in_search_mode = 1;
  n = find_all_matches(field_index,field_name,search_string,&first_match);

  if (n == 0) {
     printf (
         "No match found for search string '%s' for field '%s'\n",
         search_string,
         field_name
      );
      sleep(2);
      goto rtn;
  }

  /* if the match is unique, just display the entry. */
  
  else if (n == 1) {
     display_entry(get_entry(first_match));
     switch (entry_action(first_match)) {
       case E_CONTINUE :
         printf("No further matches...\n");
         sleep(2);
         break;
       default :
         break;
     }
     goto rtn;
  }

  /* if there are too many matches to itemize them on a single small */
  /* screen, tell the user that there are lots of matches and suggest */
  /* he specify a better search string, but give him the option of */
  /* iterating through every match. */
  
  else if (n > MAXMATCHES) {
     clear_the_screen();
     printf("There are %d entries that match '%s' !\n",n,search_string);
     printf("Type 'v' to view them one by one,\n");
     printf("or '\\' to abort and enter a more specific search string: ");
     rval = rolo_menu_data_help_or_abort (
                 "","manymatchhelp","many matching entries",&response
              );
     if (rval == MENU_ABORT) goto rtn;              
     display_list_of_entries(Begin_Rlist);
     goto rtn;
  }

  /* there are a small number of matching entries.  List the name of each */
  /* matching entry and let the user select which one he wants to view, */
  /* or whether he wants to iterate through each matching entry. */
  
  else {
     relist :
     summarize_entry_list(Begin_Rlist,search_string);
     cathelpfile(libdir("pickentrymenu"),0,0);  
     rval = menu_match (
          &menuval,&response,
          ": ",
          0,1,0,1,4,
          "\\",S_ABORT,
          "?",S_HELP,
          "Help",S_HELP,
          "",S_SCAN_ONE_BY_ONE
       );
     switch (rval) {    
       case MENU_MATCH :
         switch (menuval) {
           case S_HELP :
             cathelpfile(libdir("pickentryhelp"),"selecting entry to view",1);
             any_char_to_continue();
             goto relist;
             break;
           case S_ABORT :
             goto rtn;
             break;
           case S_SCAN_ONE_BY_ONE :
             display_list_of_entries(Begin_Rlist);
             goto rtn;
             break;
         }
         break;
         
       /* make sure the user entered a valid integer, ival */
       /* if so, find the ivalth entry marked as matched in the rolodex */
       /* and display it. */
         
       case MENU_NO_MATCH : 
         ival = str_to_pos_int(response,1,n);
         if (ival < 0) {
            printf("Not a valid number... Please try again\n");
            sleep(2);
            goto relist;
         }
         rlist = Begin_Rlist;
         for (j = 0; j < ival; j++) {
             while (rlist != 0) {
               if (get_matched(rmatch = rlist)) break;
               rlist = get_next_link(rlist);
             }
             if (rlist != 0) rlist = get_next_link(rlist);                
         }
         display_entry(get_entry(rmatch));
         switch (entry_action(rmatch)) {
           case E_CONTINUE :
           case E_PREV :
             goto relist;
             break;
           default :
             goto rtn;
             break;
         }
         break;
     }
  }

  rtn :
  in_search_mode = 0;
  
}
SHAR_EOF
if test 9108 -ne "`wc -c < 'search.c'`"
then
	echo shar: error transmitting "'search.c'" '(should have been 9108 characters)'
fi
fi
echo shar: extracting "'update.c'" '(6022 characters)'
if test -f 'update.c'
then
	echo shar: will not over-write existing file "'update.c'"
else
cat << \SHAR_EOF > 'update.c'
#include <stdio.h>
#include <ctype.h>

#include "sys5.h"

#ifdef TMC
#include <ctools.h>
#else
#include "ctools.h"
#endif
#include "args.h"
#include "menu.h"
#include "mem.h"

#include "rolofiles.h"
#include "rolodefs.h"
#include "datadef.h"
#include "choices.h"


char *get_new_value () 
{
  char buffer[200];
  int rval;
  switch (rval = getline(stdin,buffer,199)) {
    case AT_EOF :
      user_eof();
      break;
    case TOO_MANY_CHARS :
      fprintf(stderr,"Line too long, truncated...\n");
      sleep(1);
      break;
    default :
      if ('\\' == *buffer && rval == 1) return(0);
      break;
  }
  return(copystr(buffer));
}  
  

Ptr_Rolo_Entry copy_entry (entry) Ptr_Rolo_Entry entry;
{
  Ptr_Rolo_Entry new_entry;        
  int j,n;        
  char **otherfields;
  
  new_entry = (Ptr_Rolo_Entry) rolo_emalloc(sizeof(Rolo_Entry));
  
  /* copy the basic fields, but get a new timestamp */
  
  for (j = 0; j < N_BASIC_FIELDS - 1; j++) {
      set_basic_rolo_field(j,new_entry,copystr(get_basic_rolo_field(j,entry)));
  }
  set_basic_rolo_field(N_BASIC_FIELDS - 1,new_entry,timestring());
  
  /* copy the user-defined fields, if necessary */
  
  set_n_others(new_entry,n = get_n_others(entry));
  if (n > 0) {
     otherfields = (char **) rolo_emalloc(n * sizeof(char *));
     new_entry -> other_fields = otherfields;
     for (j = 0; j < n; j++) {  
         set_other_field(j,new_entry,copystr(get_other_field(j,entry)));
     }
  }     
  else new_entry -> other_fields = 0;     
     
  return(new_entry);     
     
}


rolo_update_mode (rlink) Ptr_Rolo_List rlink;

/* Update the fields of an entry.  The user is not allowed to modify the */
/* timestamp field. */

{
  int rval,menuval,findex,updated,newlen,n,nfields,j,name_changed;
  char *response,*s,*newfield,*newval,*other, **others;
  Ptr_Rolo_Entry entry,old_entry;
        
  cancel_update :
  
  entry = copy_entry(old_entry = get_entry(rlink));

  updated = 0;
  name_changed = 0;
  
  redisplay :
  
  display_entry_for_update(updated ? entry : old_entry);
  nfields = (N_BASIC_FIELDS - 1) + get_n_others(entry);
  
  reask :  
  
  cathelpfile(libdir("updatemenu"),0,0);  
  
  rval = menu_match (
       &menuval,&response,
       ": ",
       0,1,0,1,4,
       "\\",U_ABORT,
       "?",U_HELP,
       "Help",U_HELP,
       "",U_END_UPDATE
    );
    
  switch (rval) {    
    
    case MENU_MATCH :
  
      switch (menuval) {

        case U_HELP :
          cathelpfile(libdir("updatehelp"),"updating",1);
          any_char_to_continue();
          clear_the_screen();
          goto redisplay;

        case U_ABORT :
          if (updated) {
             printf("Previous updates to fields in this entry ignored\n");
          }
          return;
          break;

        case U_END_UPDATE :
          if (!updated) goto reask;
          display_entry(entry);
          if (MENU_YES == rolo_menu_yes_no (
                   "Confirm Update? ",DEFAULT_YES,1,
                   "confirmhelp","confirming update"
                )) {
             printf("Update confirmed\n");
             sleep(1);
             set_entry(rlink,entry);
             if (name_changed) {
                rolo_delete(rlink);
                rolo_insert(rlink,compare_links);
             }
             changed = 1;
             return;
          }
          else {   
             printf("Updates ignored...\n");
             sleep(1);
             updated = 0;
             goto cancel_update;
          }
         break;
       
      }
      break;
     
    case MENU_NO_MATCH : 

      /* check that the response is an integer within range */

      findex = str_to_pos_int(response,1,nfields+1);
      if (findex < 0) {
         printf("Not a valid number...Please try again\n");
         goto reask;
      }
      findex--;

      /* we can either be updating a standard field or a user-defined field */
      /* or adding a new field */

      if (findex < N_BASIC_FIELDS - 1) {
         name_changed = (findex == 0);
         printf("Updating '%s'\n",Field_Names[findex]);
         printf("Old value: %s\n",get_basic_rolo_field(findex,entry));
         printf("New value: ");
         if (0 == (newval = copystr(get_new_value()))) break;
         set_basic_rolo_field(findex,entry,newval);
         updated = 1;
      }
      else if (findex != nfields) {
         findex -= N_BASIC_FIELDS - 1;
         printf("Updating \'");
         s = other = get_other_field(findex,entry);
         while (*s != ':') putc(*s++,stdout);
         printf("\' field\n");
         printf("Old value: %s\n",++s);
         printf("New value: ");
         if (0 == (newval = copystr(get_new_value()))) break;
         if (strlen(newval) == 0) {
            for (j = findex; j < get_n_others(entry); j++) {
                set_other_field(j,entry,get_other_field(j+1,entry));
            }
            set_n_others(entry,get_n_others(entry) - 1);
         }
         else {   
            *s = '\0';
            newlen = strlen(other) + strlen(newval) + 2;
            newfield = rolo_emalloc(newlen);
            nbuffconcat(newfield,3,other," ",newval);
            set_other_field(findex,entry,newfield);
         }
         updated = 1;
      }
      else {
        loop:
        printf("New field (<name>: <value>): ");
        if (0 == (newfield = copystr(get_new_value()))) break;
        if (0 == index(newfield,':')) {
           fprintf(stderr,"No field name.  Use a ':'...\n");
           goto loop;
        }
        n = get_n_others(entry);
        set_n_others(entry,n + 1);
        others = (char **) rolo_emalloc((n + 1) * sizeof(char *));
        for (j = 0; j < n; j++) others[j] = get_other_field(j,entry);
        others[n] = newfield;
        entry -> other_fields = others;
        updated = 1;
      }
      break;

    case MENU_EOF :
      user_eof();
      break;
      
    default :      
      fprintf(stderr,"Impossible return from update menu_match\n");
      save_and_exit(-1);
      break;
     
  }

  goto redisplay;

}
SHAR_EOF
if test 6022 -ne "`wc -c < 'update.c'`"
then
	echo shar: error transmitting "'update.c'" '(should have been 6022 characters)'
fi
fi
exit 0
#	End of shell archive