ANSI C prototypes

Brad Appleton brad at SSD.CSD.HARRIS.COM
Fri Nov 9 03:48:10 AEST 1990


I grabbed a PD prototype generator utility written in C (that I think works
for both ANSI and K&R) not too long ago - its pretty small - here it is:

I have a 10 line awk script that does it too but the C program is obviously
a wee bit more portable ;-)

Its only two files (a .c and a .h file). Actually its only one file - it can
create its own .h file but you need to get it running first :-)

Anyway - im sure some of you will find some use for it!
Its written in K&R but Im sure it would be no problem to convert to ANSI.


______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad at ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~


------------------------------ cut here ---------------------------------------

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by brad on Thu Nov  8 11:41:53 EST 1990
# Contents:  mkproto.c mkproto.h
 
echo x - mkproto.c
sed 's/^@//' > "mkproto.c" <<'@//E*O*F mkproto.c//'
/* Program to extract function declarations from C source code */
/* Written by Eric R. Smith and placed in the public domain    */
/* Thanks are due to Jwahar R. Bammi for fixing several bugs   */
/* and providing the Unix makefiles. S. Manoharan included a   */
/* Getopt() function and modified the code to handle C++ style */
/* comments and member functions.                              */

#if defined(__STDC__) && !defined(minix)
#include <stddef.h>
#include <stdlib.h>
#else
#define EXIT_SUCCESS  0
#define EXIT_FAILURE  1
extern char *malloc();
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DEBUG(s) { if (dodebug) fputs(s, stderr); } /* */
/*#define DEBUG(s) /* */

#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
#define ABORTED ( (Word *) -1 )
#define MAXPARAM 20       /* max. number of parameters to a function */
#define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */

typedef enum { false = 0, true = 1 } Boolean;

Boolean newline_seen = true;  /* are we at the start of a line */
Boolean dostatic = false;  /* do static functions? */
Boolean donum    = false;  /* print line numbers? */
Boolean dopromote = true;  /* promote certain sc-specifiers */
Boolean dohead   = true;   /* do file headers? */
Boolean dodebug = false;   /* do debugging? */
Boolean docond   = false;  /* conditionalize for non-ANSI compilers */

long linenum  = 1L;        /* line number in current file */
int glastc   = ' ';        /* last char. seen by getsym() */
int inquote = 0;           /* in a quote?? */

typedef struct word {
   struct word *next;
   char   string[1];
} Word;

#include "mkproto.h"

/*
 * Routines for manipulating lists of words.
 */

Word *word_alloc(s)
char *s;
{
   Word *w;

   w = (Word *) malloc((unsigned)sizeof(Word) + strlen(s) + 1);
		/* ++jrb */
   (void)strcpy(w->string, s);
   w->next = NULL;
   return w;
}

void word_free(w)
Word *w;
{
   Word *oldw;
   while (w) {
      oldw = w;
      w = w->next;
      free((char *)oldw);
   }
}

/* return the length of a list; empty words are not counted */
int List_len(w)
Word *w;
{
   int count = 0;

   while (w) {
      if (*w->string) count++;
      w = w->next;
   }
   return count;
}

/* Append two lists, and return the result */

Word *word_append(w1, w2)
Word *w1, *w2;
{
   Word *r, *w;

   r = w = word_alloc("");

   while (w1) {
      w->next = word_alloc(w1->string);
      w = w->next;
      w1 = w1->next;
   }
   while (w2) {
      w->next = word_alloc(w2->string);
      w = w->next;
      w2 = w2->next;
   }

   return r;
}

/* see if the last entry in w2 is in w1 */

int foundin(w1, w2)
Word *w1, *w2;
{
   while (w2->next)
      w2 = w2->next;

   while (w1) {
      if (!strcmp(w1->string, w2->string))
         return 1;
      w1 = w1->next;
   }
   return 0;
}

/* add the string s to the given list of words */

void addword(w, s)
Word *w; char *s;
{
   while (w->next) w = w->next;
   w->next = word_alloc(s);
}

/* given a list representing a type and a variable name, extract just
 * the base type, e.g. "struct word *x" would yield "struct word"
 */

