/*
	CPR.C            " Prints 'C' Files with Table of Contents "
					 "             AND LOTS MORE               "

	Turbo C Version 1.0 or 1.5

Originally Written by :
		Paul Breslin
		Human Computing Resources Corp.
		10 South Mary Street
		Toronto, Ontario
		Canada, M4Y 1P9

Modified by :
		Lance E. Shepard		Rick Wise				Blake McBride
		CCI & RIT				CALCULON Corp.			3900 SW 52nd Ave. #803
		Rochester, NY			Rockville, MD 20850		Pembroke Park, FL 33023

		John Stidd				Dale Loftis
		1570 Neston Way			3020 Yorba Linda #F-16
		Los Altos, CA 94022		Fullerton, CA 92631

Special Notes:
	A.)  Table of Contents is held in Local Data Segment
		   So Compile This Program as SMALL (at Minimum).
*/
/* *************************************************************************
**                        Program Revision History
**
**  By Dale Loftis
**  10-3-87   Changed WIDTH to 132, Tabs are now every 4, modified so
**             that temporary file is deleted if control - break is hit,
**             Improved the clarity of the instructions if no parmeters
**              are given.
**
**  10-17-87  Changed so that no blank lines are printed in between structures.
**
**  11-06-87  Added -@ RESPONSE FILE feature since you couldn't fit all the
**             filenames onto the command line, upto 400 files can be printed.
**
**            Also adding the "Reading file # x of y" string and made program
**             check the amount of free disk space before writing the files
**             as the program would write the output file and run out of disk
**             space and just put alot of junk on the disk.
**
**  11-08-87  Ver 1.0    Added -h HEADER FILE, -* "Print All 'C' Files,
**                             -x Extra Lines and Program Version Number
**
**  01-10-88  Ver 1.1    Added call to setvbuf() to speed up file access.
**
**  01-25-88  Ver 1.2    Added -t  for option of just table of contents list
**
**  01-29-88  Ver 1.3    Fixed Bug.  funcname () would not be put in Table of
**                         Contents because of space between funcname and ().
**
**  04-26-88  Ver 1.4    Added -p* option for starting page number and changed
**                       Table of Contents so File Date and Time Shown.
**
**  05-12-88  Ver 3.0    Added -q* option to print files by section number
**                       rather than by page number.
**                       Added -t- option to suppress the Table of Contents
**
**  07-18-88  Ver 3.1    Fixed bug in printing by section where form feed was
**                       not being output at the top of the file.
*/
/* --------------------------------------------------------------------- */
/*
       Include Files for Library Function Prototypes
*/
#include	<stdio.h>
#include	<fcntl.h>
#include	<alloc.h>
#include	<dos.h>
#include	<dir.h>
#include	<conio.h>
#include	<ctype.h>
#include	<time.h>
#include	<string.h>
/* --------------------------------------------------------------------- */
/*
       Some Valuable Definitions
*/
#define		O_RAW  O_BINARY				/* Handle File as Binary       */

#define		TEMPORARY_FILENAME	"CPR$.TMP"	/* temporary file name (always)*/

#define		MAJOR_VERSION	3			/* Current Software Version #   */
#define		MINOR_VERSION	1

#define		BP				0x0C		/* Form feed                    */
#define		COMPRESSED		0x0F		/* Compressed Print             */
#define		MAX_S			256			/* Maximum string length        */
#define		LENGTH			66			/* Default Page Length          */
#define		WIDTH			132			/* Default page width           */
#define		N_FILES			400			/* Maximum number of files      */
#define		TOC_LEN			1024		/* Max Table of Contents entries*/
#define		MAX_FNAME_SIZE	25			/* Max filename length in response file */
#define		MAX_HDR_BYTES	500			/* Max bytes in header file     */
#define		HEADER_SIZE		3			/* Header is 3 lines big        */
/* -------------------------------------------------------------------------
/* These Macros are used to determine valid characters for Table of Contents
*/

/* returns true if letter, digit, '_' or ' '*/

#define isidchr(c)      (isalnum(c) || (c == '_') || (c == ' '))

/* returns true if letter, digit, '_', '*', '\t' or '\n' */

#define isiechr(c)      (isidchr(c) || c=='*' || c=='\t' || c=='\n')
/* --------------------------------------------------------------------- */
/*
       Internal Program Function Prototypes
*/
int		c_break(void);		/* Called when BREAK is hit                  */
int		file_stats(char *);	/* File Statistics function                  */
char    *Rs();				/* Strips trailing blanks from string        */
char	*EndComment();		/* Process string until end of comment       */
char	*EndString();		/* Process string until end of quoted string */
char	*expand();			/* Process string expanding TABS to Spaces   */
char	*substr1();
/* -------------------------------------------------------------------- */
/*
       Global Variables
*/
FILE    *TempFile;						/* Program Temporary File       */
FILE    *File;							/* Current File being worked on */
FILE	*Rfile;							/* Response File handle         */
FILE	*Hfile;							/* Header   File handle         */

struct ffblk fblock;			/* struct used for calculating file size */
struct dfree disk_space;		/* struct used for finding free disk space */

char  file_time_buff[20];		/* file time is placed here      */
char  file_date_buff[20];		/* file date is placed here      */

int		Braces;					/* Keeps track of brace depth    */
int		LineNumber;				/* Count output lines		     */
int		PageNumber = 1;			/* You figure this one out       */
int		SectionNumber = 1;		/* and this one to               */
int		PageLength = LENGTH;	/* Normal paper length           */
int		PageWidth  = WIDTH;		/* normal page width             */
int		OnePerPage = 0;			/* non-zero puts 1 function/page */
int		NumLines   = 0;			/* non-zero puts line # in file  */
int		Number     = 1;
int		WantSorted = 1;			/* Sort the table of contents   */
int		PageEnd;				/* Accounts for space at bottom */
int		InComment;				/* Indicates if inside Comment  */
int		InString;				/* Indicates if inside String   */
int		double_strike;			/* # of times + 1 to write filename             */
int		header_flag;			/* non-zero enables print codes/Dates/Page #'s  */
int		header_lines;			/* number of extra lines from header file       */
int		extra_lines;			/* non-zero enables extra lines after functions */
int		table_contents;			/* non-zero indicates Table of Contents Only    */
int		sectionflg;				/* non-zero indicates print by section          */

