rsalz@bbn.com (Rich Salz) (06/06/91)
Submitted-by: Tom Horsley <tom@hcx2.ssd.csd.harris.com> Posting-number: Volume 24, Issue 93 Archive-name: mkid2/part05 #! /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 5 (of 7)." # Contents: iidfun.c mkid.c # Wrapped by tom@hcx2 on Tue Feb 26 10:03:05 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'iidfun.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'iidfun.c'\" else echo shar: Extracting \"'iidfun.c'\" \(17113 characters\) sed "s/^X//" >'iidfun.c' <<'END_OF_FILE' X/* iidfun.c - This file holds the utility functions called from iid.y X */ X X#include "iiddef.h" X#ifdef USE_ALLOCA X X/* Apparently Sun's need the following to work correctly */ X#ifdef sun X#include <alloca.h> X#endif /* sun */ X X/* Define TEMP_ALLOC to call alloca, and TEMP_FREE to do nothing */ X X#define TEMP_ALLOC(s) alloca(s) X#define TEMP_FREE(s) X X#else X X/* Not using alloca() (not everyone has it) - define TEMP_ALLOC to call X * malloc() and TEMP_FREE to call free(). X */ X#define TEMP_ALLOC(s) malloc(s) X#define TEMP_FREE(s) free(s) X X#endif /* USE_ALLOCA */ X X/* ArgListSize - count the size of an arg list so can alloca() enough X * space for the command. X */ Xint XArgListSize(idlp) X id_list_type * idlp ; X{ X id_type * idep ; X int size = 0; X X idep = idlp->id_list ; X while (idep != NULL) { X size += 1 + strlen(idep->id); X idep = idep->next_id; X } X return size; X} X X/* SetListSize - count the size of a string build up from a set so we can X * alloca() enough space for args. X */ Xint XSetListSize(sp) X set_type * sp ; X{ X int i ; X int size = 0 ; X X for (i = 0; i < NextFileNum; ++i) { X if (FileList[i]->mask_word < sp->set_size) { X if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) { X size += 1 + strlen(FileList[i]->name); X } X } X } X return size; X} X X/* FlushFiles - clear out the TheFiles array for the start of a new X * query. X */ Xvoid XFlushFiles() X{ X int i ; X X if (TheFiles != NULL) { X for (i = 0; i <= MaxCurFile; ++i) { X TheFiles[i] = 0 ; X } X } X MaxCurFile = 0 ; X} X X/* fatal - sometimes the only thing to do is die... X */ Xvoid Xfatal(s) X{ X fprintf(stderr,"Fatal error: %s\n",s) ; X exit(1) ; X} X X/* CountBits - count the number of bits in a bit set. Actually fairly X * tricky since it needs to deal with sets having infinite tails X * as a result of a NOT operation. X */ Xint XCountBits(sp) X set_type * sp ; X{ X unsigned long bit_mask ; X int count = 0 ; X int i ; X X i = 0; X for ( ; ; ) { X for (bit_mask = high_bit; bit_mask != 0; bit_mask >>= 1) { X if (bit_mask == NextMaskBit && i == NextMaskWord) { X return(count) ; X } X if (i < sp->set_size) { X if (sp->set_data[i] & bit_mask) { X ++count ; X } X } else { X if (sp->set_tail == 0) return count; X if (sp->set_tail & bit_mask) { X ++count; X } X } X } X ++i; X } X} X X/* OneDescription - Print a description of a set. This includes X * the set number, the number of files in the set, and the X * set description string. X */ Xvoid XOneDescription(sp) X set_type * sp ; X{ X int elt_count ; X char setnum[20] ; X X sprintf(setnum,"S%d",sp->set_num) ; X elt_count = CountBits(sp) ; X printf("%5s %6d %s\n",setnum,elt_count,sp->set_desc) ; X} X X/* DescribeSets - Print description of all the sets. X */ Xvoid XDescribeSets() X{ X int i ; X X if (NextSetNum > 0) { X for (i = 0; i < NextSetNum; ++i) { X OneDescription(TheSets[i]) ; X } X } else { X printf("No sets defined yet.\n") ; X } X} X X/* SetList - Go through the bit set and add the file names in X * it to an identifier list. X */ Xid_list_type * XSetList(idlp, sp) X id_list_type * idlp ; X set_type * sp ; X{ X int i ; X id_type * idep ; X X for (i = 0; i < NextFileNum; ++i) { X if (FileList[i]->mask_word < sp->set_size) { X if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) { X idep = (id_type *)malloc(sizeof(id_type) + X strlen(FileList[i]->name)) ; X if (idep == NULL) { X fatal("Out of memory in SetList") ; X } X idep->next_id = NULL ; X strcpy(idep->id, FileList[i]->name) ; X idlp = ExtendList(idlp, idep) ; X } X } X } X return(idlp) ; X} X X/* PrintSet - Go through the bit set and print the file names X * corresponding to all the set bits. X */ Xvoid XPrintSet(sp) X set_type * sp ; X{ X int i ; X X for (i = 0; i < NextFileNum; ++i) { X if (FileList[i]->mask_word < sp->set_size) { X if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) { X printf("%s\n",FileList[i]->name) ; X } X } X } X} X X/* Free up all space used by current set of sets and reset all X * set numbers. X */ Xvoid XFlushSets() X{ X int i ; X X for (i = 0; i < NextSetNum; ++i) { X free(TheSets[i]->set_desc) ; X free(TheSets[i]) ; X } X NextSetNum = 0 ; X} X X/* InitList - create an empty identifier list. X */ Xid_list_type * XInitList() X{ X id_list_type * idlp ; X X idlp = (id_list_type *)malloc(sizeof(id_list_type)) ; X if (idlp == NULL) { X fatal("Out of memory in InitList") ; X } X idlp->id_count = 0 ; X idlp->end_ptr_ptr = & (idlp->id_list) ; X idlp->id_list = NULL ; X return(idlp) ; X} X X/* ExtendList - add one identifier to an ID list. X */ Xid_list_type * XExtendList(idlp, idp) X id_list_type * idlp ; X id_type * idp ; X{ X *(idlp->end_ptr_ptr) = idp ; X idlp->end_ptr_ptr = &(idp->next_id) ; X return(idlp) ; X} X X/* InitIid - do all initial processing for iid. X * 1) Determine the size of a unsigned long for bit set stuff. X * 2) Find out the name of the pager program to use. X * 3) Create the HelpSet (pointing to the help file). X * 4) Setup the prompt. X */ Xvoid XInitIid() X{ X unsigned long bit_mask = 1 ; /* find number of bits in long */ X int i ; X char * page ; /* pager program */ X X do { X high_bit = bit_mask ; X bit_mask <<= 1 ; X } while (bit_mask != 0) ; X X NextMaskBit = high_bit ; X X page = getenv("PAGER") ; X if (page == NULL) { X page = PAGER ; X } X strcpy(Pager, page) ; X X FlushFiles() ; X InstallFile(HELPFILE) ; X HelpSet = (set_type *) X malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ; X if (HelpSet == NULL) { X fatal("No memory for set in InitIid") ; X } X HelpSet->set_tail = 0 ; X HelpSet->set_desc = NULL ; X HelpSet->set_size = MaxCurFile + 1 ; X for (i = 0; i <= MaxCurFile; ++i) { X HelpSet->set_data[i] = TheFiles[i] ; X } X X page = getenv("PS1") ; X if (page == NULL) { X page = PROMPT ; X } X strcpy(Prompt, page) ; X} X X/* InstallFile - install a file name in the symtab. Return the X * symbol table pointer of the file. X */ Xsymtab_type * XInstallFile(fp) X char * fp ; X{ X char c ; X unsigned long hash_code ; X int i ; X char * sp ; X symtab_type * symp ; X X hash_code = 0 ; X sp = fp ; X while ((c = *sp++) != '\0') { X hash_code <<= 1 ; X hash_code ^= (unsigned long)(c) ; X if (hash_code & high_bit) { X hash_code &= ~ high_bit ; X hash_code ^= 1 ; X } X } X hash_code %= HASH_SIZE ; X symp = HashTable[hash_code] ; X while (symp != NULL && strcmp(symp->name, fp)) { X symp = symp->hash_link ; X } X if (symp == NULL) { X symp = (symtab_type *)malloc(sizeof(symtab_type) + strlen(fp)) ; X if (symp == NULL) { X fatal("No memory for symbol table entry in InstallFile") ; X } X strcpy(symp->name, fp) ; X symp->hash_link = HashTable[hash_code] ; X HashTable[hash_code] = symp ; X if (NextMaskWord >= FileSpace) { X FileSpace += 1000 ; X if (TheFiles != NULL) { X TheFiles = (unsigned long *) X realloc(TheFiles, sizeof(unsigned long) * FileSpace) ; X } else { X TheFiles = (unsigned long *) X malloc(sizeof(unsigned long) * FileSpace) ; X } X if (TheFiles == NULL) { X fatal("No memory for TheFiles in InstallFile") ; X } X for (i = NextMaskWord; i < FileSpace; ++i) { X TheFiles[i] = 0 ; X } X } X symp->mask_word = NextMaskWord ; X symp->mask_bit = NextMaskBit ; X NextMaskBit >>= 1 ; X if (NextMaskBit == 0) { X NextMaskBit = high_bit ; X ++NextMaskWord ; X } X if (NextFileNum >= ListSpace) { X ListSpace += 1000 ; X if (FileList == NULL) { X FileList = (symtab_type **) X malloc(sizeof(symtab_type *) * ListSpace) ; X } else { X FileList = (symtab_type **) X realloc(FileList, ListSpace * sizeof(symtab_type *)) ; X } X if (FileList == NULL) { X fatal("No memory for FileList in InstallFile") ; X } X } X FileList[NextFileNum++] = symp ; X /* put code here to sort the file list by name someday */ X } X TheFiles[symp->mask_word] |= symp->mask_bit ; X if (symp->mask_word > MaxCurFile) { X MaxCurFile = symp->mask_word ; X } X return(symp) ; X} X X/* RunPager - run the users pager program on the list of files X * in the set. X */ Xvoid XRunPager(pp, sp) X char * pp ; X set_type * sp ; X{ X char * cmd ; X int i ; X id_type * idep ; X X cmd = (char *)TEMP_ALLOC(SetListSize(sp) + strlen(pp) + 2); X strcpy(cmd, pp) ; X for (i = 0; i < NextFileNum; ++i) { X if (FileList[i]->mask_word < sp->set_size) { X if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) { X strcat(cmd, " ") ; X strcat(cmd, FileList[i]->name) ; X } X } X } X system(cmd) ; X TEMP_FREE(cmd) ; X} X X/* AddSet - add a new set to the universal list of sets. Assign X * it the next set number. X */ Xvoid XAddSet(sp) X set_type * sp ; X{ X if (NextSetNum >= SetSpace) { X SetSpace += 1000 ; X if (TheSets != NULL) { X TheSets = (set_type **) X realloc(TheSets, sizeof(set_type *) * SetSpace) ; X } else { X TheSets = (set_type **) X malloc(sizeof(set_type *) * SetSpace) ; X } X if (TheSets == NULL) { X fatal("No memory for TheSets in AddSet") ; X } X } X sp->set_num = NextSetNum ; X TheSets[NextSetNum++] = sp ; X} X X/* RunProg - run a program with arguments from id_list and X * accept list of file names back from the program which X * are installed in the symbol table and used to construct X * a new set. X */ Xset_type * XRunProg(pp, idlp) X char * pp ; X id_list_type * idlp ; X{ X int c ; X char * cmd ; X char * dp ; X char file [ MAXCMD ] ; X int i ; X id_type * idep ; X id_type * next_id ; X FILE * prog ; X set_type * sp ; X X cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2); X FlushFiles() ; X strcpy(cmd, pp) ; X idep = idlp->id_list ; X while (idep != NULL) { X strcat(cmd, " ") ; X strcat(cmd, idep->id) ; X next_id = idep->next_id ; X free(idep) ; X idep = next_id ; X } X free(idlp) ; X X /* run program with popen, reading the output. Assume each X * white space terminated string is a file name. X */ X prog = popen(cmd, "r") ; X dp = file ; X while ((c = getc(prog)) != EOF) { X if (isspace(c)) { X if (dp != file) { X *dp++ = '\0' ; X InstallFile(file) ; X dp = file ; X } X } else { X *dp++ = c ; X } X } X if (dp != file) { X *dp++ = '\0' ; X InstallFile(file) ; X } X if (pclose(prog) != 0) { X /* if there was an error make an empty set, who knows what X * garbage the program printed. X */ X FlushFiles() ; X } X X sp = (set_type *) X malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ; X if (sp == NULL) { X fatal("No memory for set in RunProg") ; X } X sp->set_tail = 0 ; X sp->set_desc = (char *)malloc(strlen(cmd) + 1) ; X if (sp->set_desc == NULL) { X fatal("No memory for set description in RunProg") ; X } X strcpy(sp->set_desc, cmd) ; X sp->set_size = MaxCurFile + 1 ; X for (i = 0; i <= MaxCurFile; ++i) { X sp->set_data[i] = TheFiles[i] ; X } X AddSet(sp) ; X TEMP_FREE(cmd); X return(sp) ; X} X X/* SetDirectory - change the working directory. This will X * determine which ID file is found by the subprograms. X */ Xvoid XSetDirectory(dir) X id_type * dir ; X{ X if (chdir(dir->id) != 0) { X fprintf(stderr,"Directory %s not accessible.\n",dir) ; X } X free(dir) ; X} X X/* SetIntersect - construct a new set from the intersection X * of two others. Also construct a new description string. X */ Xset_type * XSetIntersect(sp1, sp2) X set_type * sp1 ; X set_type * sp2 ; X{ X char * desc ; X int i ; X int len1 ; X int len2 ; X set_type * new_set ; X int new_size ; X X if (sp1->set_tail || sp2->set_tail) { X new_size = MAX(sp1->set_size, sp2->set_size) ; X } else { X new_size = MIN(sp1->set_size, sp2->set_size) ; X } X new_set = (set_type *)malloc(sizeof(set_type) + X (new_size - 1) * sizeof(unsigned long)) ; X if (new_set == NULL) { X fatal("No memory for set in SetIntersect") ; X } X len1 = strlen(sp1->set_desc) ; X len2 = strlen(sp2->set_desc) ; X desc = (char *)malloc(len1 + len2 + 10) ; X if (desc == NULL) { X fatal("No memory for set description in SetIntersect") ; X } X new_set->set_desc = desc ; X strcpy(desc,"(") ; X ++desc ; X strcpy(desc, sp1->set_desc) ; X desc += len1 ; X strcpy(desc, ") AND (") ; X desc += 7 ; X strcpy(desc, sp2->set_desc) ; X desc += len2 ; X strcpy(desc, ")") ; X AddSet(new_set) ; X new_set->set_size = new_size ; X for (i = 0; i < new_size; ++i) { X new_set->set_data[i] = X ((i < sp1->set_size) ? sp1->set_data[i] : sp1->set_tail) & X ((i < sp2->set_size) ? sp2->set_data[i] : sp2->set_tail) ; X } X new_set->set_tail = sp1->set_tail & sp2->set_tail ; X return(new_set) ; X} X X/* SetUnion - construct a new set from the union of two others. X * Also construct a new description string. X */ Xset_type * XSetUnion(sp1, sp2) X set_type * sp1 ; X set_type * sp2 ; X{ X char * desc ; X int i ; X int len1 ; X int len2 ; X set_type * new_set ; X int new_size ; X X new_size = MAX(sp1->set_size, sp2->set_size) ; X new_set = (set_type *)malloc(sizeof(set_type) + X (new_size - 1) * sizeof(unsigned long)) ; X if (new_set == NULL) { X fatal("No memory for set in SetUnion") ; X } X len1 = strlen(sp1->set_desc) ; X len2 = strlen(sp2->set_desc) ; X desc = (char *)malloc(len1 + len2 + 9) ; X if (desc == NULL) { X fatal("No memory for set description in SetUnion") ; X } X new_set->set_desc = desc ; X strcpy(desc,"(") ; X ++desc ; X strcpy(desc, sp1->set_desc) ; X desc += len1 ; X strcpy(desc, ") OR (") ; X desc += 6 ; X strcpy(desc, sp2->set_desc) ; X desc += len2 ; X strcpy(desc, ")") ; X AddSet(new_set) ; X new_set->set_size = new_size ; X for (i = 0; i < new_size; ++i) { X new_set->set_data[i] = X ((i < sp1->set_size) ? (sp1->set_data[i]) : sp1->set_tail) | X ((i < sp2->set_size) ? (sp2->set_data[i]) : sp2->set_tail) ; X } X new_set->set_tail = sp1->set_tail | sp2->set_tail ; X return(new_set) ; X} X X/* SetInverse - construct a new set from the inverse of another. X * Also construct a new description string. X * X * This is kind of tricky. An inverse set in iid may grow during X * the course of a session. By NOTing the set_tail extension the X * inverse at any given time will be defined as the inverse against X * a universe that grows as additional queries are made and new files X * are added to the database. X * X * Several alternative definitions were possible (snapshot the X * universe at the time of the NOT, go read the ID file to X * determine the complete universe), but this one was the one X * I picked. X */ Xset_type * XSetInverse(sp) X set_type * sp ; X{ X char * desc ; X int i ; X set_type * new_set ; X X new_set = (set_type *)malloc(sizeof(set_type) + X (sp->set_size - 1) * sizeof(unsigned long)) ; X if (new_set == NULL) { X fatal("No memory for set in SetInverse") ; X } X desc = (char *)malloc(strlen(sp->set_desc) + 5) ; X if (desc == NULL) { X fatal("No memory for set description in SetInverse") ; X } X new_set->set_desc = desc ; X strcpy(desc,"NOT ") ; X desc += 4 ; X strcpy(desc, sp->set_desc) ; X AddSet(new_set) ; X new_set->set_size = sp->set_size ; X for (i = 0; i < sp->set_size; ++i) { X new_set->set_data[i] = ~ sp->set_data[i] ; X } X new_set->set_tail = ~ sp->set_tail ; X return(new_set) ; X} X X/* RunShell - run a program with arguments from id_list. X */ Xvoid XRunShell(pp, idlp) X char * pp ; X id_list_type * idlp ; X{ X char * cmd ; X id_type * idep ; X id_type * next_id ; X X cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2); X strcpy(cmd, pp) ; X idep = idlp->id_list ; X while (idep != NULL) { X strcat(cmd, " ") ; X strcat(cmd, idep->id) ; X next_id = idep->next_id ; X free(idep) ; X idep = next_id ; X } X free(idlp) ; X system(cmd) ; X TEMP_FREE(cmd); X} END_OF_FILE if test 17113 -ne `wc -c <'iidfun.c'`; then echo shar: \"'iidfun.c'\" unpacked with wrong size! fi # end of 'iidfun.c' fi if test -f 'mkid.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mkid.c'\" else echo shar: Extracting \"'mkid.c'\" \(18633 characters\) sed "s/^X//" >'mkid.c' <<'END_OF_FILE' Xstatic char copyright[] = "@(#)Copyright (c) 1986, Greg McGary"; Xstatic char sccsid[] = "@(#)mkid.c 1.4 86/11/06"; X X#include <bool.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include <string.h> X#include <ctype.h> X#include <id.h> X#include <bitops.h> X#include <errno.h> X#include <extern.h> X#include "patchlevel.h" X Xint idnHashCmp(); Xint idnQsortCmp(); Xint round2(); Xstruct idname *newIdName(); Xvoid extractId(); Xvoid fileIdArgs(); Xvoid initHashTable(); Xvoid oldIdArgs(); Xvoid rehash(); Xvoid updateID(); Xvoid writeID(); X Xlong NameCount; /* Count of names in database */ Xlong NumberCount; /* Count of numbers in database */ Xlong StringCount; /* Count of strings in database */ Xlong SoloCount; /* Count of identifiers that occur only once */ X Xlong HashSize; /* Total Slots in hash table */ Xlong HashMaxLoad; /* Maximum loading of hash table */ Xlong HashFill; /* Number of keys inserted in table */ Xlong HashProbes; /* Total number of probes */ Xlong HashSearches; /* Total number of searches */ Xstruct idname **HashTable; /* Vector of idname pointers */ X Xbool Verbose = FALSE; X Xint ArgsCount = 0; /* Count of args to save */ Xint ScanCount = 0; /* Count of files to scan */ Xint PathCount = 0; /* Count of files covered in database */ Xint BitArraySize; /* Size of bit array slice (per name) */ X Xchar PWDname[BUFSIZ]; /* The current working directory */ Xchar absID[BUFSIZ]; /* The absolute name of the database */ X X Xchar *MyName; Xstatic void Xusage() X{ X fprintf(stderr, "Usage: %s [-f<idfile>] [-s<dir>] [-r<dir>] [(+|-)l[<lang>]] [-v] [(+|-)S<scanarg>] [-a<argfile>] [-] [-u] [files...]\n", MyName); X exit(1); X} Xmain(argc, argv) X int argc; X char **argv; X{ X char *arg; X int op; X FILE *argFILE = NULL; X char *idFile = IDFILE; X char *rcsDir = NULL; X char *sccsDir = NULL; X struct idarg *idArgs, *idArgHead; X bool keepLang = FALSE; X int argsFrom = 0; X#define AF_CMDLINE 0x1 /* file args came on command line */ X#define AF_FILE 0x2 /* file args came from a file (-f<file>) */ X#define AF_IDFILE 0x4 /* file args came from an old ID file (-u) */ X#define AF_QUERY 0x8 /* no file args necessary: usage query */ X X MyName = basename(GETARG(argc, argv)); X if (kshgetwd(PWDname) == NULL) { X fprintf(stderr,"%s: cannot get current working directory name.\n",MyName); X exit(1); X } X strcat(PWDname, "/"); X#ifdef ERRLINEBUF X setlinebuf(stderr); X#endif X X idArgs = idArgHead = NEW(struct idarg); X X /* X Process some arguments, and snarf-up some X others for processing later. X */ X while (argc) { X arg = GETARG(argc, argv); X if (*arg != '-' && *arg != '+') { X argsFrom |= AF_CMDLINE; X idArgs->ida_arg = arg; X idArgs->ida_flags = IDA_SCAN|IDA_PATH; X idArgs->ida_index = postIncr(&PathCount); X ScanCount++; X idArgs = (idArgs->ida_next = NEW(struct idarg)); X continue; X } X op = *arg++; X switch (*arg++) X { X case 'u': X argsFrom |= AF_IDFILE; X oldIdArgs(idFile, &idArgs); X break; X case '\0': X argsFrom |= AF_FILE; X fileIdArgs(stdin, &idArgs); X break; X case 'a': X if ((argFILE = fopen(arg, "r")) == NULL) { X filerr("open", arg); X exit(1); X } X argsFrom |= AF_FILE; X fileIdArgs(argFILE, &idArgs); X break; X case 'f': X idFile = arg; X break; X case 'v': X Verbose = TRUE; X break; X case 'S': X if (strchr(&arg[-2], '?')) { X setScanArgs(op, arg); X argsFrom |= AF_QUERY; X } X /*FALLTHROUGH*/ X case 'l': X case 's': X case 'r': X idArgs->ida_arg = &arg[-2]; X idArgs->ida_index = -1; X idArgs->ida_flags = IDA_ARG; X idArgs = (idArgs->ida_next = NEW(struct idarg)); X ArgsCount++; X break; X default: X usage(); X } X } X X if (argsFrom & AF_QUERY) X exit(0); X /* X File args should only come from one place. Ding the X user if arguments came from multiple places, or if none X were supplied at all. X */ X switch (argsFrom) X { X case AF_CMDLINE: X case AF_FILE: X case AF_IDFILE: X if (PathCount > 0) X break; X /*FALLTHROUGH*/ X case 0: X fprintf(stderr, "%s: Use -u, -f<file>, or cmd-line for file args!\n", MyName); X usage(); X default: X fprintf(stderr, "%s: Use only one of: -u, -f<file>, or cmd-line for file args!\n", MyName); X usage(); X } X X if (ScanCount == 0) X exit(0); X X BitArraySize = (PathCount + 7) >> 3; X initHashTable(ScanCount); X X strcpy(absID, spanPath(PWDname, idFile)); X if (access(idFile, 06) < 0 X && (errno != ENOENT || access(dirname(idFile), 06) < 0)) { X filerr("modify", idFile); X exit(1); X } X X for (idArgs = idArgHead; idArgs->ida_next; idArgs = idArgs->ida_next) { X char *(*scanner)(); X FILE *srcFILE; X char *arg, *lang, *suff, *filter; X X lang = NULL; X arg = idArgs->ida_arg; X if (idArgs->ida_flags & IDA_ARG) { X op = *arg++; X switch (*arg++) X { X case 'l': X if (*arg == '\0') { X keepLang = FALSE; X lang = NULL; X break; X } X if (op == '+') X keepLang = TRUE; X lang = arg; X break; X case 's': X sccsDir = arg; X break; X case 'r': X rcsDir = arg; X break; X case 'S': X setScanArgs(op, strsav(arg)); X break; X default: X usage(); X } X continue; X } X if (!(idArgs->ida_flags & IDA_SCAN)) X goto skip; X if (lang == NULL) { X if ((suff = strrchr(arg, '.')) == NULL) X suff = ""; X if (((lang = getLanguage(suff)) == NULL) && X ((lang = getLanguage(".default")) == NULL)) { X fprintf(stderr, "%s: No language assigned to suffix: `%s'\n", MyName, suff); X goto skip; X } X } X if ((scanner = getScanner(lang)) == NULL) { X fprintf(stderr, "%s: No scanner for language: `%s'\n", MyName, lang); X goto skip; X } X filter = getFilter(suff); X if ((srcFILE = openSrcFILE(arg, sccsDir, rcsDir, filter)) == NULL) X goto skip; X if (Verbose) X if (filter) { X fprintf(stderr, "%s: ",lang); X fprintf(stderr, filter, arg); X fprintf(stderr, "\n"); X } else { X fprintf(stderr, "%s: %s\n", lang, arg); X } X extractId(scanner, srcFILE, idArgs->ida_index); X closeSrcFILE(srcFILE, filter); X skip: X if (!keepLang) X lang = NULL; X } X X if (HashFill == 0) X exit(0); X X if (Verbose) { X fprintf(stderr, "mkid patchlevel %d:\n", PATCHLEVEL); X fprintf(stderr, "Compressing Hash Table...\n"); X } X hashCompress(HashTable, HashSize); X if (Verbose) X fprintf(stderr, "Sorting Hash Table...\n"); X qsort(HashTable, HashFill, sizeof(struct idname *), idnQsortCmp); X X if (argsFrom == AF_IDFILE) { X if (Verbose) X fprintf(stderr, "Merging Tables...\n"); X updateID(idFile, idArgHead); X } X X if (Verbose) X fprintf(stderr, "Writing `%s'...\n", idFile); X writeID(idFile, idArgHead); X X if (Verbose) { X float loadFactor = (float)HashFill / (float)HashSize; X float aveProbes = (float)HashProbes / (float)HashSearches; X float aveOccur = (float)HashSearches / (float)HashFill; X fprintf(stderr, "Names: %ld, ", NameCount); X fprintf(stderr, "Numbers: %ld, ", NumberCount); X fprintf(stderr, "Strings: %ld, ", StringCount); X fprintf(stderr, "Solo: %ld, ", SoloCount); X fprintf(stderr, "Total: %ld\n", HashFill); X fprintf(stderr, "Occurances: %.2f, ", aveOccur); X fprintf(stderr, "Load: %.2f, ", loadFactor); X fprintf(stderr, "Probes: %.2f\n", aveProbes); X } X exit(0); X} X Xvoid XextractId(getId, srcFILE, index) X register char *(*getId)(); X register FILE *srcFILE; X int index; X{ X register struct idname **slot; X register char *key; X int flags; X X while ((key = (*getId)(srcFILE, &flags)) != NULL) { X slot = (struct idname **)hashSearch(key, HashTable, HashSize, sizeof(struct idname *), h1str, h2str, idnHashCmp, &HashProbes); X HashSearches++; X if (*slot != NULL) { X (*slot)->idn_flags |= flags; X BITSET((*slot)->idn_bitv, index); X continue; X } X *slot = newIdName(key); X (*slot)->idn_flags = IDN_SOLO|flags; X BITSET((*slot)->idn_bitv, index); X if (HashFill++ >= HashMaxLoad) X rehash(); X } X} X X/* As the database is written, may need to adjust the file names. X * If we are generating the ID file in a remote directory, then X * adjust the file names to be relative to the location of the X * ID database. X * X * (This would be a common useage if you want to make a database X * for a directory which you have no write access to, so you cannot X * create the ID file.) X */ Xvoid XwriteID(idFile, idArgs) X char *idFile; X struct idarg *idArgs; X{ X register struct idname **idnp; X register struct idname *idn; X register int i; X char *vecBuf; X FILE *idFILE; X int count; X int lasti; X long before, after; X int length, longest; X struct idhead idh; X int fixnames; X char * lsl; X X if ((lsl = strrchr(relPath(PWDname, absID),'/')) == NULL) { X /* The database is in the cwd, don't adjust the names */ X fixnames = FALSE; X } else { X /* The database is not in cwd, adjust names so they are X * relative to the location of the database, make absID X * just be the directory path to ID. X */ X fixnames = TRUE; X *(lsl+1) = '\0'; X } X if ((idFILE = fopen(idFile, "w+")) == NULL) { X filerr("create", idFile); X exit(1); X } X fseek(idFILE, (long)sizeof(struct idhead), 0); X X /* write out the list of pathnames */ X idh.idh_argo = ftell(idFILE); X for (i = lasti = 0; idArgs->ida_next; idArgs = idArgs->ida_next) { X if (idArgs->ida_index > 0) X while (++lasti < idArgs->ida_index) X i++, putc('\0', idFILE); X if (fixnames) { X fputs(relPath(absID,spanPath(PWDname,idArgs->ida_arg)), idFILE); X } else { X fputs(idArgs->ida_arg, idFILE); X } X i++, putc('\0', idFILE); X } X idh.idh_argc = i; X idh.idh_pthc = PathCount; X X /* write out the list of identifiers */ X i = 1; X if (idh.idh_pthc >= 0x000000ff) X i++; X if (idh.idh_pthc >= 0x0000ffff) X i++; X if (idh.idh_pthc >= 0x00ffffff) X i++; X idh.idh_vecc = i; X X vecBuf = malloc((idh.idh_pthc + 1) * idh.idh_vecc); X X putc('\377', idFILE); X before = idh.idh_namo = ftell(idFILE); X longest = 0; X for (idnp = HashTable, i = 0; i < HashFill; i++, idnp++) { X idn = *idnp; X if ((idn == NULL) || (idn->idn_name[0] == '\0')) { X HashFill--; i--; X continue; X } X if (idn->idn_flags & IDN_SOLO) X SoloCount++; X if (idn->idn_flags & IDN_NUMBER) X NumberCount++; X if (idn->idn_flags & IDN_NAME) X NameCount++; X if (idn->idn_flags & IDN_STRING) X StringCount++; X X putc((*idnp)->idn_flags, idFILE); X fputs(idn->idn_name, idFILE); X putc('\0', idFILE); X X count = bitsToVec(vecBuf, (*idnp)->idn_bitv, idh.idh_pthc, idh.idh_vecc); X fwrite(vecBuf, idh.idh_vecc, count, idFILE); X putc('\377', idFILE); X after = ftell(idFILE); X X if ((length = (after - before)) > longest) X longest = length; X before = after; X } X idh.idh_namc = i; X putc('\377', idFILE); X idh.idh_endo = ftell(idFILE); X idh.idh_bsiz = longest; X X /* write out the header */ X strncpy(idh.idh_magic, IDH_MAGIC, sizeof(idh.idh_magic)); X idh.idh_vers = IDH_VERS; X fseek(idFILE, 0L, 0); X fwrite(&idh, sizeof(struct idhead), 1, idFILE); X X fclose(idFILE); X} X X/* X Build an idarg vector from pathnames contained in an existing X id file. Only include pathnames for files whose modification X time is later than that of the id file itself. X*/ Xvoid XoldIdArgs(idFile, idArgsP) X char *idFile; X struct idarg **idArgsP; X{ X struct stat statBuf; X struct idhead idh; X FILE *idFILE; X register int i; X register char *strings; X time_t idModTime; X X if ((idFILE = fopen(idFile, "r")) == NULL) { X filerr("open", idFile); X usage(); X } X /* X * Open the id file, get its mod-time, and read its header. X */ X if (fstat(fileno(idFILE), &statBuf) < 0) { X filerr("stat", idFile); X usage(); X } X idModTime = statBuf.st_mtime; X fread(&idh, sizeof(struct idhead), 1, idFILE); X if (!strnequ(idh.idh_magic, IDH_MAGIC, sizeof(idh.idh_magic))) { X fprintf(stderr, "%s: Not an id file: `%s'\n", MyName, idFile); X exit(1); X } X if (idh.idh_vers != IDH_VERS) { X fprintf(stderr, "%s: ID version mismatch (%ld,%ld)\n", MyName, idh.idh_vers, IDH_VERS); X exit(1); X } X X /* X * Read in the id pathnames, compare their mod-times with X * the id file, and incorporate the pathnames of recently modified X * files in the idarg vector. Also, construct a mask of X * bit array positions we want to turn off when we build the X * initial hash-table. X */ X fseek(idFILE, idh.idh_argo, 0); X strings = malloc(i = idh.idh_namo - idh.idh_argo); X fread(strings, i, 1, idFILE); X ScanCount = 0; X for (i = 0; i < idh.idh_argc; i++) { X (*idArgsP)->ida_arg = strings; X if (*strings == '+' || *strings == '-') { X (*idArgsP)->ida_flags = IDA_ARG; X (*idArgsP)->ida_index = -1; X } else { X (*idArgsP)->ida_flags = IDA_PATH; X (*idArgsP)->ida_index = postIncr(&PathCount); X if (stat(strings, &statBuf) < 0) { X filerr("stat", strings); X } else if (statBuf.st_mtime >= idModTime) { X (*idArgsP)->ida_flags |= IDA_SCAN; X ScanCount++; X } X } X (*idArgsP) = ((*idArgsP)->ida_next = NEW(struct idarg)); X while (*strings++) X ; X } X if (ScanCount == 0) { X fclose(idFILE); X exit(0); X } X fclose(idFILE); X} X Xvoid XupdateID(idFile, idArgs) X char *idFile; X struct idarg *idArgs; X{ X struct idname *idn; X struct idhead idh; X register char *bitArray; X char *entry; X register int i; X FILE *idFILE; X int cmp, count, size; X char *bitsOff; X struct idname **newTable, **mergeTable; X struct idname **t1, **t2, **tm; X X if ((idFILE = fopen(idFile, "r")) == NULL) X filerr("open", idFile); X fread(&idh, sizeof(struct idhead), 1, idFILE); X X entry = malloc(idh.idh_bsiz); X X bitsOff = malloc(BitArraySize); X bzero(bitsOff, BitArraySize); X for (i = 0; idArgs->ida_next; idArgs = idArgs->ida_next) X if (idArgs->ida_flags & IDA_SCAN) X BITSET(bitsOff, idArgs->ida_index); X X bitArray = malloc(BitArraySize); X bzero(bitArray, BitArraySize); X t2 = newTable = (struct idname **)malloc((idh.idh_namc + 1) * sizeof(struct idname *)); X fseek(idFILE, idh.idh_namo, 0); X count = 0; X for (i = 0; i < idh.idh_namc; i++) { X size = 1 + fgets0(entry, idh.idh_bsiz, idFILE); X getsFF(&entry[size], idFILE); X vecToBits(bitArray, &entry[size], idh.idh_vecc); X bitsclr(bitArray, bitsOff, BitArraySize); X if (!bitsany(bitArray, BitArraySize)) X continue; X *t2 = newIdName(ID_STRING(entry)); X bitsset((*t2)->idn_bitv, bitArray, BitArraySize); X (*t2)->idn_flags = ID_FLAGS(entry); X bzero(bitArray, BitArraySize); X t2++; count++; X } X *t2 = NULL; X X t1 = HashTable; X t2 = newTable; X tm = mergeTable = (struct idname **)calloc(HashFill + count + 1, sizeof(struct idname *)); X while (*t1 && *t2) { X cmp = strcmp((*t1)->idn_name, (*t2)->idn_name); X if (cmp < 0) X *tm++ = *t1++; X else if (cmp > 0) X *tm++ = *t2++; X else { X (*t1)->idn_flags |= (*t2)->idn_flags; X (*t1)->idn_flags &= ~IDN_SOLO; X bitsset((*t1)->idn_bitv, (*t2)->idn_bitv, BitArraySize); X *tm++ = *t1; X t1++, t2++; X } X } X while (*t1) X *tm++ = *t1++; X while (*t2) X *tm++ = *t2++; X *tm = NULL; X HashTable = mergeTable; X HashFill = tm - mergeTable; X} X X/* X Cons up a list of idArgs as supplied in a file. X*/ Xvoid XfileIdArgs(argFILE, idArgsP) X FILE *argFILE; X struct idarg **idArgsP; X{ X int fileCount; X char buf[BUFSIZ]; X char *arg; X X fileCount = 0; X while (fgets(buf, sizeof(buf), argFILE)) { X (*idArgsP)->ida_arg = arg = strnsav(buf, strlen(buf)-1); X if (*arg == '+' || *arg == '-') { X (*idArgsP)->ida_flags = IDA_ARG; X (*idArgsP)->ida_index = -1; X } else { X (*idArgsP)->ida_flags = IDA_SCAN|IDA_PATH; X (*idArgsP)->ida_index = postIncr(&PathCount); X ScanCount++; X } X (*idArgsP) = ((*idArgsP)->ida_next = NEW(struct idarg)); X } X} X Xvoid XinitHashTable(pathCount) X int pathCount; X{ X if ((HashSize = round2((pathCount << 6) + 511)) > 0x8000) X HashSize = 0x8000; X HashMaxLoad = HashSize - (HashSize >> 4); /* about 94% */ X HashTable = (struct idname **)calloc(HashSize, sizeof(struct idname *)); X} X X/* X Double the size of the hash table in the X event of overflow... X*/ Xvoid Xrehash() X{ X long oldHashSize = HashSize; X struct idname **oldHashTable = HashTable; X register struct idname **htp; X register struct idname **slot; X X HashSize *= 2; X if (Verbose) X fprintf(stderr, "Rehashing... (doubling size to %ld)\n", HashSize); X HashMaxLoad = HashSize - (HashSize >> 4); X HashTable = (struct idname **)calloc(HashSize, sizeof(struct idname *)); X X HashFill = 0; X for (htp = oldHashTable; htp < &oldHashTable[oldHashSize]; htp++) { X if (*htp == NULL) X continue; X slot = (struct idname **)hashSearch((*htp)->idn_name, (char *)HashTable, HashSize, sizeof(struct idname *), h1str, h2str, idnHashCmp, &HashProbes); X if (*slot) { X fprintf(stderr, "%s: Duplicate hash entry!\n"); X exit(1); X } X *slot = *htp; X HashSearches++; X HashFill++; X } X free(oldHashTable); X} X X/* X Round a given number up to the nearest power of 2. X*/ Xint Xround2(rough) X int rough; X{ X int round; X X round = 1; X while (rough) { X round <<= 1; X rough >>= 1; X } X return round; X} X X/* X `compar' function for hashSearch() X*/ Xint XidnHashCmp(key, idn) X char *key; X struct idname **idn; X{ X int collate; X X if (*idn == NULL) X return 0; X X if ((collate = strcmp(key, (*idn)->idn_name)) == 0) X (*idn)->idn_flags &= ~IDN_SOLO; /* we found another occurance */ X X return collate; X} X X/* X `compar' function for qsort(). X*/ Xint XidnQsortCmp(idn1, idn2) X struct idname **idn1; X struct idname **idn2; X{ X if (*idn1 == *idn2) X return 0; X if (*idn1 == NULL) X return 1; X if (*idn2 == NULL) X return -1; X X return strcmp((*idn1)->idn_name, (*idn2)->idn_name); X} X X/* X Allocate a new idname struct and fill in the name field. X We allocate memory in large chunks to avoid frequent X calls to malloc() which is a major pig. X*/ Xstruct idname * XnewIdName(name) X char *name; X{ X register struct idname *idn; X register char *allocp; X register int allocsiz; X static char *allocBuf = NULL; X static char *allocEnd = NULL; X#define ALLOCSIZ (8*1024) X X allocsiz = sizeof(struct idname) + strlen(name) + 1 + BitArraySize; X allocsiz += (sizeof(long) - 1); X allocsiz &= ~(sizeof(long) - 1); X X allocp = allocBuf; X allocBuf += allocsiz; X if (allocBuf > allocEnd) { X allocBuf = malloc(ALLOCSIZ); X allocEnd = &allocBuf[ALLOCSIZ]; X allocp = allocBuf; X allocBuf += allocsiz; X } X X idn = (struct idname *)allocp; X allocp += sizeof(struct idname); X idn->idn_bitv = allocp; X for (allocsiz = BitArraySize; allocsiz--; allocp++) X *allocp = '\0'; X idn->idn_name = strcpy(allocp, name); X X return idn; X} X Xint XpostIncr(ip) X int *ip; X{ X register int i; X int save; X X save = *ip; X i = save + 1; X if ((i & 0x00ff) == 0x00ff) X i++; X if ((i & 0xff00) == 0xff00) /* This isn't bloody likely */ X i += 0x100; X *ip = i; X X return save; X} X X/* X Move all non-NULL table entries to the front of the table. X return the number of non-NULL elements in the table. X*/ Xint XhashCompress(table, size) X char **table; X int size; X{ X register char **front; X register char **back; X X front = &table[-1]; X back = &table[size]; X X for (;;) { X while (*--back == NULL) X ; X if (back < front) X break; X while (*++front != NULL) X ; X if (back < front) X break; X *front = *back; X } X X return (back - table + 1); X} END_OF_FILE if test 18633 -ne `wc -c <'mkid.c'`; then echo shar: \"'mkid.c'\" unpacked with wrong size! fi # end of 'mkid.c' fi echo shar: End of archive 5 \(of 7\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 6 7 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 7 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 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.