[net.sources] program to create output with a table of contents

phb@hcr.UUCP (Paul Breslin) (08/04/83)

/*
 *	This program prints the files named in its argument list, preceding
 *	the output with a table of contents. Each file is assumed to be C
 *	source code (but doesn't have to be) in that the program searches
 *	for the beginning and end of functions. Function names are added to
 *	the table of contents, provided the name starts at the beginning of
 *	a line. The function name in the output is double striken. White space
 *	is inserted after every terminating '}' character. Thus functions
 *	and structure declarations are nicely isolated in the output. The only
 *	drawback to this is that structure initialization tables sometimes
 *	produce large quantities of white space.
 *
 *	The single option "-l" indicates that the following argument is to be
 *	the page length used for output (changing the page length hasn't been
 *	tested much).
 *
 *	Try it! You'll like it. (I call it cpr.c)
 *
 *	written by: Paul Breslin
 *		    Human Computing Resources Corp.
 *		    10 St. Mary St.
 *		    Toronto, Ontario
 *		    Canada, M4Y 1P9
 *
 *		    decvax!utcsrgv!hcr!phb
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#define BP		0xC		/* Form feed			*/

FILE	*File;
int	Braces;				/* Keeps track of brace depth	*/
int	LineNumber;			/* Count output lines		*/
int	PageNumber = 1;			/* You figure this one out	*/
int	PageLength = 66;		/* Normal paper length		*/
int	PageEnd;			/* Accounts for space at bottom	*/
int	InComment;
int	InString;
char	*Name;				/* Current file name		*/
char	FunctionName[40];
char	*ProgName;
char	*Today;

main(argc, argv)
char	**argv;
  {
	register int	i;
	char		*ctime();
	long		thetime, time();

	ProgName = argv[0];
	thetime	 = time(0);
	Today	 = ctime(&thetime);
	if( argv[1][0] == '-' && argv[1][1] == 'l' && argv[1][2] == '\0' )
	  {
		PageLength = atoi(argv[2]);
		i = 3;
		if( argc <= 3 ) Usage();
	  }
	else
	  {
		i = 1;
		if( argc <= 1 ) Usage();
	  }
	PageEnd = PageLength - (PageLength / 20);

	StartTempFile();

	for(; i < argc; ++i )
	  {
		if( (File = fopen( Name = argv[i], "r" )) == NULL )
		  {
			fprintf(stderr, "%s: Can't open file \"%s\"\n",
				ProgName, Name );
			continue;
		  }
		List();
		fclose(File);
	  }

	if( PageNumber > 1 || LineNumber > 0 )
		putchar(BP);
	EndTempFile();

	DumpTableOfContents();
	DumpTempFiles();
	Done();
  }

Usage()
  {
	fprintf(stderr, "Usage: %s [-l pagelength] file ...\n", ProgName);
	exit(1);
  }

int	SaveOut;
char	*TempName;
char	*Temp2Name;

StartTempFile()
  {
	extern char	*mktemp();

	CatchSignalsPlease();
	SaveOut = dup(1);
	TempName = mktemp("/tmp/cprXXXXXX");
	if( freopen(TempName, "w", stdout) == NULL )
	  {
		fprintf(stderr, "%s: Can't open temp file!\n", ProgName);
		exit(1);
	  }
  }

EndTempFile()
  {
	Temp2Name = mktemp("/tmp/CPRXXXXXX");
	if( freopen(Temp2Name, "w", stdout) == NULL )
	  {
		fprintf(stderr, "%s: Can't open temp file!\n", ProgName);
		exit(1);
	  }
  }

DumpTempFiles()
  {
	register int	pid, w;

	fclose(stdout);
	dup2(SaveOut, 1);

	while( (pid = fork()) < 0 ) sleep(1);
	if( pid )
		while ((w = wait(0)) != pid && w != -1);
	else
	  {
		execl( "/bin/cat", "cat", Temp2Name, TempName, 0 );
		fprintf(stderr, "%s: exec failed!\n", ProgName);
		exit(0);
	  }
  }

Done()
  {
	signal(SIGQUIT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);

	if( TempName )  unlink( TempName );
	if( Temp2Name ) unlink( Temp2Name );
	exit(0);
  }

CatchSignalsPlease()
  {
	signal(SIGQUIT, Done);
	signal(SIGHUP, Done);
	signal(SIGINT, Done);
  }

