/*   bref - AmigaBASIC cross referencer */ 

char   Version[6] = "V 1.01";    /* November 3, 1989

**	This program reads standard input OR one or more files
**	and prints the file(s) with line numbers added plus an
**      alphabetic list of word references by line number.
**
**	To run:
**   bref [-Q] [-Lnnn] [-Wnnn] [-Hccc] [-?] [-S] [-K] [-E] [file...]
**
**	Where 'file' must be ASCII, not binary -- AmigaBasic default.
**	To save a Basic program in ASCII, enter in Basic output window:
**		SAVE "prog.name",A
**
**	Options--first letter can be upper or lower case:

**	-Q    - don't print normal input file listing (Quiet).
**	-Lnnn - set page Length to n instead of the default 66.
**	-Wnnn - set page Width to n instead of the default 80.
** 		Maximum permitted page width is 132.
**	-Hccc - set page Heading to 'ccc' rather than file names.
**	-S    - Suppress form feeds; use for screen output
**	-K    - show BASIC Keywords in crossref table
**	-E    - print the input file 12 chars/in (Elite).  Default 10 cpi.
**      -?    - display this list.
**
**  Compiled with Manx Aztec C:
**	cc bref.c
**	ln bref.o -lc
**
**--------------------------------------------------------------------
**	Program bref is a modification of cref, the C cross referencer
**
**		History -- cref and bref
**	May 1981  Mike Edmonds wrote cref, a cross ref for C language
**	Sept 1987 Joel Swank ported cref to the Amiga
**	     1989 Dick Taylor modified cref to produce bref for BASIC
**
**	Dick Taylor			Found a bug?
**	99 Valley View Rd		Want a program change?
**	Glastonbury CT 06033 USA	Editorial remarks?
**	Tel (203) 633-0100		Your comments are welcome!!!
*/

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <exec/types.h>
#include <stat.h>

#define MAXWORD		15	/* maximum chars in word */
#define MAXPAGLINES	999	/* maximum posible lines in page */
#define MINPAGLINES	4	/* minimum posible lines in page */
#define MAXLINWIDTH	132	/* maximum posible chars in line */
#define MINLINWIDTH	MAXWORD+12  /* minimum posible chars in line */
#define MAXTABSIZE	132	/* maximum posible chars in tab */
#define MINTABSIZE	1	/* minimum posible chars in tab */

#define igncase(c)	(isupper(c)? tolower(c) : (c))

#undef FALSE
#undef TRUE
#define FALSE	0
#define TRUE	1

#ifdef	DEBUG
#define debug(a,b)	fprintf(stderr,(a),(b))
#else
#define debug(a,b)
#endif


/*  global structures*/

#define LINLIST	struct	_linlist
#define WRDLIST	struct	_wrdlist

struct	_linlist {		/* line number list node */
	long	 ln_no ;	  /* line number */
	LINLIST	*ln_next ;	  /* next element pointer (or NULL) */
} ;

struct	_wrdlist {		/* word list node */
	char	*wd_wd ;	  /* pointer to word */
	char	*wd_lwd ;	  /* pointer to word (lower case) */
	LINLIST	 wd_ln ;	  /* first element of line list */
	WRDLIST	*wd_low ;	  /* lower child */
	WRDLIST	*wd_hi ;	  /* higher child */
} ;


/*  options*/

char	*Progname ;
int	 Quiet = FALSE ;    /* don't print input file listing? (-q) */
int	 Maxpaglines = 66 ;	/* maximum lines in page (-l) */
int	 Maxlinwidth = 80 ;	/* default chars in print line (-w) */
int 	 Tabsize	= 8 ;	/* default length of tab */
int      FormFeed    = TRUE;    /* Suppress form feeds? (-S) */
int	 ShowKeyWords = FALSE;  /* Show BASIC keywords in table? (-K)*/
int	 Elite	     = FALSE;	/* Print source code in elite? (-E) */

/*  global variables*/

