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

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

Mod.sources:  Volume 4, Issue 29
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:
#	toolsdir
# This archive created: Thu Mar 13 18:29:56 1986
export PATH; PATH=/bin:$PATH
if test ! -d 'toolsdir'
then
	echo shar: creating directory "'toolsdir'"
	mkdir 'toolsdir'
fi
echo shar: extracting "'toolsdir/Makefile'" '(143 characters)'
if test -f 'toolsdir/Makefile'
then
	echo shar: will not over-write existing file "'toolsdir/Makefile'"
else
cat << \SHAR_EOF > 'toolsdir/Makefile'
CC = cc
CFLAGS = -DBSD42 -O
DBGFLAG = 
SOURCES = mem.c ctools.c args.c menu.c
OBJECTS = mem.o ctools.o args.o menu.o
LIBS =

tools: $(OBJECTS)
SHAR_EOF
if test 143 -ne "`wc -c < 'toolsdir/Makefile'`"
then
	echo shar: error transmitting "'toolsdir/Makefile'" '(should have been 143 characters)'
fi
fi
echo shar: extracting "'toolsdir/args.c'" '(8623 characters)'
if test -f 'toolsdir/args.c'
then
	echo shar: will not over-write existing file "'toolsdir/args.c'"
else
cat << \SHAR_EOF > 'toolsdir/args.c'
#include <stdio.h>
#include <ctype.h>
#include "basics.h"
#include "args.h"        
#include "sys5.h"

/***************************************************************************/
/***************************************************************************/

           /*****     COMMAND LINE ARGUMENT PARSER    *****/

/***************************************************************************/
/***************************************************************************/

/* Author: JP Massar */

/* parses command line arguments in argv under the following rules: */
/* any argument not beginning with '-' is treated as a unit. */
/* any argument beginning with a '-' must have the remaining characters */
/* be in the set {a-zA-Z}.  Each character is considered a separate option. */
/* (Thus -abc is equivalent to -a -b -c).  Non-dashed arguments are */
/* associated with the option that precedes them, e.g, 'cat -a foo -b bar' */
/* has foo associated with 'a' and bar associated with 'b'.  A non-dashed */
/* argument preceding any option is not associated with any option. */
/* users can specify whether duplicate options are errors. */

/* Non-dashed arguments are henceforth referred to as arguments, and */
/* dashed arguments are henceforth referred to as options. */
/* Arguments are ordered from left to right. */

/* The following routines are available to users: */

/* get_args()           called to parse argv.  Detects syntactic errors */
/* any_option_present() are any options present in the command line? */
/* option_present()     is an option present? */
/* option_arg()         returns an argument associated with an option */
/* non_option_arg()     returns an argument not associated with any option */
/* non_dash_arg()       returns an argument */
/* n_option_args()      returns number of args associated with an option */
/* n_non_option_args()  rtns number of args not associated with any option */
/* n_non_dash_args()    returns number of arguments. */
/* check_option_args()  checks bounds on number of args assoc with an option */
/* legal_options()      checks that options provided are a subset of a-zA-Z */
/* set_option()         turns on option */
/* error_message()      prints out an illegal syntax error message to stderr */


int option_to_index (achar) char achar;
{
  if (isupper(achar)) return(achar - 'A');
  if (islower(achar)) return(achar - 'a' + 26);
  return(NO_OPTION);
}

char index_to_option (index) int index;
{        
  if (index < 26) return('A' + index);
  return('a' + index - 26);
}  


/* the command line arguments are parsed into Cmd when get_args returns */
/* successfully */

static Ptr_Cmd_Line Cmd = NIL;

int get_args (argc, argv, dup_error, print_msg)
        
  /* Returns one of NO_ARGS, ARG_ERROR, or ARGS_PRESENT */

  int argc;
  char **argv;
  Bool print_msg, dup_error;

{
  int i,j,dash_index;
  Ptr_Cmd_Arg arg,last = NIL;
  char echar, *optr;

  Cmd = (Ptr_Cmd_Line) malloc(sizeof(Cmd_Line));
  Cmd -> non_dash_arg_list = NIL;
  for (j = 0; j < MAX_OPTIONS; j++) (Cmd -> dash_options)[j] = F;

  if (argc == 1) return(NO_ARGS);

  i = 0;
  dash_index = NO_OPTION;
  
  while (++i < argc) {
        
        /* parse arguments (i.e., anything not beginning with '-' */
        
        if (argv[i][0] != '-') {
                arg = (Ptr_Cmd_Arg) malloc(sizeof(Cmd_Arg));
                arg -> option = argv[i];
                arg -> option_index = dash_index;
                arg -> next = NIL;
                if (last == NIL) {
                        Cmd -> non_dash_arg_list = arg;
                        last = arg;
                }                
                else {
                        last -> next = arg;
                        last = arg;
                }
                continue;
        }

        /* parse options. '-' by itself is illegal syntax */
        
        if (strlen(argv[i]) < 2) {
                echar = '-';
                goto parse_error;
        }        
        optr = argv[i];
        optr++;
        while (*optr != '\0') {
                if (NO_OPTION == (dash_index = option_to_index(*optr))) {
                        echar = *optr;
                        goto parse_error;
                };
                if ((Cmd -> dash_options)[dash_index] && dup_error) {
                        echar = *optr;
                        goto duplicate_error;
                }
                (Cmd -> dash_options)[dash_index] = T;
                optr++;
        }

  }

  return(ARGS_PRESENT);

  parse_error :

  if (print_msg) fprintf(stderr,"illegal option: %c\n",echar);
  return(ARG_ERROR);

  duplicate_error:

  if (print_msg) fprintf(stderr,"duplicate option: %c\n",echar);
  return(ARG_ERROR);

}  


Bool option_present (achar) char achar;
{        
  return((Cmd -> dash_options)[option_to_index(achar)]);
}


Bool any_option_present ()
{
  int j;
  for (j = 0; j < MAX_OPTIONS; j++) {
      if ((Cmd -> dash_options)[j]) return(T);
  }      
  return(F);
}  

  
char * get_option_arg (i,n) int i; int n;

  /* get the nth option associated with the option whose index is 'i' */
        
{
  int count;
  Ptr_Cmd_Arg args;        
  args = Cmd -> non_dash_arg_list;  
  count = 0;
  while (args != NIL) {
        if (i == args -> option_index && ++count == n) {
                return(args -> option);
        }
        args = args -> next;
  }
  return(NIL);
}  


char * option_arg (achar,n) char achar; int n;
{
  return(get_option_arg(option_to_index(achar),n));
}


char * non_option_arg (n) int n;
{
  return(get_option_arg(NO_OPTION,n));
}  


char * non_dash_arg (n) int n;

{
  int count = 0;
  Ptr_Cmd_Arg arg;
  arg = Cmd -> non_dash_arg_list;
  while (arg != NIL) {
        if (++count == n) return(arg -> option);
        arg = arg -> next;
  }
  return(NIL);
}

print_args ()

  /* debugging routine which prints out the Cmd structure in readable form */

{
  int j,i,n;
  char *option,ochar;

  if (Cmd == NIL) {
        printf("\n\nNo arguments\n\n");
        return;
  }        
  
  printf("\n\narguments not associated with options: ");
  n = 1;
  while (T) {
        if (NIL == (option = non_option_arg(n++))) break;
        printf("%s ",option);
  }
  printf("\n");

  printf("\n\noptions and their arguments:\n\n");
  for (j = 0; j < MAX_OPTIONS; j++) {
      ochar = index_to_option(j);
      if (option_present(ochar)) {
         printf("%c : \t",ochar);
         i = 1;
         while (T) {
           if (NIL == (option = option_arg(ochar,i++))) break;
           printf("%s ",option);
         }
         printf("     \t(# is %d)",n_option_args(ochar));
         printf("\n");
      }
  }

  printf("\nnumber of non-dashed args is: %d\n",n_non_dash_args());
  printf("number of non-option args is  : %d\n",n_non_option_args());
  
}


#define ALL -1
#define NON_OPTION -2

int arg_counter (type) int type;

  /* general routine which counts arguments */
  /* if type isn't ALL or NON_OPTION then type is an index of an option */