List()
  {
	register int	bp;
	char		buffer[256];

	NewPage();
	NewFile();
	bp = Braces = 0;
	while( fgets(buffer, 256, File) != NULL )
	  {
		if( bp )
			NewFunction();
		if( ++LineNumber > PageEnd ) NewPage();
		if( (Braces == 0) && LooksLikeFunction(buffer) )
			AddToTableOfContents();
		bp = PutLine(buffer);
	  }
  }

PutLine(l)
register char	*l;
  {
	extern   char	*EndComment();
	extern   char	*EndString();
	register char	c;
	int		bp;
	char		*save;

	save = l;

	bp = 0;
	for( save = l; c = *l; ++l )
		if( InComment ) 
			l = EndComment(l);
		else if( InString )
			l = EndString(l);
		else
			switch(c)
			  {
			    case '{':
				++Braces;
				break;
	
			    case '}':
				if( --Braces == 0 )
					bp = 1;
				break;

			    case '\'':
				++l;
				break;
			
			    case '"':
				InString = 1;
				break;

			    case '/':
				if( *(l+1) == '*' )
				  {
					InComment = 1;
					++l;
				  }
				break;
			  }
	printf("%s", save);
	return(bp);
  }

char *
EndComment(p)
register char	*p;
  {
	register char	c;

	while( c = *p++ )
		if( c == '*' && *p == '/' )
		  {
			InComment = 0;
			break;
		  }
	return(p-1);
  }

char *
EndString(p)
register char	*p;
  {
	register char	c;

	while( c = *p++ )
		if( c == '\\' ) 
		  {
			++p;
			continue;
		  }
		else if( c == '"' )
		  {
			InString = 0;
			break;
		  }
	return(p-1);
  }

NewFunction()
  {
	register int	i;

	if( LineNumber > (PageLength * 3 / 4) )
		NewPage();
	else
	  {
		for( i=0; i < (PageLength/7); ++i ) putchar('\n');
		LineNumber += PageLength/7;
	  }
  }

#define HEADER_SIZE 3

NewPage()
  {
	if( LineNumber > HEADER_SIZE )
	  {
		if( PageNumber >= 0 ) ++PageNumber;
		putchar(BP);
		LineNumber = 0;
	  }
	if( LineNumber == 0 )
		PutHeader();
  }

PutHeader()
  {
	register int	i, l, j;

	putchar('\n');
	l = strlen(Name);
	for( j=0; j < 3; ++j )
	  {
		printf("%s", Name);
		if( j < 2 )
			for( i=0; i < l; ++i ) putchar('\b');
	  }
	if( PageNumber > 0 )
	  {
		for( i = (l+7)/8; i < 9; ++i ) putchar('\t');
		printf("Page: %d\n\n", PageNumber);
	  }
	else
	  {
		for( i = (l+7)/8; i < 7; ++i ) putchar('\t');
		printf("%s\n\n", Today);
	  }

	LineNumber += HEADER_SIZE;
  }

#define isidchr(c)	(isalnum(c) || (c == '_'))

LooksLikeFunction(s)
register char	*s;
  {
	register char	*p;
	register int	i;
	char		*save;

	if( InComment || InString ) return(0);
	p = FunctionName;
	save = s;
	if( *s == '*' ) ++s;

	if( (*s == '_') || isalpha(*s) )
	  {
		while( isidchr(*s) )
			*p++ = *s++;
		*p = '\0';
		while( *s == ' ' ) ++s;
		if( *s != '(' ) return(0);
		while( *s ) if( *s == ')' ) break; else ++s;
		if( !*s ) return(0);

		/*
		 * This will cause the function name part of the line to
		 * be double striken.
		 */
		for( i=0; *save && *save != '('; ++i ) putchar(*save++);
		while( i --> 0 ) putchar('\b');

		return(1);
	  }
	return(0);
  }

char	*Toc[1024];
int	TocPages[1024];
int	TocCount;

AddToTableOfContents()
  {
	register int	l;
	register char	*p;

	l = strlen(FunctionName);
	p = Toc[TocCount] = (char *)malloc(l+1);
	strcpy(p, FunctionName);
	TocPages[TocCount] = PageNumber;
	++TocCount;
  }

NewFile()
  {
	register int	i, l;
	char		temp[20];

	Toc[TocCount] = (char *)malloc(130);
	sprintf(Toc[TocCount], "\n\tFile: %s ", Name);
	l = strlen(Toc[TocCount]) - 1;
	if( l < 64 )
	  {
		i = (64 - l) / 8;
		for( l=0; l < i; ++l ) strcat(Toc[TocCount], "\t");
	  }
	sprintf(temp, "  Page %d\n", PageNumber);
	strcat(Toc[TocCount], temp);
	++TocCount;
  }