char	 Brefhdr[MAXLINWIDTH+1] ;/* report header */
char	*Filename ;		/* name of current input file */
char	 Date[30] ;		/* current or last file modify date */
long	 Hiline = 1L ;	/* current (and max.) input file line number */
int		 files=0 ;      /* count of source files */
WRDLIST	*Wdtree = NULL ;   /* ptr to root node of binary word list */
int	 SkipToEOL  = FALSE;   /* Skip to EOL for REM, DATA */
int	 SaveMLW;		/* Save Max line width */

/*  AmigaBASIC language reserved keywords */
/*  Entered in binary search order.  This minimizes recursion */
/*  during store of the keywords in the tree, thereby conserving on */
/*  Stack usage. If the keywords are entered in alphabetic order, it */
/*  causes a system crash (exceed Stack) during keyword store in the*/
/*  tree.  There are 202 keywords in this list. */
 
char	*Bkeywords[] = {

/* Level 1 -- #101 */  "MOUSE",
/* Level 2 -- #50 & #151 */  "EOF", "RESUME",
/* Level 3 -- #25 75 126 177 */ "COLOR", "INT", "OPTION", "SUB",
/* Level 4 */ 
"BREAK", "DECLARE", "FRE","LOF","OBJECT.PLANES","POKEW","SIN","UCASE$",
/* Level 5 */
"AREAFILL", "CINT", "CVD",  "DEFSTR", "EXIT", "IF", "LIBRARY", "MERGE",
"OBJECT.AY","OBJECT.VY", "PEEK", "PTAB","SADD","STATIC","TIME$","WEND",
/* Level 6 */
"AND",  "ATN",  "CHAIN", "CLNG", "COS",  "CVS", "DEFINT",  "EDIT",
"ERL",  "FILES", "GOSUB", "INPUT", "LEFT$", "LLIST", "LPRINT", "MKI$", 
"NEXT", "OBJECT.HIT", "OBJECT.START", "OCT$", "PAINT", "POINT","PRINT",
"READ", "RND", "SCREEN", "SPACE$", "STOP", "TAB", "TRANSLATE$",
"VARPTR", "WINDOW",
/* Level 7 */
"ABS", "APPEND", "AS", "BASE", "CALL", "CHDIR","CIRCLE","CLS","COMMON",
"CSNG", "CVI", "DATA", "DEF", "DEFLNG", "DELETE", "ELSEIF", "EQV",
"ERR", "EXP", "FIX", "FUNCTION", "GOTO", "IMP", "INPUT$",
"KILL", "LEN", "LINE", "LOC", "LOG", "LSET", "MID$", "MKS$", "NAME",
"NOT", "OBJECT.CLIP", "OBJECT.OFF", "OBJECT.PRIORITY", "OBJECT.STOP",
"OBJECT.X", "ON", "OR", "PALETTE", "PEEKL", "POKE", "POS", "PRINT#",
"PUT", "RESET", "RETURN", "RSET", "SAVE", "SGN", "SLEEP", "SPC",
"STEP", "STRIG", "SWAP", "TAN", "TIMER", "TRON", "USING",
"WAIT", "WHILE", "WRITE#",
/* Level 8 -- everything else */
"ALL", "AREA", "ASC", "BEEP", "CDBL", "CHR$", "CLEAR", "CLOSE",
"COLLISION", "CONT", "CSRLIN", "CVL", "DATE$",  "DEFDBL", "DEFSNG",
"DIM","ELSE", "END", "ERASE", "ERROR", "FIELD", "FOR", "GET", "HEX$",
"INKEY$", "INPUT$", "INSTR", "LBOUND", "LET", "LIST", "LOAD",
"LOCATE", "LPOS", "MENU", "MKD$", "MKL$", "MOD", "NEW","OBJECT.AX",
"OBJECT.CLOSE", "OBJECT.ON", "OBJECT.SHAPE",  "OBJECT.VX",
"OBJECT.Y", "OFF", "OPEN", "OUTPUT", "PATTERN", "PEEKW", "POKEL",
"PRESET", "PSET", "RANDOMIZE", "REM", "RESTORE", "RIGHT$", "RUN","SAY",
"SCROLL", "SHARED", "SOUND", "SQR", "STICK", "STR$", "STRING$",
"SYSTEM", "THEN","TO", "TROFF", "UBOUND", "VAL", "WAVE", "WIDTH",
"WRITE", "XOR",
	0
} ;


