[comp.sources.unix] v20i039: Generate C function prototypes from C source

rsalz@uunet.uu.net (Rich Salz) (10/20/89)

Submitted-by: Chin Huang <cthuang@watdragon.waterloo.edu>
Posting-number: Volume 20, Issue 39
Archive-name: cproto

[  Not to be confused with Ron Guilmette's protoize, which will also
   appear here.  This takes a neat way out in that it avoids doing any
   parsing of function bodies, which I think is really clever.  As Chin
   says, you will need FLEX which is available from your nearest archive
   site and is also distributed on most GNU sites.  /r$  ]

This is program automatically generates C function prototypes and
variable declarations from C language source code.

You don't need the source to 4.2BSD lint to compile this program but
you do need flex since the lexical analyzer specified in lex.l makes use
of mutually exclusive start conditions. 

Chin Huang
cthuang@watdragon.waterloo.edu

#
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Contents:  README Makefile cproto.1 cproto.sh grammar.y lex.l cproto.h
#	patchlevel.h semantics.h symbol.h cproto1.c semantics.c string.c
#	symbol.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
This is program automatically generates C function prototypes and
variable declarations from C language source code.

You don't need the source to 4.2BSD lint to compile this program but
you do need flex since the lexical analyzer specified in lex.l makes use
of mutually exclusive start conditions. 

Chin Huang
cthuang@watdragon.waterloo.edu
@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
#
# $Header: Makefile,v 1.1 89/03/14 20:58:56 cthuang Exp $
#
# Makefile for C prototype generator

LEX = flex
YACC = yacc
CFLAGS = -O

SOURCES =	README Makefile cproto.1 cproto.sh \
		grammar.y lex.l \
		cproto.h patchlevel.h semantics.h symbol.h \
		cproto1.c semantics.c string.c symbol.c
CSOURCES =	cproto1.c semantics.c string.c symbol.c y.tab.c
OBJECTS =	cproto1.o semantics.o string.o symbol.o y.tab.o

all: cproto1

cproto1: $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $(OBJECTS)

y.tab.o: y.tab.c lex.yy.c
	$(CC) $(CFLAGS) -c $*.c

y.tab.c: grammar.y
	$(YACC) grammar.y

lex.yy.c: lex.l grammar.y
	$(LEX) lex.l

lint:
	lint -B $(CSOURCES)

shar:
	shar -c $(SOURCES) >shar.out

ci:
	ci -u $(SOURCES)

main.o: symbol.h cproto.h
semantics.o: symbol.h cproto.h semantics.h
symbol.o: symbol.h
y.tab.o: symbol.h cproto.h semantics.h
@//E*O*F Makefile//
chmod u=rw,g=r,o=r Makefile
 
echo x - cproto.1
sed 's/^@//' > "cproto.1" <<'@//E*O*F cproto.1//'
@.\" $Header: cproto.1,v 1.1 89/03/14 21:30:01 cthuang Exp $
@.\"
@.de EX          \"Begin example
@.ne 5
@.if n .sp 1
@.if t .sp .5
@.nf
@.in +.5i
@..
@.de EE		\"End example
@.fi
@.in -.5i
@.if n .sp 1
@.if t .sp .5
@..
@.TH CPROTO 1 "March 14, 1989"
@.SH NAME
cproto \- generate C function prototypes from C source code
@.SH SYNOPSIS
@.B cproto
[ 
@.I option \fP...\fI
] [
@.I file
]
@.SH DESCRIPTION
@.B Cproto
generates C function prototypes from a C language source file.
For each function defined in the file,
@.B cproto
outputs to the standard output a line consisting of the prototype
for that function.
Optionally,
@.B cproto
also outputs declarations for any variables defined in the file.
If no
@.I file
argument is given,
@.B cproto
takes its input from the standard input.
@.SH OPTIONS
@.TP
@.B \-e
On the output, prepend the keyword
@.B extern
to declarations that have global scope.
@.TP
@.B \-g
Output only "global" declarations.
Only declarations of functions and variables that can be accessed outside
of the source file will be printed.
@.TP
@.BI \-p n
Set the style of function prototype where
@.I n
is a number from 0 to 2.
For example, consider the function definition
@.EX
main (argc, argv)
int argc;
char *argv[];
{
 ...
}
@.EE
A value of 2 produces the full function prototype:
@.EX
int main(int argc, char *argv[]);
@.EE
For a value of 1, the output has the form:
@.EX
int main(int /*argc*/, char */*argv*/[]);
@.EE
When set to 0, the output is:
@.EX
int main(/*int argc, char *argv[]*/);
@.EE
The default value is 2.
@.TP
@.B \-v
Also output declarations of variables defined in the file.
@.TP
@.BI \-D name\[=value\]
This option is passed through to the preprocessor and is used to define 
symbols for use with conditionals such as
@.I #ifdef.
@.TP
@.BI \-U name
This option is passed through to the preprocessor and is used to remove
any definitions of this symbol.
@.TP
@.BI \-I directory
This option is passed through to the preprocessor and is used to specify
a directory to search for files that are referenced with 
@.I #include.
@.SH AUTHOR
Chin Huang (cthuang@watdragon.waterloo.edu)
@.SH CAVEATS
Any error messages produced are minimal.
If the C source code is accepted by the C compiler, then
@.B cproto
will probably accept it as well.
@.SH "SEE ALSO"
cc(1),
cpp(1)
@//E*O*F cproto.1//
chmod u=r,g=r,o=r cproto.1
 