DumpTableOfContents()
  {
	register int	i, j, l;

	if( TocCount == 0 ) return;
	Name = "Table of Contents";

	PageNumber = -1;
	LineNumber = 0;
	NewPage();

	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 )
			NewPage();
		printf("\t\t%s ", Toc[i]);
		l = strlen(Toc[i]);
		for( j=l; j < 48; ++j ) putchar('.');
		printf(" %d\n", TocPages[i]);
	  }
	putchar(BP);
  }

wise@cal-unix.UUCP (08/17/83)

The table of contents program as originally given was excellent.  However,
it seemed worthwhile to make a few minor modifications and one addition.
These changes are in our local version, and if you can use them,

they are:

    An alphabetically sorted index is appended to the files.

    If no file names are given, the standard input is read.

    If a '-' (minus sign) is encountered in a file list, the standard
	input is read.

My compliments to Mr. Breslin.  The program was well done, which allowed me
to make these mods in less than an hour.


/*
 *	This program prints the files named in its argument list, preceding
 *      the output with a table of contents and appending an index.
 *      Each file is assumed to be C source code (but doesn't have to be) in
 *      that the program searches for the beginning and end of functions.
 *      Function names are added to the table of contents, provided the name
 *      starts at the beginning of a line.  The function name in the output
 *      is double striken. White space is inserted after every terminating '}'
 *      character.  Thus functions and structure declarations are nicely
 *      isolated in the output. The only drawback to this is that structure
 *      initialization tables sometimes produce large quantities of white
 *      space.
 *
 *	The single option "-l" indicates that the following argument is to be
 *	the page length used for output (changing the page length hasn't been
 *	tested much).
 *
 *      If no file names are given, stdin is read.  '-' in the file list
 *      also causes stdin to be read.
 *
 *	Try it! You'll like it. (I call it cpr.c)
 *
 *	written by: Paul Breslin
 *		    Human Computing Resources Corp.
 *		    10 St. Mary St.
 *		    Toronto, Ontario
 *		    Canada, M4Y 1P9
 *
 *		    decvax!utcsrgv!hcr!phb
 *
 *      Index and standard input reading added by Rick Wise, CALCULON Corp.,
 *      Rockville, MD. -- decvax!harpo!seismo!rlgvax!cvl!umcp-cs!cal-unix!wise
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#define BP		0xC		/* Form feed			*/

FILE	*File;
int	Braces;				/* Keeps track of brace depth	*/
int	LineNumber;			/* Count output lines		*/
int	PageNumber = 1;			/* You figure this one out	*/
int	PageLength = 66;		/* Normal paper length		*/
int	PageEnd;			/* Accounts for space at bottom	*/
int	InComment;
int	InString;
char	*Name;				/* Current file name		*/
char	FunctionName[40];
char	*ProgName;
char	*Today;

main(argc, argv)
	int     argc;
	char    **argv;
  {
	register int	i;
	char		*ctime();
	long		thetime, time();

	ProgName = argv[0];
	thetime	 = time(0);
	Today	 = ctime(&thetime);
	if( argv[1][0] == '-' && argv[1][1] == 'l' && argv[1][2] == '\0' )
	  {
		PageLength = atoi(argv[2]);
		i = 3;
		if( argc < 3 ) Usage();
	  }
	else
	  {
		i = 1;
		if( argc < 1 ) Usage();
	  }
	PageEnd = PageLength - (PageLength / 20);

	StartTempFile();

	if (i == argc) {        /* no file names */
		File = stdin;
		Name = "standard input";
		List();
	}

	for(; i < argc; ++i )
	  {
		if ( ! strcmp (argv[i], "-")) {
			File = stdin;
			Name = "standard input";
		}
		else {
			if( (File = fopen( Name = argv[i], "r" )) == NULL ) {
			      fprintf(stderr, "%s: Can't open file \"%s\"\n",
				      ProgName, Name );
			      continue;
			}
		}
		List();
		if (File != stdin)
			fclose(File);
	  }

	if( PageNumber > 1 || LineNumber > 0 )
		putchar(BP);
	ListIndex();
	EndTempFile();

	DumpTableOfContents();
	DumpTempFiles();
	Done();
  }

Usage()
  {
	fprintf(stderr, "Usage: %s [-l pagelength] file ...\n", ProgName);
	exit(1);
  }

int	SaveOut;
char	*TempName;
char	*Temp2Name;