/*  main - Store Basic keywords in tree.
 *	   Get program options and format heading lines.
 *	   Get words/line numbers from input file(s), store in tree.
 *	   Retrieve and print words in alphabetic order.  For each
 *		word, print line numbers where word appears. */

main(argc, argv)
int	 argc ;
char	*argv[] ;
{
	char	wordfilebuf[BUFSIZ] ;
	register FILE	*filep ;
	char	*getword(), *word ;
	struct	 stat	stbuf ;
	long	 time() ;
	register cnt ;

	Progname = *argv ;		/* get options */
	getcmd(argc, argv) ;
		/* If the BASIC keywords are bypassed for the output */
		/*   table, then store keywords in the tree with */
		/*   line count = 0. */
	if (!ShowKeyWords)
	    for (cnt=0 ; Bkeywords[cnt] ; cnt++)
		storword(Bkeywords[cnt], 0L) ;

	listchr(-2);	/* clear output line */
					/* read and store files */
	for (cnt=1 ; cnt < argc ; cnt++)
		if (*argv[cnt] != '-')
		{	files++ ;
			Filename = argv[cnt] ;
			if ((filep = fopen(Filename, "r")) == NULL)
				fatal("can't open %s", Filename) ;
			stat(Filename, &stbuf) ;
			mkdate((long)stbuf.st_mtime) ;
			while (word = getword(filep))
			   { storword(word, Hiline);
			     if (strcmp(word,"REM") == 0 ||
				 strcmp(word,"DATA") == 0)
				     SkipToEOL = TRUE;
			   }
			fclose(filep) ;
		}

	if (!files)			/* no files - read stdin */
	{	if (*Brefhdr)
			Filename = Brefhdr ;
		else
			Filename = "stdin" ;
		mkdate(time( (long *)0)) ;
		while (word = getword(stdin))
			storword(word, Hiline) ;
	}

	/*  print cross reference report */
	bref(Wdtree) ;
	exit(0) ;
}


/*  getcmd - get arguments from command line & build page headings*/

getcmd(argc, argv)
register argc ;
register char	*argv[] ;
{
   register cnt ;

   debug("GETCMD(%d", argc) ;
   debug(", %s)\n", argv[0]) ;

   *Brefhdr = '\0' ;
					/* get command options */
   for (cnt=1; cnt < argc; cnt++)
   {   if (*argv[cnt] == '-')
       {   switch(igncase(argv[cnt][1]))
	   {  case 'q':
		Quiet = TRUE ;
		break ;

	      case 'l':
		Maxpaglines = atoi(&argv[cnt][2]) ;
		if (Maxpaglines < MINPAGLINES
		    || Maxpaglines > MAXPAGLINES)
			fatal("Bad -l value: %s", argv[cnt]) ;
		break ;

	      case 'w':
		Maxlinwidth = atoi(&argv[cnt][2]) ;
		if (Maxlinwidth < MINLINWIDTH
		    || Maxlinwidth > MAXLINWIDTH)
			fatal("Bad -w value: %s", argv[cnt]) ;
		break ;

	      case 's':
		FormFeed = FALSE;	/* Suppress form feeds */
		break;

	      case 'k':
		ShowKeyWords = TRUE;   /* Show BASIC keywords */
		break;

	      case 'e':
		Elite = TRUE;
		printf ("%s", "[2w");  /* Send elite code to printer*/
		break;

	      case 'h':
		strncpy(Brefhdr, &argv[cnt][2], MAXLINWIDTH) ;
		Brefhdr[MAXLINWIDTH] = '\0' ;
		break ;

	      case '?':				/* help option */
		usage();
	printf(" Options--1st letter either upper or lower case:\n\n");
	printf(" -Q    - don't print input file listing (Quiet)\n");
	printf(" -Lnnn - set page Length to n, not default 66.\n");
	printf(" -Wnnn - set page Width to n, not default 80.\n");
	printf(" -Hccc - set page Heading 'ccc', not file names\n");
	printf(" -S    - Suppress form feeds; use--screen output\n");
	printf(" -K    - show BASIC Keywords in CrossRef table\n");
	printf(" -E    - print input file 12 chars/inch (Elite)\n");
	printf(" -?    - display this list.\n");
		exit(0);

	      default:
		usage();
		exit(0);
	   }
       }
   }

    if (Elite)
       { SaveMLW = Maxlinwidth;		/* Save Max Line Width */
	 Maxlinwidth = (Maxlinwidth * 6) / 5; /* New Max Line Width*/
	}
				/* insert file names in hdr */
   if (!*Brefhdr)
       for (cnt=1; cnt < argc; cnt++)
	    if (*argv[cnt] != '-')
	       strjoin(Brefhdr, ' ', argv[cnt], MAXLINWIDTH) ;
}