unsigned long	memfree;		/* amount of local memory left for file buffer  */

char	*TempName;				/* pointer to temporary filename   */
char	*fname[N_FILES];        /* file names to be worked on      */
char	*STDIN = "\n";          /* special string pointer value to */
                                /* flag a file as being stdin      */
char	*Name;					/* Current file name               */
char	*ProgName;				/* This programs Path and Name     */

char    *Toc[TOC_LEN];			/* pointers to files in TOC        */
int     TocPages[TOC_LEN];		/* page numbers                    */
int     TocCount;				/* index into TOC arrays           */

char	header_buffer[MAX_HDR_BYTES];/* 500 byte buffer for user defined Page Headers */
char    FunctionName[100];				/* Work area for parsing Func names */
char    Todayv[45];						/* Today's Time and Date	     */

/* --------------------------------------------------------------------- */
/*
	 The filenames[N_FILES][25] array allocates an array capable of holding
	N_FILES filenames that are up to 25 characters long.

	 Each filename in DOS can be 12 characters long and the response file
	has a CRLF with each line and we also need a terminating zero byte
	bringing the count up to 15. MAX_FNAME_SIZE bytes are provided for a
	possible path
*/
/* --------------------------------------------------------------------- */
char filenames[N_FILES][MAX_FNAME_SIZE];	/* files read from response file */

/* --------------------------------------------------------------------- */
/*
**  The following string is the definition of tab stops.  Every 'T' is a
**  tab, any other character is not.  It is currently defined as a tab
**  every 4 spaces.  The comment below helps if you want to modify this,
**  each row being 0+n, 50+n, 100+n, and 150+n, where n is the number
**  above the declaration.  Don't try to number each row with a comment,
**  because you'll notice that the '\'s make it one big string.
*/
char   *TabDef = "\
---T---T---T---T---T---T---T---T---T---T---T---T--\
-T---T---T---T---T---T---T---T---T---T---T---T--T-\
---T---T---T---T---T---T---T---T---T---T---T---T--\
-T---T---T---T---T---T---T---T---T---T---T---T---T";
/* ------------------------------------------------------------------------ */
/*
/*    Program Instructions (Note the %??? arguments)
/*
*/
char option_list[] = "\n\
----------------------------- VERSION %d.%d SWITCHES ------------------------\n\
           -@*    Replace * with Response Filename (1 filename per line    )\n\
           -a*    Replace * with Drive Wildcard\n\
           -c     Printer Codes/Dates/Page #'s off (default is Enabled     )\n\
           -d     Double Strike Printing Enabled   (default is Disabled    )\n\
           -h*    Replace * with Header Filename\n\
           -l##   Replace ## with PageLength       (default = %3d          )\n\
           -n     Numbers Source Lines             (Resets to 1 w/each file)\n\
           -o     Places 1 function on a Page      (default is multiple    )\n\
           -p*    Replace * with starting Page Number\n\
           -q*    Replace * with starting Section Number\n\
           -s     Sort Table of Contents by Page # (default is Func. Name  )\n\
           -t     Print Table of Contents Only     (-t- suppress's Table   )\n\
           -w##   Replace ## with Width            (default = %3d          )\n\
           -x     Enables Extra Lines after Functions (default is Disabled )\n\
          [...]   Means multiple filenames may be listed\n\n\
               Response Files should have 1 filename per line up to %d      \n\
         characters followed by 1 CRLF and may contain up to %d filenames.  \n\n\
               Header Files contain the Strings to Print at the top of      \n\
         every page of the listing, entire file can be up to %d bytes.";