{
  int index,count;
  Ptr_Cmd_Arg arg;
  arg = Cmd -> non_dash_arg_list;        
  count = 0;
  index = (type == NON_OPTION) ? NO_OPTION : type;
  while (arg != NIL) {
        if (type == ALL) {
                count++;
        }
        else if (arg -> option_index == index) count++;
        arg = arg -> next;
  }
  return(count);
}

int n_option_args (achar) char achar;
{        
  return(arg_counter(option_to_index(achar)));
}

int n_non_option_args ()
{
  return(arg_counter(NON_OPTION));
}

int n_non_dash_args ()
{
  return(arg_counter(ALL));
}


set_option (achar) char achar;
{
  (Cmd -> dash_options)[option_to_index(achar)] = T;
}


error_message (progname, argv, i, usage)
        char *progname; char ** argv; int i; char *usage;
{
  fprintf(stderr,"\nillegal argument to %s : %s\n",progname,argv[i]);
  if (usage) fprintf(stderr,"%s\n",usage);
}


Bool
check_option_args (achar,themin,themax) char achar; int themin,themax;
{
  int n;
  if (themin > themax) return(T);
  n = n_option_args(achar);
  return ((Bool) (n >= themin && n <= themax));
}


char legal_options (legalstring) char *legalstring;

  /* are all the options the user specified characters in legalstring? */
  /* returns ALL_LEGAL if so, otherwise the first option not in the string */

{
  int j;
  char option, *s;
  for (j = 0; j < MAX_OPTIONS; j++) {
      if ((Cmd -> dash_options)[j]) {
         option = index_to_option(j);
         s = legalstring;
         while (T) {
               if (*s == '\0') return(option);
               if (*s == option) break;
               s++;
         }
      }
  }
  return(ALL_LEGAL);
}
SHAR_EOF
if test 8623 -ne "`wc -c < 'toolsdir/args.c'`"
then
	echo shar: error transmitting "'toolsdir/args.c'" '(should have been 8623 characters)'
fi
fi
echo shar: extracting "'toolsdir/args.h'" '(2401 characters)'
if test -f 'toolsdir/args.h'
then
	echo shar: will not over-write existing file "'toolsdir/args.h'"
else
cat << \SHAR_EOF > 'toolsdir/args.h'
/* You must include "basics.h" to use this */

/* To use this package, first call get_args, then call the various other */
/* routines to see which options were given, etc. */

#define NIL 0                          /* null pointer  */

#define ARG_ERROR -1
#define NO_ARGS 0
#define ARGS_PRESENT 1

#define MAX_OPTIONS 52                 /* a-z A-Z */
#define NO_OPTION -1

/* an argument and the option it is associated with */

typedef struct argument {
        char *option;
        int option_index;
        struct argument *next;
} Cmd_Arg, *Ptr_Cmd_Arg;

/* all the arguments (in a list) and a toggle for every possible option */

typedef struct {
        Ptr_Cmd_Arg non_dash_arg_list; 
        int dash_options[MAX_OPTIONS];
} Cmd_Line, *Ptr_Cmd_Line;

/*--------------------------------------------------------------------------*/
        
extern char *malloc();

extern int get_args();

        /* int argc; char **argv; Bool dup_error; Bool print_msg; */
        /* returns one of ARG_ERROR, NO_ARGS, or ARGS_PRESENT */
        /* if dup_error, then having two identical options on the command */
        /* line will cause an error.  If print_msg, then  any error that */
        /* is noticed is printed out to stderr.  If !dup_error then */
        /* 'foo -a 3 4 -b -a 5' is equivalent to 'foo -a 3 4 5 -b' */
        
        
extern Bool any_option_present();

        /* no arguments */

extern Bool option_present();

        /* char achar; */

extern char * option_arg();

        /* char achar; int n; */

extern char * non_option_arg();

        /* int n; */

extern int n_option_args();

        /* char achar; */

extern int n_non_option_args();

        /* no arguments */

extern int n_non_dash_args();

        /* no arguments */

extern Bool check_option_args();        

        /* char achar; int min; int max; */

#define ALL_LEGAL 0

extern char legal_options();

        /* char *legaloptions; */
        /* legaloptions should be a string of characters all in the range */
        /* a-zA-Z.   Returns ALL_LEGAL if every option parsed in included */
        /* in the legaloptions string, otherwise returns the first option */
        /* character not in the string. */

extern set_option();

        /* char achar */

extern error_message();

        /* char *progname; char **argv; int index; char *usage; */

extern print_args();

        /* debugging routine */
SHAR_EOF
if test 2401 -ne "`wc -c < 'toolsdir/args.h'`"
then
	echo shar: error transmitting "'toolsdir/args.h'" '(should have been 2401 characters)'
fi
fi
echo shar: extracting "'toolsdir/basics.h'" '(186 characters)'
if test -f 'toolsdir/basics.h'
then
	echo shar: will not over-write existing file "'toolsdir/basics.h'"
else
cat << \SHAR_EOF > 'toolsdir/basics.h'
#define T 1
#define F 0

#define True 1
#define False 0

#define SUCCESS 1
#define FAILURE 0
#define ERROR -1

#define Bool int

#define MAXINT 2147483647
#define MAXINTSTR "2147483647"
SHAR_EOF
if test 186 -ne "`wc -c < 'toolsdir/basics.h'`"
then
	echo shar: error transmitting "'toolsdir/basics.h'" '(should have been 186 characters)'
fi
fi
echo shar: extracting "'toolsdir/ctools.c'" '(20370 characters)'
if test -f 'toolsdir/ctools.c'
then
	echo shar: will not over-write existing file "'toolsdir/ctools.c'"
else
cat << \SHAR_EOF > 'toolsdir/ctools.c'
/* -*- Mode: C; Package: (CTOOLS C) -*- */

#include <ctype.h>
#include <stdio.h>

#ifdef BSD42
#include <strings.h>
#endif

#include "ctools.h"

/* miscellaneous fairly primitive routines that deal with characters, */
/* strings, memory, simple input and pathnames. */
 

/* Author:  JP Massar */
/* Thinking Machines Corporation */

/* Included routines:

   emalloc
   anewstr
   
   copy
   fill
   
   to_upper_if_lower
   to_lower_if_upper
   
   buffconcat
   nbuffconcat
   
   slcompare
   slge_compare
   nocase_compare
   
   strfind
   strncfind
   strsearch
   strncsearch
   
   yes_or_no_check
   
   remove_excess_blanks
   ip_string_trim
   string_trim
   string_upcase
   string_downcase
   
   all_digits
   all_whitespace
   all_uppercase
   all_lowercase
   all_alphabetic
   all_alphanumeric
   all_ascii
   
   str_to_pos_int
   
   sreverse
   ip_sreverse
        
   temp_path
   perm_path
   make_path_numeric_extension
   make_path
   just_filename
   
   read_yes_or_no
   getline
   getlines
   ngetlines
   getfile
   ngetfile
   read_file_into_buffer
   efopen

   check_int
   check_string
   
*/
   

extern char *malloc();


char *emalloc (space) int space;

/* allocate 'space' bytes, die if we have run out of memory. */

{
  char *rval;        
  if (space < 0) {
     fprintf(stderr,"Fatal error: argument to emalloc < 0\n");
     exit(-1);
  }
  if (0 == (rval = malloc((unsigned) space))) {
     fprintf(stderr,"Fatal error:  No more memory\n");
     exit(-1);
  }
  return(rval);
}  


char *anewstr (astring) char *astring;

/* allocate space for and then copy a string.  Returns pointer to */
/* new string. */

{        
  char *newstr;
  newstr = emalloc(strlen(astring)+1);
  strcpy(newstr,astring);
  return(newstr);
}


copy (dest,src,n)

  /* copy n bytes */

  register char *dest,*src;
  register int n;

  { register int j = 0;
    while (j++ < n) *dest++ = *src++; 
  }
 

fill (addr,ch,n)

  /* fill n sequential bytes with 'ch' */

  register char *addr;
  register char ch;
  register int n;
  
  { register int j = 0;
    while (j++ < n) *addr++ = ch;
  }


to_upper_if_lower (ch)

  char ch;

  { return(islower(ch) ? toupper(ch) : ch); }


to_lower_if_upper (ch)

  char ch;

  { return(isupper(ch) ? tolower(ch) : ch); }


