wck@ccieng5.UUCP ( BILL K. William C. King) (08/24/83)
Here is a version of the program `cpr' which has been floating around which I have hacked up. I've added a few options. The first is the `-n' option which causes lines in the source file to be numbered. The second option is `-o' which is used for putting One-Function-Per-Page. In addition I have added line folding. (A default line width of 80 is used instead of having an option to specify the line length because I did not feel like changing things in the table of contents which use this as a default.) I have also written up a man page since I had not seen any others floating around. By the way, for some reason unknown to me now I changed the `-l' option to take the space from between that and the supplied page length. Lance E. Shepard a lowly co-op from: Computer Consoles, Inc. & Rochester Institute of Technology ...!seismo!rochester!ritcv!ritvp!les8070 or for a short time longer ...!seismo!rochester!ritcv!ccieng5!wck ________________ : Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting cpr.n' sed 's/^X//' <<'//go.sysin dd *' >cpr.n X.TH CPR 1 X.SH "NAME" cpr \- print `C' files X.SH "SYNOPSIS" X.B cpr [ -l# ] [ -n ] [ -o ] [ file ] ... X.SH "DESCRIPTION" X.I Cpr produces a printed listing of one or more X.B `C' X.I files. The output is separated into pages headed by the name of the file (in bold face) a date, and the page number. Function names are output in bold face. An index (same format as table of contents, except in alphabetical order) now follows the output file. Lines greater than 80 columns in width (minus the width of added line numbers, if any) are folded. X.PP The following options are allowed: X.TP X.BI \-l# Take the length of the page to be X.I # lines instead of the default 66. X.TP X.BI \- n Print line numbers. X.TP X.BI \- o Print X.I one\-function\-per\-page. X.PP X.SH "FILES" X.nf X/tmp/cpr$$ temp files holding text X/tmp/CPR$$ X.fi X.SH "SEE ALSO" X.nf cat(1) fold(1) num(1) pr(1) X.fi X.SH "DIAGNOSTICS" Various messages about being unable to open files. Self explanatory. X.SH "BUGS" Mistakes structure declarations as a function. X.br Unable to find function names that do not begin at the left margin. X.SH "AUTHOR" X.nf Paul Breslin original Human Computing Resources Corp. Rick Wise modified original CALCULON Corp. Lance E. Shepard modified original CCI & RIT X.fi //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 444 cpr.n /bin/echo -n ' '; /bin/ls -ld cpr.n fi /bin/echo 'Extracting cpr.c' sed 's/^X//' <<'//go.sysin dd *' >cpr.c X/* * 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 X/* X/* * Index and standard input reading added by Rick Wise, CALCULON Corp., * Rockville, MD. -- decvax!harpo!seismo!rlgvax!cvl!umcp-cs!cal-unix!wise */ X/* * The following options have been added. The `-n' option is used * to number source lines. Numbering starts at 1 (one) and is reset * to this number for each file. The `-o' option is used to put 1 * (one) function on each page instead of multiple functions per page. * Line folding has been added. * * The `-l' option has been changed so that the blank space between * it and the following number has been eliminated. ("-l60" instead * of "-l 60") * * modified by: Lance E. Shepard * CCI & RIT (a lowly co-op) * Rochester, NY * * ...!rochester!ritcv!ritvp!les8070 * * NOTE: * These modifications have been TESTED only ONCE. (And this * one time was under IDEAL CONDITIONS.) NO GUARANTEES!! * */ #include <stdio.h> #include <ctype.h> #include <signal.h> #define BP 0xC /* Form feed */ #define MAX_S 256 /* Maximum string length */ #define WIDTH 80 /* Page Width */ 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; int OnePerPage = 0; int NumLines = 0; int Number = 1; char *Name; /* Current file name */ char FunctionName[40]; char *ProgName; char *Today; main(argc, argv) int argc; char **argv; { register int i = 1; char *ctime(); long thetime, time(); ProgName = argv[0]; thetime = time(0); Today = ctime(&thetime); while (argv[i][0] == '-') { switch (argv[i][1]) { case 'l': if (!isdigit(argv[i][2])) { Usage(); break; } PageLength = atoi(&argv[i][2]); break; case 'n': NumLines = 1; break; case 'o': OnePerPage = 1; break; default: Usage(); break; } i++; } PageEnd = PageLength - (1 + 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 [-lpagelength] [-n] [-o] 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(); char *expand(); char *substr(); register char c; int bp; char *save; char *section; int offset; char Digits[15]; int Size; int pos; bp = 0; for( save = expand(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; } if (NumLines) { sprintf(Digits,"[%d] ", Number); Size = strlen(Digits); } else Size = 0; if (strlen(save) + Size > WIDTH) { section = substr(save, 0, WIDTH - Size); if (section[strlen(section) - 1] != ' ') { offset = (int) rindex(section, ' ') - (int) section; section[offset] = NULL; } else offset = strlen(section) - 1; if (NumLines) { printf("[%d] %s\n", Number++, section); } else { printf("%s\n", section); } pos = offset + 1; do { section = substr(save, pos, pos + WIDTH - 8); if (strlen(section) + 8 > WIDTH && section[strlen(section) - 1] != ' ') { offset = (int) rindex(section, ' ') - (int) section; section[offset] = NULL; } if (section[strlen(section) - 1] == '\n') section[strlen(section) - 1] = NULL; printf("C %s\n", section); if (++LineNumber > PageEnd) NewPage(); } while ((pos += offset + 1) < strlen(save)); } else { if (NumLines) printf("[%d] %s", Number++, save); else 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 { if (!OnePerPage) { for( i=0; i < (PageLength/7); ++i ) putchar('\n'); LineNumber += PageLength/7; } else NewPage(); } } #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; char *save; int Cnt; char Digits[15]; int AddOne = 0; if( InComment || InString ) return(0); p = FunctionName; save = s; if( *s == '*' ) { ++s; AddOne = 1; } 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. */ if (NumLines) { sprintf(Digits,"[%d] ", Number); Cnt = strlen(Digits) + AddOne; while (Cnt-- > 0) putchar(' '); AddOne = 0; } while (*save && *save != '(') putchar(*save++); putchar('\r'); 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; if (NumLines) Number = 1; } 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); } X/* * This is the function substr(). The calling sequence is: * * substr(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 = substr(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 * substr(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); } X/* * 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') { strcpy(&retstr[count], " "); count += 7; } else retstr[count] = *string; } retstr[count] = NULL; return(retstr); } 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); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 444 cpr.c /bin/echo -n ' '; /bin/ls -ld cpr.c fi
raj@zeppo.UUCP (Richard A. Johnson) (08/26/83)
I tried the cpr (C print) program and noticed two problems. First, and mentioned in the manual page, structure declarations are treated like functions (i.e., given extra white space after the '}'). Second, tabs are always converted to exactly 8 spaces (this is annoying if you use tabs to line up columns). I fixed the first problem by looking for a ';' after the '}' (most structure declarations that I have seen end with "};" whereas functions just end with '}'). If there is a ';' there, the '}' is treated like any other character. If not, then it is a function and given extra white space. I fixed the second problem by substituting one space for the tab. Then I add more spaces, if necessary, until the next character position is a tab stop. My fixes to cpr.c follow: 273c273,276 < bp = 1; --- > if( *(l+1) == ';' ) > ++l; > else > bp = 1; 613c616,617 < * of the tab character with 8 (eight) spaces. The function returns --- > * of the tab character with enough spaces to put the next character > * at the next tab stop. The function returns 633,634c637,639 < strcpy(&retstr[count], " "); < count += 7; --- > retstr[count] = ' '; > while (((count + 1) % 8) != 0) > retstr[++count] = ' '; Rich Johnson BTL - Whippany zeppo!raj