echo x - cproto.sh
sed 's/^@//' > "cproto.sh" <<'@//E*O*F cproto.sh//'
#!/bin/sh
# $Header$
#
cpp_opt="-I."
cproto_opt=""
source=""
for opt do
    case $opt in
    -D*|-U*|-I*)
	cpp_opt="$cpp_opt $opt"
	;;
    -*)
	cproto_opt="$cproto_opt $opt"
	;;
    *)
	source=$opt
	;;
    esac
done
/lib/cpp $cpp_opt $source | cproto1 $cproto_opt
@//E*O*F cproto.sh//
chmod u=rwx,g=r,o=r cproto.sh
 
echo x - grammar.y
sed 's/^@//' > "grammar.y" <<'@//E*O*F grammar.y//'
/*
 * $Header: grammar.y,v 1.1 89/03/14 20:59:01 cthuang Exp $
 *
 * yacc grammar for C prototype generator
 * This was derived from the grammar given in Appendix A of
 * "The C Programming Language" by the Kernighan and Ritchie.
 */

/* identifiers that are not reserved words */
%token IDENTIFIER TYPEDEF_NAME

/* storage class */
%token AUTO EXTERN REGISTER STATIC TYPEDEF

/* type specifiers */
%token CHAR DOUBLE FLOAT INT VOID
%token LONG SHORT SIGNED UNSIGNED
%token ENUM STRUCT UNION

/* type qualifiers */
%token CONST VOLATILE

/* paired braces and everything between them: { ... } */
%token BRACES

/* paired square brackets and everything between them: [ ... ]*/
%token BRACKETS

/* three periods */
%token ELLIPSIS

/* equal sign followed by constant expression or stuff between braces */
%token INITIALIZER

%type <decl_spec> decl_specs decl_spec_item
%type <decl_spec> storage_class type_spec type_qualifier
%type <decl_spec> struct_union_spec enum_spec
%type <decl_list> init_decl_list
%type <declarator> init_declarator declarator direct_decl
%type <param_list> param_type_list param_list
%type <parameter> param_decl
%type <param_list> identifier_list ident_list
%type <text> struct_or_union
%type <text> pointer type_qual_list
%type <text> IDENTIFIER TYPEDEF_NAME
%type <text> BRACKETS

%{
#include <stdio.h>
#include "cproto.h"
#include "semantics.h"

/* This variable is TRUE when the parameter declaration part of a function
 * definition is currently being parsed.
 * For example:
 *	int
 *	main (argc, argv)
 *	int argc;		| TRUE when these lines
 *	char **argv;		| are being scanned.
 *	{			 
 *	... 			 
 *      }			 
 */
static boolean func_decl = FALSE;

/* This points to the list of parameters for the current function definition. */
static ParameterList *cur_params;

/* temporary string buffer */
static char buf[MAX_TEXT_LENGTH];

/* Table of typedef names */
SymbolTable *typedef_names;
%}
%%

program		: /* empty */
		| translation_unit
		;

translation_unit : external_decl
		| translation_unit external_decl
		;

external_decl	: declaration
		| function_def
		| error
		;

declaration	: decl_specs ';'
		| decl_specs init_decl_list ';'
		{
		    if (func_decl) {
			set_param_types(cur_params, &$1, &$2);
		    } else {
			output_declarations(&$1, &$2);
		    }
		    free_decl_spec(&$1);
		    free_decl_list(&$2);
		}
		| TYPEDEF decl_specs declarator ';'
		{
		    (void)new_symbol(typedef_names, $3.name);
		    free_decl_spec(&$2);
		    free_declarator(&$3);
		}
		;

function_def	: decl_specs
		  declarator
		{
		    if ($2.func_def == FUNC_DEF_NONE) {
			yyerror("syntax error");
			YYERROR;
		    }
		    func_decl = TRUE;
		    cur_params = &($2.params);
		}
		  opt_decl_list BRACES
		{
		    func_decl = FALSE;

		    output_prototype(&$1, &$2);
		    free_decl_spec(&$1);
		    free_declarator(&$2);
		}
		| declarator
		{
		    if ($1.func_def == FUNC_DEF_NONE) {
			yyerror("syntax error");
			YYERROR;
		    }
		    func_decl = TRUE;
		    cur_params = &($1.params);
		}
		  opt_decl_list BRACES
		{
		    DeclSpec	decl_spec;

		    func_decl = FALSE;

		    new_decl_spec(&decl_spec, "int", DE_EXTERN);
		    output_prototype(&decl_spec, &$1);
		    free_decl_spec(&decl_spec);
		    free_declarator(&$1);
		}
		;

decl_list	: declaration
		| decl_list declaration
		;

decl_specs	: decl_spec_item
		| decl_specs decl_spec_item
		{
		    join_decl_specs(&$$, &$1, &$2);
		}
		;

decl_spec_item	: storage_class
		| type_spec
		| type_qualifier
		;

opt_decl_list	: decl_list
		| /* empty */
		;

storage_class	: AUTO
		{
		    new_decl_spec(&$$, "auto", DE_JUNK);
		}
		| EXTERN
		{
		    new_decl_spec(&$$, "extern", DE_EXTERN);
		}
		| REGISTER
		{
		    new_decl_spec(&$$, "register", DE_JUNK);
		}
		| STATIC
		{
		    new_decl_spec(&$$, "static", DE_STATIC);
		}
		;

