CBFalconer said:
.... snip ...
For those interested here is Jacobs code revised for portability to
C90. It will even compile under C99. I also passed it through
indent. Now you should be able to compile and thrash.
Aside to Jacob - see how easy it is to be semi-portable. Still are
portability problems, such as return 1 in main and the use of
"c <= ' '". I hope this isn't the lexer in lcc.
.... snip ...
I did some further simple reformatting and removal of hideous
coding. Now I have a peculiar anomaly. The code compiles with gcc
and runs on itself quite nicely when the output file is "a.exe" (by
default). Operation with no params gives help. However, once I
rename that to "cfunct.exe" no parameter operation goes wild, and
execution on its own code gives nothing. I suspect some of the
non-standard coding is the reason, and will look at it further
later. This was done with gcc 3.2.1 under DJGPP and W98. The
actual version compiled is below, for those interested.
My instinct tells me that the use of argv[0] is to blame. I doubt
very much that other systems will have the same misfunction. At
any rate, Jacobs code has been shown to be non-portable.
This all showed up when I started to build a simple filter to
exterminate // comments.
An aside: I always format do while loops as:
do {
...
} while (condition);
indent, as I have it set up now, makes that
do {
...
}
while (condition);
which casual reading parses as a while loop with an empty statement
phase. I initially revised that to "while (condition) continue;",
which produced parse errors and located the problem. This also
shows up the advantage of using continue in empty loops.
/* A simple scanner that will take a file of C source code and
print the names of all functions therein, in the following
format:
"Function XXXX found line dddd .... ddddd"
Algorithm. It scans for a terminating parentheses and an
immediately following opening brace. Comments can appear
between the closing paren and the opening braces, but no
other characters besides white space. Functions must have
the correct prototype, K & R syntax is not supported.
*/
#include <stdio.h>
#include <ctype.h>
/* Longest Identifier we support. Sorry Java guys. */
#define MAXID 1024
/* Buffer for remembering the function name */
static char IdBuffer[MAXID];
/* Line number counter. We start at line 1 */
static int line = 1;
/* ----------------- */
/* This function reads a character and
if it is \n it bumps the line counter. */
static int Fgetc(FILE * f)
{
int c;
if ('\n' == (c = fgetc(f))) line++;
return c;
} /* Fgetc */
/* ----------------- */
/* Return 1 if the character is a legal C identifier
character, zero if not. The parameter "Start"
means if an identifier START character (excluding
numbers) is desired */
static int IsIdentifier(int c, int start)
{
if (c == '_' || isalpha(c)) return 1;
if (start == 0 && isdigit(c)) return 1;
return 0;
} /* IsIdentifier */
/* ----------------- */
/* Just prints the function name */
static int PrintFunction(FILE * f)
{
printf("Function %s: line %d ...", IdBuffer, line);
return Fgetc(f);
} /* PrintFunction */
/* ----------------- */
/* Reads a global identifier into our name buffer */
static int ReadId(char c, FILE * f)
{
int i = 1;
IdBuffer[0] = c;
while (i < MAXID - 1) {
c = Fgetc(f);
if (EOF == c) break;
else {
if (IsIdentifier(c, 0)) IdBuffer[i++] = c;
else break;
}
}
IdBuffer
= 0;
return c;
} /* ReadId */
/* ----------------- */
/* Skips strings */
static int ParseString(FILE * f)
{
int c;
c = Fgetc(f);
while (c != EOF && c != '"') {
if (c == '\\') c = Fgetc(f);
if (c != EOF) c = Fgetc(f);
}
if (c == '"') c = Fgetc(f);
return c;
} /* ParseString */
/* ----------------- */
/* Skips comments */
static int ParseComment(FILE * f)
{
int c;
c = Fgetc(f);
while (1) {
while (c != '*') {
c = Fgetc(f);
if (c == EOF) return EOF;
}
c = Fgetc(f);
if (c == '/') break;
}
return Fgetc(f);
} /* ParseComment */
/* ----------------- */
/* Skips / * comments */
static int ParseCppComment(FILE * f)
{
int c;
c = Fgetc(f);
while (c != EOF && c != '\n') {
if (c == '\\') c = Fgetc(f);
if (c != EOF) c = Fgetc(f);
}
if (c == '\n') c = Fgetc(f);
return c;
} /* ParseCppComment */
/* ----------------- */
/* Checks if a comment is followed after a '/' char */
static int CheckComment(int c, FILE * f)
{
if (c == '/') {
c = Fgetc(f);
if (c == '*') c = ParseComment(f);
else if (c == '/') c = ParseCppComment(f);
}
return c;
} /* CheckComment */
/* ----------------- */
/* Skips white space and comments */
static int SkipWhiteSpace(int c, FILE * f)
{
c = CheckComment(c, f);
do {
if (c <= ' ') c = Fgetc(f);
c = CheckComment(c, f);
} while (c <= ' ');
return c;
} /* SkipWhiteSpace */
/* ----------------- */
/* Skips chars between simple quotes */
static int ParseQuotedChar(FILE * f)
{
int c;
c = Fgetc(f);
while (c != EOF && c != '\'') {
if (c == '\\') c = Fgetc(f);
if (c != EOF) c = Fgetc(f);
}
if (c == '\'') c = Fgetc(f);
return c;
} /* ParseQuotedChar */
/* ----------------- */
int main(int argc, char *argv[])
{
FILE *f;
int c;
int level = 0;
int parenlevel = 0;
int inFunction = 0;
if (argc == 1) {
printf("Usage: %s <file.c>\n", argv[0]);
return 1;
}
f = fopen(argv[1], "r");
if (f == NULL) {
printf("Can't find %s\n", argv[1]);
return 2;
}
c = Fgetc(f);
while (c != EOF) {
/* Note that each of the switches must advance the
character read so that we avoid an infinite loop. */
switch (c) {
case '"':
c = ParseString(f);
break;
case '/':
c = CheckComment(c, f);
break;
case '\'':
c = ParseQuotedChar(f);
break;
case '{':
level++;
c = Fgetc(f);
break;
case '}':
if (level == 1 && inFunction) {
printf(" %d\n", line);
inFunction = 0;
}
if (level > 0) level--;
c = Fgetc(f);
break;
case '(':
parenlevel++;
c = Fgetc(f);
break;
case ')':
if (parenlevel > 0) parenlevel--;
c = Fgetc(f);
if ((parenlevel | level) == 0) {
c = SkipWhiteSpace(c, f);
if (c == '{') {
level++;
inFunction = 1;
c = PrintFunction(f);
}
}
break;
default:
if ((level | parenlevel) == 0 && IsIdentifier(c, 1))
c = ReadId(c, f);
else
c = Fgetc(f);
}
}
fclose(f);
return 0;
} /* main, cfunct.c */