StartTempFile()
  {
	extern char	*mktemp();

	CatchSignalsPlease();
	SaveOut = dup(1);
	TempName = mktemp("/tmp/cprXXXXXX");
	if( freopen(TempName, "w", stdout) == NULL )
	  {
		fprintf(stderr, "%s: Can't open temp file!\n", ProgName);
		exit(1);
	  }
  }

EndTempFile()
  {
	Temp2Name = mktemp("/tmp/CPRXXXXXX");
	if( freopen(Temp2Name, "w", stdout) == NULL )
	  {
		fprintf(stderr, "%s: Can't open temp file!\n", ProgName);
		exit(1);
	  }
  }

DumpTempFiles()
  {
	register int	pid, w;

	fclose(stdout);
	dup2(SaveOut, 1);

	while( (pid = fork()) < 0 ) sleep(1);
	if( pid )
		while ((w = wait(0)) != pid && w != -1);
	else
	  {
		execl( "/bin/cat", "cat", Temp2Name, TempName, 0 );
		fprintf(stderr, "%s: exec failed!\n", ProgName);
		exit(0);
	  }
  }

Done()
  {
	signal(SIGQUIT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);

	if( TempName )  unlink( TempName );
	if( Temp2Name ) unlink( Temp2Name );
	exit(0);
  }

CatchSignalsPlease()
  {
	signal(SIGQUIT, Done);
	signal(SIGHUP, Done);
	signal(SIGINT, Done);
  }

List()
  {
	register int	bp;
	char		buffer[256];

	NewPage();
	NewFile();
	bp = Braces = 0;
	while( fgets(buffer, 256, File) != NULL )
	  {
		if( bp )
			NewFunction();
		if( ++LineNumber > PageEnd ) NewPage();
		if( (Braces == 0) && LooksLikeFunction(buffer) )
			AddToTableOfContents();
		bp = PutLine(buffer);
	  }
  }

PutLine(l)
register char	*l;
  {
	extern   char	*EndComment();
	extern   char	*EndString();
	register char	c;
	int		bp;
	char		*save;

	save = l;

	bp = 0;
	for( save = l; c = *l; ++l )
		if( InComment ) 
			l = EndComment(l);
		else if( InString )
			l = EndString(l);
		else
			switch(c)
			  {
			    case '{':
				++Braces;
				break;
	
			    case '}':
				if( --Braces == 0 )
					bp = 1;
				break;

			    case '\'':
				++l;
				break;
			
			    case '"':
				InString = 1;
				break;

			    case '/':
				if( *(l+1) == '*' )
				  {
					InComment = 1;
					++l;
				  }
				break;
			  }
	printf("%s", save);
	return(bp);
  }

char *
EndComment(p)
register char	*p;
  {
	register char	c;

	while( c = *p++ )
		if( c == '*' && *p == '/' )
		  {
			InComment = 0;
			break;
		  }
	return(p-1);
  }

char *
EndString(p)
register char	*p;
  {
	register char	c;

	while( c = *p++ )
		if( c == '\\' ) 
		  {
			++p;
			continue;
		  }
		else if( c == '"' )
		  {
			InString = 0;
			break;
		  }
	return(p-1);
  }

NewFunction()
  {
	register int	i;

	if( LineNumber > (PageLength * 3 / 4) )
		NewPage();
	else
	  {
		for( i=0; i < (PageLength/7); ++i ) putchar('\n');
		LineNumber += PageLength/7;
	  }
  }

#define HEADER_SIZE 3

NewPage()
  {
	if( LineNumber > HEADER_SIZE )
	  {
		if( PageNumber >= 0 ) ++PageNumber;
		putchar(BP);
		LineNumber = 0;
	  }
	if( LineNumber == 0 )
		PutHeader();
  }

PutHeader()
  {
	register int	i, l, j;

	putchar('\n');
	l = strlen(Name);
	for( j=0; j < 3; ++j )
	  {
		printf("%s", Name);
		if( j < 2 )
			for( i=0; i < l; ++i ) putchar('\b');
	  }
	if( PageNumber > 0 )
	  {
		for( i = (l+7)/8; i < 9; ++i ) putchar('\t');
		printf("Page: %d\n\n", PageNumber);
	  }
	else
	  {
		for( i = (l+7)/8; i < 7; ++i ) putchar('\t');
		printf("%s\n\n", Today);
	  }

	LineNumber += HEADER_SIZE;
  }

#define isidchr(c)	(isalnum(c) || (c == '_'))