usage()
{
printf("usage:\n\n");
printf("bref [-Q] [-Lnnn] [-Wnnn] [-Hheading] [-S] [-K] [-E] \
[-?] [file ...]\n\n");
}

/*  getword - read, print and return next word from file*/
 
char *
getword(filep)
FILE	*filep ;
{	static   int    first_read = TRUE;
	static	 char	wordbuf[MAXWORD+1] ;
	register char	*wp = wordbuf ;
	register maxw = sizeof(wordbuf) ;
	register chr ;
	int	 inword=0, lastchr=0 ;
	long	 slineno ;

#define	_listchr(chr)	if (!Quiet) listchr(chr)

#define	_rtrnwrd(wp) 		\
   {   ungetc(chr, filep) ;	\
       *(wp) = '\0' ;		\
       return wordbuf ;	        \
   }

   while ((chr = getc(filep)) != EOF)
   {
    if (first_read)
     {first_read = FALSE;
      if (chr == 0xf5)
       fatal ("File %s is binary, can't process--ASCII req.",Filename);
     }

     if (SkipToEOL) goto REM_DATA;  /* REM, DATA--skip to end of line*/
		/* normal char - add to current word */
       if ((chr <= 'z' && chr >= 'a') || 
	   (chr <= 'Z' && chr >= 'A') )
	   {
	   if (maxw-- <= 1)
		_rtrnwrd(wp) ;
	   *wp++ = chr ;
	   inword++ ;
	   _listchr(chr) ;
	   }

       else switch (chr)
       {
			/* These can't be 1st char in word -- */
		      /*   digit, period, suffixes for variable type */
	  case '0': case '1': case '2': case '3': case '4':
	  case '5': case '6': case '7': case '8': case '9':
	  case '.': case '%': case '&': case '!': case '#': case '$':
	      if (inword)
		{   if (maxw-- <= 1)
			_rtrnwrd(wp) ;
		   *wp++ = chr ;
	        }
	      _listchr(chr) ;
	      break ;
				/* newline - end current word */
	  case '\n':
	      if (inword)
		  _rtrnwrd(wp) ;
	      _listchr(chr) ;
	      Hiline++ ;
	      break ;
			/* Apostrophe (') comment - print & bypass */
                        /*     to end of the line. */
	  case '\'':
	      if (inword)
		  _rtrnwrd(wp) ;
REM_DATA:                 /* Come here for REM & DATA keywords */
	      _listchr(chr) ;
	      while ((chr = getc(filep)) != EOF)
	      {   _listchr(chr);
		  if (chr == '\n')
		  {   Hiline++;
		      SkipToEOL = FALSE;
		      break;
		  }
	      }
	      if (chr == EOF)  ungetc(chr, filep);
	      break ;
				/* words in quotes - print & bypass */
	  case '"':
	      if (inword)
		  _rtrnwrd(wp) ;
	      _listchr(chr) ;
	      while ((chr = getc(filep)) != EOF)
	      {   _listchr(chr);
	          if (chr == '"')
		      break;
		  if (chr == '\n')   /* Making assumption here that */
		  {   Hiline++;      /* end of line is implied end of*/
		      break;         /* quote string. Apparently this*/
		  }		     /* is what AmigaBasic does. */
	      }
	      if (chr == EOF) ungetc(chr,filep);
	      break ;

	  default:
	      if (inword)
		  _rtrnwrd(wp) ;
	      _listchr(chr) ;
	      break ;
       }		/* End of switch -- process char's */

       lastchr = chr ;
   }			/* End of while -- read char's */

   if (inword)
       _rtrnwrd(wp) ;
   _listchr(EOF) ;
   return NULL ;
}