buffconcat (buffer,s1,s2) 

  /* concatenate two null terminated strings into a buffer. */

  char *buffer, *s1, *s2;
  
  { while (*s1 != '\0') *buffer++ = *s1++;
    while (*s2 != '\0') *buffer++ = *s2++;
    *buffer = '\0';
  }


nbuffconcat (buffer,n,s1,s2,s3,s4,s5,s6)

  /* concatenates up to 6 strings into a buffer.  Returns -1 if n */
  /* is not reasonable, otherwise returns 0. */

  char *buffer;
  int n;
  char *s1,*s2,*s3,*s4,*s5,*s6;

{
  register char *b;
  register char *s;
  int i;
  b = buffer;
  if (n < 1 || n > 6) return(-1);
  for (i = 1; i <= 6; i++) {
      if (i > n) break;
      switch (i) {
        case 1 : s = s1; break;
        case 2 : s = s2; break;
        case 3 : s = s3; break;
        case 4 : s = s4; break;
        case 5 : s = s5; break;
        case 6 : s = s6; break;
      }
      while (*s != '\0') *b++ = *s++;
  }
  *b = '\0';
  return(0);
}


slcompare (s1,l1,s2,l2)

  /* compare strings with possible nulls in them given their lengths */
  /* only returns EQUAL (0) or NOT EQUAL (-1) */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j;
    if (l1 != l2) return(-1);
    j = 0;
    while (j++ < l1) 
      if (*s1++ != *s2++) return(-1);
    return(0);
  }

  
slge_compare (s1,l1,s2,l2)

  /* returns -1 if s1 < s2; 1 if s2 < s1; 0 if s1 = s2 */
  /* ignores nulls in the strings */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j,len;
    j = 0;
    len = l2 > l1 ? l1 : l2;
    while (j++ < len) {
      if (*s1 != *s2) 
         return((*s1 < *s2) ? -1 : 1);
      s1++;   
      s2++;
    }  
    return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1));
  }

nocase_compare (s1,l1,s2,l2)

  /* treats nulls as normal characters.  Returns same as slge_compare */

  char *s1;
  int l1;
  char *s2;
  int l2;

  { int j,len,ch1,ch2;
    j = 0;
    len = l2 > l1 ? l1 : l2;
    while (j++ < len) {
      ch1 = to_upper_if_lower(*s1++);
      ch2 = to_upper_if_lower(*s2++);
      if (ch1 != ch2) {
         return((ch1 < ch2) ? -1 : 1);
      }
    }  
    return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1));
  }


char *strfind(s1,s2,fast)  
  
  register char *s1;
  char *s2;
  Bool fast;
  
{  
  register int len1,len2;
  len2 = strlen(s2);
  if (fast) {
     while (*s1 != '\0')
       if (0 == strncmp(s1++,s2,len2)) return(s1-1);
  }  
  else {
     len1 = strlen(s1);
     while (len1 >= len2) {
       if (0 == strncmp(s1++,s2,len2)) return(s1-1);
       len1--;
     }
  }
  return(0);
}     


char *strncfind(s1,s2,fast)

  register char *s1;
  char *s2;
  Bool fast;
  
{  
  register int len1,len2;
  len2 = strlen(s2);
  if (fast) {
     while (*s1 != '\0')
       if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1);
  }
  else {
     len1 = strlen(s1);
     while (len1 >= len2) {
       if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1);
       len1--;
     }
  }
  return(0);
}  

  
char *strsearch(s1,s1len,s2,s2len)

  /* do a substring search without noticing nulls */
  /* finds s2 in s1, returns pointer into s1 or 0 */

  register char *s1, *s2;
  register int s1len,s2len;
  
  {  register char *pc;
     register char *bound;
     register char *pctemp;
     register char *s2temp;
     register int j;

     bound = s1 + s1len - s2len;
     for (pc = s1; pc <= bound; pc++) {
         pctemp = pc;
         s2temp = s2;
         for (j = 0; j < s2len; j++)
             if (*pctemp++ != *s2temp++) goto not_here;
         return(pc);       
         not_here :
         continue;
     }    
     return(0);
}


char *strncsearch(s1,s1len,s2,s2len)

  /* do a substring search without noticing nulls */
  /* finds s2 in s1, returns pointer into s1 or 0 */
  /* case independent */

  register char *s1, *s2;
  register int s1len,s2len;
  
  {  register char *pc;
     register char *bound;
     register char *pctemp;
     register char *s2temp;
     register int j;
     char ch1, ch2;

     bound = s1 + s1len - s2len;
     for (pc = s1; pc <= bound; pc++) {
         pctemp = pc;
         s2temp = s2;
         for (j = 0; j < s2len; j++) {
             ch1 = *pctemp++;
             ch2 = *s2temp++;
             if (to_upper_if_lower(ch1) != to_upper_if_lower(ch2))
                goto not_here;
         }
         return(pc);       
         not_here :
         continue;
     }    
     return(0);
}


int remove_excess_blanks (newstring,oldstring) 

  /* it is assumed that newstring is as long as oldstring if necessary */

  char *newstring,*oldstring;

{
  int count = 0;
  int space_found = 0;

  /* skip over all blanks at beginning */
  
  if (*oldstring == ' ') {
     while (*oldstring == ' ') oldstring++;
  }

  while (*oldstring != '\0') {
        if (space_found && *oldstring == ' ') {
           oldstring++;
           continue;
        }
        space_found = (*oldstring == ' ');
        *newstring++ = *oldstring++;
        count++;
  }

  *newstring = '\0';
  if (count > 0 && *(newstring - 1) == ' ') {
     count--;
     *(newstring - 1) = '\0';
  }

  return(count);

}

int ip_string_trim (oldstring,trimchars,pretrim,posttrim)

  char *oldstring, *trimchars;
  Bool pretrim,posttrim;

{
  Bool trim = T;
  char *np = oldstring, ch;
  int len;
  
  if (pretrim) {
     while (trim && ('\0' != (ch = *np))) {
       trim = (0 != index(trimchars,ch));
       if (trim) np++;
     }
     strcpy(oldstring,np);
  }
  if (1 >= (len = strlen(oldstring)) && pretrim) return(len);
  if (posttrim) {
     np = oldstring + len - 1;
     while (T) {
       ch = *np;
       trim = (0 != index(trimchars,ch));
       if (trim) *np == '\0';
       if (!trim || np == oldstring) break;
       np--;
     }
  }
  return(strlen(oldstring));
}
  
int string_trim (newstring,oldstring,trimchars,pretrim,posttrim)

  char *newstring, *oldstring, *trimchars;
  Bool pretrim, posttrim;
  
{  
  strcpy(newstring,oldstring);
  return(ip_string_trim(newstring,trimchars,pretrim,posttrim));
}

char *string_upcase (astring) char *astring;
{
  while (*astring) {
    *astring = to_upper_if_lower(*astring);
    astring++;
  }
}

char *string_downcase (astring) char *astring;
{
  while (*astring) {
    *astring = to_lower_if_upper(*astring);
    astring++;
  }
}


yes_or_no_check (astring) char *astring;

/* returns 1 if yes, 0 if no, -1 if neither */
/* works for 'Y' 'YES' 'NO' 'N' in any capitalization */

{  
  int len;
  len = strlen(astring);
  if (len == 0 || len > 3) return(-1);
  if (0 == nocase_compare(astring,len,"YES",3) || 
      0 == nocase_compare(astring,len,"Y",1))
     return(1);
  if (0 == nocase_compare(astring,len,"NO",2) || 
      0 == nocase_compare(astring,len,"N",1))
     return(0);
  return(-1);
}


Bool all_digits (astring) char *astring;

/* test whether every character is a digit (0-9) */

{
  while (*astring != '\0') 
    if (!isdigit(*astring++)) return(F);
  return(T);
}


Bool all_whitespace (astring) char *astring;

/* test whether every character is a blank or a tab */

{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (ch == ' ' || ch == '\t') continue;
    return(F);
  }
  return(T);
}

Bool all_uppercase(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isupper(ch)) return(F);
  }
  return(T);
}

Bool all_lowercase(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!islower(ch)) return(F);
  }
  return(T);
}

Bool all_alphabetic(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isalpha(ch)) return(F);
  }
  return(T);
}

Bool all_ascii(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isascii(ch)) return(F);
  }
  return(T);
}