Word *typelist(p)
Word *p;
{
   Word *w, *r;

   r = w = word_alloc("");
   while (p && p->next) {
      if (p->string[0] && !ISCSYM(p->string[0]))
         break;
      w->next = word_alloc(p->string);
      w = w->next;
      p = p->next;
   }
   return r;
}

/* typefixhack: promote formal parameters of type "char", 
 * "unsigned char", "short", or "unsigned short" to "int".
 */

void typefixhack(w)
Word *w;
{
   Word *oldw = 0;

   while (w) {
      if (*w->string) {
         if ( (!strcmp(w->string, "char") ||
             !strcmp(w->string, "short") )
             && (List_len(w->next) < 2) )
         {
            if (oldw && !strcmp(oldw->string, "unsigned")) {
               oldw->next = w->next;
               free((char *)w);
               w = oldw;
            }
            (void)strcpy(w->string, "int");
         }
      }
      w = w->next;
   }
}

/* read a character: if it's a newline, increment the line count */

int ngetc(f)
FILE *f;
{
   int c;

   c = getc(f);
   if (c == '\n') linenum++;

   return c;
}

/* read the next character from the file. If the character is '\' then
 * read and skip the next character. Any comment sequence is converted
 * to a blank. [ Both C and C++ style comments are considered - sam ]
 */