/*  listchr - list the input files one character at a time*/

static	Listpage = 0 ;
static	Listpline = MAXPAGLINES ;

listchr(chr)
register chr ;
{
	static	char	 linebuf[MAXLINWIDTH*2], *lineptr=linebuf ;
	static	lastchr=0, linecnt=0 ;
	static  int	remain;
	static  int	LNWid = 4;   /* Line number width on listing */
			/* Changed from 6 in CREF to 4 in BREF */

	if (chr == -2)			/* clear line buffer */
	{	setmem(linebuf,Maxlinwidth,' ');
		return;
	}

	if (chr == EOF)			/* EOF - print final line */
	{	*lineptr = '\0' ;
		listline(linebuf) ;
		Listpage = 0 ;
		Listpline = MAXPAGLINES ;
		lineptr = linebuf ;
		linecnt = 0 ;
		return ;
	}

	if (lineptr == linebuf)	    /* new line - format line number */
	{	ltoc(linebuf, Hiline, LNWid) ;
		lineptr = linebuf + LNWid ;
		*lineptr++ = ' ' ;
		*lineptr++ = ' ' ;
		linecnt = LNWid + 2;
	}

#define	_lineoflo(ctr, newctr)		\
	if ((ctr) >= Maxlinwidth)	\
	{	*lineptr = '\0' ;	\
		listline(linebuf) ;	\
		lineptr = &linebuf[LNWid + 2] ;	\
		linecnt = (newctr) ;	\
	}

	switch (chr)
	{				/* newline - print last line */
	   case '\n':
		if (lastchr != '\f')
		{	*lineptr = '\0' ;
			listline(linebuf) ;
		}
		lineptr = linebuf ;
		linecnt = 0 ;
		break ;
	 			/* formfeed - print line and end page*/
	   case '\f':
		if (linecnt != LNWid + 2)
		{	*lineptr = '\0' ;
			listline(linebuf) ;
		}
		Listpline = MAXPAGLINES ;  /* This triggers form feed*/
					   /* on next entry--listline*/
		lineptr = linebuf ;
		linecnt = 0 ;
		break ;
				/* tab - skip to next tab stop */
	   case '\t':
		linecnt += Tabsize ;
		remain =  linecnt % Tabsize ;
		linecnt -= remain;
		_lineoflo(linecnt, LNWid + 2) ;
		lineptr += Tabsize ;
		lineptr -= remain;
		break ;
				/* backspace - print, but don't count*/
	   case '\b':
		*lineptr++ = chr ;
		break ;
					/* ctl-char - print as "^x" */
		     case 001: case 002: case 003:
	   case 004: case 005: case 006: case 007:
					 case 013:
	             case 015: case 016: case 017:
	   case 020: case 021: case 022: case 023:
	   case 024: case 025: case 026: case 027:
	   case 030: case 031: case 032: case 033:
	   case 034: case 035: case 036: case 037:
		_lineoflo(linecnt+=2, LNWid + 4) ;
		*lineptr++ = '^' ;
		*lineptr++ = ('A'-1) + chr ;
		break ;

	   default:
		if (isprint(chr))
		{	_lineoflo(++linecnt, LNWid + 3) ;
			*lineptr++ = chr ;
		}
		else		/* non-ascii chars - print as "\nnn" */
		{	_lineoflo(linecnt+=4, LNWid + 6) ;
			*lineptr++ = '\\' ;
			*lineptr++ = '0' + ((chr & 0300) >> 6) ;
			*lineptr++ = '0' + ((chr & 070) >> 3) ;
			*lineptr++ = '0' + (chr & 07) ;
		}
		break ;
	}
	lastchr = chr ;
}


		/* print a completed line from the input file */