Bool all_alphanumeric(astring) char *astring;
{
  register char ch;
  while ((ch = *astring++) != '\0') {
    if (!isalnum(ch)) return(F);
  }
  return(T);
}

int str_to_pos_int (astring,low,high) char *astring; int low,high;

  /* returns -1 if *astring is not composed of digits. */
  /* returns -2 if the integer is out of range. */
  /* treats all digit strings as decimal. */

{
  int value,len,maxlen,j;
  maxlen = strlen(MAXINTSTR);
  len = strlen(astring);
  if (!all_digits(astring)) return(-1);
  if (len > maxlen) return(-2);
  if (len == maxlen) {
     if (1 == strcmp(astring,MAXINTSTR)) return(-2);
  }
  for (j = 0; j < len-1; j++) {
      if (*astring != '0') break;
      astring++;
  }
  sscanf(astring,"%d",&value);
  if (value < low || value > high) return(-2);
  return(value);
}


int sreverse (buffer,astring) char *buffer, *astring;
{
  register int last = strlen(astring);
  buffer[last--] = '\0';
  while (last >= 0) buffer[last--] = *astring++;
}

char * ip_sreverse (astring) char *astring;
{
  register int last = strlen(astring) - 1;
  register int first = 0;
  register char ch;
  while (first < last) {
    ch = astring[first];
    astring[first++] = astring[last];
    astring[last--] = ch;
  }
  return(astring);
}



static char pathbuffer[PATH_MAXPATHLEN];


char *temp_path (dir,filename) char *dir; char *filename;

{
  return(make_path(dir,filename,"",F));
}


char *perm_path (dir,filename) char *dir; char *filename;

{
  return(make_path(dir,filename,"",T));
}


char *make_path_numeric_extension (dir,filename,extension,perm)

  char *dir, *filename;
  int extension;
  Bool perm;

{
  char buffer[20];
  sprintf(buffer,"%d",extension);
  return(make_path(dir,filename,buffer,perm));
}


char *make_path (dir,filename,extension,perm)

  char *dir, *filename, *extension;
  Bool perm;

{
  char *rval;
  if (!perm && (strlen(dir) + 1 + strlen(filename) + strlen(extension) + 1 >=
                PATH_MAXPATHLEN)) {
     return((char *) 0);
  }
  nbuffconcat(pathbuffer,4,dir,"/",filename,extension);
  if (!perm) return(pathbuffer);
  rval = emalloc(strlen(pathbuffer) + 1);  
  strcpy(rval,pathbuffer);
  return(rval);
}


char *just_filename (path,new,perm) char *path; Bool new,perm;

{
  char *fnp,*rval;
  fnp = (0 == (fnp = rindex(path,'/'))) ? path : fnp + 1;
  if (!new) return(fnp);
  if (!perm) {
     strcpy(pathbuffer,fnp);
     return(pathbuffer);
  }
  else {
     rval = emalloc(strlen(fnp) + 1);
     strcpy(rval,fnp);
     return(rval);
  }
}



read_yes_or_no (iport,oport,prompt,helpstring,quitstring)

  /* prints prompt, then reads from port until it gets 'Y', 'N', 'YES' or */
  /* 'NO' (case independently).  If helpstring and/or quitstring are not */
  /* "" or (char *) 0 then if the user types in one of those ANSWER_HELP */
  /* or ANSWER_QUIT are returned, otherwise ANSWER_NO or ANSWER_YES are */
  /* eventually returned. */

  FILE *iport, *oport;
  char *prompt, *helpstring, *quitstring;

{
  char buffer[20],buffer2[20];
  int bl,hl,ql,len;
  
  buffer[19] = '\0';
  
  while (T) {
        
    fprintf(oport,"%s",prompt);
    switch (len = getline(iport,buffer,20)) {
      case (AT_EOF) :
        return(ANSWER_EOF);
        break;
      case (TOO_MANY_CHARS) :
        break;
      default :
        if (0 == (bl = remove_excess_blanks(buffer2,buffer))) break;
        switch (yes_or_no_check(buffer2)) {
          case (0) :
            return(ANSWER_NO);
          case (1) :
            return(ANSWER_YES);
          case (-1) :
            if (helpstring != (char *) 0 && (hl = strlen(helpstring)) > 0) {
               if (0 == nocase_compare(buffer2,bl,helpstring,hl)) {
                  return(ANSWER_HELP);
               }
            }
            if (quitstring != (char *) 0 && (ql = strlen(quitstring)) > 0) {
               if (0 == nocase_compare(buffer2,bl,quitstring,ql)) {
                  return(ANSWER_QUIT);
               }
            }
            break;
        }   
        break;
    }
   
    fprintf(oport,"Please answer 'YES' or 'NO'\n");
    continue;
   
  }
    
}


int getline (iport,buffer,buflen) FILE *iport; char *buffer; int buflen;

  /* reads a line into buffer.  Does not put the '\n' into buffer. */
  /* Returns AT_EOF if at end of file when called.  If it encounters */
  /* end of file after reading at least one character, the eof is treated */
  /* as if it were a newline.   Returns TOO_MANY_CHARS if more than */
  /* buflen - 1 characters are read before encountering a newline. */        
  /* In this case exactly buflen - 1 characters are read. */
  /* The last character read is always follwed by a '\0'. */
  /* if successful getline returns the number of characters read exclusive */
  /* of a terminating newline or eof. */

{
  int ch;
  char *bptr = buffer;
  int nchars = 0;
  
  if (buflen <= 0) return(TOO_MANY_CHARS);
  
  while (T) {
    switch (ch = getc(iport)) {
      case (EOF) :
      case ('\n') :
        if (ch == EOF && nchars == 0) return(AT_EOF);
        *bptr = '\0';
        return(nchars);
      default :
        if (++nchars == buflen) { 
           *bptr = '\0';
           ungetc(ch,iport);
           return(TOO_MANY_CHARS);
        }
        *bptr++ = ch;
    }
    
  }
    
}


int getlines (fp,n,ptr_lines,linebuf,maxlinelen)

  /* See documentation for getfile below */

  FILE *fp;
  int n;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  int len;
  char *line;
  if (0 > (len = getline(fp,linebuf,maxlinelen))) {
     if (len == AT_EOF) {
        *ptr_lines = (char **) emalloc(n * sizeof(char **));
        return(n);
     }
     else {
        return(TOO_MANY_CHARS);
     }
  }
  else {
     line = emalloc(len+1);
     strcpy(line,linebuf);
     len = getlines(fp,n+1,ptr_lines,linebuf,maxlinelen);
     if (len == TOO_MANY_CHARS) return(TOO_MANY_CHARS);
     (*ptr_lines)[n] = line;
     return(len);
  }
}


int getfile (filename,ptr_lines,linebuf,maxlinelen)

  /* read in a file as an array of character strings */
  /* 'maxlinelen+1' is the maximum length a line of the file is allowed */
  /* to be.  'linebuf' must be at least 'maxlinelen+1' characters long. */
  /* Returns the number of lines in the file (and therefore the number */
  /* of entries in *ptr_lines) if successful.  Returns IOERROR if it */
  /* could not open the file to read from. Returns TOO_MANY_CHARS if */
  /* it encounters a line longer than 'maxlinelen' characters. */

  /* Space for each line is malloc'ed as it is read in and the text for */
  /* the jth line is stored in (*ptr_lines)[j] */

  char *filename;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  FILE *fp;
  int nlines;
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  nlines = getlines(fp,0,ptr_lines,linebuf,maxlinelen);
  fclose(fp);
  return(nlines);
}


int ngetlines (fp,n,ptr_lines,linebuf,maxlinelen)

  /* See documentation for ngetfile below */

  FILE *fp;
  int n;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  int len;
  int nlines = 0;
  *ptr_lines = (char **) emalloc(n * sizeof(char **));
  while (T) {
    if (0 > (len = getline(fp,linebuf,maxlinelen))) {
       if (len == AT_EOF) {
          return(nlines);
       }
       else {
          return(TOO_MANY_CHARS);
       }
    }
    else {
       if (++nlines > n) {
          return(TOO_MANY_LINES);
       }
       (*ptr_lines)[nlines-1] = anewstr(linebuf);
    }
  }
}



