umdhep@eneevax.UUCP (Todd Aven) (07/31/85)
Here is a 'working' version of the same program that was posted by someone else a while back (whoever you are, take credit). It will compile on VAX/VMS4.1 with the C2.0 compiler, and I expect that it will compile on any other UNIX system since as far as I can remember there is *no* fancy code in it. It just makes it easier to read a C source listing since it finds the function definitions and breaks the pages there. The table of contents should be sorted, but I had to comment that out for my computer (VMS) which doesn't have the same sort as UNIX and I didn't care to go to the trouble. The program should be pretty self-explanatory. The following is the C code for cpg. Get out your scissors and leave /bin/sh at home, because this is *not* an archive. -------------------cut-here----------------and-here------------------- ---------don't-stop!--------------------------------okay,-enough------ /*Tcpg - c program source listing formatter */ /******************************************************************* * * cpg.c * * DESCRIPTION OF FILE CONTENTS: * C source program listing formatter source. * * Cpg provides the facility to print out a C language source file * with headers, nesting level indicators, and table of contents. * It makes use of "triggers" for page headings, titles and * subtitles, and pagination. It also recognizes function * declarations and form feeds and treats them appropriately. * *******************************************************************/ /*S includes, defines, and globals */ /*P*/ #include <stdio.h> #include <ctype.h> #include <time.h> #define EQ == #define NE != #define GT > #define GE >= #define LT < #define LE <= #define OR || #define AND && #define TRUE 1 #define FALSE 0 #define YES 1 #define NO 0 #define SPACE ' ' #define NUL '\0' typedef short BOOL; #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define LINESINHEAD 7 #define MAXWIDTH 130 #define notend(ll) ((ll[0] EQ SLASH AND ll[1] EQ STAR AND ll[2] EQ 'E') ? FALSE : TRUE) #define SLASH '/' #define STAR '*' #define DQUOTE '"' #define SQUOTE '\'' #define BSLASH '\\' #ifdef BSD #define strrchr rindex #define strchr index #endif BSD #ifndef VMS #define delete unlink #endif extern char *strrchr (); extern char *strchr (); char *basename (); char tim_lin[40]; char *file_name; char fh_name[50] = ""; char fnc_name[40] = ""; char subttl[70] = ""; char title[70] = ""; char tocname[] = "/tmp/toc_XXXXXX"; int nlvl = 0; int outfd; int pageno = 1; int LPP = 60; int page_line = 67; int tabstop = 8; int bnflag = FALSE; int tocflag = FALSE; int incomment = FALSE; int insquote = FALSE; int indquote = FALSE; char specline = FALSE; FILE *tocfile,*fd,*dest; char *pgm; char *ReservedWord[] = { "auto", "bool", "break", "case", "char", "continue", "default", "do", "double", "else", "entry", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "while", NULL }; /*S main function */ /*Hmain */ main (ac, argv) int ac; char *argv[]; { char *std_input = "standard_input"; /* input file name */ long cur_time; /* place for current raw time */ long *time(); /* return raw time from system */ register int i; /* temporary for indexes, etc. */ struct tm *tim; /* return from localtime */ struct tm *localtime (); char cmdbuf[40]; /* place to format sort command */ int optind; /* option index */ char *p,*outfile; pgm = basename (argv[0]); if (ac EQ 1) { fprintf(stderr,"<%s>Usage: %s -b -tn -ln -c file1 ...\n",pgm,pgm); exit(); } else { dest=stdout; for(optind=1;(optind LT ac) AND (*argv[optind] EQ '-');optind++) { p=argv[optind]; switch(*++p) { case 'b': bnflag = TRUE; break; case 'l': LPP = atoi(++p); if (LPP LE 0)LPP = 999999; break; case 't': tabstop = atoi(++p); break; case 'c': tocflag = TRUE; break; case 'o': outfile = ++p; if((dest=fopen(outfile,"w"))==NULL){ fprintf(stderr,"%s\ : Couldn't open output file '%s'.",outfile); exit(); } break; default: exit(2); } } } outfd = fileno(dest); /* ------------------------------------------------------------ */ /* set up the date/time portion of page headings */ /* ------------------------------------------------------------ */ time(&cur_time); tim = localtime (&cur_time); sprintf (tim_lin, "Printed: %02d/%02d/%02d at %2d:%02d %s", tim->tm_mon + 1, tim->tm_mday, tim->tm_year, tim->tm_hour GT 12 ? tim->tm_hour - 12 : tim->tm_hour, tim->tm_min, tim->tm_hour GE 12 ? "PM" : "AM" ); /* ------------------------------------------------------------ */ /* create the temporary file for the table of contents */ /* don't bother if output is to a terminal */ /* ------------------------------------------------------------ */ mktemp (tocname); if (tocflag) { tocfile = fopen (tocname, "w"); if (!tocfile) { fprintf (stderr, "%s: unable to create tocfile %s\n", pgm, tocname); exit (2); } } /* ------------------------------------------------------------ */ /* if no file names, read standard input */ /* ------------------------------------------------------------ */ if (optind EQ ac) { fd = stdin; file_name = std_input; strcpy (fh_name,file_name); dofile (fd); fclose(fd); } else { /* ------------------------------------------------------------ */ /* process each file named on the command line */ /* ------------------------------------------------------------ */ for (i = optind; i LT ac; i++) { /* ------------------------------------------------------------ */ /* special file name `-' is standard input */ /* ------------------------------------------------------------ */ if (strcmp (argv[i], "-") EQ 0) { fd = stdin; file_name = std_input; strcpy (fh_name,file_name); } else { file_name = argv[i]; fd = fopen (argv[i], "r"); if (bnflag) strcpy (fh_name,basename(argv[i])); else strcpy (fh_name,argv[i]); if (fd EQ NULL) { fprintf (stderr, "cpg: unable to open %s\n", argv[i]); } } if (fd NE NULL) { dofile (fd); fclose (fd); } } } fflush (dest); /* ------------------------------------------------------------ */ /* sort and print the table of contents - straight alpha order */ /* on function and file name */ /* ------------------------------------------------------------ */ if (tocflag) { fclose (tocfile); sprintf (cmdbuf, "sort +1 -2 +0 -1 -u -o %s %s", tocname, tocname); /* system (cmdbuf); */ tocfile = fopen (tocname, "r"); if (!tocfile) { fprintf (stderr, "%s: unable to read tocfile\n", pgm); exit (2); } else { tocout (tocfile); fclose (tocfile); delete (tocname); } } fprintf (dest, "\f"); exit (1); } /*Sdofile - process an input file */ /*Hdofile*/ dofile (fd) FILE *fd; { register int i; /* temporary */ int lineno = 1; /* line number in current file */ register char *line; /* current line pointer */ char ibuf[MAXWIDTH]; /* original input line */ char ebuf[MAXWIDTH]; /* line with tabs expanded */ register char *p; /* temporary char pointer */ /* ------------------------------------------------------------ */ /* initialize the function name to `.' - unknown */ /* ------------------------------------------------------------ */ strcpy (fnc_name, "."); /* ------------------------------------------------------------ */ /* if building TOC, add this entry */ /* ------------------------------------------------------------ */ if (tocflag) fprintf (tocfile, "%s %s %d %d\n", fh_name, fnc_name, pageno, lineno); /* ------------------------------------------------------------ */ /* if tabs are to be expanded, use the expansion buffer */ /* ------------------------------------------------------------ */ if (tabstop) line = ebuf; else line = ibuf; /* ------------------------------------------------------------ */ /* process each line in the file, looking for triggers */ /* ------------------------------------------------------------ */ while (fgets (ibuf, MAXWIDTH, fd) NE NULL) { /* ------------------------------------------------------------ */ /* expand the input line */ /* ------------------------------------------------------------ */ expand (ebuf, ibuf); if (line[0] EQ SLASH AND line[1] EQ STAR) { /* ------------------------------------------------------------ */ /* comment found - could be a trigger */ /* ------------------------------------------------------------ */ switch (line[2]) { case 'F': case 'H': { header (&lineno, line, fd); break; } case 'P': { print_head (); break; } case 'S': { nameget (line, subttl); break; } case 'T': { nameget (line, title); break; } default: { print (&lineno, line); lineno--; break; } } lineno++; } else { /* ------------------------------------------------------------ */ /* not a comment - check for function declaration */ /* if a form feed is found, start a new page with header */ /* ------------------------------------------------------------ */ if (!nlvl AND ckfunc (lineno, line)) print_head(); if (*line EQ '\f') print_head (); else print (&lineno, line); } } page_line = LPP+1; /* force new page after file */ title[0] = NUL; /* clear title and subtitle */ subttl[0] = NUL; return; } /*Sheader - construct and print header box */ header (lineno, line, fd) register int *lineno; register char *line; register FILE *fd; { register char *p; if (line[2] EQ 'F') { nameget (line, fh_name); if (bnflag) strcpy (fh_name, basename (fh_name)); strcpy (fnc_name, "."); } else if (line[2] EQ 'H') { nameget (line, fnc_name); } if (tocflag) fprintf (tocfile, "%s %s %d %d\n", fh_name, fnc_name, pageno, *lineno); print_head (); return; } /*Snameget - get a string from a signal line */ nameget (line, name) register char *line; register char *name; { register int i; register int j; /* ------------------------------------------------------------ */ /* skip leading spaces in the trigger line */ /* copy up to trailing asterisk or end-of-line */ /* strip trailing spaces */ /* ------------------------------------------------------------ */ for (i = 3; isspace(line[i]) AND i LT MAXWIDTH; i++); for (j = 0; line[i] AND line[i] NE '*'; i++, j++) { name[j] = line[i]; } while (j-- GT 0 AND isspace (name[j])); name[++j] = NUL; return; } /*Sprint - print a line with line number */ print (lineno, line) register int *lineno; register char *line; { register int llen = strlen (line); register int i; register char sc = specline ? '*' : ' '; int j = 0; register char dc = NUL; /* ------------------------------------------------------------ */ /* new page with header if page length is exceeded */ /* ------------------------------------------------------------ */ if (page_line GT LPP) { print_head (); } /* ------------------------------------------------------------ */ /* if brace(s) found, */ /* modify the nesting level by the next nesting delta */ /* select the indicator according to the next delta */ /* if nexting is back to zero (none), clear function name */ /* ------------------------------------------------------------ */ if (fnd (line, &j)) { nlvl += j; if (j LT 0) dc = '<'; else if (j EQ 0) dc = '*'; else dc = '>'; i = nlvl; if (j LT 0) i++; fprintf (dest, "%4d%c%2d%c ", (*lineno)++, sc, i, dc); if (nlvl EQ 0) strcpy (fnc_name, "."); } else { fprintf (dest, "%4d%c ", (*lineno)++, sc); } /* ------------------------------------------------------------ */ /* break up long lines by finding the first space from the end */ /* ------------------------------------------------------------ */ if (llen GT 71) { for (i = 70; i GE 0; i--) { if (line[i] EQ SPACE) { fprintf (dest, "%*.*s \\\n", i, i, line); page_line++; break; } } j = 79 - (llen - i); for (j; j GE 0; j--) putc (SPACE, dest); fprintf (dest, "%s", &line[i+1]); } else { fprintf (dest, "%s", line); } page_line++; specline = FALSE; /* true if function declaration */ return; } /*Sprint_head - print the page heading with page number */ print_head () { char headbuf[80]; register int len; sprintf (headbuf, "[ %s | %s <- %s", tim_lin, fh_name, fnc_name); for (len = strlen (headbuf); len LT 68; len++) headbuf[len] = SPACE; sprintf (&headbuf[68], "Page %-4d ]", pageno++); fprintf (dest, "\f\n"); if (!isatty(outfd)) fprintf (dest, "_______________________________________\ ________________________________________"); fprintf (dest, "\n%s\n", headbuf); fprintf (dest, "[-------------------------------+------\ ---------------------------------------]\n"); if (*title) { sprintf (headbuf, "[ %s", title); } else { sprintf (headbuf, "[ %s", fh_name); } for (len = strlen (headbuf); len LT 78; len++) headbuf[len] = SPACE; headbuf[78] = ']'; fprintf (dest, "%s\n", headbuf); if (*subttl) { sprintf (headbuf, "[ %s", subttl); } else { sprintf (headbuf, "[ %s", fnc_name); } for (len = strlen (headbuf); len LT 78; len++) headbuf[len] = SPACE; headbuf[78] = ']'; fprintf (dest, "%s", headbuf); if (!isatty(outfd)) fprintf (dest, "\r_______________________________________\ ________________________________________"); fprintf (dest, "\n\n"); page_line = LINESINHEAD; return; } /*S fnd - return true if a brace is found */ fnd (in, nchg) register char *in; register int *nchg; { # define LBRACE '{' # define RBRACE '}' # define SHARP '#' # define COLON ':' register found = FALSE; /* true if keyword found */ register char blank = TRUE; /* used to check for shell/make */ /* comments beginning with #/: */ register int inshcomment = FALSE; /* true if in shell comment */ *nchg = 0; /* initialize net change to zero */ /* ------------------------------------------------------------ */ /* check each character of the line */ /* ------------------------------------------------------------ */ for (in; *in; in++) { if (!incomment AND !inshcomment AND !indquote AND !insquote) { if (*in EQ SLASH AND *(in+1) EQ STAR) { incomment = TRUE; blank = FALSE; } else if (blank AND ((*in EQ SHARP OR *in EQ COLON) AND (*(in+1) NE LBRACE AND *(in+1) NE RBRACE)) ) { inshcomment = TRUE; blank = FALSE; } else if (*in EQ DQUOTE AND (*(in-1) NE BSLASH OR *(in-2) EQ BSLASH)) { indquote = TRUE; blank = FALSE; } else if (*in EQ SQUOTE AND (*(in-1) NE BSLASH OR *(in-2) EQ BSLASH)) { insquote = TRUE; blank = FALSE; } else if (*in EQ LBRACE) { (*nchg)++; found = TRUE; blank = FALSE; } else if (*in EQ RBRACE) { (*nchg)--; found = TRUE; blank = FALSE; } else if (!isspace (*in)) { blank = FALSE; } } else if (incomment AND *in EQ STAR AND *(in+1) EQ SLASH) incomment = FALSE; else if (indquote AND *in EQ DQUOTE AND (*(in-1) NE BSLASH OR *(in-2) EQ BSLASH)) indquote = FALSE; else if (insquote AND *in EQ SQUOTE AND (*(in-1) NE BSLASH OR *(in-2) EQ BSLASH)) insquote = FALSE; } return found; } /*Stocout - print out the table of contents */ tocout (toc) FILE *toc; { char buf[80]; char filenam[80]; char fncnam[80]; int page; int line; char outline[80]; register int toclines = LPP + 1; while (fscanf (toc, "%s%s%d%d", filenam, fncnam, &page, &line) EQ 4) { if (toclines GT LPP-7) { fprintf(dest,"\f\n\n"); if (!isatty(outfd)) fprintf (dest,"\r\ _____________________"); fprintf(dest,"\n\ [ TABLE OF CONTENTS ]"); if (!isatty(outfd)) fprintf (dest,"\r\ _____________________"); fprintf(dest,"\n\n\ File -> Function Page Line"); if (!isatty(outfd)) fprintf (dest,"\r\ ________________________________________\ ________________________________________"); fprintf(dest,"\n\n"); toclines = 0; } toclines++; fprintf (dest,"\ %16s -> %-16.16s ............ %3d %5d\n", filenam, *fncnam EQ '.' ? "START" : fncnam, page, line); } return; } /*S expand - expand tabs to tabstop */ expand (to, from) register char *to; register char *from; { register int i; register int tofill; # define BACKSPACE '\b' # define FORMFEED '\f' # define NEWLINE '\n' # define RETURN '\r' # define TAB '\t' i = 0; while (*from) { switch (*from) { case TAB: tofill = tabstop - (i % tabstop); i += tofill; while (tofill--) *(to++) = SPACE; break; case NEWLINE: case RETURN: i = 0; case FORMFEED: *(to++) = *from; break; case BACKSPACE: i--; *(to++) = *from; break; default: i++; *(to++) = *from; break; } from++; } *to = NUL; return; } /*S ckfunc - check line for function declaration */ #define isidchr(c) (isalnum(c) || (c == '_')) ckfunc (lineno, s) register int lineno; register char *s; { register char *p; register int Cnt; register int i; register int result; register char found = FALSE; static char *_fnm = "ckfunc"; char FunctionName[40]; if(!strcmp (fnc_name, ".") AND !incomment && !indquote && !insquote) { found = TRUE; while (found) { p = &FunctionName; found = FALSE; for (s; isascii (*s) && isspace (*s) && *s; s++); if( *s == '*' ) { for (++s; isascii (*s) && isspace (*s) && *s; s++); } if ((*s == '_') || isalpha(*s)) { while (isidchr (*s)) *p++ = *s++; *p = '\0'; for (found = FALSE, i = 0; !found AND ReservedWord[i]; i++) { if (result = strcmp (FunctionName, ReservedWord[i])) found = TRUE; } } } for (s; isascii (*s) && isspace (*s) && *s; s++); if (*s EQ '(') { for (found = FALSE; *s AND !found; s++) found = *s EQ ')'; if (found) { for (; *s AND isspace (*s); s++); found = (*s NE ';') AND (*s NE ','); if (found) { strcpy (fnc_name, FunctionName); fprintf (tocfile, "%s %s %d %d\n", fh_name, fnc_name, pageno-1, lineno); specline = TRUE; } } } } return found; } /*S basename - return the basename part of a pathname */ /******************************************************************* * * basename * * given a (presumed) pathname, return the part after the last slash * *********************************************************************/ char *basename (str) register char *str; { register char *ret,*mid; register int index; if (ret = strrchr (str, '/')) ret++; else if (ret = strrchr (str, ']')) ret++; else if (ret = strrchr (str, ':')) ret++; else ret = str; if(mid=strchr(ret,';'))*mid='\0'; return ret; }
schlenk@fauern.UUCP (08/06/85)
Although I like your C source listing formatter there are still 3 problems left: 1. In 'cfunc' (line 804) you write unconditionally to tocfile. In case no Table of contents is selected, this will fail. Solution: insert the if-statement. if (tocflag) fprintf (tocfile, "%s %s %d %d\n", fh_name, fnc_name, pageno-1, lineno); 2. Preprocessor #if's are not handled. In the worst case, the nesting level counting gets corrupted. I know, that's difficult to handle. 3. Where is the man-page I can give to our users ? Peter UUCP: ...!mcvax!unido!fauern!schlenk