type_spec	: CHAR
		{
		    new_decl_spec(&$$, "char", DE_EXTERN);
		}
		| DOUBLE
		{
		    new_decl_spec(&$$, "double", DE_EXTERN);
		}
		| FLOAT
		{
		    new_decl_spec(&$$, "float", DE_EXTERN);
		}
		| INT
		{
		    new_decl_spec(&$$, "int", DE_EXTERN);
		}
		| LONG
		{
		    new_decl_spec(&$$, "long", DE_EXTERN);
		}
		| SHORT
		{
		    new_decl_spec(&$$, "short", DE_EXTERN);
		}
		| SIGNED
		{
		    new_decl_spec(&$$, "signed", DE_EXTERN);
		}
		| UNSIGNED
		{
		    new_decl_spec(&$$, "unsigned", DE_EXTERN);
		}
		| VOID
		{
		    new_decl_spec(&$$, "void", DE_EXTERN);
		}
		| struct_union_spec
		| enum_spec
		| TYPEDEF_NAME
		{
		    new_decl_spec(&$$, $1, DE_EXTERN);
		}
		;

type_qualifier	: CONST
		{
		    new_decl_spec(&$$, "const", DE_EXTERN);
		}
		| VOLATILE
		{
		    new_decl_spec(&$$, "volatile", DE_EXTERN);
		}
		;

struct_union_spec : struct_or_union IDENTIFIER BRACES
		{
		    sprintf(buf, "%s %s {}", $1, $2);
		    new_decl_spec(&$$, buf, DE_EXTERN);
		}
		| struct_or_union BRACES
		{
		    sprintf(buf, "%s {}", $1);
		    new_decl_spec(&$$, buf, DE_EXTERN);
		}
		| struct_or_union IDENTIFIER
		{
		    sprintf(buf, "%s %s", $1, $2);
		    new_decl_spec(&$$, buf, DE_EXTERN);
		}
		;

struct_or_union	: STRUCT
		{
		    strcpy($$, "struct");
		}
		| UNION
		{
		    strcpy($$, "union");
		}
		;

init_decl_list	: init_declarator
		{
		    new_decl_list(&$$, &$1);
		}
		| init_decl_list ',' init_declarator
		{
		    add_decl_list(&$$, &$1, &$3);
		}
		;

init_declarator	: declarator
		| declarator INITIALIZER
		;

enum_spec	: ENUM IDENTIFIER BRACES
		{
		    sprintf(buf, "enum %s {}", $2);
		    new_decl_spec(&$$, buf, DE_EXTERN);
		}
		| ENUM BRACES
		{
		    new_decl_spec(&$$, "enum {}", DE_EXTERN);
		}
		| ENUM IDENTIFIER
		{
		    sprintf(buf, "enum %s", $2);
		    new_decl_spec(&$$, buf, DE_EXTERN);
		}
		;

declarator	: pointer direct_decl
		{
		    sprintf(buf, "%s%s", $1, $2.text);
		    $$ = $2;
		    $$.text = strdup(buf);
		    free($2.text);
		}
		| direct_decl
		;

direct_decl	: IDENTIFIER
		{
		    new_declarator(&$$, $1, $1);
		}
		| '(' declarator ')'
		{
		    sprintf(buf, "(%s)", $2.text);
		    $$ = $2;
		    $$.text = strdup(buf);
		    free($2.text);
		}
		| direct_decl BRACKETS
		{
		    sprintf(buf, "%s%s", $1.text, $2);
		    $$ = $1;
		    $$.text = strdup(buf);
		    free($1.text);
		}
		| direct_decl '(' param_type_list ')'
		{
		    sprintf(buf, "%s(%%s)", $1.text);
		    $$ = $1;
		    $$.func_def = FUNC_DEF_NEW;
		    $$.params = $3;
		    $$.text = strdup(buf);
		    free($1.text);
		}
		| direct_decl '(' identifier_list ')'
		{
		    sprintf(buf, "%s(%%s)", $1.text);
		    $$ = $1;
		    $$.func_def = FUNC_DEF_OLD;
		    $$.params = $3;
		    $$.text = strdup(buf);
		    free($1.text);
		}
		;

pointer		: '*' type_qual_list
		{
		    sprintf($$, "*%s", $2);
		}
		| '*' type_qual_list pointer
		{
		    sprintf($$, "*%s%s", $2, $3);
		}
		;

type_qual_list	: /* empty */
		{
		    strcpy($$, "");
		}
		| type_qual_list type_qualifier
		{
		    sprintf($$, "%s %s ", $1, $2.text);
		    free($2.text);
		}
		;

param_type_list	: VOID
		{
		    Parameter	p;

		    new_parameter(&p, "void", " ", " ");
		    new_param_list(&$$, &p);
		}
		| param_list
		| param_list ',' ELLIPSIS
		{
		    Parameter	p;

		    new_parameter(&p, "...", " ", " ");
		    add_param_list(&$$, &$1, &p);
		}
		;

param_list	: param_decl
		{
		    new_param_list(&$$, &$1);
		}
		| param_list ',' param_decl
		{
		    add_param_list(&$$, &$1, &$3);
		}
		;

param_decl	: decl_specs declarator
		{
		    new_parameter(&$$, $1.text, $2.text, $2.name);
		}
		;