int ngetfile (n,filename,ptr_lines,linebuf,maxlinelen)

  /* Same as getfile except that at most n lines will be read. */
  /* If it attempts to read more than n lines, TOO_MANY_LINES will */
  /* be returned. */

  int n;
  char *filename;
  char ***ptr_lines;
  char *linebuf;
  int maxlinelen;

{
  FILE *fp;
  int nlines;
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  nlines = ngetlines(fp,n,ptr_lines,linebuf,maxlinelen);
  fclose(fp);
  return(nlines);
}


extern int read_file_into_buffer (

       filename,ptr_lines,maxlines,buffer,buflen,linebuffer,linebuflen

    )
       
  char *filename; 
  char ***ptr_lines;
  int maxlines;
  char *buffer;
  int buflen;
  char *linebuffer;
  int linebuflen;

  /* *ptr_lines should be an array of character string pointers maxlines */
  /* big.  buffer should be an array of characters buflen long.  The routine */
  /* reads lines using getline and stores them into buffer, terminating each */
  /* with a null.  A pointer to the nth line read is stored in *ptr_lines[n] */
  /* Returns IOERROR if it cannot open the file for reading, TOO_MANY_LINES */
  /* if more than maxlines were read in, TOO_MANY_CHARS if buffer is */
  /* filled before end of file is reached, and LINE_TOO_LONG is any line is */
  /* longer than linebuflen.  Returns number of lines read in if successful. */
  
{  
  FILE *fp;
  int linecount,charcount,len;
  char *bp;
  char **lines;
  
  if (NULL == (fp = fopen(filename,"r"))) return(IOERROR);
  linecount = 0;
  charcount = 0;
  bp = buffer;
  lines = *ptr_lines;
  
  while (T) {
        
    if (0 > (len = getline(fp,linebuffer,linebuflen))) {
       fclose(fp);
       if (len == AT_EOF) {
          return(linecount);
       }
       else {
          return(LINE_TOO_LONG);
       }
    }
    
    if (linecount >= maxlines) {
       fclose(fp);
       return(TOO_MANY_LINES);
    }
    
    charcount += len;
    if (charcount >= buflen) {
       fclose(fp);
       return(TOO_MANY_CHARS);
    }
    
    strcpy(bp,linebuffer);
    lines[linecount++] = bp;
    bp += (len + 1);
  
  }
  
}
  
extern char *efopen (filename,mode) char *filename; char *mode;

  /* The routine simply calls fopen with the same arguments, but prints a */
  /* reasonable error message and calls exit if the call to fopen fails. */

{
  FILE *fp;
  if (NULL == (fp = fopen(filename,mode))) {
     fprintf(stderr,"Could not open %s, mode: %s\n",filename,mode);
     perror("Reason: ");
     exit(1);
  }
  return((char *) fp);
}



extern int record_fseek (fp,rnum,fromwhere,rsize,hdrsize)

  FILE *fp;
  long rnum;
  int fromwhere;
  int rsize;
  int hdrsize; 

{
  if (fromwhere == 0) {
     return(fseek(fp,(long) ((rnum - 1)*rsize + hdrsize),0));
  }
  else {
     return(fseek(fp,(long) (rnum*rsize),fromwhere));
  }
}


Bool check_string (s,minlen,maxlen) char *s; long minlen,maxlen;
{
  long len;
  if (s == 0) return(F);
  len = strlen(s);
  return (len >= minlen && len <= maxlen);
}

SHAR_EOF
if test 20370 -ne "`wc -c < 'toolsdir/ctools.c'`"
then
	echo shar: error transmitting "'toolsdir/ctools.c'" '(should have been 20370 characters)'
fi
fi
echo shar: extracting "'toolsdir/ctools.h'" '(11301 characters)'
if test -f 'toolsdir/ctools.h'
then
	echo shar: will not over-write existing file "'toolsdir/ctools.h'"
else
cat << \SHAR_EOF > 'toolsdir/ctools.h'
/* -*- Mode: C; Package: (CTOOLS C) -*- */

#ifndef Bool
#define Bool int
#endif

#ifndef T
#define T 1
#endif

#ifndef F
#define F 0
#endif

#ifndef MAXINT
#define MAXINT 2147483647
#define MAXINTSTR "2147483647"
#endif

#ifndef MAXPATHLEN
#define MAXPATHLEN 80
#endif

extern char *emalloc();

  /* int space; */
  /* space must be greater than 0 */
  /* Causes executution to halt with a 'Fatal error' message if memory */
  /* cannot be allocated, otherwise returns pointer to malloc-ed space */

extern char *anewstr();

  /* char *astring; */
  /* emalloc's space and copies astring into that space.  Returns pointer */
  /* to new string. */


extern int copy();

  /* char *dest, *src; int n; */
  /* copies exactly n bytes. */
  /* return value undefined.  Use only as procedure. */

extern int fill();

  /* char *addr, ch; int n; */
  /* copies ch into n consecutive bytes. */
  /* return value undefined.  Use only as procedure. */

extern int to_upper_if_lower();

  /* char ch;  Returns possibly upper-cased value. */

extern int to_lower_if_upper();

  /* char ch;  Returns possibly lower-cased value. */

extern int buffconcat();

  /* char *buffer, *s1, *s2; */
  /* s1 and s2 must be null terminated.  Buffer must be at least */
  /* strlen(s1) + strlen(s2) + 1 characters long.  Buffer is null */
  /* terminated upon completion. */

  /* return value undefined.  Use only as procedure. */

extern int nbuffconcat();

  /* char *buffer; int n; char *s1,*s2,*s3,*s4,*s5,*s6; */
  /* all the strings must be null terminated.  Buffer must be big enough */
  /* to hold the null terminated result.  0 < n < 7 .. 
  /* returns -1 if n is out of range, otherwise 0 */

extern int slcompare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if not equal */

extern int slge_compare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if s1 < s2, 1 if s1 > s2 */

extern int nocase_compare();

  /* char *s1; int l1; char *s2; int l2 */
  /* does not stop if it encounters a null character. */
  /* returns 0 if equal, -1 if s1 < s2, 1 if s1 > s2  case independently. */

extern char * strfind();

  /* char *s1; char *s2; int fast; */
  /* finds s2 as a substring of s1.  s1 and s2 are null terminated. */
  /* returns 0 if not found, otherwise pointer into s1 to first matching */
  /* character. */

  /* WARNING:  will access off the end of s1 in the interest of efficiency */
  /* if 'fast' is non-zero. */

extern char * strncfind();

  /* char *s1; char *s2; int fast; */
  /* finds s2 as a substring of s1 case independently.  s1 and s2 are */
  /* null terminated. */
  /* returns 0 if not found, otherwise pointer into s1 to first matching */
  /* character. */

  /* WARNING:  will access off the end of s1 in the interest of efficiency */
  /* if 'fast' is non-zero. */

extern char * strsearch();

  /* char *s1; int l1; char *s2; int l2 */
  /* finds s2 as a substring of s1.  Does not stop if it encounters a null. */
  /* returns pointer into s1, otherwise (char *) 0 if search fails */
  /* case dependent */

extern char * strncsearch();

  /* char *s1; int l1; char *s2; int l2 */
  /* finds s2 as a substring of s1. */
  /* returns pointer into s1, otherwise (char *) 0 if search fails */
  /* case independent */

extern int remove_excess_blanks();

  /* char *newstring, *oldstring; */
  /* newstring must be long enough to hold the result, which may be as */
  /* long as oldstring.  oldstring must be null terminated. */
  /* an excess blank is any blank before the first non-blank character, */
  /* any blank after the last non-blank character, and any blank immediately */
  /* following a blank. */
  /* returns length of newstring */

extern int yes_or_no_check();

  /* char *astring; */
  /* returns 1 for yes, 0 for no, -1 for neither. */
  /* astring must be one of "YES", "Y", "NO", "N" in any capitalization. */


/* These routines return T if every char satisfies a certain condition. */
/* These returns all returns T if given a null string. */

extern Bool all_digits();
extern Bool all_whitespace();
extern Bool all_uppercase();
extern Bool all_lowercase();
extern Bool all_alphabetic();
extern Bool all_alphanumeric();
extern Bool all_ascii();


extern int str_to_pos_int();

  /* char *astring; int low,high; */
  /* low must be >= 0. */
  /* returns -1 if *astring is not composed of digits. */
  /* returns -2 if the integer is out of range. */
  /* despite its name, 0 can be returned as a legitimate value. */
  /* treats all digit strings as decimal. */