/* -------------------------------------------------------------------- */
/*
      It all Starts With MAIN
*/
main(argc, argv)
int argc;
char    **argv;
{
unsigned long int total_disk_free = 0;
unsigned char ch;
register int    i;
int nextf = 0;      				/* index into fname[]                */
long    thetime;

	ctrlbrk(c_break);				/* set control-break handler         */
	double_strike = 1;				/* default to no double strike       */
	header_flag   = 1;				/* default to codes and headers      */
	header_lines  = 0;				/* no extra lines from header file   */
	extra_lines   = 0;				/* no extra lines after functions    */

	ProgName = argv[0];				/* grab pointer to the programs name */

	time(&thetime);					/* grab the DOS time and date        */
	strcpy(Todayv,ctime(&thetime));	/* convert to ascii (LF at END OF STRING) */
	strtok(Todayv,"\n");			/* strip the annoying LF off         */

	if (argc == 1)					/*if only the program name given     */
		Usage();					/* if no args, show instructions     */

	for (i = 1; i < argc; i++) {	/* other parse command line          */
		if (argv[i][0] == '-')  {	/* check for switch flag "-"         */
			switch (toupper(argv[i][1])) { /* and go thru the switches   */

			case '@':					/* Response file */
				nextf = ReadResponseFile(argv[i]);
			break;

			case 'A':					/* "*.???" Print all specified files */
				nextf = StarDot(argv[i]);
			break;

			case 'C':					/* Print codes/Dates/Time and page # */
				header_flag = 0;
			break;

			case 'D':					/* Double Strike function names      */
				double_strike = 3;
			break;

			case 'H':					/* Header file                       */
				ReadHeaderFile(argv[i]);
			break;

			case 'L':					/* Number of lines per page          */
				if ( ! argv[i][2]) {	/* if -L ?? (ie.. a space)           */
					if (++i >= argc)	/* see if another argument           */
						Usage();		/* if no then print Help             */

					if ( ! isdigit (argv[i][0]))	/* is it a number?       */
						Usage();					/* No! Print Help        */

					PageLength = atoi (argv[i]);	/* assign number         */
				}
				else {		/* Switch came as -L?? form (with no space)      */
					if (!isdigit(argv[i][2]))		/* is it a number?       */
						Usage();					/* No! Print Help        */

					PageLength = atoi(&argv[i][2]);	/* assign number         */
				}
			break;

			case 'N':					/* Number Source Lines               */
				NumLines = 1;
			break;

			case 'O':					/* 1 function per page               */
				OnePerPage = 1;
			break;

			case 'P':					/* Starting Page Number              */
				if ( ! argv[i][2]) {	/* if -P ?? (ie.. a space)           */
					if (++i >= argc)	/* see if another argument           */
						Usage();		/* if no then print Help             */
					if ( ! isdigit (argv[i][0]))	/* is it a number?       */
						Usage();					/* No! Print Help        */
					PageNumber = atoi (argv[i]);	/* assign number         */
				}
				else {		/* Switch came as -P?? form (with no space)      */
					if (!isdigit(argv[i][2]))		/* is it a number?       */
						Usage();					/* No! Print Help        */
					PageNumber = atoi(&argv[i][2]);	/* assign number         */
				}
			break;

			case 'Q':					/* Print by Section                  */
				if ( ! argv[i][2]) {	/* if -Q ?? (ie.. a space)           */
					if (++i >= argc)	/* see if another argument           */
						Usage();		/* if no then print Help             */
					if ( ! isdigit (argv[i][0]))	/* is it a number?       */
						Usage();					/* No! Print Help        */
					SectionNumber = atoi (argv[i]);	/* assign number         */
				}
				else {		/* Switch came as -Q?? form (with no space)      */
					if (!isdigit(argv[i][2]))		/* is it a number?       */
						Usage();					/* No! Print Help        */
					SectionNumber=atoi(&argv[i][2]);/* assign number         */
				}
				sectionflg = 1;
			break;

			case 'S':					/* Sorted Table of Contents          */
				WantSorted = 0;
			break;

			case 'T':					/* Print Table of Contents Only      */
				table_contents = 1;		/* enable flag                       */
				if( argv[i][2] == '-')	/* if disabling TOC                  */
					table_contents = -1;/* disable it                        */
			break;

			case 'W':					/* Page Width                        */
				if ( ! argv[i][2]) {	/* if -W ?? (ie.. a space)           */
					if (++i >= argc)	/* see if another argument           */
						Usage();		/* if no then print Help             */
					if ( ! isdigit (argv[i][0]))	/* is it a number?       */
						Usage();					/* No! Print Help        */
					PageWidth = atoi (argv[i]);		/* assign number         */
				}
				else {		/* Switch came as -W?? form (with no space)      */
					if (!isdigit(argv[i][2]))		/* is it a number?       */
						Usage();					/* No! Print Help        */
					PageWidth = atoi(&argv[i][2]);	/* assign number         */
				}
			break;

			case 'X':					/* Extra Lines after Functions       */
				extra_lines = 1;
			break;

			case '\0':					/* End of string?(out of enviroment) */
				if (nextf >= N_FILES) {
					fprintf (stderr, "%s: too many files\n", argv[0]);
					exit (1);
				}
				fname[nextf++] = STDIN;		/* any in excess goes to STDIN   */
			break;

			default:						/* No arguments given            */
				Usage();					/* show program instructions     */
			break;
			}
		}
		else {		/* Argument (argv[i]) wasn't a switch parmeter           */
			if (nextf >= N_FILES) {
				fprintf (stderr, "%s: too many files\n", argv[0]);
				exit (1);
			}
			fname[nextf++] = argv[i];	/* copy pointer to filename          */
		}
	}

	if ( ! nextf)				/* No files were specified                   */
		fname[nextf++] = STDIN; /* so use STDIN as the input file            */

	PageEnd = PageLength - (1 + PageLength / 20);

	StartTempFile();					/* open temporary file               */

	for (i = 0; i < nextf; i++) {		/* walk thru file list               */
		if (fname[i] == STDIN) {		/* if no files                       */
			File = stdin;				/* assign stdin as input             */
			Name = "Standard Input";	/* assign name for printout          */
			fprintf(stderr,"Accepting Input from STDIN, ^Z to END\n");
		}
		else  {							/* there was a filename              */
			if( (File = fopen( Name = fname[i], "r" )) == NULL )  {
				fprintf (stderr, "%s: Can't open file \"%s\"\n",
						ProgName, Name );
				continue;				/* keep going if not found           */
			}
			else {						/* file opened OK so read            */
				fprintf(stderr, "\nReading File #%3d of %3d: \" %s \"",
						i+1, nextf, fname[i]);

				memfree = coreleft();		/* compute amount of free memory */
				memfree = ( (memfree > 32767L) ?/* if to much memory then    */
								   32767L  :	/* set max for setvbuf       */
						           memfree );	/* else take as much as u can*/

				if( setvbuf(File, NULL, _IOFBF, (int) memfree) != 0) {
					fprintf(stderr,"Input Buffer failed to allocate.\n");
				}
			}
		}
		List();					/* Read File in and process data             */

		if (File != stdin) {	/* if it's not STDIN                         */
			fclose(File);		/* close file & deallocate buffer            */
		}
	}

	if( PageNumber > 1 || LineNumber > 0 )
		BreakPage();

	EndTempFile();				/* Close Temporary File                     */

	fprintf(stderr,"\n\n");

	getdfree(0, &disk_space);/* find out free disk space after cpr$.tmp created */

	total_disk_free =	( ( (long)disk_space.df_bsec   *	/* bytes per sector    */
							(long)disk_space.df_sclus) *	/* sectors per cluster */
							(long)disk_space.df_avail);		/* clusters available  */

	findfirst(TEMPORARY_FILENAME,&fblock,0);	/* read temp file size in    */

	if(fblock.ff_fsize >= total_disk_free) {	/* see if copy can be output */
		fprintf(stderr,"Insufficient Disk Space!\n\
						\nMinimum of %lu bytes needed.\
						\n%lu bytes are available\n\
						\nDo You wish to continue? (Y/N)",
						fblock.ff_fsize, total_disk_free);
		do {
			ch = (toupper(getche()) );
		}
		while((ch != 'Y') && (ch != 'N'));

		if(ch != 'Y')
			Done();					/* Delete Temporary File             */

		fprintf(stderr,"\n\n");
	}

	if((!table_contents) ||			/* if TOC and file enabled           */
		(table_contents == 1))		/* or TOC only     enabled           */
		DumpTableOfContents();		/* Print Table of Contents           */

	if((!table_contents) ||			/* if TOC and file enabled           */
		(table_contents == -1))		/* or no TOC                         */
		DumpTempFile();				/* then dump the file                */

	Done();							/* Clean things up and leave         */
}
/*****************************************************************************/
Usage()
{
        fprintf (stderr,"Usage: %s SWITCHES filename [...]",ProgName);
		fprintf (stderr,option_list ,
	               MAJOR_VERSION, MINOR_VERSION, LENGTH, WIDTH,
				   MAX_FNAME_SIZE-1, N_FILES, MAX_HDR_BYTES);
        exit(1);
}
/*****************************************************************************/
StartTempFile()
{
	TempName = TEMPORARY_FILENAME;			/* Temporary Filename */

	if( (TempFile = fopen(TempName, "w")) == NULL ) {
		fprintf (stderr, "%s: Can't open temp file!\n", ProgName);
		exit(1);
	}
}
/*****************************************************************************/
EndTempFile()
{
	fclose (TempFile);
}
/*****************************************************************************/
DumpTempFile()
{
int     fd,n;
char    buff[1025];

	if ((fd = open (TempName, O_RDONLY | O_RAW)) == -1) {
		fprintf (stderr, "%s: can't open temp file\n", ProgName);
		exit (1);
	}

	while ((n = read (fd, buff, 1024)) != 0) {
		if (write (1, buff, n) == -1) {
			fprintf (stderr, "%s: write error (1)\n", ProgName);
			exit (1);
		}
	}
}
/*****************************************************************************/
Done()
{
	if( TempName )			/* if you created a file  */
		unlink( TempName );	/* Delete temporary file  */
	exit(0);				/* and leave the hard way */
}
/*****************************************************************************/
List()			/* Process Incoming File */
{
register int    bp;
char            buffer[257];

	if( sectionflg ) {							/* if by section             */
		PageNumber = 1;							/* Pages start at 1          */
		LineNumber = 0;							/* Lines reset each time     */

		BreakPage();							/* form feed if allowed      */
	}

	NewPage();									/* bump page & output header */
	NewFile();

	bp = Braces = 0;							/* no begin page and braces  */

	while( fgets(buffer, 256, File) != NULL ) {	/* read a line upto 256 bytes*/
		Rs(buffer);								/* Remove Trailing Spaces    */

		if( bp )								/* if beginning page         */
			NewFunction();						/* setup for new function    */

		if( (LineNumber + 1) > PageEnd )		 /* if going past page end   */
			NewPage();							 /* start a new page         */

		if(!Braces && LooksLikeFunction(buffer)) /* no braces and a function */
			AddToTableOfContents();				 /* then add to TOC          */

		bp = PutLine(buffer);
		LineNumber++;
	}
	if( sectionflg )							 /* if by sections           */
		SectionNumber ++;						 /* Bump to next Section     */
}
/*****************************************************************************/
NewPage()						/* Process New Page                          */
{
	if( LineNumber > HEADER_SIZE )  {
		if( PageNumber >= 0 ) {
			PageNumber ++;		/* bump Page Number      */
		}
		BreakPage();			/* form feed if allowed  */
		LineNumber = 0;			/* set flag for a header */
	}
	if( LineNumber == 0 )		/* Each new page         */
		PutHeader();			/* Outputs a Header      */
}
/*****************************************************************************/
NewFile()
{
register int    i, l;
char            temp[80];

	if (TocCount >= TOC_LEN) {
		fprintf (stderr, "%s: too many table of contents entries\n", ProgName);
		exit (1);
	}

	if( (Toc[TocCount] = (char *) malloc(130)) == NULL)	/* allocate some memory */
		ReportMemoryError();

	sprintf (Toc[TocCount], "\n\tFile: %s ", Name);	/* to put filename into */
	l = strlen(Toc[TocCount]) - 1;					/* and findout how long it is */

	if( l < 64 )  {									/* if not to long */
		i = (64 - l) / 8;							/* calc. TAB increment */
		for( l=0; l < i; ++l )						/* keep within 64 columns */
			strcat(Toc[TocCount],"\t");				/* and put some TAB's out */
	}

	file_stats(Name);								/* findout file stats  */

	if(! sectionflg ) {
		sprintf (temp, "  Page %4d  (%s  %s)\n",	/* Page #, Time & date */
			PageNumber, file_date_buff, file_time_buff);
	}
	else {
		sprintf (temp, "  Section %4d (%s  %s)\n",	/* Section, Time & Date*/
			SectionNumber, file_date_buff, file_time_buff);
	}

	strcat(Toc[TocCount], temp);					/* copy to main buffer */
	++ TocCount;									/* bump the entries    */

	if (NumLines)									/* if numbering lines  */
		Number  = 1;								/* reset linenumber    */

	InComment = InString = 0;					/* not in comment or string */
}
/****************************************************************************/
PutLine(l)
register char   *l;
{
register char   c;
int             bp;
char            *save;
char            *section, *p;
int             offset;
char            Digits[15];
int             Size;
int             pos;

	bp = 0;
	for( save = expand(l);
		( (c = *l) != 0);
				++l )	{				/* bump thru the string */

		if( InComment )					/* if your in a comment  */
			l = EndComment(l);			/* process till your out */
		else
		if( InString )					/* if your in a string   */
			l = EndString(l);			/* process till your out */
		else
		switch(c)  {					/* it must be something  */
			case '{':					/* curly brace IN        */
				++Braces;				/* increment brace count */
			break;

			case '}':					/* curly brace OUT       */
				if( --Braces == 0)		/* coming out yet?       */
					if (*(l+1) == ';')	/* is it structure?      */
						++l;            /* continue thru string  */
					else
					if(extra_lines)	 	/* if option enabled    */
						bp = 1;		 	/* flag for extra lines */
			break;

			case '\'':					/* Forward Slash         */
				++l;					/* continue thru string  */
			break;

			case '"':					/* Quotation Mark        */
				InString = 1;			/* must be in a string   */
			break;

			case '/':					/* Start of Comment      */
				if( *(l+1) == '*' ) {	/* is next character * ? */
					InComment = 1;		/* must be in a comment  */
					++l;				/* continue thru string  */
				}
			break;
		}
	}

	if (NumLines)  {					/* if line numbering enabled */
		sprintf (Digits,"[%d]  ", Number);	/* make a string         */
		Size = strlen(Digits);				/* calc. its size        */
	}
	else {
		Size = 0;						/* else it's size is 0       */
	}

	if (strlen(save) +					/* if original strings size  */
		       Size  >					/* and the size of numbers   */
		       PageWidth)  {			/* are bigger than the width */
		section = substr1(save, 0, PageWidth - Size);

		if (section[strlen(section) - 1] != ' ')
			if (NULL == (p = strrchr(section, ' ')))
				offset = strlen(section);
			else
				offset = p - section;
			else
				offset = strlen(section) - 1;

		section[offset] = NULL;

		if (NumLines)  {	/* if line numbering is enabled */
			fprintf (TempFile, "[%d]  %s\n", Number++, section);
		}
		else  {
			fprintf (TempFile, "%s\n", section);
		}

		pos = offset + 1;
		do  {
			section = substr1(save, pos, pos + PageWidth - 8);
			if (strlen(save) - pos + 8 > PageWidth)
				if (section[strlen(section) - 1] != ' ')
					if (NULL == (p = strrchr(section, ' ')))
						offset = strlen(section);
					else
						offset = p - section;
					else
						offset = strlen (section) - 1;
					else
						offset = strlen(section);

				section[offset] = NULL;

/** ----------------------------------------------------------------- **/
/* Removed this for some reason
/*
/*				if (section[strlen(section) - 1] == '\n')
/*					section[strlen(section) - 1] = NULL;
*/

/*  Line is to long for pagewidth so continue on next line */

				fprintf (TempFile, "C       %s\n", section);
					if (++LineNumber > PageEnd)
						NewPage();
			}
			while ((pos += offset + 1) < strlen(save));
		}
		else  {
			if (NumLines)		/* if line numbering enabled */
				fprintf (TempFile, "[%d]  %s\n", Number++, save);
			else
				fprintf (TempFile, "%s\n", save);
		}
	return(bp);
}
/*****************************************************************************/
NewFunction()					/* Process New Function                     */
{
register int    i;

	if( LineNumber > (PageLength * 3 / 4) )       /*  49.5 lines            */
		NewPage();
	else  {
		if (!OnePerPage)  {					/* if mult. functions per page  */
			for( i=0; i < (PageLength/7); ++i )
				putc ('\n', TempFile);		/* add extra lines              */
				LineNumber += PageLength/7; /* set line counter also        */
			}
			else
				NewPage();					/* otherwise its 1 func. per page*/
	}
}
/*****************************************************************************/
BreakPage()						/* Process Page Break                        */
{
	if(header_flag)				/* if its allowed send a FORM FEED           */
		putc (BP, TempFile);
}
/*****************************************************************************/
PutHeader()
{
register int    i, l, j;

	if(header_flag) 			/* if its allowed send a COMPRESSED Code */
		putc (COMPRESSED, TempFile);

	putc ('\n', TempFile);

	if(header_flag ) {			/* if its allowed send out the HEADER */

		l = strlen(Name);		/* Filename or Table of Contents label length */

		for( j=0; j < double_strike; ++j) {
			fprintf (TempFile, "%s", Name);	/* Write out Filename or TOC */
			if( (j < 2) && (double_strike == 3) )
				putc('\r', TempFile);		/* Provide for Double Strike CR */
		}
		if( PageNumber > 0 ) {				/* if your not on the TOC pages */
			for( i = (l+7)/8; i < 6; ++i )
				putc ('\t', TempFile);		/* Tab out for position         */

			if(! sectionflg ) {
				fprintf (TempFile, "%s\tPage: %d\n\n\n", Todayv,PageNumber);
			}
			else {
				fprintf (TempFile, "%s\tSection %3d - %d\n\n\n",
					Todayv,SectionNumber,PageNumber);
			}

			if(header_lines)						/* Header for TOP OF PAGE */
				fprintf (TempFile, "%s", header_buffer);
		}
		else {	/* 1st line of Table of Contents page gets the time and date */

			for( i = (l+7)/8; i < 6; ++i )			/* Tab out for time/date  */
				putc ('\t', TempFile);

			fprintf (TempFile, "%s\n\n", Todayv);		/* Todays Time and Date   */

			if(header_lines)						/* Header for TOP OF PAGE */
				fprintf (TempFile, "%s", header_buffer);
		}
	}
	LineNumber += (HEADER_SIZE + header_lines);		/* bump line number counter */
}
/*****************************************************************************/
LooksLikeFunction(s)
register char   *s;
{
register char   *p;
char            *save;
int             Cnt, nosl,nolp,norp,flg;
char            Digits[15];
int             AddOne = 0;

	if( InComment || InString)		/* if your in a comment or in a string */
		return(0);					/* just leave and return 0             */

	p = FunctionName;				/* assign pointer to function name string */
	save = s;						/* save address of string                 */

	nosl = nolp = norp = 0;			/* no left or right paren or no slash     */

	flg = 1;

	for(; *s && flg  ; s++)  {		/* go until end of string or flag is set  */
		switch  (*s)  {				/* switch on each character in string     */
			case  '(':				/* is it a left paren?                    */
				if (!nolp) {		/* if no left paren already               */
					nolp = 1;		/* flag that you've got one               */

/* 3/4/87 added following line to include ARGS in TOC */

					*p++ = *s;		/* move byte of ARG into function buffer */
				}
				else
					return(0);		/* or return if you've already had one    */
			break;

			case  ')':				/* is it a right paren?                   */
				if (nolp && !norp) {/* if already a left paren and no right yet */
					norp = 1;		/* flag that you've got a right paren     */

/* 3/4/87 added following line to include ARGS in TOC */
					*p++ = *s;		/* move byte of ARG into function buffer  */
				}
				else
					return(0);		/* or return if conditions were'nt right  */
			break;

			default:				/* everything comes thru here             */
				if (!nolp)  {		/* if no left paren yet                   */
					if (isiechr(*s))  {	/* is it alpha,digit,'_*\t\n'?        */
						if (isidchr(*s))/* is it alpha,digit,'_' or ' '?      */
							*p++ = *s;	/* if it is store the byte for TOC    */
						else
							p = FunctionName;	/* start over if '*\t\n' & nolp */
					break;
					}
					else
						return(0);	/* return if conditions weren't right     */
				}

				if (!norp)  {		/* if no right paren yet                  */
					if (isiechr(*s) || *s == ',') { /* is it alpha,digit,'_ *\t\n'?*/

/* 3/4/87 added following line to include ARGS in TOC */
						*p++ = *s;	/* move byte of ARG in function buffer    */
						break;
					}
					else
						return(0);		/* return if conditions weren't right */
				}

				if (Cmemb(*s," \t\n\r") && !nosl)	/* is it ' \t\n\r' or no slash yet */
					break;

				if (*s == '/' && !nosl)  {	/* is it a '/' and no slash yet   */
					nosl = 1;				/* flag that you've got one       */
					break;
				}

				if (*s == '*' && nosl)  {	/* if its '*' & you got a slash already */
					flg = 0;				/* set flag to abort loop         */
					break;
				}
				return(0);					/* return if conditions not right */
		} /* end of switch */
	}		/* end of for loop */

	if (nolp != 1)							/* return if no left paren found  */
		return(0);

	*p = '\0';								/* else terminate function buffer */

	if (NumLines)  {						/* if line numbering enabled      */
		sprintf (Digits,"[%d]  ", Number);	/* format number into string      */
		Cnt = strlen(Digits) + AddOne;
		while (Cnt-- > 0)
			putc (' ', TempFile);
		AddOne = 0;
	}
/*
 * This will cause the function name part of the line to
 * be double striken.
*/
	if( double_strike == 3) {
		while (*save && *save != '(')		/* double strike up to func  name */
			putc (*save++, TempFile);
		putc ('\r', TempFile);				/* use CR for doublestrike        */
	}
	return(1);
}
/****************************************************************************/
AddToTableOfContents()
{
register int    l;
register char   *p;

	if (TocCount >= TOC_LEN) {
		fprintf (stderr, "%s: too many table of contents entries\n", ProgName);
		exit (1);
	}
	l = strlen(FunctionName);					/* find how long name is */
	p = Toc[TocCount] = (char *) malloc(l+1);	/* allocate just enough memory */

	if( p == NULL)								/* did it really allocate     */
		ReportMemoryError();					/* No! then report error      */

	strcpy(p, FunctionName);					/* copy the name to new space */
	TocPages[TocCount] = PageNumber;			/* plug the page number in    */
	++TocCount;									/* bump the count             */
}
/****************************************************************************/
DumpTableOfContents()
{
register int    i, j, l;

	if( TocCount == 0 )		/* if there nothing to print */
		return;				/* then just return          */

	if (WantSorted)			/* if you wanted it sorted by page number */
		SortTableOfContents();	/* then we must sort it first         */

	Name = "Table of Contents";	/* give me a title for the page       */

	PageNumber = -1;			/* Table of Contents pages are not numbered */
	LineNumber = 0;				/* and neither are the lines          */
	TempFile = stdout;			/* default output to standard out     */

	NewPage();					/* start out creating a new page      */

	for( i=0; i < TocCount; ++i ) {
		if( Toc[i][0] == '\n' ) {
			if( (LineNumber + 5) > PageEnd )
				NewPage();
			printf("%s", Toc[i]);
			LineNumber += 2;
			continue;
		}

		if( ++LineNumber > PageEnd )/* if going off end of page           */
			NewPage();				/* start a new page                   */

		printf("\t\t%s ", Toc[i]);	/* print func. name in TOC            */
		l = strlen(Toc[i]);			/* length if function name with parms */

		for( j=l; j < 48; ++j )		/* put dots out to column 48          */
			putchar('.');			/* put dots out to page num. in TOC   */

			printf(" %d\n", TocPages[i]);	/* print page number for function */
	}

	if( ! sectionflg ) {			/* if not printing by sections        */
		if(header_flag)				/* if control codes are allowed       */
			putchar(BP);			/* print a Form Feed                  */
		else
			putchar('\n');			/* otherwise its a New Line           */
	}
	fflush(stdout);					/* make sure everything is written out*/
}
/****************************************************************************/
/*
       Sort Table of Contents by Page number rather than function name
*/
SortTableOfContents()
{
register int    i, tempint;
char			*tempchar;
int				flag;

	do {
		flag = 0;				/* default to already sorted */

		for (i = 0; i < TocCount - 1; i++) {	/* look at them all */
			if (Toc[i][0] == '\n' || Toc[i+1][0] == '\n')
				continue;       /* don't sort across file names */

			if (strcmp (Toc[i], Toc[i+1]) > 0) {/* compare the strings        */
				tempchar = Toc[i];				/* copy to temp pointer       */
				Toc[i] = Toc[i+1];				/* swap the pointers in array */
				Toc[i+1] = tempchar;			/* put the temp pointer back  */
				tempint = TocPages[i];			/* grab the page number       */
				TocPages[i] = TocPages[i+1];	/* swap page numbers          */
				TocPages[i+1] = tempint;		/* put the temp page number back */
				flag = 1;						/* indicate you've swapped    */
			}
		}
	}
	while (flag);			/* go until no more swaps can be made */
}
/****************************************************************************/
char *EndComment(p)			/* Process string until you come out of COMMENT */
register char   *p;
{
register char   c;

	while( (c = *p++) != 0 )			/* while there are chars to look at */
		if( c == '*' && *p == '/' ) {	/* and there a splat or a slash     */
			InComment = 0;				/* say your not in the comment      */
			break;						/* and leave the loop               */
		}
	return(p-1);						/* returning the new pointer addr.  */
}
/*****************************************************************************/
char *EndString(p)			/* Process string until you come out of STRING  */
register char   *p;
{
register char   c;

	while( (c = *p++) != 0 )			/* while there are chars to look at */
		if( c == '\\' ) {				/* and forward slashs for next line */
			continue;					/* means "Just read on McDuck"      */
		}
		else if( c == '"' ) {			/* if you found the ending quote    */
			InString = 0;				/* say your not in a string anymore */
			break;						/* and leave the loop               */
		}
	return(p-1);						/* returning the new pointer addr.  */
}
/****************************************************************************/
/*
 *      This is the function substr1().  The calling sequence is:
 *
 *                      substr1(string, startpos, endpos).
 *
 *      The function returns a pointer to a static string (written over -
 *      on subsequent calls) which is a substring of the string `string'
 *      starting at `startpos' (the first position is 0 (zero)) and ending
 *      at `endpos' (non-inclusive).  All arguments must be present or
 *      strange things happen with the system stack.
 *
 *      An example of the use is:
 *
 *              x = substr1(string, 2, 5);
 *              (where string == "This is a test.")
 *
 *      This call returns a pointer to:
 *              "is "
 *      An error code of -1 is returned is the `endpos' is greater than
 *      `startpos'
 *
 *                                              Lance E. Shepard
 */