identifier_list	: /* empty */
		{
		    new_ident_list(&$$);
		}
		| ident_list
		;

ident_list	: IDENTIFIER
		{
		    new_ident_list(&$$);
		    add_ident_list(&$$, &$$, $1);
		}
		| ident_list ',' IDENTIFIER
		{
		    add_ident_list(&$$, &$1, $3);
		}
		;

%%
#include "lex.yy.c"

yyerror (msg)
char *msg;
{
    print_error(msg);
}

void
parse_file ()
{
    typedef_names = create_symbol_table();
    (void)yyparse();
}
@//E*O*F grammar.y//
chmod u=rw,g=r,o=r grammar.y
 
echo x - lex.l
sed 's/^@//' > "lex.l" <<'@//E*O*F lex.l//'
/*
 * $Header: lex.l,v 1.1 89/03/14 20:59:10 cthuang Exp $
 *
 * C function prototype generator
 * Lexical analyzer specification for flex
 */

WLF             ([ \t\n\f]*)
LETTER		[a-zA-Z_]
DIGIT		[0-9]
ID		{LETTER}({LETTER}|{DIGIT})*
SKIPQUOTED	(\"([^"\n]|\\\")*\"|\'.\'|\\.)

%{
static int curly = 0;		/* number of curly brace nesting levels */
int line_num = 1;
%}

%x INIT1 INIT2 CURLY COMMENT
%%
\n		{ ++line_num; }

#[ \t]*[0-9]+.*$	{ sscanf(yytext, "# %d ", &line_num); --line_num; }
#.*$			;

"("		return '(';
")"		return ')';
"*"		return '*';
","		return ',';
";"		return ';';
"..."		return ELLIPSIS;

auto		return AUTO;
extern		return EXTERN;
register	return REGISTER;
static		return STATIC;
typedef		return TYPEDEF;
char		return CHAR;
double		return DOUBLE;
float		return FLOAT;
int		return INT;
void		return VOID;
long		return LONG;
short		return SHORT;
signed		return SIGNED;
unsigned	return UNSIGNED;
enum		return ENUM;
struct		return STRUCT;
union		return UNION;
const		return CONST;
volatile	return VOLATILE;

\[[^\]]*\]	{
		    strcpy(yylval.text, yytext);
		    return BRACKETS;
		}

{ID}		{
		    strcpy(yylval.text, yytext);
		    if (is_typedef_name(yytext))
			return TYPEDEF_NAME;
		    else
			return IDENTIFIER;
		}

"="{WLF}"{"		{ curly = 1; BEGIN INIT1; }
<INIT1>\n		{ ++line_num; }
<INIT1>"{"		++curly;
<INIT1>"}"		{
			    if (--curly == 0) {
				BEGIN 0;
				return INITIALIZER;
			    }
			}
<INIT1>{SKIPQUOTED}|.	;

"="			{ BEGIN INIT2; }
<INIT2>\n		{ ++line_num; }
<INIT2>({SKIPQUOTED}|[^,;])+[,;]	{
			    yyless(yyleng-1);
			    BEGIN 0;
			    return INITIALIZER;
			}

"{"			{ curly=1; BEGIN CURLY; }
<CURLY>\n		{ ++line_num; }
<CURLY>"{"		{ ++curly; }
<CURLY>"}"		{
			    --curly;
			    if (curly == 0) {
				BEGIN 0;
				return BRACES;
			    }
			}
<CURLY>{SKIPQUOTED}|.	;

"/*"		BEGIN COMMENT;
<COMMENT>\n	{ ++line_num; }
<COMMENT>"*/"	BEGIN 0;
<COMMENT>.	;

[ \t]+		;
@.		print_error("syntax error");
@//E*O*F lex.l//
chmod u=rw,g=r,o=r lex.l
 
echo x - cproto.h
sed 's/^@//' > "cproto.h" <<'@//E*O*F cproto.h//'
/*
 * $Header: cproto.h,v 1.1 89/03/14 20:59:12 cthuang Exp $
 *
 * Definitions for C language prototype generator
 */
#include "symbol.h"

/* Boolean type */
typedef char boolean;
#define FALSE	0
#define TRUE	1

/* maximum number of characters in a text buffer */
#define MAX_TEXT_LENGTH	512

/* Declaration specifier flags */
#define DE_EXTERN	0	/* default: declaration has global scope */
#define DE_STATIC	1	/* visible only in current file */
#define DE_JUNK		2	/* we're not interested in this declaration */

/* This structure stores information about a declaration specifier. */
typedef struct _decl_spec {
    unsigned short	flags;	/* flags defined above */
    char		*text;	/* source text */
} DeclSpec;

/* Types of function definitions
 *
 * FUNC_DEF_OLD		traditional style function definition
 * FUNC_DEF_NEW		ANSI style
 */
typedef enum { FUNC_DEF_NONE, FUNC_DEF_OLD, FUNC_DEF_NEW } FuncDefType;

/* This structure stores information about a function parameter. */
typedef struct _parameter {
    char		*decl_spec;	/* declaration specifier text */
    char		*declarator;	/* declarator text */
    char		*name;		/* parameter name */
    struct _parameter	*next;		/* next parameter in list */
} Parameter;

/* This is a list of function parameters. */
typedef struct _parameter_list {
    Parameter		*first;	/* pointer to first parameter in list */
    Parameter		*last;  /* pointer to last parameter in list */  
} ParameterList;