extern int sreverse();

  /* char *buffer; char *astring; */
  /* puts the characters of astring in reverse order into buffer. */
  /* buffer must be at least as long as astring + 1. */
  /* buffer is null terminated when done. */
  /* No return value.  Use only as procedure. */

extern char *ip_sreverse();

  /* char *astring; */
  /* Returns astring with its characters reversed. */
  /* reversal is done in place. */



#define PATH_MAXPATHLEN 256

char *temp_path();

/*
  char *dir; char *filename;

  Returns a pointer to a character string containing the string
  <dir>/<filename>.  The pointer points to a buffer which will may get
  overwritten if any functions in this package are subsequently called.
  0 is returned if the pathname would exceed PATH_MAXPATHLEN-1 chars.
*/


char *perm_path();

/*
  char *dir; char *filename;

  Same as temp_path, except the pathname string is malloc'ed and is thus
  permanent unless specifically freed by the user.  Further, no limit
  on the size of the path is made.
*/


char *make_path();

/*
  char *dir; char *filename; char *extension; Bool perm;

  Creates <dir>/<filename><extension> .  The string returned is permanent
  or not depending on 'perm'.  If perm is not true, 0 will be returned if
  the resulting path exceeds PATH_MAXPATHLEN-1 chars.
*/


char *make_path_numeric_extension();

/*
  char *dir; char *filename; int extension; Bool perm;

  Same as make_path except that extension is first converted into a
  string using sprintf.
*/


char *just_filename();

/*  
  char *path; Bool new; Bool perm;

  Given a path of the form /<x>/<y>/<z> returns <z>.  If new is not set
  then a pointer into the original input string is returned.  If new is
  set a copy is returned, either permanent or not depending on perm.
*/


#define ANSWER_NO 0
#define ANSWER_YES 1
#define ANSWER_HELP 2
#define ANSWER_QUIT 3
#define ANSWER_EOF 4

#define AT_EOF -1
#define TOO_MANY_CHARS -2
#define IOERROR -3
#define TOO_MANY_LINES -4
#define LINE_TOO_LONG -5

extern read_yes_or_no ();

  /* FILE *iport, *oport; char *prompt; char *helpstring; char *quitstring; */

  /* prints prompt, then reads from iport until is gets 'Y', 'N', 'YES' or */
  /* 'NO' (case independently).  If helpstring and/or quitstring are not */
  /* "" or (char *) 0 then if the user types in one of those ANSWER_HELP */
  /* or ANSWER_QUIT are returned, otherwise ANSWER_NO or ANSWER_YES are */
  /* eventually returned. */


extern int getline ();

  /* FILE *iport; char *buffer; int buflen; */

  /* reads a line into buffer.  Does not put the '\n' into buffer. */
  /* Returns AT_EOF if at end of file when called.  If it encounters */
  /* end of file after reading at least one character, the eof is treated */
  /* as if it were a newline.   Returns TOO_MANY_CHARS if more than */
  /* buflen - 1 characters are read before encountering a newline. */        
  /* In this case exactly buflen - 1 characters are read. */
  /* The last character read is always follwed by a '\0'. */
  /* if successful getline returns the number of characters read exclusive */
  /* of a terminating newline or eof. */


extern int getlines();

  /* FILE *fp; int n; char ***ptr_lines; char *linebuf; int maxlinelen; */
  /* See documentation for getfile below */
  /* If called, 'n' must have a value 0. */

extern int getfile();

  /* char *filename; char ***ptr_lines; char *linebuf; int maxlinelen; */

  /* read in a file as an array of character strings */
  /* 'maxlinelen+1' is the maximum length a line of the file is allowed */
  /* to be.  'linebuf' must be at least 'maxlinelen+1' characters long. */
  /* Returns the number of lines in the file (and therefore the number */
  /* of entries in *ptr_lines) if successful.  Returns IOERROR if it */
  /* could not open the file to read from. Returns TOO_MANY_CHARS if */
  /* it encounters a line longer than 'maxlinelen' characters.  

  /* Space for each line is malloc'ed as it is read in and the text for */
  /* the jth line is stored in (*ptr_lines)[j] */

  /* Only works for fairly small files as it recurses its way through the */
  /* file and does a lot of malloc-ing.  Use read_file_into_buffer or */
  /* ngetfile for large files. */

extern int ngetlines();

 /* FILE *fp; int n; char ***ptr_lines; char *linebuf; int maxlinelen; */
 /* Same as getlines, except at most 'n' lines will be read.  Returns */
 /* TOO_MANY_LINES if more than 'n' lines are present. */

extern int ngetfile();

 /* int n; char *filename; char ***ptr_lines; char *linebuf; int maxlinelen; */
 /* See ngetlines above. */

extern int read_file_into_buffer();

  /* char *filename; 
     char ***ptr_lines;
     int maxlines;
     char *buffer;
     int buflen;
     char *linebuffer;
     int linebuflen;
  */

  /* *ptr_lines should be an array of character string pointers maxlines */
  /* big.  buffer should be an array of characters buflen long.  The routine */
  /* reads lines using getline and stores them into buffer, terminating each */
  /* with a null.  A pointer to the nth line read is stored in *ptr_lines[n] */
  /* Returns IOERROR if it cannot open the file for reading, TOO_MANY_LINES */
  /* if more than maxlines were read in, TOO_MANY_CHARS if buffer is */
  /* filled before end of file is reached, and LINE_TOO_LONG is any line is */
  /* longer than linebuflen.  Returns number of lines read in if successful. */

extern char *efopen();  

  /* char *filename; char *mode */

  /* Actually returns a (FILE *), so one must cast the return value to this */
  /* type.  It doesn't return a (FILE *) explicitly because then to include */
  /* this file one would have to include <stdio.h> explicitly before it. */
  /* The routine simply calls fopen with the same arguments, but prints a */
  /* reasonable error message and calls exit if the call to fopen fails. */


extern int record_fseek();

  /* FILE *fp; long rnum; int fromwhere; int rsize; int hdrsize; */

  /* Assumes a file is divided into fixed length records with a fixed length */
  /* header (possibly 0 bytes).  Performs a fseek which moves to the start */
  /* of a given record.  Record numbers begin with 1. */

  /* Returns what fseek returns. */

  /* 'rnum' is either relative or absolute, depending on 'fromwhere' which */
  /* corresponds to the 'ptrname' argument of fseek. */


Bool check_string();

  /* char *str; long minlen; long maxlen; */

  /* Returns T if str is not 0 and has a length between minlen and maxlen */
  /* inclusived, otherwise returns F. */


#ifndef check_int
#define check_int(i,minval,maxval) ((i) >= (minval) && (i) <= (maxval))
#endif
SHAR_EOF
if test 11301 -ne "`wc -c < 'toolsdir/ctools.h'`"
then
	echo shar: error transmitting "'toolsdir/ctools.h'" '(should have been 11301 characters)'
fi
fi
echo shar: extracting "'toolsdir/mem.c'" '(2157 characters)'
if test -f 'toolsdir/mem.c'
then
	echo shar: will not over-write existing file "'toolsdir/mem.c'"
else
cat << \SHAR_EOF > 'toolsdir/mem.c'
/**************************************************************************/
/**************************************************************************/


                  /***** Block Memory Allocator *****/


/**************************************************************************/
/**************************************************************************/

/* Author: JP Massar */

#include <stdio.h>

#include "sys5.h"

#ifdef BSD42
#include <strings.h>
#endif

#define NO_MORE_MEMORY -1

static int bytes_left;                  /* space left in current block */
static char *ptr_next_byte;             /* next free byte */
static char *ptr_space;                 /* current block */
static int chunk_size;                  /* size of block last allocated */

extern char *malloc();


int allocate_memory_chunk (space) int space;

/* malloc up a new block of memory.  Set our static variables */
/* returns NO_MORE_MEMORY if can't allocate block. */

{ 
  if (0 == (ptr_space = malloc(space))) {
        fprintf(stderr,"fatal error, no more memory\n");
        return(NO_MORE_MEMORY);
  }
  ptr_next_byte = ptr_space;
  bytes_left = space;
  chunk_size = space;
  return(0);
}


