xref: /original-bsd/contrib/bib/src/bibargs.c (revision 9271068d)
1 #ifndef lint
2 static char sccsid[] = "@(#)bibargs.c	2.13	05/27/93";
3 #endif not lint
4 /*
5         Authored by: Tim Budd, University of Arizona, 1983.
6                 version 7/4/83
7 
8         Various modifications suggested by:
9                 David Cherveny - Duke University Medical Center
10                 Phil Garrison - UC Berkeley
11                 M. J. Hawley - Yale University
12 
13 
14 	       version 8/23/1988
15 
16 	 Adapted to use TiB style macro calls (i.e. |macro|)
17 	       A. Dain Samples
18 
19 
20 
21 
22         read argument strings for bib and listrefs
23         do name formatting, printing lines, other actions common to both
24                                                         */
25 # include <stdio.h>
26 # include <ctype.h>
27 # include "bib.h"
28 # define LINELENGTH 1024
29 # define MAXDEFS     500             /* maximum number of defined words */
30 
31 /* global variables */
32    char bibfname[120];          /* file name currently being read            */
33    int  biblineno;              /* line number currently being referenced    */
34    int  abbrev       = false;   /* automatically abbreviate names            */
35    int  capsmcap     = false;   /* print names in caps small caps (CACM form)*/
36    int  TibOption    = false;   /* expect files in TiB format                */
37    int	TibxOption   = false;   /* to create files for bib2tib               */
38    int  numrev       = 0;       /* number of authors names to reverse        */
39    int  edabbrev     = false;   /* abbreviate editors names ?                */
40    int  edcapsmcap   = false;   /* print editors in cap small caps           */
41    int  ednumrev     = 0;       /* number of editors to reverse              */
42    int	max_klen     = 6;	/* max size of key			     */
43    int  sort         = false;   /* sort references ? (default no)            */
44    int  foot         = false;   /* footnoted references ? (default endnotes) */
45    int  doacite      = true;    /* place citations ?                         */
46    int	redefWarning = false;	/* warnings on attempted redefs ?	     */
47    int  hyphen       = false;   /* hypenate contiguous references            */
48    int  ordcite      = true;    /* order multiple citations                  */
49    char sortstr[80]  = "1";     /* sorting template                          */
50    char trailstr[80] = "";      /* trailing characters to output             */
51    char pfile[400];             /* private file name                         */
52    int  personal = false;       /* personal file given ? (default no)        */
53    char citetemplate[80] = "1"; /* citation template                         */
54    struct wordinfo words[MAXDEFS];     /* defined words */
55    struct wordinfo *wordhash[HASHSIZE];
56    struct wordinfo *wordsearch();
57    int  wordtop = 0;           /* number of defined words         */
58    char letterSeen[128];   /* keeps track of keyletters
59 		       * so we know whether to emit a .ds
60 		       * or a .as
61 		      /* */
62 
63 /* where output goes */
64    extern FILE *tfd;
65 /* reference file information */
66    extern struct refinfo refinfo[];
67    extern char reffile[];
68 #ifndef INCORE
69    extern FILE *rfd;
70 #endif not INCORE
71    extern int numrefs;
72    extern char *programName;
73 
74 char *usageArr[] = {
75 "-aa    abbreviate authors' first names",
76 "-arN   reverse first N authors' names; no N, do all",
77 "-ax    print authors' last names in Caps-Small",
78 "-cS    use template S for citations",
79 "-d	change the default directory",
80 "-ea    abbreviate editors' first names",
81 "-ex    print editors' last names in Caps-Small",
82 "-erN   reverse first N editors' names; no N, do all",
83 "-f     dump reference after citation for footnotes",
84 "-iFILE process FILE (e.g. a file of definitions)",
85 "-h     hyphenate sequences of citations (turns on -o)",
86 "-nS    turn off options; S is composed of the option letters 'afhosx'",
87 "-pFILE search these FILEs (comma separated list) instead of INDEX",
88 "-R	print warnings when duplicate definitions of names are ignored",
89 "-sS    sort references according to template S",
90 "-tTYPE use the style TYPE",
91 "-Tib   expect files to be in TiB format (which see)",
92 "-Tibx	write a file for converting bib to TiB-style |macros|",
93 "",
94 0
95 };
96 
97 void
98 usageErr(argv0, opt, str)
99     char *argv0;
100     char *opt;
101     char *str;
102 {
103     char  **p;
104     fprintf(stderr, "Illegal invocation of %s.  Acceptable options:\n",
105                                             argv0);
106     fprintf(stderr, "Argument: %s\n", opt);
107     fprintf(stderr, "Problem:  %s\n", str);
108     for (p = usageArr; *p != 0; p++) {
109         fprintf(stderr, "    %s\n", *p);
110         }
111 }
112 
113 /* bibwarning - print out a warning message */
114   /*VARARGS1*/
115   bibwarning(msg, a1, a2)
116   char *msg;
117 {
118   fprintf(stderr,"%s: `%s', line %d: ", programName, bibfname, biblineno);
119   fprintf(stderr, msg, a1, a2);
120   fprintf(stderr, "\n");
121 }
122 
123 
124 
125 /* doargs - read command argument line for both bib and listrefs
126             set switch values
127             call rdtext on file arguments, after dumping
128             default style file if no alternative style is given
129 */
130 
131    int doargs(argc, argv, defstyle)
132    int argc;
133    char **argv, defstyle[];
134 {  int numfiles, i, style;
135    char *p, *q, *walloc();
136    FILE *fd;
137 
138    numfiles = 0;
139    style = true;
140    TibxOption = false;
141    newbibdir(BMACLIB);
142 
143    programName = argv[0];
144    for (i = 1; i < argc; i++)
145       if (argv[i][0] == '-')
146          switch(argv[i][1]) {
147             case 'a':  for (p = &argv[i][2]; *p; p++)
148                           if (*p == 'a' || *p == 0)
149                              abbrev = true;
150                            else if (*p == 'x')
151                              capsmcap = true;
152                            else if (*p == 'r') {
153                              if (*(p+1))
154                                 numrev = atoi(p+1);
155                               else
156                                 numrev = 1000;
157                               break;
158                               }
159                        break;
160 
161             case 'c':  if (argv[i][2] == 0)
162                           error("citation string expected for 'c'");
163                        else
164                           for (p = citetemplate,q = &argv[i][2]; *p++ = *q++; );
165                        break;
166 
167 	    case 'd':  if (argv[i][2])
168 			    p = &argv[i][2];
169 		       else {  /* take next arg */
170 			    i++;
171 			    p = argv[i];
172 			    }
173 		       newbibdir(p);
174 		       break;
175 
176             case 'e':  for (p = &argv[i][2]; *p; p++)
177                           if (*p == 'a')
178                              edabbrev = true;
179                            else if (*p == 'x')
180                              edcapsmcap = true;
181                            else if (*p == 'r') {
182                              if (*(p+1))
183                                 ednumrev = atoi(p+1);
184                               else
185                                 ednumrev = 1000;
186                               break;
187                               }
188                        break;
189 
190             case 'f':  CASE_f:
191 		       foot = true;
192                        hyphen = false;
193                        break;
194 
195             case 'i':  CASE_i:
196 		       if (argv[i][2])
197                           p = &argv[i][2];
198                        else { /* take next arg */
199                           i++;
200                           p = argv[i];
201                           }
202                        incfile(p);
203                        break;
204 
205 	    case 'l':  if (argv[i][2]){
206                           max_klen  = atoi(&argv[i][2]);
207 			  if (max_klen > REFSIZE)
208 			      error("too long key size");
209 		       } else {
210 			  error("-l needs a numeric value");
211 		       }
212 		       break;
213 
214             case 'h':  hyphen = ordcite = true;
215                        break;
216 
217             case 'n':  for (p = &argv[i][2]; *p; p++)
218                           if (*p == 'a')
219                              abbrev = false;
220                           else if (*p == 'f')
221                              foot = false;
222                           else if (*p == 'h')
223                              hyphen = false;
224                           else if (*p == 'o')
225                              ordcite = false;
226                           else if (*p == 'R')
227                              redefWarning = false;
228                           else if (*p == 'r')
229                              numrev = 0;
230                           else if (*p == 's')
231                              sort = false;
232                           else if (*p == 'x')
233                              capsmcap = false;
234                           else if (*p == 'v')
235                              doacite = true;
236                        break;
237 
238             case 'o':  ordcite = true;
239                        break;
240 
241             case 'p':  if (argv[i][2])
242                           p = &argv[i][2];
243                        else {  /* take next arg */
244                           i++;
245                           p = argv[i];
246                           }
247                        strcpy(pfile, p);
248                        personal = true;
249                        break;
250 
251 	    case 'R':  redefWarning = true;
252 		       break;
253 
254             case 'r':  if (argv[i][2] == 0)  /* synonym -ar */
255                           numrev = 1000;
256                        else
257                           numrev = atoi(&argv[i][2]);
258                        break;
259 
260             case 's':  sort = true;
261                        if (argv[i][2])
262                           for (p = sortstr,q = &argv[i][2]; *p++ = *q++; );
263                        break;
264 
265             case 't':  style = false;
266 		       goto CASE_i;
267 
268 	   case 'T':   if (strcmp("Tib", &(argv[i][1])) == 0)
269 			   TibOption = true;
270 		       else if (strcmp("Tibx",&(argv[i][1])) == 0)
271 			   TibxOption = true;
272 		       else {
273 			    usageErr(argv[0], argv[i],
274 				    "Did you want the Tib option?");
275 			    error("'%s' invalid switch", argv[i]);
276 			    }
277 		       break;
278 
279             case 'v':  doacite = false;
280 		       goto CASE_f;
281 
282             case 'x':  capsmcap = true; /* synonym for -ax */
283                        break;
284 
285             case 0:    if (style) {  /* no style command given, take default */
286                           style = false;
287                           incfile( defstyle );
288                           }
289                        strcpy(bibfname,"<stdin>");
290                        rdtext(stdin);
291                        numfiles++;
292                        break;
293 
294             default:   usageErr(argv[0], argv[i], "Invalid switch");
295                        error("'%c' invalid switch", argv[i][1]);
296             }
297       else { /* file name */
298          numfiles++;
299          if (style) {
300             style = false;
301             incfile( defstyle );
302             }
303          fd = fopen(argv[i], "r");
304          if (fd == NULL) {
305             error("can't open file %s", argv[i]);
306             }
307          else {
308             strcpy(bibfname, argv[i]);
309             rdtext(fd);
310             fclose(fd);
311             }
312          }
313 
314    if (style) incfile( defstyle );
315    if (TibxOption) {
316      /*
317      Emits m4 macros that allow easy transformation of old bib-style
318      bibliographic databases into tib-style.  The primary problem
319      (although not the only one) is the change of |macro| calls.
320       */
321       reg struct wordinfo *wp;
322       FILE *outf;
323       outf = fopen("bib.m4.in","w");
324       for (i=0; i<HASHSIZE; i++) {
325 	 for (wp = wordhash[i]; wp != NULL; wp = wp->wi_hp) {
326 	    fprintf(outf,"define(%s,|%s__m4_|)dnl\n",wp->wi_word,wp->wi_word);
327 	    }
328 	 }
329       fclose(outf);
330       }
331    return(numfiles);
332 
333 }
334 
335 newbibdir(name)
336 	char *name;
337 {
338    strreplace(COMFILE, BMACLIB, name);
339    strreplace(DEFSTYLE, BMACLIB, name);
340    strcpy(BMACLIB, name);
341    wordrestuff("BMACLIB", BMACLIB);
342    fprintf(tfd, ".ds l] %s\n", BMACLIB);
343 }
344 
345 /* incfile - read in an included file  */
346 incfile(np)
347    char *np;
348 {  char name[120];
349    FILE *fd;
350    char *p, line[LINELENGTH], dline[LINELENGTH], word[80], *tfgets();
351    int  i, getwrd();
352 
353    strcpy(line, bibfname); /* temporary save in case of errors */
354    /* first try ./<yourfile> */
355       strcpy(bibfname, np);
356       fd = fopen(bibfname, "r");
357    /* try BMACLIB/<yourfile> */
358    if (fd == NULL && *np != '/') {
359       strcpy(name, BMACLIB); strcat(name, "/"); strcat(name, np);
360       strcpy(bibfname, name);
361       fd = fopen(bibfname, "r");
362       }
363    /* try BMACLIB/tibmacs/<yourfile> */
364    if (TibOption && fd == NULL && *np != '/') {
365       strcpy(name, BMACLIB); strcat(name, "/tibmacs/"); strcat(name, np);
366       strcpy(bibfname, name);
367       fd = fopen(bibfname, "r");
368       }
369    /* try BMACLIB/bibmacs/<yourfile> */
370    if (!TibOption && fd == NULL && *np != '/') {
371       strcpy(name, BMACLIB); strcat(name, "/bibmacs/"); strcat(name, np);
372       strcpy(bibfname, name);
373       fd = fopen(bibfname, "r");
374       }
375    /* try ./bib.<yourfile> */
376    if (fd == NULL && *np != '/') {
377       strcpy(name, "bib."); strcat(name, np);
378       strcpy(bibfname, name);
379       fd = fopen(bibfname, "r");
380       }
381    /* try BMACLIB/bib.<yourfile> */
382    if (fd == NULL && *np != '/') {
383       strcpy(name,BMACLIB); strcat(name, "/bib."); strcat(name, np);
384       strcpy(bibfname, name);
385       fd = fopen(bibfname, "r");
386       }
387    if (fd == NULL) {
388       /* unsave old name */
389       strcpy(bibfname, line);
390       bibwarning("%s: can't find", np);
391       exit(1);
392       }
393 
394    /* now go off and process file */
395    biblineno = 1;
396    while (tfgets(line, LINELENGTH, fd) != NULL) {
397       biblineno++;
398       switch(line[0]) {
399 
400          case '#': break;
401 
402          case 'A': for (p = &line[1]; *p; p++)
403                       if (*p == 'A' || *p == '\0')
404                          abbrev = true;
405                       else if (*p == 'X')
406                          capsmcap = true;
407                       else if (*p == 'R') {
408                          if (*(p+1))
409                             numrev = atoi(p+1);
410                          else
411                             numrev = 1000;
412                          break;
413                          }
414                    break;
415 
416          case 'C': for (p = &line[1]; *p == ' '; p++) ;
417                    strcpy(citetemplate, p);
418                    break;
419 
420          case 'D': if ((i = getwrd(line, 1, word)) == 0)
421                       error("word expected in definition");
422 		   if (wordsearch(word)) { /* already there-toss rest of def.*/
423 			if (redefWarning)
424 			   bibwarning("Attempted redefine of %s ignored.",word);
425 			while(line[strlen(line)-1] == '\\' ) {
426                             if (tfgets(line, LINELENGTH, fd) == NULL) break;
427 			}
428 			break;
429 		   }
430                    for (p = &line[i]; isspace(*p); p++) ;
431                    for (strcpy(dline, p); dline[strlen(dline)-1] == '\\'; ){
432                        dline[strlen(dline)-1] = '\n';
433                        if (tfgets(line, LINELENGTH, fd) == NULL) break;
434                        strcat(dline, line);
435                        }
436 		   wordstuff(word, dline);
437                    break;
438 
439          case 'E': for (p = &line[1]; *p; p++)
440                       if (*p == 'A')
441                          edabbrev = true;
442                       else if (*p == 'X')
443                          edcapsmcap = true;
444                       else if (*p == 'R') {
445                          if (*(p+1))
446                             ednumrev = atoi(p+1);
447                          else
448                             ednumrev = 1000;
449                          break;
450                          }
451                    break;
452 
453          case 'F': foot = true;
454                    hyphen = false;
455                    break;
456 
457          case 'I': for (p = &line[1]; *p == ' '; p++);
458                    expand(p);
459                    incfile(p);
460                    break;
461 
462          case 'H': hyphen = ordcite = true;
463                    break;
464 
465          case 'O': ordcite = true;
466                    break;
467 
468          case 'R': if (line[1] == 0)  /* this is now replaced by AR */
469                       numrev = 1000;
470                    else
471                       numrev = atoi(&line[1]);
472                    break;
473 
474          case 'S': sort = true;
475                    for (p = &line[1]; *p == ' '; p++) ;
476                    strcpy(sortstr, p);
477                    break;
478 
479          case 'T': for (p = &line[1]; *p == ' '; p++) ;
480                    strcpy(trailstr, p);
481                    break;
482 
483          case 'X': capsmcap = true;     /* this is now replace by AX */
484                    break;
485 
486          default:  fprintf(tfd,"%s\n",line);
487                    while (fgets(line, LINELENGTH, fd) != NULL)
488                       fputs(line, tfd);
489                    return;
490          }
491 
492    }
493    /* close up */
494    fclose(fd);
495 }
496 
497 /* error - report unrecoverable error message */
498   /*VARARGS1*/
499   error(str, a1, a2)
500   char *str;
501 {
502   bibwarning(str, a1, a2);
503   /*
504    *	clean up temp files and exit
505    */
506   cleanup(1);
507 }
508 
509 #ifndef INCORE
510 #ifdef READWRITE
511 /*
512 ** fixrfd( mode ) -- re-opens the rfd file to be read or write,
513 **      depending on the mode.  Uses a static int to save the current mode
514 **      and avoid unnecessary re-openings.
515 */
516 fixrfd( mode )
517 register int mode;
518 {
519 	static int cur_mode = WRITE;    /* rfd open for writing initially */
520 
521 	if (mode != cur_mode)
522 	{
523 		rfd = freopen(reffile, ((mode == READ)? "r" : "a"), rfd);
524 		cur_mode = mode;
525 		if (rfd == NULL)
526 		      error("Hell!  Couldn't re-open reference file %s",
527 			reffile);
528 	}
529 }
530 #endif
531 #endif not INCORE
532 
533 
534 /* tfgets - fgets which trims off newline */
535    char *tfgets(line, n, ptr)
536    char line[];
537    int  n;
538    FILE *ptr;
539 {  reg char *p;
540 
541    p = fgets(line, n, ptr);
542    if (p == NULL)
543       return(NULL);
544    else
545       for (p = line; *p; p++)
546          if (*p == '\n')
547             *p = 0;
548    return(line);
549 }
550 
551 /* getwrd - place next word from in[i] into out */
552 int getwrd(in, i, out)
553    reg char in[], out[];
554    reg int i;
555 {  int j;
556 
557    j = 0;
558    while (isspace(in[i]))
559       i++;
560    if (in[i] != '\0')
561       while (in[i]  != '\0' && !isspace(in[i]))
562          out[j++] = in[i++];
563    else
564       i = 0;    /* signals end of in[i..]   */
565    out[j] = 0;
566    return (i);
567 }
568 
569 /* walloc - allocate enough space for a word */
570 char *walloc(word)
571    char *word;
572 {  char *i, *malloc();
573    i = malloc(1 + strlen(word));
574    if (i == NULL)
575       error("out of storage");
576    strcpy(i, word);
577    return(i);
578 }
579 
580 /* isword - see if character is legit word char */
581 #define iswordc(c) (isalnum(c) || c == '&' || c == '_')
582 
583    expand(line)
584    char *line;
585 {  char line2[REFSIZE], word[REFSIZE];
586    reg	struct wordinfo *wp;
587    reg	char *p, *q, *w;
588 
589    q = line2;
590    if (TibOption) {
591       /* expand only macro names in |name| vertical bars; name must exist */
592       for (p = line; *p != '\0'; /* VOID */ ) {
593 	 if (*p == '|') {
594 	    p++;
595 	    w = word;
596 	    while (*p != '|' && *p != '\0' && !isspace(*p)) { *w++ = *p++; }
597 	    *w = '\0';
598 	    /* skip second '|', if present */
599 	    if (*p++ != '|') {
600 	       --p;
601 	       bibwarning("Unbalanced |macro| bars\n");
602 	       }
603 	    else if ((wp = wordsearch(word)) != 0) {
604 	       strcpy(word, wp->wi_def);
605 	       if (wp->wi_expanding) {
606 		  bibwarning("Recursive definition for |%s|\n", word);
607 		  }
608 	       else {
609 		  wp->wi_expanding = true;
610 		  expand(word);
611 		  wp->wi_expanding = false;
612 		  }
613 	       }
614 	    else {
615 	       char errword[REFSIZE];
616 	       bibwarning("word |%s| not defined\n", word);
617 	       strcpy(errword, "?");
618 	       strcat(errword, word);
619 	       strcat(errword, "?");
620 	       wordstuff(word, errword);
621 	       strcpy(word, errword);
622 	       }
623 	    for (w = word; *w != '\0'; *q++ = *w++);
624 	    }
625 	 else {
626 	    *q++ = *p++;
627 	    }
628 	 }
629       }
630    else {
631       for (p = line; *p != '\0'; /*VOID*/){
632 	 if (isalnum(*p)) {
633 	    for (w = word; *p && iswordc(*p); ) *w++ = *p++;
634 	    *w = 0;
635 	    if (wp = wordsearch(word)){
636 	       if (wp->wi_expanding)
637 		  bibwarning("Recursive definition for %s\n", word);
638 	       else {
639 		  strcpy(word, wp->wi_def);
640 		  wp->wi_expanding = true;
641 		  expand(word);
642 		  wp->wi_expanding = false;
643 		  }
644 	       }
645 	    for (w = word; *w != '\0'; *q++ = *w++);
646 	    }
647 	 else if (*p == '\\' && *(p+1) != '\0') {
648 	    *q++ = *p++;
649 	    *q++ = *p++;
650 	    }
651 	 else {
652 	    *q++ = *p++;
653 	    }
654 	 }
655       }
656    *q = 0;
657    strcpy(line, line2);
658 }
659 
660 /* wordstuff- save a word and its definition, building a hash table */
661    wordstuff(word, def)
662    char *word, *def;
663 {
664    int i;
665    if (wordtop >= MAXDEFS)
666 	error("too many definitions, max of %d", MAXDEFS);
667    words[wordtop].wi_length = strlen(word);
668    words[wordtop].wi_word = word ? walloc(word) : NULL;
669    words[wordtop].wi_def = def ? walloc(def) : NULL;
670    i = strhash(word);
671    words[wordtop].wi_expanding = false;
672    words[wordtop].wi_hp = wordhash[i];
673    wordhash[i] = &words[wordtop];
674    wordtop++;
675 }
676    struct wordinfo *wordsearch(word)
677    char *word;
678 {
679    reg int lg;
680    reg struct wordinfo *wp;
681    lg = strlen(word);
682    for (wp = wordhash[strhash(word)]; wp; wp = wp->wi_hp){
683 	if (wp->wi_length == lg && (strcmp(wp->wi_word, word) == 0)){
684 		return(wp);
685 	}
686    }
687    return(0);
688 }
689 /* wordrestuff - save a word and its definition, but replace any existing
690  * definition; this could be more efficient, but it is only used to
691  * redefine BMACLIB at the present.  -ads 8/88
692  */
693    wordrestuff(word, def)
694    char *word, *def;
695 {
696    struct wordinfo *wp = wordsearch(word);
697    if (wp == NULL) wordstuff(word, def);
698    else {
699       if (wp->wi_word != NULL) free(wp->wi_word);
700       if (wp->wi_def != NULL) free(wp->wi_def);
701       wp->wi_length = strlen(word);
702       wp->wi_word = word ? walloc(word) : NULL;
703       wp->wi_def = def ? walloc(def) : NULL;
704       wp->wi_expanding = false;
705       }
706 }
707 
708    int strhash(str)
709    reg char *str;
710 {
711    reg int value = 0;
712    for (value = 0; *str; value <<= 2, value += *str++)/*VOID*/;
713    value %= HASHSIZE;
714    if (value < 0)
715 	value += HASHSIZE;
716    return(value);
717 }
718 
719 /* rdref - read text for an already cited reference */
720    rdref(p, ref)
721    struct refinfo *p;
722    char ref[REFSIZE];
723 {
724    ref[0] = 0;
725 #ifndef INCORE
726 #ifdef READWRITE
727    fixrfd( READ );                      /* fix access mode of rfd, if nec. */
728 #endif
729    fseek(rfd, p->ri_pos, 0);
730    fread(ref, p->ri_length, 1, rfd);
731 #else INCORE
732    strcpy(ref, p->ri_ref);
733 #endif INCORE
734 }
735 
736 /* wrref - write text for a new reference */
737    wrref(p, ref)
738    struct refinfo *p;
739    char ref[REFSIZE];
740 {
741 #ifndef INCORE
742 #ifdef READWRITE
743     fixrfd( WRITE );                 /* fix access mode of rfd, if nec. */
744 #else
745     fseek(rfd, p->ri_pos, 0);        /* go to end of rfd */
746 #endif
747     fwrite(ref, p->ri_length, 1, rfd);
748 #else INCORE
749    p->ri_ref = walloc(ref);
750 #endif INCORE
751 }
752 
753 /* breakname - break a name into first and last name */
754    breakname(line, first, last)
755    char line[], first[], last[];
756 {  reg char *t, *f, *q, *r, *p;
757 
758    for (t = line; *t != '\n'; t++);
759    for (t--; isspace(*t); t--);
760 
761    /* now strip off last name */
762    for (q = t; isspace(*q) == 0 || ((*q == ' ') & (*(q-1) == '\\')); q--)
763       if (q == line)
764          break;
765    f = q;
766    if (q != line) {
767       q++;
768       for (; isspace(*f); f--);
769       f++;
770       }
771 
772    /* first name is start to f, last name is q to t */
773 
774    for (r = first, p = line; p != f; )
775       *r++ = *p++;
776    *r = 0;
777    for (r = last, p = q, t++; q != t; )
778       *r++ = *q++;
779    *r = 0;
780 
781 }
782 
783 /* match - see if string1 is a substring of string2 (case independent)*/
784    int match(str1, str2)
785    reg char str1[], str2[];
786 {  reg int  j, i;
787    char a, b;
788 
789    for (i = 0; str2[i]; i++) {
790       for (j = 0; str1[j]; j++) {
791          if (isupper(a = str2[i+j]))
792             a = (a - 'A') + 'a';
793          if (isupper(b = str1[j]))
794             b = (b - 'A') + 'a';
795          if (a != b)
796             break;
797          }
798       if (str1[j] == 0)
799          return(true);
800       }
801    return(false);
802 }
803 
804 /* scopy - append a copy of one string to another */
805    char *scopy(p, q)
806    reg char *p, *q;
807 {
808    while (*p++ = *q++)
809       ;
810    return(--p);
811 }
812 
813 /* rcomp - reference comparison routine for qsort utility */
814    int rcomp(ap, bp)
815    struct refinfo *ap, *bp;
816 {  char ref1[REFSIZE], ref2[REFSIZE], field1[MAXFIELD], field2[MAXFIELD];
817    reg	char *p, *q;
818    char *getfield();
819    int  neg, res;
820    int  fields_found;
821 
822    rdref(ap, ref1);
823    rdref(bp, ref2);
824    for (p = sortstr; *p; p = q) {
825       if (*p == '-') {
826          p++;
827          neg = true;
828          }
829       else
830          neg = false;
831       q = getfield(p, field1, ref1);
832       fields_found = true;
833       if (q == 0) {
834 	 res = 1;
835 	 fields_found = false;
836       } else if (strcmp (field1, "") == 0) {	/* field not found */
837          if (*p == 'A') {
838             getfield("F", field1, ref1);
839 	    if (strcmp (field1, "") == 0) {
840                getfield("I", field1, ref1);
841 	       if (strcmp (field1, "") == 0) {
842 	          res = 1;
843 		  fields_found = false;
844 	       }
845 	    }
846 	 } else {
847 	    res = 1;
848 	    fields_found = false;
849 	 }
850       }
851 
852       if (getfield(p, field2, ref2) == 0) {
853 	 res = -1;
854 	 fields_found = false;
855       } else if (strcmp (field2, "") == 0) {	/* field not found */
856          if (*p == 'A') {
857             getfield("F", field2, ref2);
858 	    if (strcmp (field2, "") == 0) {
859                getfield("I", field2, ref2);
860 	       if (strcmp (field2, "") == 0) {
861 	          res = -1;
862 		  fields_found = false;
863 	       }
864 	    }
865 	 } else {
866 	    res = -1;
867 	    fields_found = false;
868 	 }
869       }
870       if (fields_found) {
871          if (*p == 'A') {
872             if (isupper(field1[0]))
873                field1[0] -= 'A' - 'a';
874             if (isupper(field2[0]))
875                field2[0] -= 'A' - 'a';
876             }
877          res = strcmp(field1, field2);
878          }
879       if (neg)
880          res = - res;
881       if (res != 0)
882          break;
883       }
884    if (res == 0)
885       if (ap < bp)
886          res = -1;
887       else
888          res = 1;
889    return(res);
890 }
891 
892 /* makecites - make standard citation strings, using citetemplate currently in effect */
893    makecites()
894 {  char ref[REFSIZE], tempcite[100], *malloc();
895    reg int  i;
896 
897    for (i = 0; i < numrefs; i++) {
898       rdref(&refinfo[i], ref);
899       bldcite(tempcite, i, ref);
900       refinfo[i].ri_cite = malloc(2 + strlen(tempcite));
901       if (refinfo[i].ri_cite == NULL)
902          error("out of storage");
903       strcpy(refinfo[i].ri_cite, tempcite);
904       }
905 }
906 
907 /* bldcite - build a single citation string */
908    bldcite(cp, i, ref)
909    char *cp, ref[];
910    int  i;
911 {  reg char *p, *q, *fp;
912    char c;
913    char field[REFSIZE];
914    char *getfield(), *aabet(), *aabetlast(),
915         *fullaabet(), *multfull();
916 
917    getfield("F", field, ref);
918    if (field[0] != 0)
919       for (p = field; *p; p++)
920          *cp++ = *p;
921    else {
922       p = citetemplate;
923       field[0] = 0;
924       while (c = *p++) {
925          if (isalpha(c)) {                      /* field name   */
926             q = getfield(p-1, field, ref);
927             if (q != 0) {
928                p = q;
929                for (fp = field; *fp; )
930                   *cp++ = *fp++;
931                }
932             }
933          else if (c == '1') {                   /* numeric  order */
934             sprintf(field,"%d",1 + i);
935             for (fp = field; *fp; )
936                *cp++ = *fp++;
937             }
938          else if (c == '2')                     /* alternate alphabetic */
939             cp = aabet(cp, ref);
940          else if (c == '3')                     /* Astrophysical Journal style*/
941             cp = multfull(cp, ref, 3);
942          else if (c == '4')                     /* Computing Surveys style*/
943             cp = multfull(cp, ref, 2);
944 	 else if (c == '8')			/* Full alphabetic */
945 	    cp = fullaabet(cp, ref);
946          else if (c == '9')                     /* Last name of Senior Author*/
947             cp = aabetlast(cp, ref);
948 	 else if (c == '0') {			/* print nothing */
949             for (fp = field; *fp; )
950                *cp++ = *fp++;
951             }
952          else if (c == '{') {                   /* other information   */
953             while (*p != '}')
954                if (*p == 0)
955                   error("unexpected end of citation template");
956                else
957                   *cp++ = *p++;
958             p++;
959             }
960          else if (c == '<') {
961             while (*p != '>') {
962                if (*p == 0)
963                   error("unexpected end of citation template");
964                else
965                   *cp++ = *p++;
966                }
967             p++;
968             }
969          else if (c != '@')
970             *cp++ = c;
971          }
972       }
973    *cp++ = 0;
974 }
975 
976 /* alternate alphabetic citation style -
977         if 1 author - first three letters of last name
978         if 2 authors - first two letters of first, followed by first letter of
979                                 seond
980         if 3 or more authors - first letter of first three authors */
981    char *aabet(cp, ref)
982    char *cp, ref[];
983 {  char field[REFSIZE], temp[100];
984    reg char *np, *fp;
985    int j, getname();
986 
987    if (getname(1, field, temp, ref)) {
988       np = cp;
989       fp = field;
990       for (j = 1; j <= 3; j++)
991          if (*fp != 0)
992             *cp++ = *fp++;
993       if (getname(2, field, temp, ref))
994          np[2] = field[0];
995       if (getname(3, field, temp, ref)) {
996          np[1] = np[2];
997          np[2] = field[0];
998          }
999       }
1000 return(cp);
1001 }
1002 
1003 /* alternate alphabetic citation style -
1004 	first two characters of last names of all authors
1005 	up to max_klen characters.
1006 */
1007    char *fullaabet(cp, ref)
1008    char *cp, ref[];
1009 {  char field[REFSIZE], temp[100];
1010    reg char	*fp;
1011    char	*lastcp;
1012    int getname();
1013    int i;
1014 
1015    lastcp = cp + max_klen;
1016    for (i= 1; getname(i, field, temp, ref); i++) {
1017       for (fp = field; *fp && (fp < &(field[3])); )
1018 	 if (cp > lastcp)
1019 	     break;
1020          else if (isalpha(*fp))
1021 	     *cp++ = *fp++;
1022 	 else
1023 	     fp++;
1024    }
1025    return(cp);
1026 }
1027 
1028 
1029 /* alternate alphabetic citation style -
1030 	entire last name of senior author
1031 */
1032    char *aabetlast(cp, ref)
1033    char *cp, ref[];
1034 {  char field[REFSIZE], temp[100];
1035    reg char	*fp;
1036    int getname();
1037 
1038    if (getname(1, field, temp, ref)) {
1039       for (fp = field; *fp; )
1040          *cp++ = *fp++;
1041    }
1042    return(cp);
1043 }
1044 
1045 /*
1046   Multiple full authors last names (1, 2 or 3 full names).
1047 
1048   If maxauthors<3
1049         if 1 author - last name date
1050         if 2 authors - last name and last name date
1051         if 3 or more authors - last name et al. date
1052   If maxauthors>=3
1053         if 1 author - last name date
1054         if 2 authors - last name and last name date
1055         if 3 authors - last name, last name and last name date
1056         if 4 or more authors - last name et al. date */
1057    char *multfull(cp, ref, maxauthors)
1058    char *cp, ref[];
1059    int maxauthors;
1060 {  char name1[100], name2[100], name3[100], temp[100];
1061    reg char *fp;
1062    int getname();
1063 
1064    if (getname(1, name1, temp, ref)) {
1065       for (fp = name1; *fp; )
1066          *cp++ = *fp++;
1067       if (((maxauthors >= 3) && (getname(4, name3, temp, ref)))
1068 	  || ((maxauthors < 3) && (getname(3, name3, temp, ref)))) {
1069          for (fp = " \\*(e]"; *fp; )
1070             *cp++ = *fp++;
1071          }
1072       else if (getname(2, name2, temp, ref)) {
1073          if (getname(3, name3, temp, ref)) {
1074             for (fp = "\\*(c]"; *fp; )
1075                *cp++ = *fp++;
1076             for (fp = name2; *fp; )
1077                *cp++ = *fp++;
1078             for (fp = "\\*(m]"; *fp; )
1079                *cp++ = *fp++;
1080             for (fp = name3; *fp; )
1081                *cp++ = *fp++;
1082             }
1083          else {
1084             for (fp = "\\*(n]"; *fp; )
1085                *cp++ = *fp++;
1086             for (fp = name2; *fp; )
1087                *cp++ = *fp++;
1088             }
1089          }
1090     }
1091 return(cp);
1092 }
1093 
1094 /* getfield - get a single field from reference */
1095    char *getfield(ptr, field, ref)
1096    char *ptr, field[], ref[];
1097 {  reg	char *p, *q;
1098    char	temp[100];
1099    int  n, len, i, getname();
1100 
1101    field[0] = 0;
1102    if (*ptr == 'A')
1103       getname(1, field, temp, ref);
1104    else
1105       for (p = ref; *p != '\0'; p++)
1106          if (*p == '%' && *(p+1) == *ptr) {
1107             for (p = p + 2; isspace(*p); p++)
1108                ;
1109             for (q = field; (*p != '\n') && (*p != '\0'); )
1110                *q++ = *p++;
1111             *q = 0;
1112             break;
1113             }
1114    n = 0;
1115    len = strlen(field);
1116    if (*++ptr == '-') {
1117       for (ptr++; isdigit(*ptr); ptr++)
1118          n = 10 * n + (*ptr - '0');
1119       if (n > len)
1120          n = 0;
1121       else
1122          n = len - n;
1123       for (i = 0; field[i] = field[i+n]; i++)
1124          ;
1125       }
1126    else if (isdigit(*ptr)) {
1127       for (; isdigit(*ptr); ptr++)
1128          n = 10 * n + (*ptr - '0');
1129       if (n > len)
1130          n = len;
1131       field[n] = 0;
1132       }
1133 
1134    if (*ptr == 'u') {
1135       ptr++;
1136       for (p = field; *p; p++)
1137          if (islower(*p))
1138             *p = (*p - 'a') + 'A';
1139       }
1140    else if (*ptr == 'l') {
1141       ptr++;
1142       for (p = field; *p; p++)
1143          if (isupper(*p))
1144             *p = (*p - 'A') + 'a';
1145       }
1146    return(ptr);
1147 }
1148 
1149 /* getname - get the nth name field from reference, breaking into
1150              first and last names */
1151    int getname(n, last, first, ref)
1152    int  n;
1153    char last[], first[], ref[];
1154 {  reg char *p;
1155    int  m;
1156 
1157    m = n;
1158    for (p = ref; *p; p++)
1159       if (*p == '%' & *(p+1) == 'A') {
1160          n--;
1161          if (n == 0) {
1162             for (p = p + 2; *p == ' '; p++) ;
1163             breakname(p, first, last) ;
1164             return(true);
1165             }
1166          }
1167 
1168    if (n == m)          /* no authors, try editors */
1169       for (p = ref; *p; p++)
1170          if (*p == '%' & *(p+1) == 'E') {
1171             n--;
1172             if (n == 0) {
1173                for (p = p + 2; *p == ' '; p++) ;
1174                breakname(p, first, last) ;
1175                return(true);
1176                }
1177             }
1178 
1179    if (n == m) {        /* no editors, either, try institution */
1180       first[0] = last[0] = '\0';
1181       getfield("I", last, ref);
1182       if (last[0] != '\0')
1183          return(true);
1184       }
1185 
1186    return(false);
1187 }
1188 
1189 /* disambiguate - compare adjacent citation strings, and if equal, add
1190                   single character disambiguators */
1191    disambiguate()
1192 {  reg int i, j;
1193 	char adstr;
1194 
1195    for (i = 0; i < numrefs-1; i = j) {
1196       j = i + 1;
1197       if (strcmp(refinfo[i].ri_cite, refinfo[j].ri_cite)==0) {
1198          adstr = 'a';
1199          for(j = i+1;
1200 	     j<numrefs && strcmp(refinfo[i].ri_cite,refinfo[j].ri_cite) == 0;
1201 	     j++) {
1202             adstr = 'a' + (j-i);
1203 	    refinfo[j].ri_disambig[0] = adstr;
1204             }
1205 	 refinfo[i].ri_disambig[0] = 'a';
1206          }
1207      }
1208   for (i = 0; i < numrefs; i++){
1209 	strcat(refinfo[i].ri_cite, refinfo[i].ri_disambig);
1210   }
1211 }
1212 
1213 
1214 /* bldname - build a name field
1215              doing abbreviations, reversals, and caps/small caps
1216 */
1217    bldname(first, last, name, reverse)
1218    char *first, *last, name[];
1219    int reverse;
1220 {
1221    char newfirst[120], newlast[120];
1222    reg char *p, *q, *f, *l;
1223    char *scopy();
1224    int  flag;
1225 
1226    if (abbrev) {
1227       p = first;
1228       q = newfirst;
1229       flag = false;
1230       while (*p) {
1231          while (*p == ' ')
1232             p++;
1233          if (*p == 0)
1234             break;
1235          if (isupper(*p)) {
1236             if (flag)           /* between initial gap */
1237                q = scopy(q, "\\*(a]");
1238             flag = true;
1239             *q++ = *p;
1240             q = scopy(q, "\\*(p]");
1241             }
1242          if (*++p == '.')
1243             p++;
1244          else while (*p != 0 && ! isspace(*p))
1245             p++;
1246          }
1247       *q = 0;
1248       f = newfirst;
1249       }
1250    else
1251       f = first;
1252 
1253    if (capsmcap) {
1254       p = last;
1255       q = newlast;
1256       flag = 0;  /* 1 - printing cap, 2 - printing small */
1257       while (*p)
1258          if (islower(*p)) {
1259             if (flag != 2)
1260                q = scopy(q, "\\s-2");
1261             flag = 2;
1262             *q++ = (*p++ - 'a') + 'A';
1263             }
1264          else {
1265             if (flag == 2)
1266                q = scopy(q,"\\s+2");
1267             flag = 1;
1268             *q++ = *p++;
1269             }
1270       if (flag == 2)
1271          q = scopy(q, "\\s+2");
1272       *q = 0;
1273       l = newlast;
1274       }
1275    else
1276       l = last;
1277 
1278    if (f[0] == 0)
1279       sprintf(name, "%s\n", l);
1280    else if (reverse)
1281       sprintf(name, "%s\\*(b]%s\n", l, f);
1282    else
1283       sprintf(name, "%s %s\n", f, l);
1284 }
1285 
1286 /* prtauth - print author or editor field */
1287    prtauth(c, line, num, max, ofd, abbrev, capsmcap, numrev)
1288    char c, *line;
1289    int  num, max, abbrev, capsmcap, numrev;
1290    FILE *ofd;
1291 {  char first[LINELENGTH], last[LINELENGTH];
1292 
1293    if (num <= numrev || abbrev || capsmcap) {
1294       breakname(line, first, last);
1295       bldname(first, last, line, num <= numrev);
1296       }
1297    if (num == 1)
1298       fprintf(ofd,".ds [%c %s", c, line);
1299    else if (num < max)
1300       fprintf(ofd,".as [%c \\*(c]%s", c, line);
1301    else if (max == 2)
1302       fprintf(ofd,".as [%c \\*(n]%s", c, line);
1303    else
1304       fprintf(ofd,".as [%c \\*(m]%s", c, line);
1305    if (num == max && index(trailstr, c))
1306       fprintf(ofd,".ds ]%c %c\n", c, line[strlen(line)-2]);
1307 }
1308 
1309 /* doline - actually print out a line of reference information */
1310    doline(c, line, numauths, maxauths, numeds, maxeds, ofd)
1311    char c, *line;
1312    int numauths, maxauths, numeds, maxeds;
1313    FILE *ofd;
1314 {
1315    int appending;
1316 
1317    switch(c) {
1318       case 'A':
1319           prtauth(c, line, numauths, maxauths, ofd, abbrev, capsmcap, numrev);
1320           break;
1321 
1322       case 'E':
1323           prtauth(c, line, numeds, maxeds, ofd, edabbrev, edcapsmcap, ednumrev);
1324           if (numeds == maxeds)
1325              fprintf(ofd,".nr [E %d\n", maxeds);
1326           break;
1327 
1328       case 'P':
1329           if (index(line, '-'))
1330              fprintf(ofd,".nr [P 1\n");
1331           else
1332              fprintf(ofd,".nr [P 0\n");
1333           fprintf(ofd,".ds [P %s",line);
1334           if (index(trailstr, 'P'))
1335              fprintf(ofd,".ds ]P %c\n",line[strlen(line)-2]);
1336           break;
1337 
1338       case 'F': break;
1339 
1340       /* these now accumulate their entries */
1341       /* defined by official bib documentation */
1342       case 'K': case 'O': case 'W':
1343       /* not defined by official bib documentation */
1344       case 'H': case 'L': case 'M': case 'Q': case 'U': case 'X': case 'Y':
1345       case 'Z':
1346 	 appending = letterSeen[c];
1347 	 letterSeen[c] = true;
1348 	 if (appending)
1349 	    fprintf(ofd, ".as [%c , %s", c, line);
1350 	 else
1351 	    fprintf(ofd, ".ds [%c %s", c, line);
1352 	 if (index(trailstr, c))
1353 	    fprintf(ofd, ".ds ]%c %c\n", c, line[strlen(line) - 2]);
1354 	 break;
1355 
1356       default:
1357 	  if (!isupper(c)) break; /* ignore what you don't understand */
1358           fprintf(ofd,".ds [%c %s", c, line);
1359           if (index(trailstr, c))
1360              fprintf(ofd,".ds ]%c %c\n", c, line[strlen(line)-2]);
1361           }
1362 }
1363 
1364 /* dumpref - dump reference number i */
1365    dumpref(i, ofd)
1366    int i;
1367    FILE *ofd;
1368 {  char ref[REFSIZE], line[REFSIZE];
1369    reg char *p, *q;
1370    char *from;
1371    int numauths, maxauths, numeds, maxeds;
1372    int j;
1373 
1374    if ( i < 0 ) ref[0] = 0; /* ref not found */
1375    else {
1376 	   rdref(&refinfo[i], ref);
1377 	   maxauths = maxeds = 0;
1378 	   numauths = numeds = 0;
1379 	   for (j=0; j < 128; j++) letterSeen[j] = 0;
1380 	   for (p = ref; *p; p++)
1381 	      if (*p == '%')
1382 	         if (*(p+1) == 'A') maxauths++;
1383 	         else if (*(p+1) == 'E') maxeds++;
1384 	   fprintf(ofd, ".[-\n");
1385 	   fprintf(ofd, ".ds [F %s\n", refinfo[i].ri_cite);
1386 #ifndef INCORE
1387 	   fseek(rfd, (long)refinfo[i].ri_pos, 0);
1388 	   while (fgets(line, REFSIZE, rfd) != NULL) {
1389 #else INCORE
1390 	   for (q = line, from = refinfo[i].ri_ref; *from; /*VOID*/) { /*} */
1391 		if (*from == '\n'){
1392 			*q++ = '\n';
1393 			*q = 0;
1394 			q = line;
1395 			from++;
1396 		} else {
1397 			*q++ = *from++;
1398 			continue;
1399 		}
1400 #endif INCORE
1401 		switch(line[0]){
1402 		case 0:
1403 			goto doneref;
1404 		case '.':
1405 			fprintf(ofd, "%s", line);
1406 			break;
1407 		case '%':
1408 			switch(line[1]){
1409 			case 'A':	numauths++;	break;
1410 			case 'E':	numeds++;	break;
1411 			}
1412 			for (p = &line[2]; *p == ' '; p++) /*VOID*/;
1413 			doline(line[1], p, numauths, maxauths, numeds, maxeds, ofd);
1414 		}
1415 	   }
1416 	   doneref:;
1417 	   fprintf(ofd,".][\n");
1418    }
1419 }
1420