LooksLikeFunction(s)
register char	*s;
  {
	register char	*p;
	register int	i;
	char		*save;

	if( InComment || InString ) return(0);
	p = FunctionName;
	save = s;
	if( *s == '*' ) ++s;

	if( (*s == '_') || isalpha(*s) )
	  {
		while( isidchr(*s) )
			*p++ = *s++;
		*p = '\0';
		while( *s == ' ' ) ++s;
		if( *s != '(' ) return(0);
		while( *s ) if( *s == ')' ) break; else ++s;
		if( !*s ) return(0);

		/*
		 * This will cause the function name part of the line to
		 * be double striken.
		 */
		for( i=0; *save && *save != '('; ++i ) putchar(*save++);
		while( i --> 0 ) putchar('\b');

		return(1);
	  }
	return(0);
  }

char	*Toc[1024];
int	TocPages[1024];
int	TocCount;

AddToTableOfContents()
  {
	register int	l;
	register char	*p;

	l = strlen(FunctionName);
	p = Toc[TocCount] = (char *)malloc(l+1);
	strcpy(p, FunctionName);
	TocPages[TocCount] = PageNumber;
	++TocCount;
  }

NewFile()
  {
	register int	i, l;
	char		temp[20];

	Toc[TocCount] = (char *)malloc(130);
	sprintf(Toc[TocCount], "\n\tFile: %s ", Name);
	l = strlen(Toc[TocCount]) - 1;
	if( l < 64 )
	  {
		i = (64 - l) / 8;
		for( l=0; l < i; ++l ) strcat(Toc[TocCount], "\t");
	  }
	sprintf(temp, "  Page %d\n", PageNumber);
	strcat(Toc[TocCount], temp);
	++TocCount;
  }

DumpTableOfContents()
  {
	register int	i, j, l;

	if( TocCount == 0 ) return;
	Name = "Table of Contents";

	PageNumber = -1;
	LineNumber = 0;
	NewPage();

	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 )
			NewPage();
		printf("\t\t%s ", Toc[i]);
		l = strlen(Toc[i]);
		for( j=l; j < 48; ++j ) putchar('.');
		printf(" %d\n", TocPages[i]);
	  }
	putchar(BP);
  }

ListIndex()
{
	register int	i, j, l;
	int     index[1024],
		temp,
		flag;

	if( TocCount == 0 ) return;
	for (i = 0; i < TocCount; i++)
		index[i] = i;
	do {
		flag = 0;
		for (i = 0; i < TocCount - 1; i++) {
			if (Toc[index[i]][0] == '\n' || Toc[index[i+1]][0] == '\n')
				continue;       /* don't sort across file names */
			if (strcmp (Toc[index[i]], Toc[index[i+1]]) > 0) {
				temp = index[i];
				index[i] = index[i+1];
				index[i+1] = temp;
				flag = 1;
			}
		}
	} while (flag);


	Name = "Index";

	PageNumber = -1;
	LineNumber = 0;
	NewPage();

	for( i=0; i < TocCount; ++i )
	  {
		if( Toc[index[i]][0] == '\n' )
		  {
			if( (LineNumber + 5) > PageEnd )
				NewPage();
			printf("%s", Toc[index[i]]);
			LineNumber += 2;
			continue;
		  }
		if( ++LineNumber > PageEnd )
			NewPage();
		printf("\t\t%s ", Toc[index[i]]);
		l = strlen(Toc[index[i]]);
		for( j=l; j < 48; ++j ) putchar('.');
		printf(" %d\n", TocPages[index[i]]);
	  }
	putchar(BP);
  }

jt@druca.UUCP (08/25/83)

I attempted to compile the program (that generates a table of contents)
submitted to net.sources about a week ago only to find that dup2 is
unknown here.  What does dup2 do?  Is it available on USG UNIX(tm)?  

Any information would be appreciated.  Thanks in advace.

	Jeff Turner
	AT&T Information Systems Laboratories
	...ihnp4!drux3!druca!jt

(See there really are machines in Denver on the net!)

caf@cdi.UUCP (caf) (08/25/83)

Here is the "fix" for SYSTEM III unix.
You might be able to use dup(2) if running V7 but the rules
for determining which fd gets duped to are different.

#include <fcntl.h>

	fclose(stdout);
	fcntl(SaveOut, F_DUPFD, 1);
/*	dup2(SaveOut, 1);	*/
-- 
Chuck Forsberg WA7KGX CDI Portland OR cdi!caf (503)-646-1599