char * get_memory_chunk (size) int size;

/* allocate a small segment out of our large block of memory.  If we */
/* run out allocate another block.  Adjust our static variables. */
/* returns 0 if no more memory. */

{ char *rval;
        
  if (size > chunk_size) {
        fprintf(stderr,"attempt to allocate too large a chunk\n");
        return(0);
  }
        
  if (size > bytes_left) {
        if (NO_MORE_MEMORY == allocate_memory_chunk(chunk_size)) {
                return(0);
        }
        return(get_memory_chunk(size));
  }

  rval = ptr_next_byte;
  ptr_next_byte += size;
  bytes_left -= size;
  return(rval);
  
}


char * store_string (str,len) char *str; int len;

/* put copy of a string into storage in a memory block.  Return a pointer to */
/* the copied string.  Returns 0 if out of memory. */

{ char *ptr_space;

  if (0 == (ptr_space = get_memory_chunk(len+1))) {
        return(0);
  }
  strcpy(ptr_space,str);
  return(ptr_space);
}
SHAR_EOF
if test 2157 -ne "`wc -c < 'toolsdir/mem.c'`"
then
	echo shar: error transmitting "'toolsdir/mem.c'" '(should have been 2157 characters)'
fi
fi
echo shar: extracting "'toolsdir/mem.h'" '(209 characters)'
if test -f 'toolsdir/mem.h'
then
	echo shar: will not over-write existing file "'toolsdir/mem.h'"
else
cat << \SHAR_EOF > 'toolsdir/mem.h'
extern int allocate_memory_chunk();     /* arg1: int space */
       
extern char * get_memory_chunk();       /* arg1: int size */

extern char * store_string();           /* arg1: char *str, arg2: int len */
SHAR_EOF
if test 209 -ne "`wc -c < 'toolsdir/mem.h'`"
then
	echo shar: error transmitting "'toolsdir/mem.h'" '(should have been 209 characters)'
fi
fi
echo shar: extracting "'toolsdir/menu.c'" '(7644 characters)'
if test -f 'toolsdir/menu.c'
then
	echo shar: will not over-write existing file "'toolsdir/menu.c'"
else
cat << \SHAR_EOF > 'toolsdir/menu.c'
#include <stdio.h>
#include <varargs.h>
#include "ctools.h"
#include "menu.h"
#include "sys5.h"


static char line[MAX_MENU_RESPONSE_LENGTH];


menu_match (

     ptr_rval,ptr_ur,prompt,casetest,isub,r_no_match,r_ambiguous,n_options,
     va_alist
     
   )

  int *ptr_rval;
  char **ptr_ur;
  char *prompt;
  int casetest;
  int isub;
  int r_no_match;
  int r_ambiguous;
  int n_options;
  va_dcl
    
{

  char sline[MAX_MENU_RESPONSE_LENGTH];
  char *option, *options[MAX_MENU_OPTIONS];
  int rvals[MAX_MENU_OPTIONS];
  int j,found,*addr,len,optionlen,rval,compare,blankindex;
  va_list pvar;
        
  if (n_options > MAX_MENU_OPTIONS) return(MENU_ERROR);
  for (j = 0; j < MAX_MENU_RESPONSE_LENGTH; j++) line[j] = ' ';
  
  /* grab all the menu options and return values.  */
  
  blankindex = -1;
  va_start(pvar);
  for (j = 0; j < n_options; j++) {
      options[j] = va_arg(pvar,char *);
      if (0 == strlen(options[j])) {
         if (blankindex == -1) blankindex = j;
      }
      rvals[j] = va_arg(pvar,int);
  }
  va_end(pvar);
  
  try_again :
  
  /* get the user's response */
  
  printf("%s",prompt);
  switch (rval = getline(stdin,line,MAX_MENU_RESPONSE_LENGTH)) {
    case AT_EOF :
      return(MENU_EOF);
      break;
    case TOO_MANY_CHARS :
     fprintf(stderr,"Response is too long to handle.  Please try again\n");
     goto try_again;
     break;
   default :
     *ptr_ur = line;
     remove_excess_blanks(sline,line);
     len = strlen(sline);
     break;
 }
    
  found = 0;
  rval = MENU_NO_MATCH;
  
  if (all_whitespace(sline)) {
     if (blankindex == -1) goto try_again;
     rval = MENU_MATCH;
     *ptr_rval = rvals[blankindex];
     goto rtn;
  }     
  
  for (j = 0; j < n_options; j ++) {

      /* if what he typed in is longer than any option it can't match */
        
      optionlen = strlen(options[j]);
      if (len > optionlen) continue;  
      
      /* if we aren't matching initial substrings, the response must */
      /* match exactly. */
      
      if (!isub && len != optionlen) continue;
      
      /* use different comparision functions depending on whether case */
      /* is important or not. */
      
      compare = casetest ? 
                strncmp(sline,options[j],len) : 
                nocase_compare(sline,len,options[j],len);
                
      /* if we must match exactly, if we find a match exit immediately. */
      /* if we can match on an initial substring, if we've already found */
      /* a match then we have an ambiguity, otherwise note that we've */
      /* matched and continue looking in case of ambiguities */
                
      if (0 == compare) {
         if (!isub) {
            found = 1;
            *ptr_rval = rvals[j];
            rval = MENU_MATCH;
            break;
         }
         if (found && isub) {
            rval = MENU_AMBIGUOUS;
            break;
         }
         else {
            found = 1;
            *ptr_rval = rvals[j];
            rval = MENU_MATCH;
         }
      }
      else {
         continue;
      }
  }

  rtn :
  
  switch (rval) {
    case MENU_MATCH :
      break;
    case MENU_NO_MATCH :
      if (r_no_match) {
         printf("Your response does not match any option.  Try again\n");
         goto try_again;
      }
      break;
    case MENU_AMBIGUOUS :
      if (r_ambiguous) {
         printf("Your response is ambiguous.  Try again\n");
         goto try_again;
      }
      break;
    default :
      fprintf(stderr,"Impossible case value in menu_match\n");
      exit(-1);
  }
      
  return(rval);
  
}


menu_yes_no (prompt,allow_help) char *prompt; int allow_help;

{
  int menuval,rval;
  char *response;
  
  redo :
  
  if (!allow_help) {
     rval = menu_match (
          &menuval,&response,
          prompt,
          0,1,0,0,2,
          "Yes",MENU_YES,
          "No",MENU_NO
       );
  }
  else {
     rval = menu_match (
          &menuval,&response,
          prompt,
          0,1,0,0,4,
          "Yes",MENU_YES,
          "No",MENU_NO,
          "?",MENU_HELP,
          "Help",MENU_HELP
       );
  }
  switch (rval) {
    case MENU_MATCH :
      return(menuval);
      break;
    case MENU_NO_MATCH :
      fprintf(stderr,"Please type 'Yes' or 'No'...\n");
      goto redo;
      break;
    case MENU_EOF :
      return(MENU_EOF);
      break;
    default :
      fprintf(stderr,"Fatal error:  Impossible return in menu_yes_no\n");
      exit(-1);
  }
}


extern int menu_data_help_or_abort (prompt,abortstring,ptr_response)

  char *prompt, *abortstring, **ptr_response;

{
  int rval,menuval;
  rval = menu_match (
       &menuval,ptr_response,
       prompt,
       0,1,0,1,3,
       abortstring,MENU_ABORT,
       "?",MENU_HELP,
       "Help",MENU_HELP
    );
  switch (rval) {
    case MENU_NO_MATCH :
      return(MENU_DATA);
      break;
    case MENU_MATCH :
      return(menuval);
      break;
    case MENU_EOF :
      return(MENU_EOF);
      break;
    default :
      fprintf(stderr,"Impossible return from menu_data_help_or_abort\n");
      exit(-1);
  }
}


menu_number_help_or_abort (prompt,abortstring,low,high,ptr_ival)

  char *prompt, *abortstring;
  int low,high,*ptr_ival;