/* This structure stores information about a declarator. */
typedef struct _declarator {
    char		*name;	/* name of declared variable or function */
    char		*text;	/* source text */
    FuncDefType		func_def;	/* style of function definition */
    ParameterList	params;	/* list of parameters if this is a function */
    struct _declarator	*next;	/* next declarator in list */
} Declarator;

/* This is a list of declarators. */
typedef struct _declarator_list {
    Declarator		*first;	/* pointer to first declarator in list */
    Declarator		*last;  /* pointer to last declarator in list */  
} DeclaratorList;

/* parser stack entry type */
typedef union {
    char		text[MAX_TEXT_LENGTH];
    DeclSpec		decl_spec;
    Parameter		parameter;
    ParameterList	param_list;
    Declarator		declarator;
    DeclaratorList	decl_list;
} YYSTYPE;

/* Program options */
extern boolean extern_out;
extern boolean globals_only;
extern int proto_style;
extern boolean variables_out;

/* Global declarations */
extern int line_num;
extern SymbolTable *typedef_names;
extern void print_error();
extern void parse_file();

extern char *strdup(), *strstr();
extern char *malloc();
@//E*O*F cproto.h//
chmod u=rw,g=r,o=r cproto.h
 
echo x - patchlevel.h
sed 's/^@//' > "patchlevel.h" <<'@//E*O*F patchlevel.h//'
#define PATCHLEVEL 0
@//E*O*F patchlevel.h//
chmod u=rw,g=r,o=r patchlevel.h
 
echo x - semantics.h
sed 's/^@//' > "semantics.h" <<'@//E*O*F semantics.h//'
/*
 * $Header: semantics.h,v 1.1 89/03/14 20:59:16 cthuang Exp $
 *
 * Declarations for semantics action routines
 */

extern boolean	is_typedef_name();

extern void	new_decl_spec();
extern void	join_decl_specs();
extern void	free_decl_spec();

extern void	new_parameter();
extern void	free_parameter();

extern void	new_param_list();
extern void	add_param_list();
extern void	free_param_list();

extern void	new_ident_list();
extern void	add_ident_list();

extern void	new_declarator();
extern void	free_declarator();

extern void	new_decl_list();
extern void	add_decl_list();
extern void	free_decl_list();

extern void	set_param_types();
extern void	output_declarations();
extern void	output_prototype();
@//E*O*F semantics.h//
chmod u=rw,g=r,o=r semantics.h
 
echo x - symbol.h
sed 's/^@//' > "symbol.h" <<'@//E*O*F symbol.h//'
/*
 * $Header: symbol.h,v 1.1 89/03/14 20:59:18 cthuang Exp $
 *
 * Definitions for a symbol table
 */
#ifndef _SYMBOL_H
#define _SYMBOL_H

/* maximum length of symbols */
#define SYM_MAX_LENGTH 64

typedef struct _symbol {
	struct _symbol	*next;			/* next symbol in list */
	char		name[SYM_MAX_LENGTH];	/* name of symbol */
} Symbol;

/* hash table length */
#define SYM_MAX_HASH 256

typedef struct _symbol_table {
	Symbol		*bucket[SYM_MAX_HASH];	/* hash buckets */
} SymbolTable;

extern SymbolTable	*create_symbol_table();	/* Create symbol table */
extern Symbol		*find_symbol();		/* Lookup symbol name */
extern Symbol		*new_symbol();		/* Define new symbol */

#endif
@//E*O*F symbol.h//
chmod u=rw,g=r,o=r symbol.h
 
echo x - cproto1.c
sed 's/^@//' > "cproto1.c" <<'@//E*O*F cproto1.c//'
/*
 * $Header: cproto1.c,v 1.1 89/03/14 20:59:21 cthuang Exp $
 *
 * C prototype generator
 * This filter reads C source code and outputs ANSI C function prototypes.
 */
#ifndef lint
static char *rcsid = "$Header: cproto1.c,v 1.1 89/03/14 20:59:21 cthuang Exp $";
#endif
#include <stdio.h>
#include "cproto.h"

/* getopt declarations */
extern int getopt();
extern char *optarg;
extern int optind;

/* Name of the program */
static char *progname = "cproto";

/* Program options */

/* TRUE when "extern" should appear to external declarations. */
boolean extern_out = FALSE;

/* TRUE when only external declarations will be generated. */
boolean globals_only = FALSE;

/* This variable controls the style of function prototype. */
int proto_style = 2;

/* TRUE when variable declarations are printed also. */
boolean variables_out = FALSE;

/* Output an error message along with the line number where it was found.
 */
void
print_error (msg)
char *msg;
{
    fprintf(stderr, "%s: line %d: %s\n", progname, line_num, msg);
}

main (argc, argv)
int argc;
char **argv;
{
    int		c;
    boolean	error_flag;

    error_flag = FALSE;
    while ((c = getopt(argc, argv, "egp:v")) != EOF) {
	switch (c) {
	case 'e':
	    extern_out = TRUE;
	    break;
	case 'g':
	    globals_only = TRUE;
	    break;
	case 'p':
	    proto_style = atoi(optarg);
	    break;
	case 'v':
	    variables_out = TRUE;
	    break;
	case '?':
	default:
	    error_flag = TRUE;
	}
    }

    if (optind == argc-1) {
	if (freopen(argv[optind], "r", stdin) == NULL) {
	    fprintf(stderr, "%s: cannot open file %s\n", progname,
	    	    argv[optind]);
	    exit(1);
	}
    } else {
	error_flag = (boolean)(optind < argc-1);
    }

    if (error_flag) {
	fprintf(stderr, "usage: %s [ -egv ] [ -p n ] [ file ]\n", progname);
	exit(1);
    }

    parse_file();
}
@//E*O*F cproto1.c//
chmod u=rw,g=r,o=r cproto1.c
 
