rsalz@bbn.com (Rich Salz) (02/20/88)
Submitted-by: Dave Ihnat <ihnp4!homebru!ignatz> Posting-number: Volume 13, Issue 52 Archive-name: rolodex/part04 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 4 (of 4)." PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f './toolsdir/ctools.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'./toolsdir/ctools.c'\" else echo shar: Extracting \"'./toolsdir/ctools.c'\" \(21027 characters\) sed "s/^X//" >'./toolsdir/ctools.c' <<'END_OF_FILE' X/* -*- Mode: C; Package: (CTOOLS C) -*- */ X X#include <ctype.h> X#include <stdio.h> X X#ifdef BSD42 X#include <strings.h> X#endif X X#include "sys5.h" X X#include "ctools.h" X X#include "basics.h" Xchar maxintstr[30] = { '\0' }; X X#ifdef MSDOS X#define rindex strrchr X#define index strchr X#include <string.h> X#endif X X/* miscellaneous fairly primitive routines that deal with characters, */ X/* strings, memory, simple input and pathnames. */ X X X/* Author: JP Massar */ X/* Thinking Machines Corporation */ X X/* Included routines: X X emalloc X anewstr X X copy X fill X X to_upper_if_lower X to_lower_if_upper X X buffconcat X nbuffconcat X X slcompare X slge_compare X nocase_compare X X strfind X strncfind X strsearch X strncsearch X X yes_or_no_check X X remove_excess_blanks X ip_string_trim X string_trim X string_upcase X string_downcase X X all_digits X all_whitespace X all_uppercase X all_lowercase X all_alphabetic X all_alphanumeric X all_ascii X X str_to_pos_int X X sreverse X ip_sreverse X X temp_path X perm_path X make_path_numeric_extension X make_path X just_filename X X read_yes_or_no X getline X getlines X ngetlines X getfile X ngetfile X read_file_into_buffer X efopen X X check_int X check_string X X*/ X X Xextern char *malloc(); X X Xchar *emalloc (space) int space; X X/* allocate 'space' bytes, die if we have run out of memory. */ X X{ X char *rval; X if (space < 0) { X fprintf(stderr,"Fatal error: argument to emalloc < 0\n"); X exit(-1); X } X if (0 == (rval = malloc((unsigned) space))) { X fprintf(stderr,"Fatal error: No more memory\n"); X exit(-1); X } X return(rval); X} X X Xchar *anewstr (astring) char *astring; X X/* allocate space for and then copy a string. Returns pointer to */ X/* new string. */ X X{ X char *newstr; X newstr = emalloc(strlen(astring)+1); X strcpy(newstr,astring); X return(newstr); X} X X Xcopy (dest,src,n) X X /* copy n bytes */ X X register char *dest,*src; X register int n; X X { register int j = 0; X while (j++ < n) *dest++ = *src++; X } X X Xfill (addr,ch,n) X X /* fill n sequential bytes with 'ch' */ X X register char *addr; X register char ch; X register int n; X X { register int j = 0; X while (j++ < n) *addr++ = ch; X } X X Xto_upper_if_lower (ch) X X char ch; X X { return(islower(ch) ? toupper(ch) : ch); } X X Xto_lower_if_upper (ch) X X char ch; X X { return(isupper(ch) ? tolower(ch) : ch); } X X Xbuffconcat (buffer,s1,s2) X X /* concatenate two null terminated strings into a buffer. */ X X char *buffer, *s1, *s2; X X { while (*s1 != '\0') *buffer++ = *s1++; X while (*s2 != '\0') *buffer++ = *s2++; X *buffer = '\0'; X } X X Xnbuffconcat (buffer,n,s1,s2,s3,s4,s5,s6) X X /* concatenates up to 6 strings into a buffer. Returns -1 if n */ X /* is not reasonable, otherwise returns 0. */ X X char *buffer; X int n; X char *s1,*s2,*s3,*s4,*s5,*s6; X X{ X register char *b; X register char *s; X int i; X b = buffer; X if (n < 1 || n > 6) return(-1); X for (i = 1; i <= 6; i++) { X if (i > n) break; X switch (i) { X case 1 : s = s1; break; X case 2 : s = s2; break; X case 3 : s = s3; break; X case 4 : s = s4; break; X case 5 : s = s5; break; X case 6 : s = s6; break; X } X while (*s != '\0') *b++ = *s++; X } X *b = '\0'; X return(0); X} X X Xslcompare (s1,l1,s2,l2) X X /* compare strings with possible nulls in them given their lengths */ X /* only returns EQUAL (0) or NOT EQUAL (-1) */ X X char *s1; X int l1; X char *s2; X int l2; X X { int j; X if (l1 != l2) return(-1); X j = 0; X while (j++ < l1) X if (*s1++ != *s2++) return(-1); X return(0); X } X X Xslge_compare (s1,l1,s2,l2) X X /* returns -1 if s1 < s2; 1 if s2 < s1; 0 if s1 = s2 */ X /* ignores nulls in the strings */ X X char *s1; X int l1; X char *s2; X int l2; X X { int j,len; X j = 0; X len = l2 > l1 ? l1 : l2; X while (j++ < len) { X if (*s1 != *s2) X return((*s1 < *s2) ? -1 : 1); X s1++; X s2++; X } X return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1)); X } X Xnocase_compare (s1,l1,s2,l2) X X /* treats nulls as normal characters. Returns same as slge_compare */ X X char *s1; X int l1; X char *s2; X int l2; X X { int j,len,ch1,ch2; X j = 0; X len = l2 > l1 ? l1 : l2; X while (j++ < len) { X ch1 = to_upper_if_lower(*s1++); X ch2 = to_upper_if_lower(*s2++); X if (ch1 != ch2) { X return((ch1 < ch2) ? -1 : 1); X } X } X return((l2 == l1) ? 0 : ((l1 < l2) ? -1 : 1)); X } X X Xchar *strfind(s1,s2,fast) X X register char *s1; X char *s2; X Bool fast; X X{ X register int len1,len2; X len2 = strlen(s2); X if (fast) { X while (*s1 != '\0') X if (0 == strncmp(s1++,s2,len2)) return(s1-1); X } X else { X len1 = strlen(s1); X while (len1 >= len2) { X if (0 == strncmp(s1++,s2,len2)) return(s1-1); X len1--; X } X } X return(0); X} X X Xchar *strncfind(s1,s2,fast) X X register char *s1; X char *s2; X Bool fast; X X{ X register int len1,len2; X len2 = strlen(s2); X if (fast) { X while (*s1 != '\0') X if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1); X } X else { X len1 = strlen(s1); X while (len1 >= len2) { X if (0 == nocase_compare(s1++,len2,s2,len2)) return(s1-1); X len1--; X } X } X return(0); X} X X Xchar *strsearch(s1,s1len,s2,s2len) X X /* do a substring search without noticing nulls */ X /* finds s2 in s1, returns pointer into s1 or 0 */ X X register char *s1, *s2; X register int s1len,s2len; X X { register char *pc; X register char *bound; X register char *pctemp; X register char *s2temp; X register int j; X X bound = s1 + s1len - s2len; X for (pc = s1; pc <= bound; pc++) { X pctemp = pc; X s2temp = s2; X for (j = 0; j < s2len; j++) X if (*pctemp++ != *s2temp++) goto not_here; X return(pc); X not_here : X continue; X } X return(0); X} X X Xchar *strncsearch(s1,s1len,s2,s2len) X X /* do a substring search without noticing nulls */ X /* finds s2 in s1, returns pointer into s1 or 0 */ X /* case independent */ X X register char *s1, *s2; X register int s1len,s2len; X X { register char *pc; X register char *bound; X register char *pctemp; X register char *s2temp; X register int j; X char ch1, ch2; X X bound = s1 + s1len - s2len; X for (pc = s1; pc <= bound; pc++) { X pctemp = pc; X s2temp = s2; X for (j = 0; j < s2len; j++) { X ch1 = *pctemp++; X ch2 = *s2temp++; X if (to_upper_if_lower(ch1) != to_upper_if_lower(ch2)) X goto not_here; X } X return(pc); X not_here : X continue; X } X return(0); X} X X Xint remove_excess_blanks (newstring,oldstring) X X /* it is assumed that newstring is as long as oldstring if necessary */ X X char *newstring,*oldstring; X X{ X int count = 0; X int space_found = 0; X X /* skip over all blanks at beginning */ X X if (*oldstring == ' ') { X while (*oldstring == ' ') oldstring++; X } X X while (*oldstring != '\0') { X if (space_found && *oldstring == ' ') { X oldstring++; X continue; X } X space_found = (*oldstring == ' '); X *newstring++ = *oldstring++; X count++; X } X X *newstring = '\0'; X if (count > 0 && *(newstring - 1) == ' ') { X count--; X *(newstring - 1) = '\0'; X } X X return(count); X X} X Xint ip_string_trim (oldstring,trimchars,pretrim,posttrim) X X char *oldstring, *trimchars; X Bool pretrim,posttrim; X X{ X Bool trim = T; X char *np = oldstring, ch; X int len; X X if (pretrim) { X while (trim && ('\0' != (ch = *np))) { X trim = (0 != index(trimchars,ch)); X if (trim) np++; X } X strcpy(oldstring,np); X } X if (1 >= (len = strlen(oldstring)) && pretrim) return(len); X if (posttrim) { X np = oldstring + len - 1; X while (T) { X ch = *np; X trim = (0 != index(trimchars,ch)); X if (trim) *np = '\0'; X if (!trim || np == oldstring) break; X np--; X } X } X return(strlen(oldstring)); X} X Xint string_trim (newstring,oldstring,trimchars,pretrim,posttrim) X X char *newstring, *oldstring, *trimchars; X Bool pretrim, posttrim; X X{ X strcpy(newstring,oldstring); X return(ip_string_trim(newstring,trimchars,pretrim,posttrim)); X} X Xstring_upcase (astring) char *astring; X{ X while (*astring) { X *astring = to_upper_if_lower(*astring); X astring++; X } X} X Xstring_downcase (astring) char *astring; X{ X while (*astring) { X *astring = to_lower_if_upper(*astring); X astring++; X } X} X X Xyes_or_no_check (astring) char *astring; X X/* returns 1 if yes, 0 if no, -1 if neither */ X/* works for 'Y' 'YES' 'NO' 'N' in any capitalization */ X X{ X int len; X len = strlen(astring); X if (len == 0 || len > 3) return(-1); X if (0 == nocase_compare(astring,len,"YES",3) || X 0 == nocase_compare(astring,len,"Y",1)) X return(1); X if (0 == nocase_compare(astring,len,"NO",2) || X 0 == nocase_compare(astring,len,"N",1)) X return(0); X return(-1); X} X X XBool all_digits (astring) char *astring; X X/* test whether every character is a digit (0-9) */ X X{ X while (*astring != '\0') X if (!isdigit(*astring++)) return(F); X return(T); X} X X XBool all_whitespace (astring) char *astring; X X/* test whether every character is a blank or a tab */ X X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (ch == ' ' || ch == '\t') continue; X return(F); X } X return(T); X} X XBool all_uppercase(astring) char *astring; X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (!isupper(ch)) return(F); X } X return(T); X} X XBool all_lowercase(astring) char *astring; X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (!islower(ch)) return(F); X } X return(T); X} X XBool all_alphabetic(astring) char *astring; X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (!isalpha(ch)) return(F); X } X return(T); X} X XBool all_ascii(astring) char *astring; X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (!isascii(ch)) return(F); X } X return(T); X} X XBool all_alphanumeric(astring) char *astring; X{ X register char ch; X while ((ch = *astring++) != '\0') { X if (!isalnum(ch)) return(F); X } X return(T); X} X Xint str_to_pos_int (astring,low,high) char *astring; int low,high; X X /* returns -1 if *astring is not composed of digits. */ X /* returns -2 if the integer is out of range. */ X /* treats all digit strings as decimal. */ X X{ X int value,len,maxlen,j; X maxlen = strlen(MAXINTSTR); X len = strlen(astring); X if (!all_digits(astring)) return(-1); X if (len > maxlen) return(-2); X if (len == maxlen) { X if (1 == strcmp(astring,MAXINTSTR)) return(-2); X } X for (j = 0; j < len-1; j++) { X if (*astring != '0') break; X astring++; X } X sscanf(astring,"%d",&value); X if (value < low || value > high) return(-2); X return(value); X} X X Xint sreverse (buffer,astring) char *buffer, *astring; X{ X register int last = strlen(astring); X buffer[last--] = '\0'; X while (last >= 0) buffer[last--] = *astring++; X} X Xchar * ip_sreverse (astring) char *astring; X{ X register int last = strlen(astring) - 1; X register int first = 0; X register char ch; X while (first < last) { X ch = astring[first]; X astring[first++] = astring[last]; X astring[last--] = ch; X } X return(astring); X} X X X Xstatic char pathbuffer[PATH_MAXPATHLEN]; X X Xchar *temp_path (dir,filename) char *dir; char *filename; X X{ X return(make_path(dir,filename,"",F)); X} X X Xchar *perm_path (dir,filename) char *dir; char *filename; X X{ X return(make_path(dir,filename,"",T)); X} X X Xchar *make_path_numeric_extension (dir,filename,extension,perm) X X char *dir, *filename; X int extension; X Bool perm; X X{ X#ifdef UNIX X char *sprintf(); X#else X int sprintf(); X#endif X char buffer[20]; X sprintf(buffer,"%d",extension); X return(make_path(dir,filename,buffer,perm)); X} X X Xchar *make_path (dir,filename,extension,perm) X X char *dir, *filename, *extension; X Bool perm; X X{ X char *rval; X if (!perm && (strlen(dir) + 1 + strlen(filename) + strlen(extension) + 1 >= X PATH_MAXPATHLEN)) { X return((char *) 0); X } X#ifdef UNIX X nbuffconcat(pathbuffer,4,dir,"/",filename,extension); X#endif X X#ifdef VMS X nbuffconcat(pathbuffer,4,dir,filename,extension); X#endif X X#ifdef MSDOS X nbuffconcat(pathbuffer,4,dir,"/",filename,extension); X#endif X X if (!perm) return(pathbuffer); X rval = emalloc(strlen(pathbuffer) + 1); X strcpy(rval,pathbuffer); X return(rval); X} X X Xchar *just_filename (path,new,perm) char *path; Bool new,perm; X X{ X char *fnp,*rval; X#ifdef UNIX X fnp = ((char *)NULL == (fnp = rindex(path,'/'))) ? path : fnp + 1; X#endif X X#ifdef VMS X fnp = ((char *)NULL == (fnp = rindex(path,']'))) ? path : fnp + 1; X#endif X X#ifdef MSDOS X if((fnp = rindex(path,'/')) != (char *)NULL) X fnp += 1; X X if(fnp == (char *)NULL) X if((fnp = rindex(path,'\\')) == (char *)NULL) X fnp = path; X else X fnp += 1; X#endif X X if (!new) return(fnp); X if (!perm) { X strcpy(pathbuffer,fnp); X return(pathbuffer); X } X else { X rval = emalloc(strlen(fnp) + 1); X strcpy(rval,fnp); X return(rval); X } X} X X X Xread_yes_or_no (iport,oport,prompt,helpstring,quitstring) X X /* prints prompt, then reads from port until it gets 'Y', 'N', 'YES' or */ X /* 'NO' (case independently). If helpstring and/or quitstring are not */ X /* "" or (char *) 0 then if the user types in one of those ANSWER_HELP */ X /* or ANSWER_QUIT are returned, otherwise ANSWER_NO or ANSWER_YES are */ X /* eventually returned. */ X X FILE *iport, *oport; X char *prompt, *helpstring, *quitstring; X X{ X char buffer[20],buffer2[20]; X int bl,hl,ql; X X buffer[19] = '\0'; X X while (T) { X X fprintf(oport,"%s",prompt); X switch ((char)getline(iport,buffer,20)) { X case (AT_EOF) : X return(ANSWER_EOF); X /* break; */ X case (TOO_MANY_CHARS) : X break; X default : X if (0 == (bl = remove_excess_blanks(buffer2,buffer))) break; X switch (yes_or_no_check(buffer2)) { X case (0) : X return(ANSWER_NO); X case (1) : X return(ANSWER_YES); X case (-1) : X if (helpstring != (char *) 0 && (hl = strlen(helpstring)) > 0) { X if (0 == nocase_compare(buffer2,bl,helpstring,hl)) { X return(ANSWER_HELP); X } X } X if (quitstring != (char *) 0 && (ql = strlen(quitstring)) > 0) { X if (0 == nocase_compare(buffer2,bl,quitstring,ql)) { X return(ANSWER_QUIT); X } X } X break; X } X break; X } X X fprintf(oport,"Please answer 'YES' or 'NO'\n"); X continue; X X } X X} X X Xint getline (iport,buffer,buflen) FILE *iport; char *buffer; int buflen; X X /* reads a line into buffer. Does not put the '\n' into buffer. */ X /* Returns AT_EOF if at end of file when called. If it encounters */ X /* end of file after reading at least one character, the eof is treated */ X /* as if it were a newline. Returns TOO_MANY_CHARS if more than */ X /* buflen - 1 characters are read before encountering a newline. */ X /* In this case exactly buflen - 1 characters are read. */ X /* The last character read is always follwed by a '\0'. */ X /* if successful getline returns the number of characters read exclusive */ X /* of a terminating newline or eof. */ X X{ X int ch; X char *bptr = buffer; X int nchars = 0; X X if (buflen <= 0) return(TOO_MANY_CHARS); X X while (T) { X switch (ch = getc(iport)) { X case (EOF) : X case ('\n') : X if (ch == EOF && nchars == 0) return(AT_EOF); X *bptr = '\0'; X return(nchars); X default : X if (++nchars == buflen) { X *bptr = '\0'; X ungetc(ch,iport); X return(TOO_MANY_CHARS); X } X *bptr++ = ch; X } X X } X X} X X Xint getlines (fp,n,ptr_lines,linebuf,maxlinelen) X X /* See documentation for getfile below */ X X FILE *fp; X int n; X char ***ptr_lines; X char *linebuf; X int maxlinelen; X X{ X int len; X char *line; X if (0 > (len = getline(fp,linebuf,maxlinelen))) { X if (len == AT_EOF) { X *ptr_lines = (char **) emalloc(n * sizeof(char **)); X return(n); X } X else { X return(TOO_MANY_CHARS); X } X } X else { X line = emalloc(len+1); X strcpy(line,linebuf); X len = getlines(fp,n+1,ptr_lines,linebuf,maxlinelen); X if (len == TOO_MANY_CHARS) return(TOO_MANY_CHARS); X (*ptr_lines)[n] = line; X return(len); X } X} X X Xint getfile (filename,ptr_lines,linebuf,maxlinelen) X X /* read in a file as an array of character strings */ X /* 'maxlinelen+1' is the maximum length a line of the file is allowed */ X /* to be. 'linebuf' must be at least 'maxlinelen+1' characters long. */ X /* Returns the number of lines in the file (and therefore the number */ X /* of entries in *ptr_lines) if successful. Returns IOERROR if it */ X /* could not open the file to read from. Returns TOO_MANY_CHARS if */ X /* it encounters a line longer than 'maxlinelen' characters. */ X X /* Space for each line is malloc'ed as it is read in and the text for */ X /* the jth line is stored in (*ptr_lines)[j] */ X X char *filename; X char ***ptr_lines; X char *linebuf; X int maxlinelen; X X{ X FILE *fp; X int nlines; X if (NULL == (fp = fopen(filename,"r"))) return(IOERROR); X nlines = getlines(fp,0,ptr_lines,linebuf,maxlinelen); X fclose(fp); X return(nlines); X} X X Xint ngetlines (fp,n,ptr_lines,linebuf,maxlinelen) X X /* See documentation for ngetfile below */ X X FILE *fp; X int n; X char ***ptr_lines; X char *linebuf; X int maxlinelen; X X{ X int len; X int nlines = 0; X *ptr_lines = (char **) emalloc(n * sizeof(char **)); X while (T) { X if (0 > (len = getline(fp,linebuf,maxlinelen))) { X if (len == AT_EOF) { X return(nlines); X } X else { X return(TOO_MANY_CHARS); X } X } X else { X if (++nlines > n) { X return(TOO_MANY_LINES); X } X (*ptr_lines)[nlines-1] = anewstr(linebuf); X } X } X} X X X Xint ngetfile (n,filename,ptr_lines,linebuf,maxlinelen) X X /* Same as getfile except that at most n lines will be read. */ X /* If it attempts to read more than n lines, TOO_MANY_LINES will */ X /* be returned. */ X X int n; X char *filename; X char ***ptr_lines; X char *linebuf; X int maxlinelen; X X{ X FILE *fp; X int nlines; X if (NULL == (fp = fopen(filename,"r"))) return(IOERROR); X nlines = ngetlines(fp,n,ptr_lines,linebuf,maxlinelen); X fclose(fp); X return(nlines); X} X X Xint read_file_into_buffer ( X X filename,ptr_lines,maxlines,buffer,buflen,linebuffer,linebuflen X X ) X X char *filename; X char ***ptr_lines; X int maxlines; X char *buffer; X int buflen; X char *linebuffer; X int linebuflen; X X /* *ptr_lines should be an array of character string pointers maxlines */ X /* big. buffer should be an array of characters buflen long. The routine */ X /* reads lines using getline and stores them into buffer, terminating each */ X /* with a null. A pointer to the nth line read is stored in *ptr_lines[n] */ X /* Returns IOERROR if it cannot open the file for reading, TOO_MANY_LINES */ X /* if more than maxlines were read in, TOO_MANY_CHARS if buffer is */ X /* filled before end of file is reached, and LINE_TOO_LONG is any line is */ X /* longer than linebuflen. Returns number of lines read in if successful. */ X X{ X FILE *fp; X int linecount,charcount,len; X char *bp; X char **lines; X X if (NULL == (fp = fopen(filename,"r"))) return(IOERROR); X linecount = 0; X charcount = 0; X bp = buffer; X lines = *ptr_lines; X X while (T) { X X if (0 > (len = getline(fp,linebuffer,linebuflen))) { X fclose(fp); X if (len == AT_EOF) { X return(linecount); X } X else { X return(LINE_TOO_LONG); X } X } X X if (linecount >= maxlines) { X fclose(fp); X return(TOO_MANY_LINES); X } X X charcount += len; X if (charcount >= buflen) { X fclose(fp); X return(TOO_MANY_CHARS); X } X X strcpy(bp,linebuffer); X lines[linecount++] = bp; X bp += (len + 1); X X } X X} X Xchar *efopen (filename,mode) char *filename; char *mode; X X /* The routine simply calls fopen with the same arguments, but prints a */ X /* reasonable error message and calls exit if the call to fopen fails. */ X X{ X FILE *fp; X if (NULL == (fp = fopen(filename,mode))) { X fprintf(stderr,"Could not open %s, mode: %s\n",filename,mode); X perror("Reason: "); X exit(1); X } X return((char *) fp); X} X X X Xint record_fseek (fp,rnum,fromwhere,rsize,hdrsize) X X FILE *fp; X long rnum; X int fromwhere; X int rsize; X int hdrsize; X X{ X if (fromwhere == 0) { X return(fseek(fp,(long) ((rnum - 1)*rsize + hdrsize),0)); X } X else { X return(fseek(fp,(long) (rnum*rsize),fromwhere)); X } X} X X XBool check_string (s,minlen,maxlen) char *s; long minlen,maxlen; X{ X long len; X if (s == 0) return(F); X len = strlen(s); X return (len >= minlen && len <= maxlen); X} END_OF_FILE if test 21027 -ne `wc -c <'./toolsdir/ctools.c'`; then echo shar: \"'./toolsdir/ctools.c'\" unpacked with wrong size! fi # end of './toolsdir/ctools.c' fi echo shar: End of archive 4 \(of 4\). cp /dev/null ark4isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.