[comp.sources.misc] v16i090: mkptypes - Generate prototype declarations for C, Part01/01

ersmith@uwovax.bitnet (Eric R. Smith) (02/04/91)

Submitted-by: ersmith@uwovax.bitnet (Eric R. Smith)
Posting-number: Volume 16, Issue 90
Archive-name: mkptypes/part01

Here is mkptypes, a program for generating prototype declarations for
all functions appearing in a C source file. The output declarations
are enclosed in a macro so as to be usable by either an ANSI or an
older C compiler. Typical usage would be to do 'mkptypes *.c >proto.h'
and then have a '#include "proto.h"' line in the relevant C source
code files. See the README file for more details.

An earlier version of this program was released on comp.sources.misc
under the name "mkproto"; this updated version (with new command line
options and several bug fixes) was renamed to avoid conflict with a
file system utility available on some versions of Unix.

The source will probably compile as-is on most systems, providing you
edit the makefile appropriately (the default is for my configuration,
an Atari ST with gcc).

Eric R. Smith
eric.smith@uwo.ca
ersmith@uwovax.bitnet

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Manifest
#	Makefile
#	mkptypes.1
#	mkptypes.c
#	mkptypes.h
#	mkptypes.man
# This archive created: 21 January 1991 12:16:52 PM EST
# By:	eric (at home)
echo shar: extracting README
sed 's/^X//' << \SHAR_EOF > README
XHere is mkptypes, a program for generating prototype declarations for all
Xfunctions appearing in a C source file. The input C code may be either
XK&R or ANSI C (i.e. it's OK if the functions are defined using prototypes).
XUnlike some of the sed-based scripts floating around, it (usually)
Xhandles prototype promotion (e.g. the prototype for 'int foo() char x;...'
Xis 'int foo(int x)'). Also, it should work OK on just about any computer,
Xnot just Unix-based ones (it's been tested under minix, Unix, and TOS).
X
XUse: typically, you would type 'mkptypes *.c >proto.h' and then add a
X'#include "proto.h"' line to all the C source files. An ANSI conformant
Xcompiler will then be able to do type checking on function calls across
Xmodule boundaries. As a bonus, proto.h will tell you which source files
Xfunctions were defined in, and (if you gave the -n function to mkptypes)
Xtheir line numbers. The resulting include file may also be used by
Xnon-ANSI compilers; you can disable this feature (for cleaner, strictly
XANSI-conforming output) with the -A flag. See the mkptypes.man file for
Xa description of all the flags mkptypes accepts.
X
XPlease read the description of bugs in mkptypes.man; definitely mkptypes
Xwill not handle all programs correctly, but it does work on the majority of
Xthem. A sample of its output is provided in the file "mkptypes.h"; this
Xis the result of 'mkptypes mkptypes.c >mkptypes.h'.
X
XThere is ABSOLUTELY NO WARRANTY for the program; as I said, it doesn't work
Xon all programs (complicated function definitions can make it produce bogus
Xoutput). It does what I need, though, and it can certainly make porting stuff
Xto ANSI compilers easier.
X
XAn earlier version of mkptypes was released on Usenet under the name
X"mkproto". This version has several new command line options, and some bug
Xfixes. It has been renamed to avoid conflict with a file system utility
Xavailable under some versions of Unix.
X
XMkptypes is in the public domain. If you find any bugs (other than the ones
Xdocumented) please let me know.
X--
XEric R. Smith                     email:
XDept. of Mathematics            ersmith@uwovax.uwo.ca
XUniversity of Western Ontario   ersmith@uwovax.bitnet
XLondon, Ont. Canada N6A 5B7
Xph: (519) 661-3638
SHAR_EOF
if test 2224 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 2224 characters)'
fi
echo shar: extracting Manifest
sed 's/^X//' << \SHAR_EOF > Manifest
XMakefile:	makefile for mkptypes. Default configuration is for Atari ST.
XManifest:	this file
Xmkptypes.1:	Unix manual page for mkptypes
Xmkptypes.c:	source code for mkptypes
Xmkptypes.h:	prototypes for mkptypes (generated by mkptypes, too)
Xmkptypes.man:	text version of mkptypes.1
XREADME:		do what it says
SHAR_EOF
if test 302 -ne "`wc -c Manifest`"
then
echo shar: error transmitting Manifest '(should have been 302 characters)'
fi
echo shar: extracting Makefile
sed 's/^X//' << \SHAR_EOF > Makefile
X#
X# Makefile for mkptypes. Edit the lines below to suit your tastes; the default
X# is for my computer (Atari ST running the gcc 1.37); a Unix configuration is
X# also provided.
X
X#CC = cc
X#PROG = mkptypes
X#CFLAGS = -O
X
XCC = gcc
XPROG = mkptypes.ttp
XCFLAGS = -mshort -O
X
X$(PROG) : mkptypes.c mkptypes.h
X	$(CC) $(CFLAGS) -o $(PROG) mkptypes.c
X
Xclean:
X	rm -f mkptypes.o
X
Xrealclean: clean
X	rm -f $(PROG) report core
SHAR_EOF
if test 409 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 409 characters)'
fi
echo shar: extracting mkptypes.1
sed 's/^X//' << \SHAR_EOF > mkptypes.1
X.TH MKPTYPES 1
X.SH NAME
Xmkptypes \- make prototypes for functions
X.SH SYNOPSIS
X.B mkptypes
X[
X.B -e
X] [
X.B -n
X] [
X.B -p
X.I symbol
X] [
X.B -s
X] [
X.B -x
X] [
X.B -z
X] [
X.B -A
X] [
X.I file ...
X]
X.SH DESCRIPTION
X.I Mkptypes
Xtakes as input one or more C source code files, and
Xproduces as output (on the standard output stream) a list of function
Xprototypes (a la ANSI) for the external functions defined in the
Xgiven source files. This output, redirected to a file, is suitable
Xfor #include'ing in a C source file.
X.PP
XThe function definitions in the original source
Xmay be either ``old-style'' (in which case appropriate prototypes are
Xgenerated for the functions) or ``new-style'' (in which the definition
Xincludes a prototype already).
X.PP
XThe -e option causes the ``extern'' keyword to be explicitly printed
Xfor external functions. Some non-ANSI compilers may need this.
X.PP
XThe -n option causes the line number where each function was defined
Xto be prepended to the prototype declaration as a comment.
X.PP
XThe -p option controls the name of the macro used to guard prototype
Xdefinitions. Normally this is ``_P'', but you can change it to any string
Xyou like. To eliminate the guard macro entirely, use the -A option.
X.PP
XThe -s option causes prototypes to be generated for functions declared
X``static'' as well as extern functions.
X.PP
XThe -x option causes parameter names to be omitted from the output
Xprototypes. This may be necessary for some brain-damaged pseudo-ANSI
Xcompilers. You may also prefer this style of output. This option has not
Xbeen thoroughly tested.
X.PP
XThe -z option suppresses the definition of the prototype macro given by
X-p. Header files generated by
X.I mkptypes
Xwith this option will not work unless the prototype macro has been defined
Xelsewhere in the program by the user. Used with the -p option, the -z
Xoption allows use of predefined prototype hiding macros that may exist
Xon some systems.
X.PP
XThe -A option causes the prototypes emitted to be only readable by ANSI
Xcompilers. Normally, the prototypes are "macro-ized" so that compilers
Xwith __STDC__ not defined don't see them.
X.PP
XIf files are specified on the command line, then a comment specifying
Xthe file of origin is emitted before the prototypes constructed from
Xthat file. If no files are given, then no comments are emitted and
Xthe C source code is taken from the standard input stream.
X.SH BUGS
XMkptypes is easily confused by complicated declarations, such as
X.nf
X         int ((*signal)())() { ...
X.fi
Xor
X.nf
X         struct foo { int x, y; } foofunc() { ...
X.fi
XThis is because the program doesn't actually understand type definitions.
X.PP
XSome programs may need to be run through the preprocessor before
Xbeing run through
X.I mkptypes .
XThe -n option will not work correctly on preprocessor output if function
Xdefinitions (as opposed to declarations) appear in header files.
X.PP
XTypedef'd types aren't correctly promoted, e.g. for
X.nf
X        typedef schar char; int foo(x) schar x;...
X.fi
X.I mkptypes
Xincorrectly generates the prototype int foo(schar x) rather than the
X[correct] int foo(int x).
X.PP
XFunctions named "inline" with no explicit type qualifiers are not
Xrecognized.
X.SH SEE ALSO
X.I cc
X(1),
X.I lint
X(1).
X.SH AUTHOR
XEric R. Smith <ersmith@uwovax.uwo.ca>
X.SH NOTE
XThere is no warranty for this program (as noted above, it's guaranteed
Xto break sometimes anyways!). Mkptypes is in the public domain.
SHAR_EOF
if test 3399 -ne "`wc -c mkptypes.1`"
then
echo shar: error transmitting mkptypes.1 '(should have been 3399 characters)'
fi
echo shar: extracting mkptypes.c
sed 's/^X//' << \SHAR_EOF > mkptypes.c
X/* Program to extract function declarations from C source code
X * Written by Eric R. Smith and placed in the public domain
X * Thanks to:
X * Jwahar R. Bammi, for catching several bugs and providing the Unix makefiles
X * Byron T. Jenings Jr. for cleaning up the space code, providing a Unix
X *   manual page, and some ANSI and C++ improvements.
X * Skip Gilbrech for code to skip parameter names in prototypes.
X * ... and many others for helpful comments and suggestions.
X */
X
X/* if your compiler claims to be ANSI but doesn't have stddef.h or stdlib.h,
X * change the next line.
X * (and then complain to the supplier of the defective compiler)
X */
X
X#if defined(__STDC__) && !defined(minix)
X#include <stddef.h>
X#include <stdlib.h>
X#else
Xextern char *malloc();
Xextern long atol();
X#endif
X
X#ifndef EXIT_SUCCESS
X#define EXIT_SUCCESS  0
X#define EXIT_FAILURE  1
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X
X/*#define DEBUG(s) (fputs(s, stderr)) /* */
X#define DEBUG(s) /* */
X
X#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
X#define ABORTED ( (Word *) -1 )
X#define MAXPARAM 20 		/* max. number of parameters to a function */
X#define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */
X
Xint dostatic = 0;		/* do static functions? */
Xint donum    = 0;		/* print line numbers? */
Xint define_macro   = 1;		/* define macro for prototypes? */
Xint use_macro   = 1;		/* use a macro for prototypes? */
Xchar *macro_name = "_P";	/*   macro to use for prototypes */
Xint no_parm_names = 0;		/* no parm names - only types */
Xint print_extern = 0;		/* use "extern" before function declarations */
X#ifdef CPP
Xint call_cpp = 0;		/* preprocess files */
X#endif
X
Xchar *ourname;			/* our name, from argv[] array */
Xint inquote = 0;		/* in a quote?? */
Xint newline_seen = 1;		/* are we at the start of a line */
Xlong linenum  = 1L;		/* line number in current file */
Xint glastc   = ' ';		/* last char. seen by getsym() */
X
Xtypedef struct word {
X	struct word *next;
X	char   string[1];
X} Word;
X
X#include "mkptypes.h"
X
X/*
X * Routines for manipulating lists of words.
X */
X
XWord *word_alloc(s)
X	char *s;
X{
X	Word *w;
X
X/* note that sizeof(Word) already contains space for a terminating null
X * however, we add 1 so that typefixhack can promote "float" to "double"
X *  by just doing a strcpy.
X */
X	w = (Word *) malloc(sizeof(Word) + strlen(s) + 1);
X	strcpy(w->string, s);
X	w->next = NULL;
X	return w;
X}
X
Xvoid word_free(w)
X	Word *w;
X{
X	Word *oldw;
X	while (w) {
X		oldw = w;
X		w = w->next;
X		free(oldw);
X	}
X}
X
X/* return the length of a list; empty words are not counted */
Xint
XList_len(w)
X	Word *w;
X{
X	int count = 0;
X
X	while (w) {
X		if (*w->string) count++;
X		w = w->next;
X	}
X	return count;
X}
X
X/* Append two lists, and return the result */
X
XWord *word_append(w1, w2)
X	Word *w1, *w2;
X{
X	Word *r, *w;
X
X	r = w = word_alloc("");
X
X	while (w1) {
X		w->next = word_alloc(w1->string);
X		w = w->next;
X		w1 = w1->next;
X	}
X	while (w2) {
X		w->next = word_alloc(w2->string);
X		w = w->next;
X		w2 = w2->next;
X	}
X
X	return r;
X}
X	
X/* see if the last entry in w2 is in w1 */
X
Xint
Xfoundin(w1, w2)
X	Word *w1, *w2;
X{
X	while (w2->next)
X		w2 = w2->next;
X
X	while (w1) {
X		if (!strcmp(w1->string, w2->string))
X			return 1;
X		w1 = w1->next;
X	}
X	return 0;
X}
X
X/* add the string s to the given list of words */
X
Xvoid addword(w, s)
X	Word *w; char *s;
X{
X	while (w->next) w = w->next;
X	w->next = word_alloc(s);
X}
X
X/* typefixhack: promote formal parameters of type "char", "unsigned char",
X   "short", or "unsigned short" to "int".
X*/
X
Xvoid typefixhack(w)
X	Word *w;
X{
X	Word *oldw = 0;
X
X	while (w) {
X		if (*w->string) {
X			if ( (!strcmp(w->string, "char") ||
X			      !strcmp(w->string, "short") )
X			    && (List_len(w->next) < 2) )
X			{
X/* delete any "unsigned" specifier present -- yes, it's supposed to do this */
X				if (oldw && !strcmp(oldw->string, "unsigned")) {
X					oldw->next = w->next;
X					free(w);
X					w = oldw;
X				}
X				strcpy(w->string, "int");
X			}
X			else if ( !strcmp(w->string, "float") &&
X				  List_len(w->next) < 2 )
X			{
X				strcpy(w->string, "double");
X			}
X		}
X		w = w->next;
X	}
X}
X
X/* read a character: if it's a newline, increment the line count */
X
X#ifdef __GNUC__	/* ++jrb */
Xinline
X#endif
Xint ngetc(f)
X	FILE *f;
X{
X	int c;
X
X	c = getc(f);
X	if (c == '\n') linenum++;
X
X	return c;
X}
X
X/* read the next character from the file. If the character is '\' then
X * read and skip the next character. Any comment sequence is converted
X * to a blank.
X */
X
Xint fnextch(f)
X	FILE *f;
X{
X	int c, lastc, incomment;
X
X	c = ngetc(f);
X	while (c == '\\') {
XDEBUG("fnextch: in backslash loop\n");
X		c = ngetc(f);	/* skip a character */
X		c = ngetc(f);
X	}
X	if (c == '/' && !inquote) {
X		c = ngetc(f);
X		if (c == '*') {
X			incomment = 1;
X			c = ' ';
XDEBUG("fnextch: comment seen\n");
X			while (incomment) {
X				lastc = c;
X				c = ngetc(f);
X				if (lastc == '*' && c == '/')
X					incomment = 0;
X				else if (c < 0)
X					return c;
X			}
X			return fnextch(f);
X		}
X		else {
X/* if we pre-fetched a linefeed, remember to adjust the line number */
X			if (c == '\n') linenum--;
X			ungetc(c, f);
X			return '/';
X		}
X	}
X	return c;
X}
X
X
X/* Get the next "interesting" character. Comments are skipped, and strings
X * are converted to "0". Also, if a line starts with "#" it is skipped.
X */
X
Xint nextch(f)
X	FILE *f;
X{
X	int c, n;
X	char *p, numbuf[10];
X
X	c = fnextch(f);
X
X/* skip preprocessor directives */
X/* EXCEPTION: #line nnn or #nnn lines are interpreted */
X
X	if (newline_seen && c == '#') {
X/* skip blanks */
X		do {
X			c = fnextch(f);
X		} while ( c >= 0 && (c == '\t' || c == ' ') );
X/* check for #line */
X		if (c == 'l') {
X			c = fnextch(f);
X			if (c != 'i')	/* not a #line directive */
X				goto skip_rest_of_line;
X			do {
X				c = fnextch(f);
X			} while (c >= 0 && c != '\n' && !isdigit(c));
X		}
X
X/* if we have a digit it's a line number, from the preprocessor */
X		if (c >= 0 && isdigit(c)) {
X			p = numbuf;
X			for (n = 8; n >= 0; --n) {
X				*p++ = c;
X				c = fnextch(f);
X				if (c <= 0 || !isdigit(c))
X					break;
X			}
X			*p = 0;
X			linenum = atol(numbuf) - 1;
X		}
X
X/* skip the rest of the line */
Xskip_rest_of_line:
X		while (c >= 0 && c != '\n')
X			c = fnextch(f);
X		if (c < 0)
X			return c;
X	}
X	newline_seen = (c == '\n');
X
X	if (c == '\'' || c == '\"') {
XDEBUG("nextch: in a quote\n");
X		inquote = c;
X		while ( (c = fnextch(f)) >= 0 ) {
X			if (c == inquote) {
X				inquote = 0;
XDEBUG("nextch: out of quote\n");
X				return '0';
X			}
X		}
XDEBUG("nextch: EOF in a quote\n");
X	}
X	return c;
X}
X
X/*
X * Get the next symbol from the file, skipping blanks.
X * Return 0 if OK, -1 for EOF.
X * Also collapses everything between { and }
X */
X
Xint
Xgetsym(buf, f)
X	char *buf; FILE *f;
X{
X	register int c;
X	int inbrack = 0;
X
XDEBUG("in getsym\n");
X	c = glastc;
X	while ((c > 0) && isspace(c)) {
X		c = nextch(f);
X	}
XDEBUG("getsym: spaces skipped\n");
X	if (c < 0) {
XDEBUG("EOF read in getsym\n");
X		return -1;
X	}
X	if (c == '{') {
X		inbrack = 1;
XDEBUG("getsym: in bracket\n");
X		while (inbrack) {
X			c = nextch(f);
X			if (c < 0) {
XDEBUG("getsym: EOF seen in bracket loop\n");
X				glastc = c;
X				return c;
X			}
X			if (c == '{') inbrack++;
X			else if (c == '}') inbrack--;
X		}
X		strcpy(buf, "{}");
X		glastc = nextch(f);
XDEBUG("getsym: out of in bracket loop\n");
X		return 0;
X	}
X	if (!ISCSYM(c)) {
X		*buf++ = c;
X		*buf = 0;
X		glastc = nextch(f);
XDEBUG("getsym: returning special symbol\n");
X		return 0;
X	}
X	while (ISCSYM(c)) {
X		*buf++ = c;
X		c = nextch(f);
X	}
X	*buf = 0;
X	glastc = c;
XDEBUG("getsym: returning word\n");
X	return 0;
X}
X
X/*
X * skipit: skip until a ";" or the end of a function declaration is seen
X */
Xint skipit(buf, f)
X	char *buf;
X	FILE *f;
X{
X	int i;
X
X	do {
XDEBUG("in skipit loop\n");
X		i = getsym(buf, f);
X		if (i < 0) return i;
X	} while (*buf != ';' && *buf != '{');
X
X	return 0;
X}
X
X/*
X * find most common type specifiers for purpose of ruling them out as
X * parm names
X */
X
Xint is_type_word(s)
Xchar *s;
X{
X    static char *typewords[] = {
X	"char",		"const",	"double",	"enum",
X	"float",	"int",		"long",		"short",
X	"signed",	"struct",	"union",	"unsigned",
X	"void",		"volatile",	(char *)0
X    };
X
X    register char **ss;
X
X    for (ss = typewords; *ss; ++ss)
X	if (strcmp(s, *ss) == 0)
X	    return 1;
X
X    return 0;
X}
X
X
X/* Ad-hoc macro to recognize parameter name for purposes of removal.
X * Idea is to remove the bulk of easily recognized parm names without
X * losing too many type specifiers. (sg)
X */
X#define IS_PARM_NAME(w) \
X	(ISCSYM(*(w)->string) && !is_type_word((w)->string) && \
X	(!(w)->next || *(w)->next->string == ',' || \
X	 *(w)->next->string == '['))
X
X
X/*
X * given a list representing a type and a variable name, extract just
X * the base type, e.g. "struct word *x" would yield "struct word"
X */
X
XWord *typelist(p)
X	Word *p;
X{
X	Word *w, *r;
X
X	r = w = word_alloc("");
X	while (p && p->next) {
X/* handle int *x --> int */
X		if (p->string[0] && !ISCSYM(p->string[0]))
X			break;
X/* handle int x[] --> int */
X		if (p->next->string[0] == '[')
X			break;
X		w->next = word_alloc(p->string);
X		w = w->next;
X		p = p->next;
X	}
X	return r;
X}
X
X/*
X * Get a parameter list; when this is called the next symbol in line
X * should be the first thing in the list.
X */
X
XWord *getparamlist(f)
X	FILE *f;
X{
X	static Word *pname[MAXPARAM]; /* parameter names */
X	Word	*tlist,		  /* type name */
X		*plist;		  /* temporary */
X	int  	np = 0;		  /* number of parameters */
X	int  	typed[MAXPARAM];  /* parameter has been given a type */
X	int	tlistdone;	  /* finished finding the type name */
X	int	sawsomething;
X	int  	i;
X	int	inparen = 0;
X	char buf[80];
X
XDEBUG("in getparamlist\n");
X	for (i = 0; i < MAXPARAM; i++)
X		typed[i] = 0;
X
X	plist = word_alloc("");
X
X/* first, get the stuff inside brackets (if anything) */
X
X	sawsomething = 0;	/* gets set nonzero when we see an arg */
X	for (;;) {
X		if (getsym(buf, f) < 0) return NULL;
X		if (*buf == ')' && (--inparen < 0)) {
X			if (sawsomething) {	/* if we've seen an arg */
X				pname[np] = plist;
X				plist = word_alloc("");
X				np++;
X			}
X			break;
X		}
X		if (*buf == ';') {	/* something weird */
X			return ABORTED;
X		}
X		sawsomething = 1;	/* there's something in the arg. list */
X		if (*buf == ',' && inparen == 0) {
X			pname[np] = plist;
X			plist = word_alloc("");
X			np++;
X		}
X		else {
X			addword(plist, buf);
X			if (*buf == '(') inparen++;
X		}
X	}
X
X/* next, get the declarations after the function header */
X
X	inparen = 0;
X
X	tlist = word_alloc("");
X	plist = word_alloc("");
X	tlistdone = 0;
X	sawsomething = 0;
X	for(;;) {
X		if (getsym(buf, f) < 0) return NULL;
X
X/* handle a list like "int x,y,z" */
X		if (*buf == ',' && !inparen) {
X			if (!sawsomething)
X				return NULL;
X			for (i = 0; i < np; i++) {
X				if (!typed[i] && foundin(plist, pname[i])) {
X					typed[i] = 1;
X					word_free(pname[i]);
X					pname[i] = word_append(tlist, plist);
X				/* promote types */
X					typefixhack(pname[i]);
X					break;
X				}
X			}
X			if (!tlistdone) {
X				tlist = typelist(plist);
X				tlistdone = 1;
X			}
X			word_free(plist);
X			plist = word_alloc("");
X		}
X/* handle the end of a list */
X		else if (*buf == ';') {
X			if (!sawsomething)
X				return ABORTED;
X			for (i = 0; i < np; i++) {
X				if (!typed[i] && foundin(plist, pname[i])) {
X					typed[i] = 1;
X					word_free(pname[i]);
X					pname[i] = word_append(tlist, plist);
X					typefixhack(pname[i]);
X					break;
X				}
X			}
X			tlistdone = 0;
X			word_free(tlist); word_free(plist);
X			tlist = word_alloc("");
X			plist = word_alloc("");
X		}
X/* handle the  beginning of the function */
X		else if (!strcmp(buf, "{}")) break;
X/* otherwise, throw the word into the list (except for "register") */
X		else if (strcmp(buf, "register")) {
X			sawsomething = 1;
X			addword(plist, buf);
X			if (*buf == '(') inparen++;
X			if (*buf == ')') inparen--;
X		}
X	}
X
X/* Now take the info we have and build a prototype list */
X
X/* empty parameter list means "void" */
X	if (np == 0)
X		return word_alloc("void");
X
X	plist = tlist = word_alloc("");
X	for (i = 0; i < np; i++) {
X
X/* If no type provided, make it an "int" */
X		if ( !(pname[i]->next) ||
X	   (!(pname[i]->next->next)&&strcmp(pname[i]->next->string, "void"))) {
X			addword(tlist, "int");
X		}
X		while (tlist->next) tlist = tlist->next;
X		tlist->next = pname[i];
X		if (i < np - 1)
X			addword(tlist, ",");
X	}
X	return plist;
X}
X
X/*
X * emit a function declaration. The attributes and name of the function
X * are in wlist; the parameters are in plist.
X */
X
Xvoid emit(wlist, plist, startline)
X	Word *wlist, *plist;
X	long  startline;
X{
X	Word *w;
X	int count = 0;
X	int needspace = 0;
X	int isstatic = 0;
X
XDEBUG("emit called\n");
X	if (donum)
X		printf("/*%8ld */ ", startline);
X
X	for (w = wlist; w; w = w->next) {
X		if (w->string[0]) {
X			count ++;
X			if (!strcmp(w->string, "static"))
X				isstatic = 1;
X		}
X	}
X
X/* if the -e flag was given, and it's not a static function, print "extern" */
X
X	if (print_extern && !isstatic) {
X		printf("extern ");
X	}
X
X	if (count < 2) {
X		printf("int");
X		needspace = 1;
X	}
X
X	for (w = wlist; w; w = w->next) {
X		if (needspace)
X			putchar(' ');
X		printf("%s", w->string);
X		needspace = ISCSYM(w->string[0]);
X	}
X	if (use_macro)
X		printf(" %s((", macro_name);
X	else
X		putchar('(');
X	needspace = 0;
X	for (w = plist; w; w = w->next) {
X		if (no_parm_names && IS_PARM_NAME(w))
X			continue;
X		if (w->string[0] == ',')
X			needspace = 1;
X		else if (w->string[0] == '[')
X			needspace = 0;
X		else
X		{
X			if (needspace)
X				putchar(' ');
X			needspace = ISCSYM(w->string[0]);
X		}
X		printf("%s", w->string);
X	}
X	if (use_macro)
X		printf("));\n");
X	else
X		printf(");\n");
X}
X
X/*
X * get all the function declarations
X */
X
Xvoid getdecl(f)
X	FILE *f;
X{
X	Word *plist, *wlist = NULL;
X	char buf[80];
X	int sawsomething;
X	long startline;		/* line where declaration started */
X	int oktoprint;
Xagain:
X	word_free(wlist);
X	wlist = word_alloc("");
X	sawsomething = 0;
X	oktoprint = 1;
X
X	for(;;) {
XDEBUG("main getdecl loop\n");
X		if (getsym(buf,f) < 0) {
XDEBUG("EOF in getdecl loop\n");
X			 return;
X		}
X/* try to guess when a declaration is not an external function definition */
X		if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
X		    !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
X		    !strcmp(buf, "extern")) {
X			skipit(buf, f);
X			goto again;
X		}
X		if (!dostatic && !strcmp(buf, "static")) {
X			oktoprint = 0;
X		}
X/* for the benefit of compilers that allow "inline" declarations */
X		if (!strcmp(buf, "inline") && !sawsomething)
X			continue;
X		if (!strcmp(buf, ";")) goto again;
X
X/* A left parenthesis *might* indicate a function definition */
X		if (!strcmp(buf, "(")) {
X			startline = linenum;
X			if (!sawsomething || !(plist = getparamlist(f))) {
X				skipit(buf, f);
X				goto again;
X			}
X			if (plist == ABORTED)
X				goto again;
X
X/* It seems to have been what we wanted */
X			if (oktoprint)
X				emit(wlist, plist, startline);
X			word_free(plist);
X			goto again;
X		}
X		addword(wlist, buf);
X		sawsomething = 1;
X	}
X}
X
Xvoid
Xmain(argc, argv)
Xint argc; char **argv;
X{
X	FILE *f;
X	char *t, *iobuf;
X	extern void Usage();
X
X	if (argv[0] && argv[0][0])
X		ourname = argv[0];
X	else
X		ourname = "mkptypes";
X
X	argv++; argc--;
X
X	if (argc < 0)		/* strange -- no args at all */
X		Usage();
X
X	iobuf = malloc(NEWBUFSIZ);
X	while (*argv && **argv == '-') {
X		t = *argv++; --argc; t++;
X		while (*t) {
X			if (*t == 'e')
X				print_extern = 1;
X			else if (*t == 'n')
X				donum = 1;
X			else if (*t == 'p') {
X				t = *argv++; --argc;
X				if (!t)
X					Usage();
X				use_macro = 1;
X				macro_name = t;
X				break;
X			}
X			else if (*t == 's')
X				dostatic = 1;
X			else if (*t == 'x')
X				/* no parm names, only types (sg) */
X				no_parm_names = 1;
X			else if (*t == 'z')
X				define_macro = 0;
X			else if (*t == 'A')
X				use_macro = 0;
X			else
X				Usage();
X			t++;
X		}
X	}
X
X	if (use_macro && define_macro) {
X		printf("#if defined(__STDC__) || defined(__cplusplus)\n");
X		printf("# define %s(s) s\n", macro_name);
X		printf("#else\n");
X		printf("# define %s(s) ()\n", macro_name);
X		printf("#endif\n\n");
X	}
X	if (argc == 0)
X		getdecl(stdin);
X	else
X		while (argc > 0 && *argv) {
XDEBUG("trying a new file\n");
X			if (!(f = fopen(*argv, "r"))) {
X				perror(*argv);
X				exit(EXIT_FAILURE);
X			}
X			if (iobuf)
X				setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ);
X
X			printf("\n/* %s */\n", *argv);
X			linenum = 1;
X			newline_seen = 1;
X			glastc = ' ';
XDEBUG("calling getdecl\n");
X			getdecl(f);
XDEBUG("back from getdecl\n");
X			argc--; argv++;
X			fclose(f);
XDEBUG("back from fclose\n");
X		}
X	if (use_macro && define_macro) {
X		printf("\n#undef %s\n", macro_name);	/* clean up namespace */
X	}
X	exit(EXIT_SUCCESS);
X}
X
X
Xvoid Usage()
X{
X	fprintf(stderr, 
X	   "Usage: %s [-e][-n][-p sym][-s][-x][-z][-A][files ...]\n", ourname);
X	fputs("   -e: put an explicit \"extern\" keyword in declarations\n",
X	   stderr);
X	fputs("   -n: put line numbers of declarations as comments\n",stderr);
X	fputs("   -p nm: use \"nm\" as the prototype macro (default \"_P\")\n",
X	   stderr);
X	fputs("   -s: include declarations for static functions\n", stderr);
X	fputs("   -x: omit parameter names in prototypes\n", stderr);
X	fputs("   -z: omit prototype macro definition\n", stderr);
X	fputs("   -A: omit prototype macro; header files are strict ANSI\n",
X	   stderr);
X	exit(EXIT_FAILURE);
X}
SHAR_EOF
if test 17019 -ne "`wc -c mkptypes.c`"
then
echo shar: error transmitting mkptypes.c '(should have been 17019 characters)'
fi
echo shar: extracting mkptypes.h
sed 's/^X//' << \SHAR_EOF > mkptypes.h
X#if defined(__STDC__) || defined(__cplusplus)
X# define _P(s) s
X#else
X# define _P(s) ()
X#endif
X
X
X/* mkptypes.c */
XWord *word_alloc _P((char *s));
Xvoid word_free _P((Word *w));
Xint List_len _P((Word *w));
XWord *word_append _P((Word *w1, Word *w2));
Xint foundin _P((Word *w1, Word *w2));
Xvoid addword _P((Word *w, char *s));
Xvoid typefixhack _P((Word *w));
Xint ngetc _P((FILE *f));
Xint fnextch _P((FILE *f));
Xint nextch _P((FILE *f));
Xint getsym _P((char *buf, FILE *f));
Xint skipit _P((char *buf, FILE *f));
Xint is_type_word _P((char *s));
XWord *typelist _P((Word *p));
XWord *getparamlist _P((FILE *f));
Xvoid emit _P((Word *wlist, Word *plist, long startline));
Xvoid getdecl _P((FILE *f));
Xvoid main _P((int argc, char **argv));
Xvoid Usage _P((void));
X
X#undef _P
SHAR_EOF
if test 761 -ne "`wc -c mkptypes.h`"
then
echo shar: error transmitting mkptypes.h '(should have been 761 characters)'
fi
echo shar: extracting mkptypes.man
sed 's/^X//' << \SHAR_EOF > mkptypes.man
XNAME
X    mkptypes - make prototypes for functions
X
X
XSYNOPSIS
X    mkptypes [ -e ][ -n ][ -p symbol ][ -s ][ -x ][ -z ][ -A ] [ file ... ]
X
X
XDESCRIPTION
X    Mkptypes takes as input one or more C source code files, and produces as
X    output (on the standard output stream) a list of function prototypes (a
X    la ANSI) for the external functions defined in the given source files. This
X    output, redirected to a file, is suitable for #include'ing in a C source
X    file. The function definitions in the original source may be either "old-
X    style" (in which case appropriate prototypes are generated for the funct-
X    ions) or "new-style" (in which the definition includes a prototype already).
X
X    The -e option causes the "extern" keyword to be explicitly printed for
X    external functions. Some non-ANSI compilers may need this.
X
X    The -n option causes the line number where each function was defined to
X    be prepended to the prototype declaration as a comment.
X
X    The -p option controls the name of the macro used to guard prototype
X    definitions. Normally this is "_P", but you can change it to any string you
X    like. To eliminate the guard macro entirely, use the -A option.
X
X    The -s option causes prototypes to be generated for functions declared
X    "static" as well as extern functions.
X
X    The -x option causes parameter names to be omitted from the output proto-
X    types. This may be necessary for some brain-damaged pseudo-ANSI com-
X    pilers. You may also prefer this style of output. This option has not been
X    thoroughly tested.
X
X    The -z option suppresses the definition of the prototype macro given by -p.
X    Header files generated by mkptypes with this option will not work unless
X    the prototype macro has been defined elsewhere in the program by the user.
X    Used with the -p option, the -z option allows use of predefined prototype
X    hiding macros that may exist on some systems.
X
X    The -A option causes the prototypes emitted to be only readable by ANSI
X    compilers.  Normally, the prototypes are "macro-ized" so that compilers
X    with __STDC__not defined don't see them.
X
X    If files are specified on the command line, then a comment specifying the
X    file of origin is emitted before the prototypes constructed from that file.
X    If no files are given, then no comments are emitted and the C source code
X    is taken from the standard input stream.
X
XBUGS
X    Mkptypes is easily confused by complicated declarations, such as
X
X           int ((*signal)())() f ...
X    or
X
X           struct foo f int x, y; g foofunc() f ...
X
X    This is because the program doesn't actually understand type definitions.
X    Some programs may need to be run through the preprocessor before being
X    run through mkptypes . The -n option will not work correctly on prepro-
X    cessor output if function definitions (as opposed to declarations) appear
X    in header files.
X
X    Typedef'd types aren't correctly promoted, e.g. for
X
X           typedef schar char; int foo(x) schar x;...
X    mkptypes incorrectly generates the prototype int foo(schar x) rather than
X    the [correct] int foo(int x).
X
X    Functions named "inline" with no explicit type qualifiers are not recog-
X    nized.
X
X
XSEE ALSO
X    cc (1), lint (1).
X
X
XAUTHOR
X    Eric R. Smith <ersmith@uwovax.uwo.ca>
X
X
XNOTE
X    There is no warranty for this program (as noted above, it's guaranteed to
X    break sometimes anyways!). Mkptypes is in the public domain.
SHAR_EOF
if test 3462 -ne "`wc -c mkptypes.man`"
then
echo shar: error transmitting mkptypes.man '(should have been 3462 characters)'
fi
#	End of shell archive
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.