{
  char *response,errprompt[80],numstring[MAX_MENU_RESPONSE_LENGTH];
  int rval,check;
  
  if (!(check = (low <= high)))
     nbuffconcat(errprompt,1,"Please enter a non-negative number...\n");
  else
     sprintf(errprompt,"Please enter a number between %d and %d\n",low,high);
  
  reask :     
     
  rval = menu_data_help_or_abort(prompt,abortstring,&response);
  switch (rval) {
    case MENU_EOF :
    case MENU_ABORT :
    case MENU_HELP :
      return(rval);
      break;
    case MENU_DATA :
      remove_excess_blanks(numstring,response);
      switch (*ptr_ival = str_to_pos_int(numstring,0,MAXINT)) {
        case -1:
        case -2:
          fprintf(stderr,"%s",errprompt);
          goto reask;
          break;
        default:
          return(MENU_DATA);
          break;
      }
  }

}


menu_yes_no_abort_or_help (prompt,abortstring,helpallowed,return_for_yes)

/*
    Returns one of MENU_YES, MENU_NO, MENU_ABORT or MENU_HELP.
    If !helpallowed, MENU_HELP will not be returned.  
    If return_for_yes is 0, hitting return will re-prompt.
    If it is 1, hitting return is like typing yes.
    If it is any other value, hitting return is like typing no.
*/

  char *prompt;
  char *abortstring;
  int helpallowed; 
  int return_for_yes;

{
  int menuval,rval;
  char *response;
  
  redo :
  
  if (!helpallowed) {
     rval = menu_match (
          &menuval,&response,
          prompt,
          0,1,0,0,4,
          "Yes",MENU_YES,
          "No",MENU_NO,
          abortstring,MENU_ABORT,
          "",MENU_RETURN
       );
  }
  else {
     rval = menu_match (
          &menuval,&response,
          prompt,
          0,1,0,0,6,
          "Yes",MENU_YES,
          "No",MENU_NO,
          "?",MENU_HELP,
          "Help",MENU_HELP,
          abortstring,MENU_ABORT,
          "",MENU_RETURN
       );
  }
  switch (rval) {
    case MENU_MATCH :
      if (menuval != MENU_RETURN) return(menuval);
      switch (return_for_yes) {
        case NO_DEFAULT :
          goto redo;
          break;
        case DEFAULT_YES :
          return(MENU_YES);
          break;
        default :
          return(MENU_NO);
          break;
      }
      break;
    case MENU_NO_MATCH :
      printf("Please type 'Yes' or 'No'...\n");
      goto redo;
      break;
    case MENU_EOF :
      return(MENU_EOF);
      break;
    default :
      fprintf(stderr,"Fatal error:  Impossible return in menu_yes_no\n");
      exit(-1);
  }
}

SHAR_EOF
if test 7644 -ne "`wc -c < 'toolsdir/menu.c'`"
then
	echo shar: error transmitting "'toolsdir/menu.c'" '(should have been 7644 characters)'
fi
fi
echo shar: extracting "'toolsdir/menu.h'" '(4423 characters)'
if test -f 'toolsdir/menu.h'
then
	echo shar: will not over-write existing file "'toolsdir/menu.h'"
else
cat << \SHAR_EOF > 'toolsdir/menu.h'
/* You must include "<ctools.h>" before including this file. */

#define MENU_EOF -1
#define MENU_MATCH 0
#define MENU_AMBIGUOUS 1
#define MENU_NO_MATCH 2        
#define MENU_ERROR 3
#define MENU_RETURN 4

#define MAX_MENU_OPTIONS 20
#define MAX_MENU_RESPONSE_LENGTH 255


extern int menu_match();

/* menu_match (

        int *ptr_rval,
        char **ptr_user_response,
        char *prompt,
        int case_significant,
        int initial_substring_sufficient,
        int repeat_if_no_match,
        int repeat_if_ambiguous,
        int n_options,
        char *option1, int rval1,
        char *option2, int rval2,
        .
        .
        .

    );

    Returns one of MENU_MATCH, MENU_AMBIGUOUS, MENU_NO_MATCH, and MENU_ERROR.
    If MENU_MATCH is returned, *ptr_rval contains the value that the caller
    asked to be returned.  *ptr_user_response is what exactly what the user
    typed in, minus the linefeed at the end.
    
    prompt is a character string that is printed out.  A line of input is
    then read from the terminal, stripped of excess blanks, and compared
    with the menu items.  Up to MAX_MENU_OPTIONS can be specified.  Each
    option is a character string and has an associated integer return value.
    
    If case_significant is not zero, the user's response will be checked
    against the options with upper/lower case distinction. 
    If initial_substring_sufficient is not zero, then a match will occur
    if the user's response is the same as an initial substring of an option,
    otherwise the response must match the option exactly.
    
    If more than one menu option matches the response, MENU_AMBIGUOUS is
    returned.  If n_options exceeds MAX_MENU_OPTIONS or the read fails
    MENU_ERROR is returned.  If no option matches the response, MENU_NO_MATCH
    is returned.  However, if repeat_if_no_match and/or repeat_if_ambiguous
    are non_zero, the user is prompted again until he answers correctly if
    he answers wrongly/ambiguously, respectively.
    
    If "" is not specified as an option, and the user enters a blank line,
    the routine simply prompts the user again.  It is impossible to match
    on a string of blanks (e.g., "  "), because any blank line the user
    types is considered to be "".
    
    If the response the user types is too long, an error message is printed
    out and the user is asked to try again.
    
*/


#define MENU_NO 0
#define MENU_YES 1
#define MENU_HELP 2
#define MENU_ABORT 3
#define MENU_DATA 4

extern int menu_yes_no();

/* menu_yes_no (prompt,allow_help) char *prompt; int allow_help;

        returns one of MENU_NO, MENU_YES or, if help is allowed,
        MENU_HELP.  Help can be obtained via "?" or any initial
        substring of "Help", case independently.  Returns MENU_EOF
        if end of file encountered when reading. 

        Yes or no can be obtained via any case independent initial
        substring of "Yes" or "No" respectively.

*/


#define DEFAULT_YES 0
#define DEFAULT_NO 1
#define NO_DEFAULT 2

extern int menu_yes_no_abort_or_help ();

/*  menu_yes_no_abort_or_help (prompt,abortstring,helpallowed,return_for_yes)

    char *prompt, *abortstring;
    int helpallowed; 
    int return_for_yes;

    Returns one of MENU_YES, MENU_NO, MENU_ABORT, MENU_HELP or MENU_EOF.
    If !helpallowed, MENU_HELP will not be returned.  
    If return_for_yes is DEFAULT_NO, hitting return will re-prompt.
    If it is DEFAULT_YES, hitting return is like typing yes.
    If it is any other value, hitting return is like typing no.

*/    

extern int menu_data_help_or_abort ();

/*

extern int menu_data_help_or_abort (prompt,abortstring,ptr_response)

  char *prompt, *abortstring, **ptr_response;

  Returns either MENU_ABORT, MENU_HELP or MENU_DATA.  If MENU_DATA is
  returned, *response contains the user's response.  MENU_HELP is
  returned in the user types in "?" or any initial substring of "Help"
  (case independently).
  
*/  
  

extern int menu_number_help_or_abort ();

/*
menu_number_help_or_abort (prompt,abortstring,low,high,ptr_ival)

  char *prompt; *abortstring;
  int low,high,*ptr_ival;

  Returns either MENU_DATA, MENU_ABORT, MENU_HELP or MENU_EOF.
  If low > high any number will be considered valid, otherwise the
  range is checked, inclusivewise.  If MENU_DATA is returned, *ptr_ival
  contains the number entered.

  At the moment number can only be non-negative.
*/
SHAR_EOF
if test 4423 -ne "`wc -c < 'toolsdir/menu.h'`"
then
	echo shar: error transmitting "'toolsdir/menu.h'" '(should have been 4423 characters)'
fi
fi
echo shar: extracting "'toolsdir/sys5.h'" '(103 characters)'
if test -f 'toolsdir/sys5.h'
then
	echo shar: will not over-write existing file "'toolsdir/sys5.h'"
else
cat << \SHAR_EOF > 'toolsdir/sys5.h'
#ifdef SYS5
#define index(a,b) strchr(a,b)
#define rindex(a,b) strrchr(a,b)
#include <string.h>
#endif
SHAR_EOF
if test 103 -ne "`wc -c < 'toolsdir/sys5.h'`"
then
	echo shar: error transmitting "'toolsdir/sys5.h'" '(should have been 103 characters)'
fi
fi
echo shar: done with directory "'toolsdir'"
exit 0
#	End of shell archive