listline(line)
register char	*line ;
{
	if (*line)
	{	if (++Listpline >= (Maxpaglines-8))
		{	if (FormFeed)
			    if (files >1 || Listpage) putchar('\f') ;
			printf("\nBREF %s %s %s  Page %d\n\n",
			   Version, Date, Filename, ++Listpage) ;
			Listpline = 0 ;
		}
		puts(line) ;
		listchr(-2);	/* clear line buffer */
	}
}


/*  storword - store word and line # in binary word tree or word file*/

storword(word, lineno)
register char	*word ;
long	 lineno ;
{
	char	 lword[MAXWORD+1] ;
	register char	*cp1, *cp2 ;
	WRDLIST	*addword() ;

				/* convert word to lower case */
	for (cp1=word, cp2=lword ; *cp2++ = igncase(*cp1) ; cp1++)
		;

					/* store words and lineno */
	Wdtree = addword(Wdtree, word, lword, lineno) ;
}


/*  addword - add word and line# to in-core word list*/
 
WRDLIST *
addword(wdp, word, lword, lineno)
register WRDLIST *wdp ;
char	*word, *lword ;
long	 lineno ;
{
	char	*malloc() ;
	int	 comp ;
					/* insert new word into list */
	if (wdp == NULL)
	{	register wordlen = strlen(word) + 1 ;

		wdp = (WRDLIST *)malloc((wordlen * 2)+sizeof(WRDLIST));
		if (wdp == NULL)
			goto nomemory ;

		wdp->wd_wd  = (char *)wdp + sizeof(WRDLIST) ;
		wdp->wd_lwd = wdp->wd_wd + wordlen ;
		strcpy(wdp->wd_wd,  word) ;
		strcpy(wdp->wd_lwd, lword) ;

		wdp->wd_hi = wdp->wd_low = NULL ;
		wdp->wd_ln.ln_no = lineno ;
		wdp->wd_ln.ln_next = NULL ;
	}

					/* word matched in list? */
	else if (((comp = strcmp(lword, wdp->wd_lwd)) == 0)
	      && ((comp = strcmp(word,  wdp->wd_wd))  == 0))
	{	register LINLIST *lnp, **lnpp ;

		if (wdp->wd_ln.ln_no)
		{			  /* add line# to linked list*/
			lnp = &wdp->wd_ln ;
			do
			{	if (lineno == lnp->ln_no)
					return wdp ;
				lnpp = &lnp->ln_next ;
			} while ((lnp = *lnpp) != NULL) ;

			*lnpp = (LINLIST *)malloc(sizeof(LINLIST)) ;
			if ((lnp = *lnpp) == NULL)
				goto nomemory ;
			lnp->ln_no = lineno ;
			lnp->ln_next = NULL ;
		}
	}

	else if (comp < 0)	/* search for word in children */
		wdp->wd_low = addword(wdp->wd_low, word, lword,lineno);
	else
		wdp->wd_hi = addword(wdp->wd_hi, word, lword, lineno) ;

	return wdp ;
				/* not enough memory - convert to -b */
nomemory:
	fatal("not enough memory for in-core word list") ;
}


/*  bref - print cross reference report from internal word list*/

#define MAXLNOS 2000		/* maximum line nos. for a word */
long	Linenos[MAXLNOS] ;	/* list of line numbers for a word */

bref(wdtree)
register WRDLIST *wdtree ;
{
	if (Elite)
	{printf ("%s","[1w");   /* Turn off elite for printer */
	 Maxlinwidth = SaveMLW;  /* Restore original Max line width*/
	}

	breftree(wdtree) ;
	if (FormFeed)
	    putchar ('\f'); /*Final form feed after print x-ref table*/
}