echo x - semantics.c
sed 's/^@//' > "semantics.c" <<'@//E*O*F semantics.c//'
/*
 * $Header: semantics.c,v 1.1 89/03/14 20:59:22 cthuang Exp $
 *
 * C prototype generator
 * These routines implement the semantic actions executed by the yacc parser.
 * 
 */
#include <stdio.h>
#include <strings.h>
#include "cproto.h"
#include "semantics.h"

/* Create a new string by joining two strings with a space between them.
 * Return a pointer to the resultant string or NULL if an error occurred.
 */
static char *
concat_string (a, b)
char	*a, *b;
{
    char	*result;

    if ((result = malloc((unsigned)(strlen(a) + strlen(b) + 2))) != NULL) {
	strcpy(result, a);
	strcat(result, " ");
	strcat(result, b);
    }
    return result;
}

/* Delete the first occurence of a substring from a string.
 * Return a pointer to a static buffer containing the result.
 * If the substring is not found, then return the entire string.
 */
static char *
delete_string (src, key)
char	*src, *key;
{
    static char buf[MAX_TEXT_LENGTH];
    char	*s;
    int		n;

    if ((s = strstr(src, key)) == NULL) {
	strcpy(buf, src);
    } else {
	strcpy(buf, "");
	n = s - src;
	if (n > 0) strncat(buf, src, n);
	n = strlen(src) - n - strlen(key);
	if (n > 0) strncat(buf, s+strlen(key), n);
    }
    return buf;
}

/* Check if the given identifier is really a typedef name by searching the
 * symbol table.
 * Return TRUE if it is a typedef name.
 */
boolean
is_typedef_name (name)
char *name;
{
    return (boolean)(find_symbol(typedef_names, name) != NULL);
}

/* Initialize a new declaration specifier part.
 */
void
new_decl_spec (decl_spec, text, flags)
DeclSpec	*decl_spec;
char		*text;
unsigned short	flags;
{
    decl_spec->text = strdup(text);
    decl_spec->flags = flags;
}

/* Append two declaration specifier parts together.
 */
void
join_decl_specs (result, a, b)
DeclSpec	*result, *a, *b;
{
    result->text = concat_string(a->text, b->text);
    result->flags = a->flags | b->flags;
    free(a->text);
    free(b->text);
}

/* Free storage used by a declaration specifier part.
 */
void
free_decl_spec (decl_spec)
DeclSpec	*decl_spec;
{
    if (decl_spec->text != NULL)
	free(decl_spec->text);
}

/* Initialize the parameter structure.
 */
void
new_parameter (param, decl_spec, declarator, name)
Parameter	*param;
char		*decl_spec, *declarator, *name;
{
    param->decl_spec = strdup(decl_spec);
    param->declarator = strdup(delete_string(declarator, "%s"));
    param->name = strdup(name);
}

/* Free the storage used by the parameter.
 */
void
free_parameter (param)
Parameter	*param;
{
    if (param->decl_spec != NULL)
	free(param->decl_spec);
    if (param->declarator != NULL)
	free(param->declarator);
    free(param->name);
}

/* Initialize an empty list of function parameters.
 */
void
new_param_list (param_list, param)
ParameterList	*param_list;
Parameter	*param;
{
    Parameter	*p;

    p = (Parameter *)malloc(sizeof(Parameter));
    *p = *param;
    
    param_list->first = param_list->last = p;
    p->next = NULL;
}

/* Add the function parameter declaration to the list.
 */
void
add_param_list (to, from, param)
ParameterList	*to, *from;
Parameter	*param;
{
    Parameter	*p;

    p = (Parameter *)malloc(sizeof(Parameter));
    *p = *param;

    to->first = from->first;
    from->last->next = p;
    to->last = p;
    p->next = NULL;
}

/* Free storage used by the elements in the function parameter list.
 */
void
free_param_list (param_list)
ParameterList	*param_list;
{
    Parameter	*p, *next;

    p = param_list->first;
    while (p != NULL) {
	next = p->next;
	free_parameter(p);
	free((char *)p);
	p = next;
    }
}

/* Initialize an empty list of function parameter names.
 * This is just a list of function parameter declarations with
 * only the name field set.
 */
void
new_ident_list (param_list)
ParameterList	*param_list;
{
    param_list->first = param_list->last = NULL;
}

/* Add an item to the list of function parameter declarations but set only
 * the parameter name field.
 */
void
add_ident_list (to, from, name)
ParameterList	*to, *from;
char		*name;
{
    Parameter	*p;

    p = (Parameter *)malloc(sizeof(Parameter));
    p->decl_spec = NULL;
    p->declarator = NULL;
    p->name = strdup(name);

    to->first = from->first;
    if (to->first == NULL) {
	to->first = p;
    } else {
	from->last->next = p;
    }
    to->last = p;
    p->next = NULL;
}

