cpg -- C source lister

Todd Aven umdhep at eneevax.UUCP
Thu Aug 1 03:18:51 AEST 1985


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;
}



More information about the Comp.sources.unix mailing list