int fnextch(f)
FILE *f;
{
   int c, lastc, incomment;

   c = ngetc(f);
   while (c == '\\') {
      DEBUG("fnextch: in backslash loop\n");
      c = ngetc(f);   /* skip a character */
      c = ngetc(f);
   }
   if (c == '/' && !inquote) {
      c = ngetc(f);
      if (c == '*') {  /* C comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: comment seen\n");
         while (incomment) {
            lastc = c;
            c = ngetc(f);
            if (lastc == '*' && c == '/')
               incomment = 0;
            else if (c < 0)
               return c;
         }
         return fnextch(f);
      }
      else if ( c == '/' ) {  /* C++ comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: C++ comment seen\n");
         while ( c != '\n' ) {
            c = ngetc(f);
         }
	 incomment = 0;
         return fnextch(f);
      }
      else {
         if (c == '\n') linenum--;
         (void)ungetc(c, f);
         return '/';
      }
   }
   return c;
}


/* Get the next "interesting" character. Comments are skipped, and 
 * strings are converted to "0". Also, if a line starts with "#" 
 * it is skipped.
 */

int nextch(f)
FILE *f;
{
   int c;

   c = fnextch(f);
   if (newline_seen && c == '#') {
      do {
         c = fnextch(f);
      } while (c >= 0 && c != '\n');
      if (c < 0)
         return c;
   }
   newline_seen = (c == '\n') ? true : false;

   if (c == '\'' || c == '\"') {
      DEBUG("nextch: in a quote\n");
      inquote = c;
      while ( (c = fnextch(f)) >= 0 ) {
         if (c == inquote) {
            inquote = 0;
            DEBUG("nextch: out of quote\n");
            return '0';
         }
      }
      DEBUG("nextch: EOF in a quote\n");
   }
   return c;
}

/*
 * Get the next symbol from the file, skipping blanks.
 * Return 0 if OK, -1 for EOF.
 * Also collapses everything between { and }
 */

int getsym(buf, f)
char *buf; FILE *f;
{
   register int c;
   int inbrack = 0;

   DEBUG("in getsym\n");
   c = glastc;
   while ((c > 0) && isspace(c)) {
      c = nextch(f);
   }
   DEBUG("getsym: spaces skipped\n");
   if (c < 0) {
      DEBUG("EOF read in getsym\n");
      return -1;
   }
   if (c == '{') {
      inbrack = 1;
      DEBUG("getsym: in bracket\n");
      while (inbrack) {
         c = nextch(f);
         if (c < 0) {
            DEBUG("getsym: EOF seen in bracket loop\n");
            glastc = c;
            return c;
         }
         if (c == '{') inbrack++;
         else if (c == '}') inbrack--;
      }
      (void)strcpy(buf, "{}");
      glastc = nextch(f);
      DEBUG("getsym: out of in bracket loop\n");
      return 0;
   }
   if (!ISCSYM(c)) {
      *buf++ = c;
      *buf = 0;
      glastc = nextch(f);
      DEBUG("getsym: returning special symbol\n");
      return 0;
   }
   while (ISCSYM(c)) {
      *buf++ = c;
      c = nextch(f);
   }
   *buf = 0;
   glastc = c;
   DEBUG("getsym: returning word\n");
   return 0;
}

/*
 * skipit: skip until a ";" or the end of a function declaration is seen
 */

int skipit(buf, f)
char *buf; FILE *f;
{
   int i;

   do {
      DEBUG("in skipit loop\n");
      i = getsym(buf, f);
      if (i < 0) return i;
   } while (*buf != ';' && *buf != '{');

   return 0;
}

/*
 * Get a parameter list; when this is called the next symbol in line
 * should be the first thing in the list.
 */

Word *getparamlist(f)
FILE *f;
{
   static Word *pname[MAXPARAM]; /* parameter names */
   Word   *tlist,      /* type name */
   *plist;      /* temporary */
   int     np = 0;      /* number of parameters */
   int     typed[MAXPARAM];  /* parameter has been given a type */
   int   tlistdone;   /* finished finding the type name */
   int   sawsomething;
   int     i;
   int   inparen = 0;
   char buf[80];

   DEBUG("in getparamlist\n");
   for (i = 0; i < MAXPARAM; i++)
      typed[i] = 0;

   plist = word_alloc("");

   /* first, get the stuff inside brackets (if anything) */

   sawsomething = 0;   /* gets set nonzero when we see an arg */
   for (;;) {
      if (getsym(buf, f) < 0) return NULL;
      if (*buf == ')' && (--inparen < 0)) {
         if (sawsomething) {   /* if we've seen an arg */
            pname[np] = plist;
            plist = word_alloc("");
            np++;
         }
         break;
      }
      if (*buf == ';') {   /* something weird */
         return ABORTED;
      }
      sawsomething = 1;   /* there's something in the arg. list */
      if (*buf == ',' && inparen == 0) {
         pname[np] = plist;
         plist = word_alloc("");
         np++;
      }
      else {
         addword(plist, buf);
         if (*buf == '(') inparen++;
      }
   }

   /* next, get the declarations after the function header */

   inparen = 0;

   tlist = word_alloc("");
   plist = word_alloc("");
   tlistdone = 0;
   sawsomething = 0;
   for(;;) {
      if (getsym(buf, f) < 0) return NULL;

      /* handle a list like "int x,y,z" */
      if (*buf == ',' && !inparen) {
         if (!sawsomething)
            return NULL;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         if (!tlistdone) {
            tlist = typelist(plist);
            tlistdone = 1;
         }
         word_free(plist);
         plist = word_alloc("");
      }
      /* handle the end of a list */
      else if (*buf == ';') {
         if (!sawsomething)
            return ABORTED;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         tlistdone = 0;
         word_free(tlist); 
         word_free(plist);
         tlist = word_alloc("");
         plist = word_alloc("");
      }
      /* handle the  beginning of the function */
      else if (!strcmp(buf, "{}")) break;
      /* otherwise, throw the word into the list 
			    (except for "register") */
      else if (strcmp(buf, "register")) {
         sawsomething = 1;
         addword(plist, buf);
         if (*buf == '(') inparen++;
         if (*buf == ')') inparen--;
      }
   }

   /* Now take the info we have and build a prototype list */

   /* empty parameter list means "void" */
   if (np == 0)
      return word_alloc("void");

   plist = tlist = word_alloc("");
   for (i = 0; i < np; i++) {

      /* If no type provided, make it an "int" */
      if ( !(pname[i]->next) ||
          (!(pname[i]->next->next)&&strcmp(pname[i]->next->string,
	  "void"))) {
         addword(tlist, "int");
      }
      while (tlist->next) tlist = tlist->next;
      tlist->next = pname[i];
      if (i < np - 1)
         addword(tlist, ", ");
   }
   return plist;
}

/*
 * emit a function declaration. The attributes and name of the function
 * are in wlist; the parameters are in plist.
 */

void emit(wlist, plist, startline)
Word *wlist, *plist;
long  startline;
{
   Word *w;
   int count = 0;

   DEBUG("emit called\n");
   if (donum)
      (void)printf("/*%8ld */ ", startline);

   for (w = wlist; w; w = w->next) {
      if ( w->string[0] == ':' )
	 return; /* C++ member function detected */
      if (w->string[0])
         count ++;
   }

   if (count < 2)
      (void)printf("int ");

   for (w = wlist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("PROTO((");
   else
      (void)printf("( ");
   for (w = plist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("));\n");
   else
      (void)printf(");\n");
}

/*
 * get all the function declarations
 */

void getdecl(f)
FILE *f;
{
   Word *plist, *wlist = NULL;
   char buf[80];
   int sawsomething;
   long startline;      /* line where declaration started */
   int oktoprint;
again:
   word_free(wlist);
   wlist = word_alloc("");
   sawsomething = 0;
   oktoprint = 1;

   for(;;) {
      DEBUG("main getdecl loop\n");
      if (getsym(buf,f) < 0) {
         DEBUG("EOF in getdecl loop\n");
         return;
      }
      /* try to guess when a declaration is not an external 
		    function definition */
      if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
          !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
          !strcmp(buf, "extern")) {
         (void)skipit(buf, f);
         goto again;
      }
      if (!dostatic && !strcmp(buf, "static")) {
         oktoprint = 0;
      }
      /* for the benefit of compilers that allow "inline" 
		  declarations */
      if (!strcmp(buf, "inline") && !sawsomething)
         continue;
      if (!strcmp(buf, ";")) goto again;

      /* A left parenthesis *might* indicate a function definition */
      if (!strcmp(buf, "(")) {
         startline = linenum;
         if (!sawsomething || !(plist = getparamlist(f))) {
            (void)skipit(buf, f);
            goto again;
         }
         if (plist == ABORTED)
            goto again;

         /* It seems to have been what we wanted */
         if (oktoprint)
            emit(wlist, plist, startline);
         word_free(plist);
         goto again;
      }
      addword(wlist, buf);
      sawsomething = 1;
   }
}

void main(argc, argv)
int argc; 
char *argv[];
{
   FILE *f;
   char *iobuf;
   char *title = "UNSPECIFIEDHEADERTITLE";
   extern void Usage();
   int opch; extern int Optind; extern char *Optarg;


   while ( ( opch = Getopt(argc,argv,"h:snpPD") ) != -1 )
      switch ( opch ) {
      case 'h'   :
	 (void)strcpy(title, Optarg);
	 break;
      case 's'   :
	 dostatic = true;
	 break;
      case 'n'   :
	 donum = true;
	 break;
      case 'p'   :
	 docond = true;
	 break;
      case 'P'   :
	 dopromote = false;
	 break;
      case 'D'   :
	 dodebug = true;
	 break;
      default    :
	 Usage(argv[0]);
	 exit(0);
      } /* ensw */

   iobuf = malloc((unsigned)NEWBUFSIZ);

   (void)printf("#ifndef %s\n", title);
   (void)printf("#define %s\n", title);

   if (docond) {
      (void)printf("#ifdef __STDC__\n");
      (void)printf("# define\tPROTO(s) s\n");
      (void)printf("#else\n");
      (void)printf("# define PROTO(s) ()\n");
      (void)printf("#endif\n\n");
   }
   if ( Optind > argc )
      getdecl(stdin);
   else
      for ( ; Optind < argc; ++Optind ) {
         DEBUG("trying a new file\n");
         if ( ( f = fopen(argv[Optind],"r") ) == (FILE *)0 ) {
	    (void)fprintf(stderr,"%s: cannot open %s\n",
	       argv[0], argv[Optind]);
	    continue;
         }

         /* do the file operations here */
	 /*
         if (iobuf)
            (void)setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ); /* */
         if (dohead)
            (void)printf("\n/* %s */\n", argv[Optind]);
         linenum = 1;
         newline_seen = true;
         glastc = ' ';
         DEBUG("calling getdecl\n");
         getdecl(f);
         DEBUG("back from getdecl\n");
         DEBUG("back from fclose\n");

         (void)fclose(f);
      } /* enfo */

   if (docond) {
      (void)printf("\n#undef PROTO\n");   /* clean up namespace */
   }
   (void)printf("\n#endif\n");
   exit(EXIT_SUCCESS);
}

void Usage(progname)
char *progname;
{
   (void)fprintf(stderr,"Usage: %s [options][files ...]\n",progname);
   (void)fprintf(stderr,
      "\t-n: put line numbers of declarations as comments\n");
   (void)fprintf(stderr,
      "\t-s: include declarations for static functions\n");
   (void)fprintf(stderr,
      "\t-p: make header files readable by non-ANSI compilers\n");
   (void)fprintf(stderr,
      "\t-P: don't promote any sc-specifiers\n");
   (void)fprintf(stderr,
      "\t-h HeaderTitle: use HeaderTitle to title the header file\n");
   (void)fprintf(stderr,
      "\t-D: operate on debug mode\n");
   exit(EXIT_FAILURE);
}



#ifndef lint
static char *scid = "s. manoharan edinburgh univ";
#endif

#include<stdio.h>

char *Optarg; int Optind;


int Getopt(argc,argv,options)
int argc; char **argv; char *options;
{
   char *str, *ptr; char opch; char *Strchr();
   static int flag = 0; static int Argc; static char **Argv;

   if (  flag == 0 ) {
      Argc = argc; Argv = argv; flag = 1; Optind = 1;
   }

   if ( Argc <= 1 ) return -1;

   if ( --Argc >= 1 ) {
      str = *++Argv;
      if (*str != '-') return -1; /* argument is not an option   */
      else {  /* argument is an option */
	 if ( ( ptr = Strchr(options, opch = *++str) ) != (char *) 0 ) {
	    ++Optind;
            Optarg = ++str; /* point to rest of argument if any  */
            if ( ( *++ptr == ':' ) && ( *Optarg == '\0' ) ) {
               if (--Argc <= 0) return '?';
               Optarg = *++Argv; ++Optind;
            }
	    return opch;
         }
	 else if ( opch == '-' ) { /* end of options */
	    ++Optind;
	    return -1;
	 }
         else return '?';
      }
   }
   return 0; /* this will never be reached */

} /* EnGetopt */

char *Strchr(s,c)
char *s; char c;
{
   while ( *s != '\0' ) {
      if ( *s == c ) return s;
      else ++s;
   }
   return ( (char *) 0 );
} /* EnStrchr */
@//E*O*F mkproto.c//
chmod u=rw,g=,o= mkproto.c
 
echo x - mkproto.h
sed 's/^@//' > "mkproto.h" <<'@//E*O*F mkproto.h//'
#ifdef __STDC__
# define	PROTO(s) s
#else
# define PROTO(s) ()
#endif


/* mkproto.c */
Word *word_alloc PROTO((char *s ));
void word_free PROTO((Word *w ));
int List_len PROTO((Word *w ));
Word *word_append PROTO((Word *w1 , Word *w2 ));
int foundin PROTO((Word *w1 , Word *w2 ));
void addword PROTO((Word *w , char *s ));
Word *typelist PROTO((Word *p ));
void typefixhack PROTO((Word *w ));
int ngetc PROTO((FILE *f ));
int fnextch PROTO((FILE *f ));
int nextch PROTO((FILE *f ));
int getsym PROTO((char *buf , FILE *f ));
int skipit PROTO((char *buf , FILE *f ));
Word *getparamlist PROTO((FILE *f ));
void emit PROTO((Word *wlist , Word *plist , long startline ));
void getdecl PROTO((FILE *f ));
void main PROTO((int argc , char **argv ));
void Usage PROTO((void ));

#undef PROTO
@//E*O*F mkproto.h//
chmod u=rw,g=,o= mkproto.h
 
exit 0
______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad at ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~



More information about the Comp.lang.c mailing list