/* Initialize a declarator.
 */
void
new_declarator (d, name, text)
Declarator	*d;
char		*name, *text;
{
    d->name = strdup(name);
    d->text = strdup(text);
    d->func_def = FUNC_DEF_NONE;
    d->params.first = d->params.last = NULL;
}

/* Free storage used by a declarator.
 */
void
free_declarator (d)
Declarator	*d;
{
    if (d->name != NULL)
	free(d->name);
    if (d->text != NULL)
	free(d->text);
    free_param_list(&(d->params));
}

/* Initialize a declarator list and add the given declarator to it.
 */
void
new_decl_list (decl_list, declarator)
DeclaratorList	*decl_list;
Declarator	*declarator;
{
    Declarator	*d;

    d = (Declarator *)malloc(sizeof(Declarator));
    *d = *declarator;

    decl_list->first = decl_list->last = d;
    d->next = NULL;
}

/* Add the declarator to the declarator list.
 */
void
add_decl_list (to, from, declarator)
DeclaratorList	*to, *from;
Declarator	*declarator;
{
    Declarator	*d;

    d = (Declarator *)malloc(sizeof(Declarator));
    *d = *declarator;

    to->first = from->first;
    from->last->next = d;
    to->last = d;
    to->last->next = NULL;
}

/* Free storage used by the declarators in the declarator list.
 */
void
free_decl_list (decl_list)
DeclaratorList	*decl_list;
{
    Declarator	*d, *next;

    d = decl_list->first;
    while (d != NULL) {
	next = d->next;
	free_declarator(d);
	free((char *)d);
	d = next;
    }
}

/* Search the list of parameters for a matching parameter name.
 * Return a pointer to the matching parameter or NULL if not found.
 */
static Parameter *
search_parameter_list (params, name)
ParameterList	*params;
char		*name;
{
    Parameter	*p;

    for (p = params->first; p != NULL; p = p->next) {
	if (strcmp(p->name, name) == 0)
	    return p;
    }
    return (Parameter *)NULL;
}

/* For each declared variable name in the declarator list,
 * set the declaration specifier and declarator for the parameter
 * with the same name.
 */
void
set_param_types (params, decl_spec, declarators)
ParameterList	*params;
DeclSpec	*decl_spec;
DeclaratorList	*declarators;
{
    Declarator	*d;
    Parameter	*p;
    char	buf[MAX_TEXT_LENGTH];

    for (d = declarators->first; d != NULL; d = d->next) {
	/* Search the parameter list for a matching name. */
	p = search_parameter_list(params, d->name);
	if (p == NULL) {
	    sprintf(buf, "declared argument \"%s\" is missing", d->name);
	    print_error(buf);
	} else {
	    p->decl_spec = strdup(decl_spec->text);
	    p->declarator = strdup(delete_string(d->text, "%s"));
	}
    }
}

/* Output a declaration specifier for an external declaration.
 */
static void
output_decl_spec (decl_spec)
DeclSpec	*decl_spec;
{
    if (extern_out && (decl_spec->flags & DE_STATIC) == 0) {
	if (strstr(decl_spec->text, "extern") == NULL) {
	    printf("extern ");
	}
    }
    printf("%s ", decl_spec->text);
}

/* Output a function parameter type and variable declaration.
 */
static void
output_parameter (p)
Parameter	*p;
{
    char	*s;

    if (proto_style == 1) {
	s = strstr(p->declarator, p->name);
	*s = '\0';
	printf("%s %s/*%s*/%s", p->decl_spec, p->declarator, p->name,
	       s+strlen(p->name));
	*s = *(p->name);
    } else {
	printf("%s %s", p->decl_spec, p->declarator);
    }
}

/* Output the list of function parameters.
 */
static void
output_parameters (params)
ParameterList	*params;
{
    Parameter	*p;

    if (proto_style == 0)
	printf("/*");

    p = params->first;
    if (p == NULL) {
	printf("void");
    } else {
	output_parameter(p);
	p = p->next;
	while (p != NULL) {
	    printf(", ");
	    output_parameter(p);
	    p = p->next;
	}
    }

    if (proto_style == 0)
	printf("*/");
}

/* Output a declarator.
 */
static void
output_declarator (d)
Declarator	*d;
{
    char	*s;

    if (d->func_def == FUNC_DEF_NONE) {
	printf("%s", d->text);
    } else {
	if ((s = strstr(d->text, "%s")) != NULL) {
	    *s = '\0';
	    printf("%s", d->text);
	    output_parameters(&(d->params));
	    printf("%s", s+2);
	    *s = '%';
	}
    }
}

/* Output variable declarations.
 */
void
output_declarations (decl_spec, decl_list)
DeclSpec	*decl_spec;	/* declaration specifier */
DeclaratorList	*decl_list;	/* list of declared variables */
{
    Declarator	*d;

    if (variables_out == FALSE)
	return;
    if (globals_only && (decl_spec->flags & DE_STATIC))
	return;

    for (d = decl_list->first; d != NULL; d = d->next) {
	if (d->func_def == FUNC_DEF_NONE) {
	    if (strstr(decl_spec->text, "extern") == NULL) {
		output_decl_spec(decl_spec);
		output_declarator(d);
		printf(";\n");
	    }
	}
    }
}

/* Output the prototype for the function definition.
 */