breftree(wdp)			/* recursively print word tree nodes */
register WRDLIST *wdp ;
{
	register LINLIST *lnp ;
	register nos ;

	if (wdp != NULL)
	{	breftree(wdp->wd_low) ;	/* print lower children */

		nos = 0 ;
		if (Linenos[0] = wdp->wd_ln.ln_no)
		{	lnp = &wdp->wd_ln ;
			while ((lnp = lnp->ln_next) != NULL)
				if (nos < (MAXLNOS-2))
					Linenos[++nos] = lnp->ln_no ;
			printword(wdp->wd_wd, nos) ;
		}

		breftree(wdp->wd_hi) ;	/* print higher children */
	}
}


/*  printword - print a word and all its line number references*/

printword(word, nos)
char	*word ;
register nos ;
{
	static	firstime=TRUE, linecnt, maxlnos, lnosize ;
	register cnt ;

	if (firstime)
	{	firstime = FALSE ;
		linecnt = Maxpaglines ;
		for (lnosize=1 ; Hiline ; lnosize++)
			Hiline /= 10L ;
		maxlnos = (Maxlinwidth - (MAXWORD+7)) / lnosize ;
	}

	if (linecnt >= (Maxpaglines - 5))
	{	printheads() ;
		linecnt = 5 ;
	}

	printf("%-15s%5d  ", word, ++nos) ;
	Linenos[nos] = 0 ;

	for (nos=0, cnt=0 ; Linenos[nos] ; nos++)
	{	if (++cnt > maxlnos)
		{	cnt = 1 ;
			if (linecnt++ >= (Maxpaglines - 4))
			{	printheads() ;
				linecnt = 5 ;
				printf("%-15s(cont) ", word);
			}
			else
				printf("\n%22s", " ") ;
		}
		printf("%*ld", lnosize, Linenos[nos]) ;
	}
	putchar('\n') ;

	linecnt++ ;
}


/*  printheads - print page headings*/

printheads()
{
	static	page=0 ;
	long	time() ;

	if (!page)
		mkdate(time( (long *)0)) ;

	if (FormFeed) putchar('\f') ;		/* Form feed */
	printf("\nBREF %s %s %.*s  Page %d\n\n",
	   Version, Date, (Maxlinwidth-36), Brefhdr, ++page) ;
	printf("word             refs    line numbers\n\n") ;
}


/*  ltoc - store ASCII equivalent of long value in given field*/

ltoc(fld, lval, len)
register char	*fld ;
register long	lval ;
register len ;
{
	fld += len ;
	while (len-->0)
		if (lval)
		{	*--fld = '0' + (lval%10L) ;
			lval /= 10L ;
		}
		else
			*--fld = ' ' ;
}


/*  mkdate - build time/date for use in heading lines*/
 
mkdate(atime)
long	atime ;
{
	long	mtime ;
	char	*cp, *ctime() ;

	debug("MKDATE(%ld)\n", atime) ;

	mtime = atime ;
	cp = ctime(&mtime) ;
	*(cp+24) = ' ' ;		/* clear newline */
	strcpy(cp+16, cp+19) ;		/* shift over seconds */
	strcpy(Date, cp+4) ;
}


/*  strjoin - join "str1" to "str2" (separated by "sep")
 *	Truncate if necessary to "max" chars.*/

strjoin(str1, sep, str2, max)
register char	*str1, *str2;
char	sep ;
register max ;
{
	if (*str2)
	{	if (*str1)
		{	while (*str1++)
				if (--max <= 0)
					goto oflo ;
			max--, str1-- ;
			*str1++ = sep ;
		}
		while (*str1++ = *str2++)
			if (--max <= 0)
				goto oflo ;
	}
	return ;

oflo:
	*--str1 = '\0' ;
	return ;
}

/*  fatal - print standard error msg and halt process*/

fatal(ptrn, data1, data2)
register char	*ptrn, *data1, *data2 ;
{
	fprintf(stderr, "%s: ", Progname) ;
	fprintf(stderr, ptrn, data1, data2) ;
	putc('\n', stderr) ;
	exit(1);
}
