Path: utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!uunet!samsung!ernie.viewlogic.com!news From: josh@concept.viewlogic.com (Josh Marantz) Newsgroups: comp.lang.c Subject: Function prototype generator Message-ID: <1991Jan14.230152.16776@viewlogic.com> Date: 14 Jan 91 23:01:52 GMT Sender: news@viewlogic.com (News Administrator) Organization: Viewlogic Systems, Inc. Lines: 399 The following is a program I wrote to parse a K&R C program and generate function prototypes for all the functions. I've found that it works well in many circumstances. It is immune to most programming style issues that plague quick & dirty C parsers, but it has some limitations. It takes the C file foo.i after its been processed by CPP, and generates two files: foo.ext and foo.sta, to hold the external definitions and the static definitions. The only known limitation is that I found it too hard to parse function declarations that take function pointers as arguments: int foo(x) int (*x) (); { } You can, however, do this: typedef int (*intproc) (); int foo(x) intproc x; { } I've found this is not a bad limitation in practice, and I always define new types function pointers to be passed as arguments. It generates prototypes of the form extern int foo vlARGS((intproc)); You should define vlARGS to as #ifdef __STDC__ #define vlARGS(a) a #else #define vlARGS(a) #endif Then you can use the same headers for both Ansi and K&R. Other than the known limitation, this program has been a workhorse for me for a couple of years now. I never write function declarations manually anymore. I've avoided posting it because I'm a little ashamed of the style, and I haven't had time to fix it up, and because of the procedure pointer limitation. But it seems like the demand for such a beast is high, so here it is. First, a script to run it on unix (unix is not required -- this runs fine under DOS using the MS-C preprocessor): mkproto: ---------------------------------------------------------------- #!/bin/csh /lib/cpp $1.c $argv[2-] > $1.i proto $1 ---------------------------------------------------------------- Now, the proto.c file: ---------------------------------------------------------------- #include #include #define MAX_VARS 20 #define MAX_CHARS 80 char inname[MAX_CHARS]; char word[MAX_CHARS]; char var[MAX_CHARS]; char type[MAX_CHARS]; char proc_name[MAX_CHARS]; char vars[MAX_VARS][MAX_CHARS]; char types[MAX_VARS][MAX_CHARS]; int braces = 0, parens = 0, brackets = 0, line_count = 1; int bslash = 0, squote = 0, dquote = 0; int num_var, num_matches; static FILE *infile, *statics, *externs; #define IS_IDENT(c) \ (isalnum ((c)) || ((c) == '$') || ((c) == '_') || ((c) == '*') || \ ((c) == '[') || ((c) == ']') || ((c) == '+')) static FILE *open_file(name, ext, mode) char *name, *ext, *mode; { char fname[80]; FILE *f; sprintf (fname, "%s.%s", name, ext); if ((f = fopen (fname, mode)) == NULL) { perror (fname); exit (1); } /* if */ return (f); } /* static FILE *open_file */ main(argc, argv) int argc; char *argv[]; { if (argc != 2) { fprintf (stderr, "Usage: %s module\n", argv[0]); fprintf (stderr, "Reads for cpp output module.i.\n"); fprintf (stderr, "Writes external prototypes to module.ext.\n"); fprintf (stderr, "Writes static prototypes to module.sta.\n"); exit (1); } /* if */ sprintf (inname, "%s.i", argv[1]); infile = open_file (argv[1], "i", "r"); externs = open_file (argv[1], "ext", "w"); statics = open_file (argv[1], "sta", "w"); while (getword () != EOF) { if ((strcmp (word, "extern") == 0) || (strcmp (word, "{") == 0) || (strcmp (word, ";") == 0) || (strcmp (word, ",") == 0) || (strcmp (word, "pragma") == 0) || (strcmp (word, "typedef") == 0)) next_statement (); else if (IS_IDENT (word[0])) try_proc (); else error ("Unexpected token"); } /* while */ } /* main */ next_statement() { while ((braces != 0) || ((strcmp (word, ";") != 0) && (strcmp (word, "}") != 0))) getword (); } /* next_statement */ getword() { int c, i, done, x; i = 0; done = 0; while (!done) { c = getc (infile); if (c == EOF) { if (braces || parens || brackets) fprintf (stderr, "%s: %d: {}=%d, ()=%d, []=%d\n", inname, line_count, braces, parens, brackets); fclose (infile); fclose (statics); fclose (externs); exit (0); } /* if */ else if (IS_IDENT (c) && !dquote && !squote) { word[i] = c; i++; } /* if */ else if (isspace (c) || (c == '\f') && !dquote && !squote) { if (c == '\n') line_count++; if (i > 0) { word[i] = 0; c = 0; done = 1; } /* if */ } /* else if */ else if ((c == '#') && !dquote && !squote) { if ((fgets (word, MAX_CHARS - 1, infile) == NULL) || ((sscanf (word, " %d \"%s\"", &line_count, inname) != 2) && (sscanf (word, "line %d \"%s\"", &line_count, inname) != 2))) { x = strlen (word) - 1; if ((x >= 0) && (word[x] == '\n')) word[x] = '\0'; error ("Unknown # directive"); line_count++; } else { x = strlen (inname) - 1; if ((x >= 0) && (inname[x] == '"')) inname[x] = '\0'; } } /* else if */ else { if (i == 0) { switch (c) { case '{': if (!dquote && !squote) braces++; break; case '}': if (!dquote && !squote) braces--; break; case '(': if (!dquote && !squote) parens++; break; case ')': if (!dquote && !squote) parens--; break; case '[': if (!dquote && !squote) brackets++; break; case ']': if (!dquote && !squote) brackets--; break; case '"': if (!bslash && !squote) dquote = !dquote; break; case 39: if (!bslash && !dquote) squote = !squote; break; case '\\': bslash = !bslash; break; } /* switch */ if (c != '\\') bslash = 0; word[0] = c; word[1] = '\0'; if (!dquote && !squote) done = 1; } /* if */ else { ungetc (c, infile); word[i] = 0; c = 0; done = 1; } /* else */ } /* else if */ } /* while */ return (c); } /* getword */ try_proc() { int i, end_of_type; FILE *f; if (strcmp (word, "static") == 0) { proc_name[0] = '\0'; f = statics; } /* if */ else { strcpy (proc_name, "extern "); f = externs; } /* else */ do { strcat (proc_name, word); strcat (proc_name, " "); } while (getword () == 0); if ((strcmp (word, ";") == 0) || /* variable/type declaration */ (strcmp (word, ",") == 0) || /* multi var/type declaration */ (strcmp (word, "[") == 0) || /* array initializer */ (strcmp (word, "=") == 0) || /* static initializer */ (strcmp (word, "{") == 0) || /* struct/union definition */ (strcmp (word, ":") == 0)) /* label */ { next_statement (); return; } /* if */ if (strcmp (word, "(") != 0) {error ("( expected"); return;} /* Accumulate variable names */ num_var = 0; do { if (getword () != 0) { if ((num_var == 0) && (strcmp (word, ")") == 0)) break; error ("expected arg"); return; } if (word[0] == '*') { /* Procedure ptr declaration */ next_statement (); return; } strcpy (vars[num_var], word); strcpy (types[num_var], "int"); num_var++; } while (getword () == ','); if (IS_IDENT (word[0]) || /* identifier */ (strcmp (word, "(") == 0)) /* function argument decl */ { next_statement (); return; } /* if */ if (strcmp (word, ")") != 0) {error (") expected"); return;} if (num_var == 0) { getword (); if (strcmp (word, "{") == 0) { /* Proc with no args */ fprintf (f, "%svlARGS((void));\n", proc_name); next_statement (); } /* if */ else if ((strcmp (word, ";") == 0) || (strcmp (word, ",") == 0)) ; /* proc declaration w/o extern */ else error ("Unexpected termination of procedure declaration"); return; } /* if */ /* Find argument type declarations */ end_of_type = 9999; for (num_matches = 0; num_matches < num_var;) { type[0] = 0; while (getword () == 0) { end_of_type = strlen (type) - 1; strcat (type, word); strcpy (var, word); strcat (type, " "); } /* while */ if (end_of_type < 1) { error ("Empty declaration"); return; } /* if */ /* If we hit a ; the first time through, we are looking at an existing prototyped procedure declaration! Punt! */ if ((strcmp (word, ";") == 0) && (end_of_type == 9999)) return; if ((strcmp (word, ";") != 0) && (strcmp (word, ",") != 0)) { strcpy (word, proc_name); error ("arg type not found, declaration supressed"); return; } /* if */ else { type[end_of_type] = '\0'; match_variable (var); while (strcmp (word, ",") == 0) { getword (); match_variable (word); getword (); } /* while */ if (strcmp (word, ";") != 0) { error ("Arg decl did not end with ';'"); return; } /* if */ } /* else */ } /* for */ fprintf (f, "%svlARGS((", proc_name); for (i = 0; i < num_var - 1; i++) fprintf (f, "%s, ", types[i]); fprintf (f, "%s));\n", types[num_var - 1]); next_statement (); } /* try_proc */ match_variable(compare) char *compare; { int i; char prefix[10], suffix[10]; prefix[0] = suffix[0] = '\0'; while (*compare == ' ') compare++; while (*compare == '*') { compare++; strcat (prefix, "*"); } /* while */ while ((strlen (compare) > 2) && (strcmp (&compare[strlen (compare) - 2], "[]") == 0)) { compare[strlen (compare) - 2] = '\0'; strcat (suffix, "[]"); } /* while */ for (i = 0; i < num_var; i++) { if (strcmp (compare, vars[i]) == 0) { if (prefix[0] == '\0') sprintf (types[i], "%s%s", type, suffix); else sprintf (types[i], "%s %s%s", type, prefix, suffix); num_matches++; return; } /* if */ } /* for */ sprintf (word, "%s(%s)", proc_name, compare); error ("Failed to match type"); } /* match_variable */ error(s) char *s; { fprintf (stderr, "%s: %d: %s: %s\n", inname, line_count, s, word); next_statement (); } /* error */ ---------------------------------------------------------------- Good luck, and feel free to make improvements and send them back to me! -- Joshua Marantz Viewlogic Systems, Inc. josh@viewlogic.com Why not pass the time by playing a little solitaire?