void
output_prototype (decl_spec, declarator)
DeclSpec	*decl_spec;
Declarator	*declarator;
{
    Parameter	*p;

    if (globals_only && (decl_spec->flags & DE_STATIC))
	return;

    /* Check for function parameters for which no type has been specified.
     * This happens when a parameter name appears in the function
     * declaration but does not appear in the parameter declaration part.
     * The default type in this cause is "int".
     */
    for (p = declarator->params.first; p != NULL; p = p->next) {
	if (p->decl_spec == NULL) {
	    p->decl_spec = strdup("int");
	    p->declarator = strdup(p->name);
	}
    }

    output_decl_spec(decl_spec);
    output_declarator(declarator);
    printf(";\n");
}
@//E*O*F semantics.c//
chmod u=rw,g=r,o=r semantics.c
 
echo x - string.c
sed 's/^@//' > "string.c" <<'@//E*O*F string.c//'
/*
 * $Header: string.c,v 1.1 89/03/14 20:59:24 cthuang Exp $
 *
 * Some are string handling routines
 */
#include <stdio.h>
#include <strings.h>

extern char	*malloc();

/* Copy the string into an allocated memory block.
 * Return a pointer to the copy.
 */
char *
strdup (s)
char	*s;
{
    char	*dest;

    if ((dest = malloc((unsigned)(strlen(s)+1))) != NULL)
	strcpy(dest, s);
    return dest;
}

/* Return a pointer to the first occurence of the substring 
 * within the string, or NULL if not found.
 */
char *
strstr (src, key)
char	*src, *key;
{
    char	*s;
    int		keylen;

    keylen = strlen(key);
    s = index(src, *key);
    while (s != NULL) {
	if (strncmp(s, key, keylen) == 0)
	    return s;
	s = index(s+1, *key);
    }
    return NULL;
}
@//E*O*F string.c//
chmod u=rw,g=r,o=r string.c
 
echo x - symbol.c
sed 's/^@//' > "symbol.c" <<'@//E*O*F symbol.c//'
/*
 * $Header: symbol.c,v 1.1 89/03/14 20:59:26 cthuang Exp $
 *
 * Symbol table maintenance. Implements an abstract data type called
 * the symbol table.
 */
#include <stdio.h>
#include <strings.h>
#include "symbol.h"

extern char *malloc();

/* Create a symbol table.
 * Return a pointer to the symbol table or NULL if an error occurs.
 */
SymbolTable *
create_symbol_table ()
{
    SymbolTable *symtab;
    int i;

    if ((symtab = (SymbolTable *)malloc(sizeof(SymbolTable))) != NULL) {
	for (i = 0; i < SYM_MAX_HASH; ++i)
	    symtab->bucket[i] = NULL;
    }
    return symtab;
}


/* This is a simple hash function mapping a symbol name to a hash bucket. */

static int
hash (name)
char *name;
{
    return (name[0] + name[1] + strlen(name)) % SYM_MAX_HASH;
}


/* Search the list of symbols <list> for the symbol <name>.
 * Return a pointer to the symbol or NULL if not found.
 */
static Symbol *
search_symbol_list (list, name)
Symbol *list;
char *name;
{
    Symbol *sym;

    for (sym = list; sym != NULL; sym = sym->next) {
	if (strncmp(sym->name, name, SYM_MAX_LENGTH-1) == 0)
	    return sym;
    }
    return NULL;
}


/* Look for symbol <name> in symbol table <symtab>.
 * Return a pointer to the symbol or NULL if not found.
 */
Symbol *
find_symbol (symtab, name)
SymbolTable *symtab;
char *name;
{
    return search_symbol_list(symtab->bucket[hash(name)], name);
}


/* If the symbol <name> does not already exist in symbol table <symtab>,
 * then add the symbol to the symbol table.
 * Return a pointer to the symbol or NULL on an error.
 */
Symbol *
new_symbol (symtab, name)
SymbolTable *symtab;	/* symbol table */
char *name;		/* symbol name */
{
    Symbol	*sym;
    int		i;

    if ((sym = find_symbol(symtab, name)) == NULL) {
	if ((sym = (Symbol *)malloc(sizeof(Symbol))) != NULL) {
	    strncpy(sym->name, name, SYM_MAX_LENGTH-1);
	    sym->name[SYM_MAX_LENGTH-1] = '\0';

	    i = hash(name);
	    sym->next = symtab->bucket[i];
	    symtab->bucket[i] = sym;
	}
    }
    return sym;
}
@//E*O*F symbol.c//
chmod u=rw,g=r,o= symbol.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
       9      50     344 README
      43     110     826 Makefile
     105     402    2247 cproto.1
      20      32     264 cproto.sh
     432     939    7877 grammar.y
     103     263    1946 lex.l
      86     373    2616 cproto.h
       1       3      21 patchlevel.h
      32      75     707 semantics.h
      28      98     673 symbol.h
      88     272    1827 cproto1.c
     466    1296    9642 semantics.c
      43     124     759 string.c
      93     308    2014 symbol.c
    1549    4345   31763 total
!!!
wc  README Makefile cproto.1 cproto.sh grammar.y lex.l cproto.h patchlevel.h semantics.h symbol.h cproto1.c semantics.c string.c symbol.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
--
cthuang@watdragon.uwaterloo.ca
cthuang@watdragon.waterloo.edu
cthuang@watdragon.waterloo.cdn
{uunet,utzoo}!watmath!watdragon!cthuang

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.