char *substr1(string, start, end)
char    *string;
int     start;
int     end;
{
static char  retstr[MAX_S];
int loop1;
int loop2;

	if (end < start)  {
		exit(-1);
	}

	for (loop2 = 0; loop2 < MAX_S; loop2++)
		retstr[loop2] = NULL;

	for (loop1 = start, loop2 = 0; string[loop1] != NULL &&
		 loop1 < end && loop2 <= MAX_S; loop1++, loop2++)
			retstr[loop2] = string[loop1];

	retstr[++loop2] = NULL;
	return(retstr);
}
/****************************************************************************/
/*
 *      This is the function `char *expand().'  This function takes as
 *      an argument a NULL terminated string and replaces all occurances
 *      of the tab character with 8 (eight) spaces.  The function returns
 *      a pointer to a static string which is overwritten on subsequent
 *      calls.
*/
/****************************************************************************/
char *expand(string)
char *string;
{
int count;
static char retstr[MAX_S];

	for (count = 0; count < MAX_S; retstr[count++] = NULL)
		;

	for (count = 0; *string != NULL; count++, string++)  {
		if (*string == '\t')  {
			retstr[count] = ' ';
/* 			while (((count + 1) % 8) != 0)     */
				while (TabDef[count] != 'T')
					retstr[++count] = ' ';
		}
		else
			retstr[count] = *string;
	}
	retstr[count] = NULL;
	return(retstr);
}
/****************************************************************************/
char *Rs(s)             /* strip trailing blanks from string  */
char s[];
{
int n;
	for (n=strlen(s)-1 ; n >= 0 && isspace(s[n]) ; n--)
		;				/* find the last space in the string */
	s[n+1] = '\0';		/* plop a null char on top of it     */
	return(s);			/* return pointer to string          */
}
/****************************************************************************/
int Cmemb(a,b)  /*  is character "a" a member of string "b"  */
char  a, *b;
{
	while  (*b)				/* go until the null character */
		if (a == *b++)		/* looking for the character and bumping the pointer */
			return(1);		/* returning 1 if its found */
	return( (!a) ? 1 : 0 );	/* return 0 if "a" was a non-zero character */
}
/****************************************************************************/
StarDot(file_string)
char * file_string;
{
extern char filenames[N_FILES][MAX_FNAME_SIZE];
extern char fnames[N_FILES];
extern struct ffblk fblock;
extern struct dfree disk_space;

unsigned long int total_file_size = 0;
unsigned long int total_disk_free = 0;
int done;
int i = 0;
	file_string += 2;						/* bump past -A switch */

	if( (done = findfirst(file_string,&fblock,0)) != 0) {/* Attempt 1st file read */
		fprintf(stderr,"No Files Found\n");
		exit(1);
	}
	while(!done && i < N_FILES) {		/* go until done or to many files */
		strcpy(&filenames[i][0],fblock.ff_name);/* copy names into array  */
		fname[i] = &filenames[i++][0];		/* copy pointer to name       */

		total_file_size += fblock.ff_fsize;	/* keep running total of filesizes */

		done = findnext(&fblock);			/* read next availables file info  */
	}
	if( i == N_FILES)
		fprintf(stderr,"Printing ONLY 1st %d Files\n", N_FILES);

	getdfree(0, &disk_space);				/* find free disk space */
	total_disk_free =	( 	((long)disk_space.df_bsec  *	/* convert to bytes */
							(long)disk_space.df_sclus) *
							(long)disk_space.df_avail);

	if((total_file_size * 2) >= total_disk_free) {
		fprintf(stderr,"Insufficient Disk Space!\nMinimum of %lu bytes needed.\
						\nYou Have %lu bytes free\n",
						total_file_size * 2, total_disk_free);
		exit(1);
	}
	return(i);			/* return new number of files */
}
/*****************************************************************************/
ReportMemoryError()
{
	fprintf(stderr,"Memory Allocation Error\n");

	if( TempName ) {			/* if there is a file to work on */
		EndTempFile();			/* Make sure file is closed */
		unlink( TempName );		/* Delete temporary file */
	}

	exit(1);
}
/*****************************************************************************/
ReadHeaderFile(header_file)
char *header_file;
{
extern char		header_buffer[MAX_HDR_BYTES];
extern int		header_lines;
int ch;
int i = 0;
int x = 0;
char *string;

	header_file += 2;		/* strip the "-h" from the string */
	if(( Hfile = fopen(header_file, "r")) == NULL) {
		fprintf(stderr,"Can't Open Header File %s\n",header_file);
		exit(1);			/* terminate abruptly */
	}
	else {
/* read entire file that is up to MAX_HDR_BYTES characters long */
		while( (ch = fgetc(Hfile)) != EOF) {
			header_buffer[i++] = (unsigned char) ch;
			if(i < MAX_HDR_BYTES)
				continue;
			else
				break;
		}
		header_buffer[i] = '\0';	/* terminate buffer */

		fclose(Hfile);		/* and close the file */

		for(x = 0, string = header_buffer; x <= i; x++) {
			if(*string++ == '\n')		/* count number of line feeds */
				header_lines++;			/* tell the NewPage Function  */
		}
	}
}
/*****************************************************************************/
ReadResponseFile(response_file)
char *response_file;
{
char fpntr[80];				/* area for string to be read into */
extern char filenames[N_FILES][MAX_FNAME_SIZE];
extern char fnames[N_FILES];
extern struct ffblk fblock;
extern struct dfree disk_space;

unsigned long int total_file_size = 0;
unsigned long int total_disk_free = 0;

int i = 0;

	response_file += 2;	/* strip the "-@" from the string */
	if(( Rfile = fopen(response_file, "r")) == NULL) {
		fprintf(stderr,"Can't Open Response File %s\n",response_file);
		exit(1);			/* terminate abruptly */
	}
	else {
/* read a entire line from the Rfile that is up to 80 characters long */
		while(fgets(fpntr,80,Rfile) != NULL && i < N_FILES) {
			strtok(fpntr," \n");				/* strip to LF or space */
			strcpy(&filenames[i][0],fpntr);		/* copy names into array */
			fname[i] = &filenames[i++][0];		/* copy pointer to name */

			findfirst(fpntr,&fblock,0);			/* read file size in */

			total_file_size += fblock.ff_fsize;	/* keep running total of filesizes */
		}
		fclose(Rfile);		/* and close the file */

		getdfree(0, &disk_space);		/* find free disk space */
		total_disk_free =	( ((long)disk_space.df_bsec * (long)disk_space.df_sclus) *
							   (long)disk_space.df_avail);

		if((total_file_size * 2) >= total_disk_free) {
			fprintf(stderr,"Insufficient Disk Space!\nMinimum of %lu bytes needed.\
							\nYou Have %lu bytes free\n",
							total_file_size * 2, total_disk_free);
			exit(1);
		}
		return(i);			/* return new number of files */
	}
	return(0);
}
/*****************************************************************************
	This routine is executed when the CONTROL-BREAK key combination is hit
*****************************************************************************/
c_break()
{
	fprintf(stderr,"Control-Break hit.  Program aborting ...\n");

	if( TempName ) {			/* if you created a file    */
		EndTempFile();			/* Make sure file is closed */
		unlink( TempName );		/* Delete temporary file    */
	}
	return(0);					/* ABORT sequence           */
}
/*****************************************************************************/
file_stats(filename)
char *filename;
{
extern struct ffblk fblock;

	findfirst(filename,&fblock,0);		/* read data into structure     */

/* ------------------- Show date in standard format --------------------*/

	sprintf(file_date_buff,"%02d-%02d-%02d",
		((fblock.ff_fdate >> 5) & 0x0f),   /* month     */
		(fblock.ff_fdate & 0x1f)       ,   /* day       */
		((fblock.ff_fdate >> 9) + 80  ));  /* year      */

/* -----------------   Show time in 12 Hour Format   ------------------- */
	if ( (fblock.ff_ftime >> 0x0b) <= 12)
	{
		sprintf(file_time_buff,"%02d:%02d",
		(fblock.ff_ftime >> 0x0b),
		((fblock.ff_ftime >> 5 ) & 0x3f ));  /* minutes   */
	}
	else
	{
		sprintf(file_time_buff,"%02d:%02d",
		((fblock.ff_ftime >> 0x0b) - 12),
		((fblock.ff_ftime >> 5 ) & 0x3f ));  /* minutes   */
	}
/* ------------------     Decipher whether its AM or PM  ---------------- */
	if ( (fblock.ff_ftime >> 0x0b) < 12)
		strcat(file_time_buff," am");	/* for AM */
	else
		strcat(file_time_buff," pm");	/* for PM */
}
/*************************************************************************/