/*************************************************************************** *striphdr.c - Program to read source files and produce alphabetised * listing of header blocks, telling which files they came * from. * * Copyright (c) 1983-2001, Microsoft Corporation. All rights reserved. * *Author: Tom Corbett, Brian Lewis - Microsoft Corp. * *Usage: striphdr [switches] file {file ...} * Accepts native code or C source; determines source * type based on filename suffix. * Wildcards may be used in the file names. * * switches: * * -m process module headers only * -b process both procedure and module headers * [default is procedure headers only] * -l process only first line of each header * -n process none of header (i.e. lists only function name) * -s process named section only (may be used with -l) * [default is to process whole header] * -d delete processed section from input file * -q quiet, do not print headers (useful only with -d) * -x gives extension for output file when -d used * [default: .new] * -r remove revision histories from file headers * (equivalent to -m -d -q -s "Revision History") * *Purpose: * Given a list of Native code and/or C sources, strip out routine or module * headers and produce an alphabetised listing of the headers, telling where * (filename) each header came from. Optionally, delete part or all of the * headers from the source file. * * For each file on the command line (wildcards may be used), striphdr reads * the file, and records information from the file and/or procedure headers. * This information is then sorted by procedure name/file name, and printed * to stdout, with an indication of which file each procedure is from. If * the -d flag is activated, the information is also deleted from the input * file and the new file placed in a file with the same name but the * extension .new (this extension can be changed with the -x switch). The * actual input file is also left as is. When using the -d switch, -q will * eliminate the output, so that only the deleting action takes place. By * default only the procedure headers are scanned, the -m and -b switches * change this. By default all the information in a header is printed or * deleted, the -l, -n, and -s switches change this. The -r switch is an * abbreviation which will remove revision histories from the file headers. * * Input filenames with suffixes of .c or .h are assumed to contain C source, * and suffixes of .asm or .inc imply native code source. Routine or module * names must be on the first or second line following the start of header; * if on the second line then the first line must contain only whitespace * after an optional '*' (if C source) or ';' (if native code source). * Routine names can contain a return type and parameters; multiple entry * point should be seperated by commas. Header start and end symbols must * start in the left-most column. Module headers and routine headers are * marked in the same manner; position relative to the beginning of file is * used to determine which header type is appropriate. * * Source is detabbed (1 tab assumed to equal 4 spaces in C, 8 spaces in native * code) and routine names are parsed to ensure that the correct name is * grabbed for sorting. * * C Headers are started with a '/' characters in the first column followed * by at least 3 '*' characters, and are ended with at least 4 '*' characters * followed by a '/'. Each line within a header must begin with a '*', except * lines beginning with '#if', '#else', or '#endif'. Module headers must be * preceded with nothing except (perhaps) blank lines; if any non-blank lines * are found prior to the first header in a module, it is assumed that the * header belongs to a routine, otherwise, to the entire module. * * Native code headers are started by a ';' followed by at least 3 '*' * characters, and the header end is denoted by a ';' followed by at least * 4 '*' characters. There must be a ';' character in the left-most column of * every line of the header block; the only exception to this is that the ' * if', 'else', and 'endif' switches are allowed inside header blocks. Module * headers can be preceded with any number of blank lines, a TITLE statement, * a NAME statement, and/or a PAGE statement; if anything else is * encountered, subsequent headers will be assumed to be routine headers. * * Sections within a header must have a title beginning on the 2nd * character of the line, and the section is assumed to extend to the next * line with a non-blank character in position 2. * * No non-header comments should begin in column 1 with '/***' in C or * ';***' in native code; this will confuse striphdr. * * *Revision History: * * 01-01-83 TOMC Created [tomc] * 09-09-85 BL Modified to allow the option of stripping module headers * instead of routine headers via the -m switch * 09-30-85 BL Modified to accept either C Source or native code source. * Modified to detab C Source assuming 3 spaces per tab. * Modified to parse C Routine names properly, allowing * types, macros, etc. to precede routine names. * Modified to make the sorting be case insensitive. * 11-12-85 BL Fixed bug that caused 1st char to always be removed from * headers, and that caused problems when a blank line follows * header starts. * 06-01-87 PHG fixed GetNonBlank() * allowed 'title' and 'Title" as well as 'TITLE' * allowed NAME, PAGE as well as TITLE * made tab expansion for both ASM and C files * both C and ASM procedure names can have args, * multiple entries * simplified some code and a few minor bug fixes * added -b, -n, -s, -d, -q, -x, -r switches * 05-10-90 JCR Accept .cxx/.hxx files (C++ support), misc cleanup * 04-06-95 SKS Accept .cpp/.hpp as equivalent to .cxx/.hxx, respectively ***************************************************************************/ #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define NAMBUFSZ 20000 /* size of buffer for holding all procedure names */ #define LINEBUFSZ 250 /* maximum size of 1 physical line */ #define MAXENT 3000 /* maximum number of procedure entry points */ #define MAXFUNCS 2000 /* maximum number of function headers */ /* abort codes */ #define NMBUFOV 0 /* name buffer table overflow */ #define FILENF 1 /* file not found */ #define FUNCSOV 2 /* function table overflow */ #define ENTOV 3 /* entry table overflow */ #define FILENO 4 /* cannot open file */ #define OUTSPACE 5 /* out of disk space */ /* tab sizes, pagelength */ #define TABSIZE_C 4 #define TABSIZE_ASM 8 /* tab sizes for code */ #define PAGELEN 60 /* pagelength for page breaks; 60 for HP LaserJet */ struct { char *entname; /* name of entry point */ int funcid; /* function index for this entry point */ char printed; /* flag to ensure each entry printed just once */ } entry [MAXENT]; int nentries; /* number of entries parsed so far */ struct { char *filename; /* name of file which contains this function */ long filepos; /* seek position on file "temp" */ int nlines; /* number of lines in function header */ } func [MAXFUNCS]; int nfuncs; /* number of function parsed so far */ long ofpos; /* output file seek position */ char linebuf[LINEBUFSZ]; char nambuf[NAMBUFSZ]; /* buffer for holding function names */ int nambufi; /* next free byte in nambuf */ char fEof; FILE *ofd; /* output file descriptor */ FILE *ifd; /* input file descriptor */ FILE *cfd; /* copy file descriptor */ FILE *fopen(); int outline; /* output line number (1..PAGELEN) for page ejects */ int fDelete = FALSE; /* TRUE if copy and delete instead of display */ int fGetModNams = FALSE; /* TRUE if strip module names */ int fGetProcNams = TRUE; /* TRUE if strip procedure names */ int fC_Source; /* TRUE if we're processing C source, false if .asm */ int fDoAll = TRUE; /* TRUE if we process all of header */ int fDoFirst = FALSE; /* TRUE if we process first line of header */ int fDoSection = FALSE; /* TRUE if we process named section of header */ int fQuiet = FALSE; /* TRUE is quiet mode */ char SectionHead[80]; /* Section name */ char CopyExt[4] = "new"; /* extension of new files */ char *szFilenameCur; /* name of file being read (for error reporting) */ /*** *MyAbort(code, s) - abort program with message *Purpose: * abort the program with correct message. * *Entry: * code = error code * s = filename for code == FILENF * *Exit: * exits to DOS * *Exceptions: * *******************************************************************************/ void MyAbort(code, s) int code; char *s; { fprintf(stderr, "Fatal Error - "); if (code == NMBUFOV) fprintf(stderr, "NAME Buffer overflow\n"); if (code == FILENF) fprintf(stderr, "File not found: %s\n", s); if (code == FUNCSOV) fprintf(stderr, "Function table overflow\n"); if (code == ENTOV) fprintf(stderr, "Entry table overflow\n"); if (code == FILENO) fprintf(stderr, "Cannot open file: %s\n", s); if (code == OUTSPACE) fprintf(stderr, "Out of disk space\n", s); exit(code); } /* MyAbort */ /*** *FindIfC(szFile) - find if filename if C or ASM * *Purpose: * Set global flag fC_Source to TRUE if the filename ends in * .c, .h, .cxx (.cpp), or .hxx (.hpp); FALSE if it ends in .asm or .inc. * The extension comparison is NOT case senstive. If none of * these are found, don't set the flag,and return FALSE, * else return TRUE. * *Exit: * sets flag fC_Source * returns FALSE if neither C nor ASM source * returns TRUE if fC_Source set ********************************************************************/ FindIfC(szFile) char *szFile; { int cbFileName = strlen(szFile); char *pbPastName = szFile + cbFileName; /* points at NULL at string end */ if ((!_stricmp (".c", pbPastName - 2)) || (!_stricmp (".h", pbPastName - 2)) || (!_stricmp (".s", pbPastName - 2)) || (!_stricmp (".cpp", pbPastName - 4)) || (!_stricmp (".hpp", pbPastName - 4)) || (!_stricmp (".cxx", pbPastName - 4)) || (!_stricmp (".hxx", pbPastName - 4))) fC_Source = TRUE; /* file is assumed to be C source */ else if ((!_stricmp (".asm", pbPastName - 4)) || (!_stricmp (".inc", pbPastName - 4))) fC_Source = FALSE; /* file is assumed to be Native Code source */ else return (FALSE); /* didn't find an appropriate suffix */ return (TRUE); } /* FindIfC */ /*** *MakeCopyfileName(filename, copyname) - make copyfile name from input name * *Purpose: * Put the copy file suffix onto a file name * *Entry: * filename = input file name * *Exit: * copyname = filled in with copy file name * *Exceptions: * *******************************************************************************/ void MakeCopyfileName(filename, copyname) char *filename, *copyname; { char *p; strcpy(copyname, filename); p = copyname + strlen(copyname); while (*(--p) != '.') ; /* p points to '.' */ *(p+1) = '\0'; /* cut off string after '.' */ strcat(copyname, CopyExt); /* put on copy file extension */ } /*** *linelen(pLine) * *Purpose: * Return the number of characters in a passed line. The line terminator * ('\n') is not included in the count. * *Entry: * pLine = a pointer to the first char in the line. *Exit: * returns the number of characters in the line (an int). ***************************************************************************/ int linelen(pLine) char *pLine; { register int index; for (index = 0; *(pLine + index) != '\n'; index++); return (index); } static int cLinesRead; static char fInHeader = FALSE; /*** *ReadLine(copyit) - read line and analyze it * *Purpose: * read 1 line from input file into 'linebuf'. * if copyit == TRUE, copy previous line to copy file * *Entry: * copyit = TRUE means copy the line to the copy file * *Exit: * returns -1 for EOF, * 0 for vanilla lines, * if C Source, * 1 for lines which begin "/***" (start of header) * 2 for lines which begin "/****" (end of header) * if Native Code Source, * 1 for lines which begin ";***" (start of header) * 2 for lines which begin ";****" (end of header) * ***************************************************************************/ int ReadLine(copyit) int copyit; /* TRUE = copy line to copy file */ { register int i; register int cbLine; if (copyit) { if (fputs(linebuf, cfd) == EOF) MyAbort(OUTSPACE); } /* copy previous line if requested */ if ((fgets(linebuf, LINEBUFSZ, ifd)) == NULL) { fEof = TRUE; return(-1); } cLinesRead++; cbLine = linelen(linebuf); if ((linebuf[0] == ';') && (!fC_Source)) { /* native code comment */ if ((linebuf[1] == '*') && (linebuf[2] == '*') && (linebuf[3] == '*')) /* know we have either start or end of a header */ if (fInHeader) { fInHeader = FALSE; if (linebuf[4] == '*') return (2); /* valid end of header */ /* error, got start of header when we're already in a header */ fprintf(stderr, "Unterminated header for function: %s\n", entry[nentries].entname); fprintf(stderr, " before line %d of file %s\n", cLinesRead, szFilenameCur); } else { /* have a valid start of header */ fInHeader = TRUE; return (1); } } else if ((linebuf[0] == '/') && (fC_Source)) { /* C code comment? */ if ((linebuf[1] == '*') && (linebuf[2] == '*') && (linebuf[3] == '*')) if (fInHeader) { /* error, got start of header when we're already in a header */ fprintf(stderr, "Unterminated header for function: %s\n", entry[nentries].entname); fprintf(stderr, " before line %d of file %s\n", cLinesRead, szFilenameCur); } else { fInHeader = TRUE; return (1); /* valid start of header */ } } else if ((linebuf[0] == '*') && (fC_Source)) { /* C code comment? */ for (i = 1; (i < cbLine) && (linebuf[i] == '*'); i++); if ((linebuf[i] == '/') && (i > 3)) { /* have a valid end of header */ if (fInHeader) { fInHeader = FALSE; return (2); } /* got a header terminator when we weren't within a header */ fprintf(stderr, "Illegal termination to header for function: %s", entry[nentries].entname); fprintf(stderr, " in line %d of file %s\n", cLinesRead, szFilenameCur); } /* if */ } /* else if */ return(0); /* vanilla line */ } /* ReadLine */ /*** *WriteLine(pb, of) - write a line and expand tabs * *Purpose: * Write a zero terminated string to file 'of'. * If we're writing C Source header lines, detab them as we go. * *Entry: * pb = points to 1st byte of 0-terminated string. * of = aft index for destination file. * *Exit: * 'ofpos' is bumped to reflect current output file position. * ***************************************************************************/ void WriteLine(pb, of) register char *pb; FILE *of; { register int linepos = 0; /* column position on output line */ int modTS; /* linepos mod tabSize */ int cSpaces; /* number of spaces to print to next tab stop */ int index; /* 'for' index for printing spaces */ int tabSize; /* current tab size */ if (fC_Source) tabSize = TABSIZE_C; else tabSize = TABSIZE_ASM; while (*pb != '\0') { if (*pb == '\t') { /* if a tab character found */ modTS = linepos % tabSize; cSpaces = tabSize - modTS; for (index = 0; index < cSpaces; index++) { fputc(' ', of); linepos++; } } /* if */ else { fputc(*pb, of); linepos++; } pb++; } /* while */ if (ferror(of)) MyAbort(OUTSPACE); ofpos = ftell(of); /* update output file seek position for next line */ } /* WriteLine */ /*** *SwitchFound(s) - see if IF, ELSE, ENDIF switch on this line * *Purpose: return TRUE if an "if", "else", or "endif" are identified at * the start of string 's'. This is used to check to see if a * header line which doesn't start with ';' is an assembler switch. *Entry: * s = pointer to a char which represents the string to be checked. * *Exit: * returns TRUE if the string matches "if", "else", or "endif" of either * upper or lower case, FALSE otherwise. ***************************************************************************/ SwitchFound(s) char *s; { if (fC_Source) { /* strncmp returns 0 (which maps to FALSE) iff a match was found */ if (strncmp(s, "#if", 3) && strncmp(s, "#IF", 3) && strncmp(s, "#else", 5) && strncmp(s, "#ELSE", 5) && strncmp(s, "#endif", 6) && strncmp(s, "#ENDIF", 6)) return (FALSE); } else { if (strncmp(s, "if", 2) && strncmp(s, "IF", 2) && strncmp(s, "else", 4) && strncmp(s, "ELSE", 4) && strncmp(s, "endif", 5) && strncmp(s, "ENDIF", 5)) return (FALSE); } return (TRUE); } /*** *GetNonBlank(copyit) - read line, find first non blank character * *Purpose: * Read in a line; return linebuf index if non-blank line found, -1 otherwise * *Entry: * copyit = TRUE means copy line to copy file * *Exit: * returns -1 if blank line read * return index of first non-whitespace char otherwise * ***************************************************************************/ GetNonBlank(copyit) int copyit; /* copy lines to copy file if TRUE */ { register int i; register int cbLine; if (ReadLine(copyit) == -1) { return(-1); } cbLine = linelen(linebuf); for (i = 0; ((linebuf[i] == ' ') || (linebuf[i] == '\t')) && (i < cbLine); i++); if (i >= cbLine) return (-1); else return (i); } /* GetNonBlank */ /*** *ReadToHeader() - reads to beginning of next header * *Purpose: * Reads to beginning of next header or EOF * *Entry: * *Exit: * Returns -1 if at EOF, 0 if at a header * ************************************************************************/ int ReadToHeader() { while (!fEof && ReadLine(fDelete) != 1) ; if (fEof) return -1; /* at EOF */ else return 0; /* at beginning of header */ } /*** *Get1stHdr() - read to first header * *Purpose: * Read lines until the first header is found. Return TRUE if this is * the header for the module, or FALSE if no module header is found. * * In C Sources, we assume that if the first non-blank line in the source * starts with '/***', then it's the start of a module header. * In Native-Code Sources, we assume that there can exist any number * of blank lines, optionally followed by a TITLE statement, followed * by any number of blanks lines, and then by the module header if * it exists. * *Entry: * NONE *Exit: * TRUE if module header found, FALSE otherwise. Note that if fEof is found, * FALSE is returned, and if !fEof and no module header is found, this * routine will read in lines until the first routine header is found,or * fEof. ***************************************************************************/ Get1stHdr() { register int index; register int i; while (((index = GetNonBlank(fDelete)) == -1) && (!fEof)) ; if (fEof) { fprintf(stderr, "warning: no file header on file "); fprintf(stderr, "%s\n", szFilenameCur); return (FALSE); } /* now, index is set into linebuf for a non-blank character */ if (fC_Source) { /* if header exists, must be first non-blank line */ if ((linebuf[0] == '/') && (linebuf[1] == '*') && (linebuf[2] == '*') && (linebuf[3] == '*')) { fInHeader = TRUE; return (TRUE); } else { /* read to start of first header, tell caller no module header found */ ReadToHeader(); fprintf(stderr, "warning: no file header on file "); fprintf(stderr, "%s\n", szFilenameCur); return (FALSE); } } /* must be native-code source - - - can have a TITLE line with blank lines before and after it - - - all prior to module header */ for (i = 1; i <= 3; ++i) { /* do thrice, might have TITLE, NAME, and PAGE */ if (strncmp("TITLE", linebuf + index, 5) == 0 || strncmp("title", linebuf + index, 5) == 0 || strncmp("Title", linebuf + index, 5) == 0 || strncmp("NAME", linebuf + index, 4) == 0 || strncmp("name", linebuf + index, 4) == 0 || strncmp("Name", linebuf + index, 4) == 0 || strncmp("PAGE", linebuf + index, 4) == 0 || strncmp("Page", linebuf + index, 4) == 0 || strncmp("page", linebuf + index, 4) == 0) { /* found TITLE, NAME, or PAGE statement */ while (((index = GetNonBlank(fDelete)) == -1) && (!fEof)) ; if (fEof) { fprintf(stderr, "warning: no file header on file "); fprintf(stderr, "%s\n", szFilenameCur); return (FALSE); } } } /* if there was a TITLE and/or NAME statement, we've eaten it, and any following blank lines; must now have module header, if it exists */ if ((linebuf[0] == ';') && (linebuf[1] == '*') && (linebuf[2] == '*') && (linebuf[3] == '*')) { fInHeader = TRUE; return (TRUE); } else { /* read to start of first header, tell caller no module header found */ ReadToHeader(); fprintf(stderr, "warning: no file header on file "); fprintf(stderr, "%s\n", szFilenameCur); return (FALSE); } } /* Get1stHdr */ /*** *AdvanceOne(s) - advance one char if one whitespace or comment token * *Purpose: * 's' points to the first character in an input line; if it's a whitespace * token or a comment token, return a pointer to the next character in the * line, otherwise, return 's' unchanged. * *Entry: * s = ptr to first char in input line * *Exit: * returns ptr the next char if *s is whatspace, ';', or '*', * otherwise returns s * *******************************************************************************/ char *AdvanceOne(s) char *s; { if (*s == '*') { if (fC_Source) s++; } else if (*s == ';') { if (!fC_Source) s++; } else if ((*s == ' ') || (*s == '\t')) s++; return(s); } /* AdvanceOne */ /*** *FuncNamePtr(pbName) - find function name from this or next line * *Purpose: * Given a pointer into the current header line, return a pointer to * the first non-blank or comment symbol; if none found in the existing * line, read in another and try that one. * if a C routine line, skip any types, etc., and return a pointer to * the filename itself. * * If a C routine line: * ------------------- * Assumes that C routine names will end with a left paren. * Allows left parens in a summary of purpose if said summary is * preceded by a '-' which comes after the routine name. * Assumes that there are 1 or 0 spaces between the the routine name * and the mandatory left paren. * *Entry: * pbName = a pointer to the place to begin searching in linebuf. *Exit: * Returns a pointer to the filename. * ***************************************************************************/ char *FuncNamePtr(pbName) register char *pbName; { register int cbLine; char *pbTmp; int iStart; /* first, see if we have a blank line - if so, read in another one, and assume that it has the filename; don't check that one ... */ pbTmp = AdvanceOne(pbName); cbLine = linelen(pbTmp); for (iStart = 0; ((*(pbTmp+iStart) == ' ') || (*(pbTmp+iStart) == ' ')) && (iStart < cbLine); iStart++); if (iStart >= cbLine) { if (ReadLine(fDelete) != 0) { /* then we got a procedure end w/out a name! */ if (fEof) fprintf(stderr, "EOF not expected\n"); else fprintf(stderr, "Illegal header termination\n"); fprintf(stderr, " in line %d of file %s\n", cLinesRead, szFilenameCur); return((char *)-1); } pbName = linebuf; } /* now, assume that there is a filename somewhere at or after pbName */ iStart = 0; while (*(pbName+iStart) != '(' && *(pbName+iStart) != '-' && *(pbName+iStart) != ',' && *(pbName+iStart) != '\n') ++iStart; /* search fwd for '(', '-', ',' or '\n' */ do { --iStart; } while ((*(pbName+iStart) == ' ' || *(pbName+iStart) == '\t') && iStart > 0); /* search back through whitespace */ while (*(pbName+iStart) != ' ' && *(pbName+iStart) != '\t' && *(pbName+iStart) != ';' && *(pbName+iStart) != '*') --iStart; /* search back to beginning of name */ ++iStart; /* point to first letter of name */ return (pbName+iStart); /* return pointer to name */ } /* FuncNamePtr */ /*** *MyReadFile(pbNam) - read file and remember headers * *Purpose: * Read one entire file, stripping out and remembering function headers * or module headers, as appropriate. * ***************************************************************************/ void MyReadFile(char *pbNam) { char CopyName[65]; /* name of copy file */ char *s, done, backout; int funci, svnfuncs, svnentries; int inmodhdr; /* TRUE if in module header, else FALSE */ int insection; /* TRUE if in scetion to process */ int onfirst; /* TRUE if on first line of header */ szFilenameCur = pbNam; if ((ifd = fopen(pbNam, "r")) <= 0) MyAbort(FILENF, pbNam); linebuf[0] = '\0'; /* make linebuf blank so file copying works */ fEof = FALSE; cLinesRead = 0; if (!FindIfC(pbNam)) { /* filename had invalid suffix */ fprintf(stderr, "Illegal suffix for file %s", pbNam); fprintf(stderr, "; must have .c or .asm suffix\n"); fclose(ifd); return; } if (fDelete) { MakeCopyfileName(pbNam, CopyName); /* put the right extension on */ cfd = fopen(CopyName, "w"); if (cfd == NULL) MyAbort(FILENO, CopyName); /* can't open file */ } inmodhdr = Get1stHdr(); if (inmodhdr && !fGetModNams) { /* don't want module header */ ReadToHeader(); /* skip module header */ inmodhdr = FALSE; } /* we are currently at the beginning of a new header. copy it to file TEMP, remember all entry points in NAMBUF. */ while (!fEof && (inmodhdr || fGetProcNams)) { svnfuncs = nfuncs; svnentries = nentries; if (++nfuncs >= MAXFUNCS) MyAbort(FUNCSOV); func[nfuncs].filename = pbNam; func[nfuncs].nlines = 0; func[nfuncs].filepos = ofpos; if (ReadLine(fDelete) == -1) done = TRUE; /* got end-of-file */ else done = FALSE; funci = nfuncs; s = linebuf; while (!done) { if (++nentries >= MAXENT) MyAbort(ENTOV); entry[nentries].entname = nambuf + nambufi; entry[nentries].printed = FALSE; entry[nentries].funcid = funci; s = FuncNamePtr(s); if (s == (char *)-1) { fprintf(stderr, "Error found in line %d of file %s\n", cLinesRead, szFilenameCur); s = linebuf; } while ((*s != '\0') && /* transfer function name to nambuf */ (*s != ',') && (*s != '(') && (*s != ' ') && (*s != '\n') && (*s != '\t') && (nambufi < (NAMBUFSZ - 2))) { nambuf[nambufi++] = *(s++); } if (nambufi >= (NAMBUFSZ - 2)) MyAbort(NMBUFOV, NULL); nambuf[nambufi++] = '\0'; /* make all secondary entry points reference primary entry point */ if (funci >= 0) funci = -1 - nentries; /* next we see if another entry point exists */ while (*s == ' ' || *s == '\t') ++s; /* skip whitespace */ if (*s == '(') { do { ++s; } while (*s != ')' && *s != '\n'); /* skip param list */ ++s; /* goto next character */ } while (*s == ' ' || *s == '\t') ++s; /* skip whitespace */ if (*(s++) != ',') done = TRUE; /* no more entry pts */ else { ++s; done = FALSE; } } /* while !done */ backout = FALSE; if (fDoAll || fDoFirst) insection = TRUE; else insection = FALSE; /* are we in the correct section */ onfirst = TRUE; /* on first line */ do { s = linebuf; if ((!fC_Source && (*s != ';') && !SwitchFound(s)) || (fC_Source && (*s != '*') && !SwitchFound(s))) { /* Illegal Header Format, leave garbage in file TEMP, but restore nfuncs and nentries to previous values and backout of this header gracefully. */ fprintf(stderr, "Invalid Header for function: %s", entry[nentries].entname); fprintf(stderr, " in line %d", cLinesRead); fprintf(stderr, ", in file: %s\n", pbNam); nfuncs = svnfuncs; nentries = svnentries; backout = TRUE; } s = AdvanceOne(s); if (!fDoAll && fDoSection && !onfirst && *s != ' ' && *s != '\t' && *s != '\n') { /* now at a section beginning -- and it's significant */ if (insection) insection = FALSE; /* come to end of section */ else if (strncmp(s, SectionHead, strlen(SectionHead)) == 0) insection = TRUE; /* come to beginning of section */ } /* if in the section, don't copy, but write to TEMP */ if (insection) { WriteLine(s, ofd); func[nfuncs].nlines++; ReadLine(FALSE); } else { ReadLine(fDelete); } if (onfirst && !fDoAll) insection = FALSE; onfirst = FALSE; /* no longer on first line */ } while ((!fEof) && (!backout) && fInHeader); /* skip to start of next function header */ if (fGetProcNams) ReadToHeader(); inmodhdr = FALSE; } /* while !fEof etc. */ while (!fEof) { ReadLine(fDelete); } /* read rest of file */ if (fInHeader) { /* Error: reached EOF with unterminated header */ fInHeader = FALSE; fprintf(stderr, "Error: function %s not terminated before end-of-file in %s\n", entry[nentries].entname, szFilenameCur); } fclose(ifd); fclose(cfd); } /* MyReadFile */ /*** *PrintSep() - print a seperator * *Purpose: * Output a some lines which mark function header boundaries. * ***************************************************************************/ void PrintSep(void) { printf("---------------------------------------"); printf("----------------------------------------\n"); } /* PrintSep */ /*** *PrintEntry(i) - print out an entry point header/module header * *Purpose: * Prints the given header out * ***************************************************************************/ PrintEntry(i) int i; { int linecnt, m, entrysize; if ((m = entry[i].funcid) >= 0) entrysize = 4 + func[m].nlines; /* primary entry point */ else entrysize = 4; /* secondary entry point */ if (outline + entrysize > PAGELEN && outline > 1) { while (outline > PAGELEN) outline -= PAGELEN; while (outline <= PAGELEN) { printf("\n"); outline++; } outline = 1; } printf("\n"); PrintSep(); printf("%s - ", entry[i].entname); if (m < 0) printf("see %s\n", entry[-1-m].entname); else printf("File: %s\n", func[m].filename); PrintSep(); outline = outline + 4; if (m < 0 || func[m].nlines == 0) return(0); fseek(ifd, func[m].filepos, 0); fEof = FALSE; linecnt = func[m].nlines; ReadLine(FALSE); do { WriteLine(linebuf, stdout); outline++; ReadLine(FALSE); } while ((!fEof) && ((--linecnt) > 0)); } /* PrintEntry */ #if 0 /*************************************************************************** *_stricmp(pbLeft, pbRight) - case insensitive string compare * *Purpose: * Case-insensitive string comparison. * *Entry: * pbLeft, pbRight = ptrs to strings to compare * *Exit: * Return 0 if left string == right string, -1 if left string less than * right string, 1 otherwise * *NOTE: * This routine is provided because not all C runtime libraries support * this; specifically, MS C for DOS does have this routine, but * on a 68k it doesn't seem to be there. * ***************************************************************************/ char *malloc(); #define isLcase(c) (((c) >= 'a') && ((c) <= 'z')) #define upit(c) ((isLcase(c))? (c) - 'a' + 'A' : (c)) _stricmp(pbLeft, pbRight) char *pbLeft, *pbRight; { int cbLeft = strlen(pbLeft); int cbRight = strlen(pbRight); char *pbUCleft = malloc(cbLeft +1); char *pbUCright = malloc(cbRight +1); register char *pbSrc; register char *pbDst; register int i, retval; pbDst = pbUCleft; pbSrc = pbLeft; for (i = 0; i <= cbLeft; i++, pbDst++, pbSrc++) *pbDst = (char)upit(*pbSrc); pbDst = pbUCright; pbSrc = pbRight; for (i = 0; i <= cbRight; i++, pbDst++, pbSrc++) *pbDst = (char)upit(*pbSrc); retval = strcmp(pbUCleft, pbUCright); free(pbUCright); free(pbUCleft); return(retval); } /* strless */ #endif /* 0 */ /*************************************************************************** * SortFunctions() * * Purpose: * Print sorted list of functions headers. * ***************************************************************************/ void SortFunctions(void) { int i, j, low; ifd = fopen("temp", "r"); i = -1; while (++i <= nentries) { low = 0; j = -1; while (++j <= nentries) { if (!entry[j].printed) { if (entry[low].printed) low = j; else if (0 > _stricmp(entry[j].entname, entry[low].entname)) low = j; } } PrintEntry(low); entry[low].printed = TRUE; } } /* SortFunctions */ /*** *UsageError() - print out usage *Purpose: * prints out the usage guidelines * *Entry: * *Exit: * exits to DOS * *Exceptions: * *******************************************************************************/ void UsageError(void) { printf("Usage: striphdr [switches] file {file ...}\n\n"); printf(" Accepts native code or C source; determines source\n"); printf(" type based on filename suffix.\n"); printf(" Wildcards may be used in the file names.\n"); printf("\n"); printf("Switches:\n"); printf(" -m process module headers only\n"); printf(" -b processes both procedure and module headers\n"); printf(" [default is procedure headers only]\n"); printf(" -l processes only first line of each header\n"); printf(" -n processes none of header (i.e. lists only function name)\n"); printf(" -s processes named section only (may be used with -l)\n"); printf(" [default is to process whole header]\n"); printf(" -d delete processed section from input file\n"); printf(" -q quiet, do not print headers (useful only with -d)\n"); printf(" -x gives extension for output file when -d used\n"); printf(" [default: .new]\n"); printf(" -r remove revision histories from file headers\n"); printf(" (equivalent to -m -d -q -s \"Revision History\")\n"); exit(-1); } /* UsageError */ void gdir( char * dst, char * src) { int i; for ( i = strlen(src) -1; i >= 0 && (src[i] != '\\'); i--); strncpy(dst, src, i); dst[i] = 0; } /*** *main() - parse command line and process all file * *Purpose: * To run the other procedures based on the command line. * *******************************************************************************/ int main(int argc, char *argv[]) { int filei; int fScanningSwitches = TRUE; char base_dir[256], curr_dir[256]; long h_find; struct _finddata_t f_data; nfuncs = -1; nentries = -1; nambufi = 0; ofpos = 0; filei = 1; outline = 1; if (argc == 1) /* no args given - print usage message and quit */ UsageError(); while (fScanningSwitches) { if (filei >= argc) { fScanningSwitches = FALSE; } else if (_stricmp("-d", argv[filei]) == 0) { fDelete = TRUE; filei++; } else if (_stricmp("-q", argv[filei]) == 0) { fQuiet = TRUE; filei++; } else if (_stricmp("-n", argv[filei]) == 0) { fDoFirst = fDoSection = fDoAll = FALSE; filei++; } else if (_stricmp("-m", argv[filei]) == 0) { fGetModNams = TRUE; fGetProcNams = FALSE; filei++; } else if (_stricmp("-b", argv[filei]) == 0) { fGetModNams = TRUE; fGetProcNams = TRUE; filei++; } else if (_stricmp("-l", argv[filei]) == 0) { fDoFirst = TRUE; fDoAll = FALSE; filei++; } else if (_stricmp("-s", argv[filei]) == 0) { fDoSection = TRUE; fDoAll = FALSE; filei++; if (filei >= argc) UsageError(); strcpy(SectionHead, argv[filei]); /* copy section header in */ filei++; } else if (_stricmp("-x", argv[filei]) == 0) { filei++; if (argv[filei][0] == '.') strncpy(CopyExt, argv[filei]+1, 3); /* skip '.' */ else strncpy(CopyExt, argv[filei], 3); CopyExt[3] = '\0'; /* terminate string */ filei++; } else if (_stricmp("-r", argv[filei]) == 0) { fDelete = TRUE; fGetModNams = TRUE; fGetProcNams = FALSE; fDoAll = FALSE; fDoSection = TRUE; fQuiet = TRUE; strcpy(SectionHead, "Revision History"); filei++; } else if (*(argv[filei]) == '-') UsageError(); else fScanningSwitches = FALSE; } if (filei >= argc) UsageError(); /* no files specified */ if (fQuiet) ofd = fopen("nul", "w"); /* in quiet mode, so no need to save the headers */ else ofd = fopen("temp", "w"); if (ofd == NULL) MyAbort(FILENO, "temp"); /* can't open file */ filei--; if ( _getcwd(base_dir, 255) == NULL) exit(0); while (++filei < argc) { gdir(curr_dir, argv[filei]); if (_chdir(curr_dir) == -1) { printf("%s: %s\n", curr_dir, strerror(errno)); exit(0); } if ( (h_find = _findfirst(argv[filei], &f_data)) == -1) continue; do { MyReadFile(f_data.name); } while ( _findnext(h_find, &f_data) == 0); _findclose(h_find); if (_chdir(base_dir) == -1) { printf("%s: %s\n", curr_dir, strerror(errno)); exit(0); } } fclose(ofd); if (!fQuiet) SortFunctions(); return 0 ; }