v13i061: Check for mistakes in C programs

Rich Salz rsalz at bbn.com
Wed Feb 24 00:30:40 AEST 1988


Submitted-by: Nick Crossley <ccicpg!nick at uunet.uu.net>
Posting-number: Volume 13, Issue 61
Archive-name: check


This program has been posted as a result of a discussion in comp.lang.c
concerning some possible errors in writing C programs.  It is a C syntax
checker, to be used as an adjunct to lint, not a replacement.  It makes
no attempt to duplicate any warning given by the standard lint, and is not
at all forgiving about simple syntax errors.

Warnings include things like "if (a = b);" nested comments, and dangling
else's.

See the README file for more details.  If anyone implements the software
metrics or upgrades the grammar significantly, please send the results
back to me!

	Nick Crossley
	CCI
	email:  ..!uunet!ccicpg!nick

#! /bin/sh
# This is a Shell Archive, containing the files/directories :-
#	Makefile README check.1 check.c check.h lex.l main.c metric.c parse.awk
#	parse.y symbol.c tables.sh test.c tree.c

# To unpack it, execute it with /bin/sh
# If run with standard input from a terminal, you will be prompted
# for the name of a directory to hold the unpacked files.
# Otherwise, it will unpack into the current directory.

if	[ -t 0 ]
then
	while	:
	do
		echo 'Directory for output : \c'
		read outdir

		if	[ X$outdir = X ]
		then	outdir=.
			break
		elif	[ -d ${outdir} ]
		then	break
		elif	echo "$outdir does not exist; create it? \c"
			read reply
			[ `expr "$reply" : '[Yy].*'` = 0 ]
		then	continue
		elif	mkdir $outdir
		then	break
		else	echo "Cannot create $outdir" 1>&2
			continue
		fi
	done
	cd $outdir
fi

sed -e 's/^XX//' <<'====End of SHAR Makefile====' >Makefile
XX#	Makefile for C checker
XX#	Has been used on SysV and BSD/SysV mixtures
XX#	I don't guarantee it will work on real BSD
XX#	Set the following macros :-
XX#		CHECK	if you want to change the program name
XX#		LIB	for the installation directory
XX#		CFLAGS	for cc flags
XX#		LDFLAGS for final cc flags
XX#		BLIBS	for any other libraries to scan
XX#		LFLAGS	for lex flags
XX#		YFLAGS	for yacc flags
XX#		LINTF	for lint flags
XX
XXCHECK	= check
XXLIB	= bin
XXCFLAGS	= -O
XXLDFLAGS	=
XXBLIBS	=
XXLFLAGS	=
XXYFLAGS	= -vd
XXLINTF	=
XX
XXOBJ	= main.o  lex.o  parse.o  symbol.o  tree.o  check.o  metric.o
XXSRC	= main.c  lex.l  parse.y  symbol.c  tree.c  check.c  metric.c
XX
XXbuild	:	$(CHECK)
XX
XXinstall	:	$(CHECK)
XX		cp $(CHECK) $(LIB)
XX		-touch install
XX
XX$(CHECK):	$(OBJ)
XX		$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(BLIBS) -lm
XX
XX$(OBJ)	:	y.xxx.h check.h
XX
XXtree.o	:	tables.h
XX
XXtables.h:	y.xxx.h tables.sh
XX		tables.sh
XX
XXy.xxx.h	:	y.tab.h
XX		-cmp -s y.xxx.h y.tab.h || cp y.tab.h y.xxx.h
XX
XXy.tab.h	:	parse.c
XX
XX.y.c	:;	$(YACC) $(YFLAGS) $<
XX		mv y.tab.c $*.c
XX		parse.awk &
XX
XX.y.o	:;	$(YACC) $(YFLAGS) $<
XX		mv y.tab.c $*.c
XX		parse.awk &
XX		$(CC) $(CFLAGS) -c $*.c
XX
XX.l.o	:;	$(LEX) $(LFLAGS) $<
XX		mv lex.yy.c $*.c
XX		$(CC) $(CFLAGS) -c $*.c
XX
XX.c.o	:;	$(CC) $(CFLAGS) -c $*.c
XX
XXlist	:	$(SRC) Makefile
XX		pr -n -f -h "C Checker" $? | lp
XX		-touch list
XX
XXlint	:	$(OBJ:.o=.c) tables.h
XX		lint $(LINTF) $(OBJ:.o=.c)
XX
XXclean	:;	rm -f *.o y.* yacc* parse.c lex.c tables.h
XX
XXclobber	:	clean
XX		rm -f $(CHECK) list install
====End of SHAR Makefile====
if	[ "`wc -c <'Makefile'`" != '    1426' ]
then	echo 'Unpack for Makefile failed!'
	exit 1
else	echo 'Unpacked Makefile'
fi
sed -e 's/^XX//' <<'====End of SHAR README====' >README
XX			C Program Checker
XX			-----------------
XX
XXThis program has been posted as a result of a discussion in comp.lang.c
XXconcerning some possible errors in writing C programs.  It is a C syntax
XXchecker, to be used as an adjunct to lint, not a replacement.  It makes
XXno attempt to duplicate any warning given by the standard lint, and is not
XXat all forgiving about simple syntax errors.  It warns about the following
XXpossible errors :-
XX
XX1.	Assignment in conditional context
XX	A warning is issued for any assignment in any conditional context,
XX	where the programmer might have mistyped = when == was intended.
XX	Thus, 'if (a=b)' will give a warning, while 'if ((a=b)!=0)' will not.
XX	(NOTE:  I am not saying that people should not write 'if (a=b)' and
XX	similar constructs - I frequently do - but that on SOME occasions
XX	programmers make typing errors which are not picked up by lint or cc,
XX	and are not easily visually seen as errors.)
XX
XX2.	if with neither then nor else
XX	One of my colleagues once wrote
XX		if	(a == b);
XX		{
XX			lots of useful code ...
XX		}
XX	and we spent a whole day tracking down why this was not doing as
XX	we expected before I noticed the extra semicolon.  (The condition
XX	was actually more complicated, so the semicolon was not as obvious,
XX	and we spent our time working out why the condition did not appear
XX	to have the right value.  We were also working on a machine which
XX	did not supply adb, only sdb, and that did not work!)
XX
XX3.	I come from an Algol68 background, and I HATE the 'dangling-else'
XX	language ambiguity in C, Pascal, ...!  So, not that I have ever
XX	seen it as a problem in practice, the program warns about potentially
XX	ambiguous elses :-
XX
XX		if	(a == b)
XX			if	(c == d)
XX				action1 ();
XX			else	action2 ();
XX	will generate a warning, while :-
XX
XX		if	(a == b)
XX		{
XX			if	(c == d)
XX				action1 ();
XX			else	action2 ();
XX		}
XX	will not.
XX
XX4.	Nested comment
XX	I just added this as a result of further net discussion of errors;
XX	this will give a warning if '/*' is seen inside a comment, so that
XX	errors such as that below are noticed.
XX	a = 1;	/* initialise a
XX	b = 2;  /* initialise b  */
XX	The current line number and that where the outer comment started are
XX	included in the warning message.
XX
XX5.	Unterminated comment
XX	If end-of-file is reached while in a comment.  This is also noticed
XX	by both cpp and cc, but check gives the line number where the comment
XX	started, not just the line number where the error was detected, i.e.,
XX	the last line!
XX
XX
XXThe program uses yacc and lex.  The grammar was written by me, and is not a
XXcomplete C grammar - some obsolete or weird constructs are (deliberately) not
XXsupported.  ('Weird' as defined by me!!)  No attempt has been made (yet) to
XXsupport ANSI C.  It is not very fast - profiling shows that most of the time
XXis spent in the lex-generated analyser, as one might expect.
XX
XXThere is some code, mostly commented out, to calculate and print software
XXmetrics, but this needs changes to the lexing, parsing and symbol handling
XXwhich I have not yet done.  If anyone completes this work, or has any improved
XXmetrics to suggest, please send it back to me!
XX
XXAs an example, the output of 'check *.c' in the check source directory is :-
XXlex.c: assignment in conditional context at or near line 163
XXlex.c: assignment in conditional context at or near line 588
XXlex.c: assignment in conditional context at or near line 616
XXsymbol.c: assignment in conditional context at or near line 79
XXtest.c: nested comment, starting at line 20, at or near line 21
XXtest.c: unterminated comment, starting at line 194, at or near line 202
XXtest.c: IF with neither THEN nor ELSE at or near line 93
XXtest.c: potentially mismatched ELSE - use {} at or near line 117
XXtest.c: assignment in conditional context at or near line 132
XX
XXThis program is in no way derived from lint sources, which I have not even
XXlooked at, and is free of all copyright restrictions.
XX
XXWed Jan 20 1988
XX
XXNick Crossley
XXComputer Consoles Inc.
XX9801 Muirlands Boulevard,
XXIrvine
XXCA 92718
XX(714) 458-7282
XXemail: ...!uunet!ccicpg!nick
====End of SHAR README====
if	[ "`wc -c <'README'`" != '    4011' ]
then	echo 'Unpack for README failed!'
	exit 1
else	echo 'Unpacked README'
fi
sed -e 's/^XX//' <<'====End of SHAR check.1====' >check.1
XX.TH CHECK 1 
XX.SH NAME
XXcheck \- a C program checker
XX.SH SYNOPSIS
XX.B check
XX[ option ] ... file ...
XX.SH DESCRIPTION
XX.I Check\^
XXattempts to find possible errors in C programs.
XXIt does not duplicate or replace
XX.I lint
XXbut finds some constructs about which
XX.I lint
XXis silent.
XXAmong the things that are detected are
XXnested comments,
XXunterminated comments,
XXassignments in conditional contexts,
XXif statements with null then and no else
XXand
XXpotentially ambiguous else statements
XX.PP
XXFor each such construct a warning is given,
XXidentifying the source file and line number where it occurs.
XXIf multiple files are being checked,
XXthe name of each file is printed as checking starts.
XX.PP
XXAll leading '-' options are assumed to be C preprocessor directives,
XXexcept that an argument '--' ends the options and is otherwise ignored.
XXAll following arguments are assumed to be C source file names.
XX.SH SEE ALSO
XXcpp(1), lint(1).
XX.SH BUGS
XX.I Check\^
XXis not at all forgiving of simple syntax errors,
XXsuch as missing or extra punctuation.
XX.PP
XXThe grammar (deliberately) does not accept all currently legal C programs;
XXsome obsolescent or weird constructs are not allowed.
XXNo attempt has yet been made to handle ANSI C.
====End of SHAR check.1====
if	[ "`wc -c <'check.1'`" != '    1193' ]
then	echo 'Unpack for check.1 failed!'
	exit 1
else	echo 'Unpacked check.1'
fi
sed -e 's/^XX//' <<'====End of SHAR check.c====' >check.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*	This module contains the actual checking logic			*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XX
XX/*ARGSUSED*/
XXvoid checknode (depth, parent, branch, np)
XXint	depth, parent, branch;
XXNodePtr	np;
XX{
XX	/*  This routine is called as an 'action' by treewalk.	*/
XX	/*  It checks for the following things :-		*/
XX	/*	1) assignment in conditional contexts;		*/
XX	/*	2) potentially mismatched ELSE clauses;		*/
XX	/*	3) IFs with no THEN or ELSE.			*/
XX
XX
XX	if  (np->type == Asgn_Op)
XX	{
XX		if	((parent == IF && branch == 1) ||
XX			(parent == WHILE && branch == 1) ||
XX			(parent == DO && branch == 2) ||
XX			(parent == FOR && branch == 2) ||
XX			parent == And || parent == Or)
XX
XX			warn ("assignment in conditional context");
XX	}
XX
XX	if	(np->type == IF && np->f3.ptr == NilNode)  /* IF with no ELSE */
XX	{
XX		if	(np->f2.ptr == NilNode ||
XX			 np->f2.ptr->type == 0 || np->f2.ptr->type == ';')
XX			warn ("IF with neither THEN nor ELSE");
XX
XX		if	(np->f2.ptr->type==IF && np->f2.ptr->f3.ptr!=NilNode)
XX			warn ("potentially mismatched ELSE - use {}");
XX	}
XX}
XX
XX
XXvoid check_prog ()
XX{
XX	/*  Start off the checking process  */
XX
XX	walk_prog (checknode);
XX}
====End of SHAR check.c====
if	[ "`wc -c <'check.c'`" != '    1304' ]
then	echo 'Unpack for check.c failed!'
	exit 1
else	echo 'Unpacked check.c'
fi
sed -e 's/^XX//' <<'====End of SHAR check.h====' >check.h
XX/*===========	Header file for C Checker Program	================*/
XX
XX#define		elif		else if
XX#define		TRUE		1
XX#define		FALSE		0
XX#define		Printf		(void) printf
XX#define		Fprintf		(void) fprintf
XX#define		NilNode		((NodePtr) 0)
XX#define		NilSym		((Symbol *) 0)
XX
XX
XX/*===========	Type Definitions			================*/
XX
XXtypedef int	Token;			/*  type of Lex tokens		*/
XX
XXtypedef	enum	{
XX	Find,		/* Find only */
XX	Create		/* Find and create if required */
XX}	Action;
XX
XXtypedef struct	Symbol {		/*  type of Symbol Table Entry	*/
XX		char		*name;
XX		Token		token;
XX		struct Symbol	*next;
XX}	Symbol;
XX
XXtypedef	struct	Node {			/*  type of Parse Tree Node	*/
XX	short	type;			/*  variant for a node		*/
XX	short	line;			/*  source line number		*/
XX	union	u {
XX		int		ival;	/*  integer value		*/
XX		unsigned	uval;	/*  unsigned value		*/
XX		struct Node	*ptr;	/*  pointer to another node	*/
XX	}	f1, f2, f3, f4;		/*  variable number of fields	*/
XX}	Node, *NodePtr;			/*  Node and pointer to Node	*/
XX
XX
XX/*===========	Parse Tree Node Types			================*/
XX
XX#define		Seq		1000	/*  a sequence of two nodes	*/
XX#define		Type		1001	/*  a type specifier		*/
XX#define		Label		1002	/*  a labelled statement	*/
XX#define		Pre_Inc		1003	/*  ++ (expression)		*/
XX#define		Pre_Dec		1004	/*  -- (expression)		*/
XX#define		Post_Inc	1005	/*  (expression) ++		*/
XX#define		Post_Dec	1006	/*  (expression) --		*/
XX#define		Indirect	1007	/*  a -> b			*/
XX#define		Addr		1008	/*  & id			*/
XX#define		Uplus		1009	/*  + expression		*/
XX#define		Uminus		1010	/*  - expression		*/
XX#define		Cast		1011	/*  (type) expression		*/
XX#define		Size_Type	1012	/*  SIZEOF (type)		*/
XX#define		Size_Expr	1013	/*  SIZEOF (expression)		*/
XX#define		Error		9999	/*  error			*/
XX
XX
XX/*===========	External Function Definitions		================*/
XX
XXextern	void	lex_init ();		/*  Lexical Analyser initialise	*/
XXextern	Token	yylex ();		/*  Lexical Analyser interface	*/
XX
XXextern	void	sy_init ();		/*  Initialise symbol table	*/
XXextern	void	sy_tidy ();		/*  Tidy symbol table		*/
XXextern	Symbol	*findsym ();		/*  look up identifier in table	*/
XXextern	char	*emalloc ();		/*  allocate space		*/
XX
XXextern	int	yyparse ();		/*  interface to Yacc Parser	*/
XX
XXextern	NodePtr	new_node ();		/*  build a parse tree node	*/
XXextern	void	check_prog ();		/*  check the parse tree	*/
XXextern	void	tidy_prog ();		/*  delete the parse tree	*/
XXextern	void	walk_prog ();		/*  walk the parse tree		*/
XXextern	void	treeprint ();		/*  print the parse tree	*/
XX
XXextern	void	metrics ();		/*  produce software metrics	*/
XXextern	void	met_init ();		/*  initialise metrics		*/
XXextern	void	proc_start ();		/*  initialise local metrics	*/
XX
XXextern	void	yyerror ();		/*  Yacc syntax error report	*/
XXextern	void	warn ();		/*  generate warning messages	*/
XXextern	void	error ();		/*  stop with error message	*/
XXextern	void	setline ();		/*  set line and filename	*/
XX
XX
XX/*===========	External Data Definitions		================*/
XX
XXextern	int	yychar;			/*  current lookahead token	*/
XXextern	char	*filename;		/*  current filename		*/
XXextern	int	yylineno;		/*  current line number		*/
XXextern	FILE	*yyin;			/*  current input file		*/
XXextern	NodePtr	Tree;			/*  Root of Parse Tree		*/
====End of SHAR check.h====
if	[ "`wc -c <'check.h'`" != '    3136' ]
then	echo 'Unpack for check.h failed!'
	exit 1
else	echo 'Unpacked check.h'
fi
sed -e 's/^XX//' <<'====End of SHAR lex.l====' >lex.l
XX%{
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*		LEX generated C lexical analyser			*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XX#define		yywrap()	(1)
XX
XXextern	Token	ident ();
XXextern	void	strings (), comment ();    /*  forward declarations  */
XX%}
XX
XXD			[0-9]
XXNUM			({D}+|({D}+"."{D}*)|("."{D}+))([eE][-+]?{D}+)?[lL]?
XXHEX			0[xX][0-9a-fA-F]+[lL]?
XXID			[A-Za-z_][A-Za-z0-9_]*
XXASG			([-+*/%&^|]?"=")|"<<="|">>="
XX
XX%%
XX
XX^"#".*"\n"		{ setline (yytext); }
XX{ID}			{ return ident(); }
XX{NUM}			{ return CONSTANT; }
XX{HEX}			{ return CONSTANT; }
XX{ASG}			{ return Asgn_Op; }
XX"\""			{ strings ('"');  return CONSTANT; }
XX"'"			{ strings ('\'');  return CONSTANT; }
XX"<<"			{ return Shift; }
XX">>"			{ return Shift; }
XX"&&"			{ return And; }
XX"||"			{ return Or; }
XX"->"			{ return Point; }
XX"<"|"<="|">="|">"	{ return Rel_Op; }
XX"=="|"!="		{ return Eq_Op; }
XX"++"|"--"		{ return IncDec; }
XX[ \t\n]+		;
XX"/*"			{ comment (); }
XX.			{ return yytext[0]; }
XX
XX%%
XX
XXToken ident ()
XX{
XX	/*  Handle an Identifier or Reserved Word  */
XX
XX	yylval.id = findsym (yytext, IDENTIFIER, Create);
XX	return yylval.id->token;
XX}
XX
XX
XXvoid cwarn (ln, s)
XXint	ln;
XXchar	*s;
XX{
XX	/*  Give a warning about a comment, including starting line number  */
XX
XX
XX	char	msg [120];
XX	(void) sprintf (msg, "%s, starting at line %d,", s, ln);
XX	warn (msg);
XX	free (msg);
XX}
XX
XX
XXvoid comment ()
XX{
XX	/*  Swallow the rest of a comment  */
XX
XX	register int	c = input ();
XX	register int	startline = yylineno;
XX
XX	while	(c)
XX	{
XX		if	(c == '*')
XX		{
XX			if  ((c = input()) == '/')  return;
XX		}
XX		elif	(c == '/')
XX		{
XX			if  ((c = input()) != '*')  continue;
XX			cwarn (startline, "nested comment");
XX		}
XX		else	c = input();
XX	}
XX	cwarn (startline, "unterminated comment");
XX}
XX
XX
XXvoid strings (term)
XXint	term;
XX{
XX	/*  A string terminating with 'term'  */
XX
XX	register int c;
XX
XX	while	(c = input ())
XX	{
XX		if	(c == term)	break;
XX		elif	(c == '\\')	(void) input ();
XX	}
XX}
XX
XX
XXvoid lex_init ()
XX{
XX	/*  Initialise the Lexical Analyser for a new file  */
XX	/*  lex itself should provide this!                 */
XX
XX	yyleng		=  0;
XX	yymorfg		=  0;
XX	yytchar		=  0;
XX	yybgin		=  yysvec+1;
XX	yylineno	=  1;
XX	yysptr		=  yysbuf;
XX	yyprevious	=  YYNEWLINE;
XX}
====End of SHAR lex.l====
if	[ "`wc -c <'lex.l'`" != '    2254' ]
then	echo 'Unpack for lex.l failed!'
	exit 1
else	echo 'Unpacked lex.l'
fi
sed -e 's/^XX//' <<'====End of SHAR main.c====' >main.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*		C Checker Main Entry Point				*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#include	<ctype.h>
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XX#define		MAXNAME		120
XX#define		CPP		"/lib/cpp -C %s %s"
XX
XXextern void	perror(), exit();
XX
XXint	errors = 0;			/*  count of number of errors	*/
XXchar	filebuf [MAXNAME];		/*  Buffer for file names	*/
XXchar	*filename;			/*  Pointer to file name	*/
XXchar	*progname;			/*  Name of this program	*/
XX
XX/*  Really should recode this to avoid using fixed buffer sizes;  */
XX/*  malloc and realloc are not difficult to use!                  */
XXchar	options [BUFSIZ];		/*  Buffer for cpp options	*/
XX
XX
XXint main (argc, argv)
XXint	argc;
XXchar	*argv[];
XX{
XX	/*	Main Entry Point to C Checker		*/
XX	/*  Check each file as specified by arguments	*/
XX
XX	char		command [BUFSIZ];    /*  for cpp command  */
XX	register char	*opts = options;
XX	register char	*opt_end = & options [BUFSIZ-2];
XX	register int	pnames;
XX	register int	ptree = FALSE;
XX	extern char	*strncat ();
XX	extern FILE	*popen ();
XX
XX	sy_init ();
XX	progname = *argv++;
XX
XX	/*  Extract C PreProcessor options  */
XX
XX	while	(--argc && **argv == '-')
XX	{
XX		register char *ap = *argv++;
XX
XX		if	(strcmp ("--", ap) == 0)
XX		{
XX			/*  End of Options  */
XX
XX			break;
XX		}
XX#if	YYDEBUG
XX		elif	(strcmp ("-yydebug", ap) == 0)
XX		{
XX			/*  Turn on Yacc Tracing  */
XX
XX			extern int yydebug;
XX			yydebug = 1;
XX		}
XX#endif
XX		elif	(strcmp ("-TP", ap) == 0)
XX		{
XX			/*  Print Parse Tree option (debug)  */
XX
XX			ptree = TRUE;
XX		}
XX		else
XX		{
XX			/*  C PreProcessor option  */
XX
XX			while	(*ap && (opts < opt_end)) *opts++ = *ap++;
XX			*opts++ = ' ';
XX		}
XX	}
XX	*opts++ = ' ';
XX	*opts++ = '\0';
XX
XX	pnames = argc - 1;
XX	while	(argc--)
XX	{
XX		/*  Open a pipe for reading from C PreProcessor  */
XX
XX		filename = *argv++;
XX		(void) sprintf (command, CPP, options, filename);
XX		if  (pnames > 0)  Printf ("%s:\n", filename);
XX
XX		if	((yyin = popen (command, "r")) == NULL)
XX		{
XX			perror ("cannot open pipe");
XX			exit (1);
XX		}
XX		else
XX		{
XX			/*  Analyse one C source file  */
XX
XX			lex_init ();
XX			met_init ();
XX			if  (yyparse ())  warn ("fatal syntax error");
XX
XX			/*  Report on results of analysis  */
XX
XX			if  (ptree)  treeprint ();
XX			check_prog ();
XX			metrics ((char *) 0);
XX
XX			/*  Tidy up, ready for another source file  */
XX
XX			(void) pclose (yyin);
XX			tidy_prog ();
XX			sy_tidy ();
XX		}
XX	}
XX
XX	return errors;
XX}
XX
XX
XXchar *emalloc (n)
XXunsigned n;
XX{
XX	/*====  Allocate Memory  ====*/
XX
XX	char	*p, *malloc ();
XX
XX	p = malloc (n);
XX
XX	if (p == 0)  error ("out of memory");
XX
XX	return p;
XX}
XX
XX
XXvoid yyerror (s)
XXchar	*s;
XX{
XX	/*  Syntax Error from Yacc  */
XX
XX	errors++;
XX	Fprintf (stderr, "%s: %s at or near line %d, token %d\n",
XX		filename, s, yylineno, yychar);
XX}
XX
XX
XXvoid warn (s)
XXchar	*s;
XX{
XX	/*  Generate a warning message  */
XX
XX	errors++;
XX	Fprintf (stderr, "%s: %s at or near line %d\n",
XX		filename, s, yylineno);
XX}
XX
XX
XXvoid error (s)
XXchar	*s;
XX{
XX	/*  Handle fatal errors  */
XX
XX	warn (s);
XX	exit (errors);
XX}
XX
XX
XXvoid setline (line)
XXchar	*line;
XX{
XX	/*  Handle a #line directive  */
XX
XX	register char	*cp  = line;
XX	register char	*fn  = filebuf;
XX	register int	lnum = 0;
XX
XX	if	(*cp != '#')		error ("invalid call to setline");
XX
XX	while	(*cp && !isdigit(*cp))	cp++;
XX	while	(*cp && isdigit (*cp))	lnum = lnum*10 + (*cp++ - '0');
XX	while	(*cp && *cp != '"')	cp++;
XX	if	(lnum)			yylineno = lnum;
XX
XX	if	(*cp++ == 0)		return;
XX	while	(*cp && *cp != '"')	*fn++ = *cp++;
XX	if	(fn == filename)	return;
XX
XX	*fn	 = '\0';
XX	filename = filebuf;
XX}
====End of SHAR main.c====
if	[ "`wc -c <'main.c'`" != '    3532' ]
then	echo 'Unpack for main.c failed!'
	exit 1
else	echo 'Unpacked main.c'
fi
sed -e 's/^XX//' <<'====End of SHAR metric.c====' >metric.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*	This module will do software metrics one day...			*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XXtypedef	struct	Metric {
XX	int	sep_nouns;
XX	int	sep_verbs;
XX	int	tot_nouns;
XX	int	tot_verbs;
XX	int	stmts;
XX	int	comments;
XX	int	lines;
XX	int	decisions;
XX	int	knots;
XX}	Metric;
XX
XXMetric	total, local;
XX
XX
XXvoid swm (m)
XXMetric	*m;
XX{
XX	/*  This procedure will print the Software Metrics  */
XX
XX/*  Commented out until Lex & Grammar re-written to increment counts ...
XX	extern	double	log();
XX
XX	double	vocab, length, volume, diff, effort, lang;
XX	double	decid, comms, layout;
XX	double	mccabe, knots;
XX
XX	vocab	= m->sep_verbs + m->sep_nouns;
XX	length	= m->tot_verbs + m->tot_nouns;
XX	volume	= length * log(vocab) / log(2.0);
XX	diff	= (m->sep_verbs*m->tot_nouns)/(2.0*m->sep_nouns);
XX	effort	= volume * diff;
XX	lang	= volume / (diff * diff);
XX
XX	decid	= m->decisions / m->stmts;
XX	comms	= m->comments / m->stmts;
XX	layout	= (m->stmts + m->comments) / m->lines;
XX
XX	mccabe	= m->decisions + 1;
XX	knots	= m->knots;
XX
XX	Printf ("%8.2g %8.2g %8.2g %8.2g %8.2g %8.2g %8.2g\n",
XX		volume, diff, effort, lang,
XX		decid, comms, layout,
XX		mccabe, knots
XX	);
XX............................................................ */
XX}
XX
XX
XXvoid metrics (this_proc)
XXchar	*this_proc;
XX{
XX	/*  Report on the Software Metrics for current procedure, or  */
XX	/*  report on the total for the file if this_proc is null.    */
XX
XX/*  Commented out until Lex re-written to increment counts ...
XX	if	(this_proc)
XX	{
XX		Printf ("%12.10s ", this_proc);
XX		swm (&local);
XX	}
XX	else
XX	{
XX		Printf ("%12.10s ", "Total:");
XX		swm (&total);
XX	}
XX............................................................ */
XX}
XX
XX
XXvoid proc_start ()
XX{
XX	/*  Initialise the counts for a procedure  */
XX
XX	local.sep_nouns	= 1;
XX	local.sep_verbs	= 1;
XX	local.tot_nouns	= 1;
XX	local.tot_verbs	= 1;
XX	local.stmts	= 1;
XX	local.comments	= 1;
XX	local.lines	= 1;
XX	local.decisions	= 1;
XX	local.knots	= 1;
XX}
XX
XX
XXvoid met_init ()
XX{
XX	/*  Initialise the counts for a file  */
XX
XX	proc_start ();
XX
XX	total.sep_nouns	= 1;
XX	total.sep_verbs	= 1;
XX	total.tot_nouns	= 1;
XX	total.tot_verbs	= 1;
XX	total.stmts	= 1;
XX	total.comments	= 1;
XX	total.lines	= 1;
XX	total.decisions	= 1;
XX	total.knots	= 1;
XX}
====End of SHAR metric.c====
if	[ "`wc -c <'metric.c'`" != '    2326' ]
then	echo 'Unpack for metric.c failed!'
	exit 1
else	echo 'Unpacked metric.c'
fi
sed -e 's/^XX//' <<'====End of SHAR parse.awk====' >parse.awk
XXawk '
XXBEGIN			{ state = 0; }
XX/^state/		{ --state; }
XX/shift\/reduce/		{ state = 2; }
XX/reduce\/reduce/	{ state = 2; }
XX/terminals,/		{ state = 2; }
XX			{ if (state > 0) printf "%s\n", $0; }
XX
XX' y.output >yacclist
====End of SHAR parse.awk====
chmod +x parse.awk
if	[ "`wc -c <'parse.awk'`" != '     208' ]
then	echo 'Unpack for parse.awk failed!'
	exit 1
else	echo 'Unpacked parse.awk'
fi
sed -e 's/^XX//' <<'====End of SHAR parse.y====' >parse.y
XX/*	YACC Grammar for C - not very strict.		*/
XX/*	No attempt is made to conform to or accept	*/
XX/*	ANSI C yet.  Some obsolete constructs are	*/
XX/*	not supported (deliberately).			*/
XX/*							*/
XX/*	If your terminal can handle it, view/edit this	*/
XX/*	file in 120 or 132 column mode, as all the	*/
XX/*	actions start in column 72.			*/
XX/*							*/
XX/*	Note that TYPEDEF names must be recognised as	*/
XX/*	such, and return a different token from the	*/
XX/*	Lexical Analyser.				*/
XX/*							*/
XX/*	The Actions build a Parse Tree.			*/
XX
XX%{
XX#include	<stdio.h>
XX#include	"check.h"
XX
XX
XX	/*----------  Macros for Tree Building  ----------*/
XX
XX
XX#define		Z			(NodePtr) 0
XX#define		node0(t)		new_node (t, Z, Z, Z, Z);
XX#define		node1(t,a)		new_node (t, a, Z, Z, Z);
XX#define		node2(t,a,b)		new_node (t, a, b, Z, Z);
XX#define		node3(t,a,b,c)		new_node (t, a, b, c, Z);
XX#define		node4(t,a,b,c,d)	new_node (t, a, b, c, d);
XX
XX%}
XX
XX%union {				/*  Type for Parser Stack	*/
XX	Symbol		*id;		/*  Name for ID, or string	*/
XX	int		ival;		/*  integer constants		*/
XX	unsigned	uval;		/*  octal & hex constants	*/
XX	NodePtr		ptr;		/*  pointer to Parse Tree Node	*/
XX}
XX
XX%token	<id>	IDENTIFIER	TYPENAME
XX
XX%token	<ival>	CONSTANT
XX
XX%token		AUTO		BREAK		CASE		CHAR
XX		CONTINUE	DEFAULT		DO		DOUBLE
XX		ELSE		ENUM		EXTERN		FLOAT
XX		FOR		GOTO		IF		INT
XX		LONG		REGISTER	RETURN		SHORT
XX		SIZEOF		STATIC		STRUCT		SWITCH
XX		TYPEDEF		UNION		UNSIGNED	VOID
XX		WHILE
XX
XX		Shift		And		Or		Rel_Op
XX		Eq_Op		IncDec		Asgn_Op		Point
XX
XX%right		Asgn_Op
XX%right		'?'		':'
XX%left		Or
XX%left		And
XX%left		'|'
XX%left		'^'
XX%left		'&'
XX%left		Eq_Op
XX%left		Rel_Op
XX%left		Shift
XX%left		'+'		'-'
XX%left		'*'		'/'		'%'
XX%left		Prefix
XX%left		SizeOf
XX%left		IncDec		Point		'.'
XX%left		'['		'('
XX
XX%type	<id>	declarator
XX
XX%type	<ptr>	top_level_decls		top_level_decl		function_decl
XX		type_name		compound		statements
XX		statement		label			expr_opt
XX		expr_list		expression		tag
XX
XX%%
XX
XX
XX/*--------------------------   DECLARATIONS   -------------------------------*/
XX
XX
XXprogram		:	top_level_decls					{ Tree = $1; }
XX		;
XX
XXtop_level_decls	:	/*  empty  */					{ proc_start(); $$ = node0 (0); }
XX		|	top_level_decls top_level_decl			{ proc_start(); $$ = node2 (Seq, $1, $2); }
XX		;
XX
XXtop_level_decl	:	function_decl					{ $$ = $1; }
XX		|	declaration					{ $$ = node0 (0); }
XX		|	error '}'					{ $$ = node0 (Error); }
XX		|	error ';'					{ $$ = node0 (Error); }
XX		;
XX
XXfunction_decl	:	specifiers declarator declarations compound	{ $$ = $4; metrics ($2->name); }
XX		|	declarator declarations compound		{ $$ = $3; metrics ($1->name); }
XX		;
XX
XXdeclarations	:	/*  empty  */
XX		|	declarations declaration
XX		;
XX
XXdeclaration	:	specifiers init_dclrtrs ';'
XX		|	specifiers ';'
XX		;
XX
XXspecifiers	:	storage
XX		|	storage type_spec
XX		|	type_spec
XX		;
XX
XXstorage		:	AUTO
XX		|	EXTERN
XX		|	REGISTER
XX		|	STATIC
XX		;
XX
XXtype_spec	:	int_spec
XX		|	UNSIGNED int_spec
XX		|	UNSIGNED
XX		|	float_spec
XX		|	enum_spec
XX		|	struct_spec
XX		|	union_spec
XX		|	TYPENAME
XX		|	VOID
XX		;
XX
XXdeclarator	:	IDENTIFIER					{ $$ = $1; }
XX		|	'(' declarator ')'				{ $$ = $2; }
XX		|	declarator '(' parameter_list ')'		{ $$ = $1; }
XX		|	declarator '(' ')'				{ $$ = $1; }
XX		|	declarator '[' expr_opt ']'			{ $$ = $1; }
XX		|	'*' declarator %prec Prefix			{ $$ = $2; }
XX		;
XX
XXparameter_list	:	IDENTIFIER
XX		|	parameter_list ',' IDENTIFIER
XX		;
XX
XXinit_dclrtrs	:	declarator
XX		|	declarator Asgn_Op initialiser
XX		|	init_dclrtrs ',' declarator
XX		|	init_dclrtrs ',' declarator Asgn_Op initialiser
XX		;
XX
XXinitialiser	:	expression
XX		|	'{' init_list '}'
XX		|	'{' init_list ',' '}'
XX		;
XX
XXinit_list	:	initialiser
XX		|	init_list ',' initialiser
XX		;
XX
XXdeclaration	:	TYPEDEF type_spec type_decls ';'
XX		;
XX
XXtype_decls	:	declarator					{ $1->token = TYPENAME; }
XX		|	type_decls ',' declarator			{ $3->token = TYPENAME; }
XX		;
XX
XX
XX/*----------------------------   TYPES   ----------------------------------*/
XX
XX
XXint_spec	:	CHAR | SHORT | SHORT INT | INT | LONG INT | LONG ;
XXfloat_spec	:	FLOAT | LONG FLOAT | DOUBLE ;
XX
XXenum_spec	:	ENUM IDENTIFIER
XX		|	ENUM IDENTIFIER '{' enum_fields '}'
XX		|	ENUM '{' enum_fields '}'
XX		;
XX
XXstruct_spec	:	STRUCT IDENTIFIER
XX		|	STRUCT IDENTIFIER '{' struct_fields '}'
XX		|	STRUCT '{' struct_fields '}'
XX		;
XX
XXunion_spec	:	UNION IDENTIFIER
XX		|	UNION IDENTIFIER '{' struct_fields '}'
XX		|	UNION '{' struct_fields '}'
XX		;
XX
XXenum_fields	:	enum_const
XX		|	enum_fields ',' enum_const
XX		;
XX
XXenum_const	:	IDENTIFIER
XX		|	IDENTIFIER Asgn_Op expression
XX		;
XX
XXstruct_fields	:	type_spec field_list ';'
XX		|	struct_fields type_spec field_list ';'
XX		|	error ';'
XX		;
XX
XXfield_list	:	field
XX		|	field_list ',' field
XX		;
XX
XXfield		:	declarator
XX		|	declarator ':' expression
XX		|	':' expression
XX		;
XX
XXtype_name	:	type_spec					{ $$ = node0 (Type); }
XX		|	type_spec abstract				{ $$ = node0 (Type); }
XX		;
XX
XXabstract	:	'(' abstract ')'
XX		|	'(' ')'
XX		|	abstract '(' ')'
XX		|	'[' expr_opt ']'
XX		|	abstract '[' expr_opt ']'
XX		|	'*' %prec Prefix
XX		|	'*' abstract %prec Prefix
XX		;
XX
XX
XX/*--------------------------   STATEMENTS   --------------------------------*/
XX
XX
XXcompound	:	'{' declarations statements '}'			{ $$ = node1 (Seq, $3); }
XX		|	'{' '}'						{ $$ = node0 (0); }
XX		|	error '}'					{ $$ = node0 (Error); }
XX		;
XX
XXstatements	:	statement					{ $$ = $1; }
XX		|	statements statement				{ $$ = node2 (Seq, $1, $2); }
XX		;
XX
XXstatement	:	expr_list ';'					{ $$ = $1; }
XX		|	label ':' statement				{ $$ = node2 (Label, $1, $3); }
XX		|	compound					{ $$ = $1; }
XX		|	IF '(' expr_list ')' statement			{ $$ = node3 (IF, $3, $5, Z); }
XX		|	IF '(' expr_list ')' statement ELSE statement	{ $$ = node3 (IF, $3, $5, $7); }
XX		|	WHILE '(' expr_list ')' statement		{ $$ = node2 (WHILE, $3, $5); }
XX		|	DO statement WHILE '(' expr_list ')' ';'	{ $$ = node2 (DO, $2, $5); }
XX		|	FOR '(' expr_opt ';' expr_opt ';' expr_opt ')'
XX			statement					{ $$ = node4 (FOR, $3, $5, $7, $9); }
XX		|	SWITCH '(' expr_list ')' statement		{ $$ = node2 (SWITCH, $3, $5); }
XX		|	BREAK ';'					{ $$ = node0 (BREAK); }
XX		|	CONTINUE ';'					{ $$ = node0 (CONTINUE); }
XX		|	RETURN expr_opt ';'				{ $$ = node1 (RETURN, $2); }
XX		|	GOTO tag ';'					{ $$ = node1 (GOTO, Z); }
XX		|	';'						{ $$ = node0 (';'); }
XX		|	error ';'					{ $$ = node0 (Error); }
XX		;
XX
XXlabel		:	tag						{ $$ = $1; }
XX		|	CASE expression					{ $$ = node1 (CASE, $2); }
XX		|	DEFAULT						{ $$ = node0 (DEFAULT); }
XX		;
XX
XX
XX/*---------------------------   EXPRESSIONS   -------------------------------*/
XX
XX
XXexpr_opt	:	/*  empty  */					{ $$ = node0 (0); }
XX		|	expr_list					{ $$ = $1; }
XX		;
XX
XXexpr_list	:	expression					{ $$ = $1; }
XX		|	expr_list ',' expression			{ $$ = node2 (',', $1, $3); }
XX		;
XX
XXexpression	:	expression Asgn_Op expression			{ $$ = node2 (Asgn_Op, $1, $3); }
XX		|	expression '?' expr_list ':' expression		{ $$ = node3 ('?', $1, $3, $5); }
XX		|	expression Or expression			{ $$ = node2 (Or, $1, $3); }
XX		|	expression And expression			{ $$ = node2 (And, $1, $3); }
XX		|	expression '|' expression			{ $$ = node2 ('|', $1, $3); }
XX		|	expression '^' expression			{ $$ = node2 ('^', $1, $3); }
XX		|	expression '&' expression			{ $$ = node2 ('&', $1, $3); }
XX		|	expression Eq_Op expression			{ $$ = node2 (Eq_Op, $1, $3); }
XX		|	expression Rel_Op expression			{ $$ = node2 (Rel_Op, $1, $3); }
XX		|	expression Shift expression			{ $$ = node2 (Shift, $1, $3); }
XX		|	expression '+' expression			{ $$ = node2 ('+', $1, $3); }
XX		|	expression '-' expression			{ $$ = node2 ('-', $1, $3); }
XX		|	expression '*' expression			{ $$ = node2 ('*', $1, $3); }
XX		|	expression '/' expression			{ $$ = node2 ('/', $1, $3); }
XX		|	expression '%' expression			{ $$ = node2 ('%', $1, $3); }
XX		|	'*' expression %prec Prefix			{ $$ = node1 (Indirect, $2); }
XX		|	'&' expression %prec Prefix			{ $$ = node1 (Addr, $2); }
XX		|	'+' expression %prec Prefix			{ $$ = node1 (Uplus, $2); }
XX		|	'-' expression %prec Prefix			{ $$ = node1 (Uminus, $2); }
XX		|	'!' expression %prec Prefix			{ $$ = node1 ('!', $2); }
XX		|	'~' expression %prec Prefix			{ $$ = node1 ('~', $2); }
XX		|	'(' type_name ')' expression %prec Prefix	{ $$ = node2 (Cast, $2, $4); }
XX		|	IncDec expression %prec Prefix			{ $$ = node1 (Pre_Inc, $2); }
XX		|	SIZEOF '(' type_name ')' %prec SizeOf		{ $$ = node1 (Size_Type, $3); }
XX		|	SIZEOF expression %prec SizeOf			{ $$ = node1 (Size_Expr, $2); }
XX		|	expression IncDec				{ $$ = node1 (Post_Inc, $1); }
XX		|	expression Point tag				{ $$ = node2 (Point, $1, $3); }
XX		|	expression '.' tag				{ $$ = node2 ('.', $1, $3); }
XX		|	expression '(' ')'				{ $$ = node2 ('(', $1, Z); }
XX		|	expression '(' expr_list ')'			{ $$ = node2 ('(', $1, $3); }
XX		|	expression '[' expr_list ']'			{ $$ = node2 ('[', $1, $3); }
XX		|	'(' expr_list ')'				{ $$ = $2; }
XX		|	tag						{ $$ = $1; }
XX		|	CONSTANT					{ $$ = node1 (CONSTANT, Z); }
XX		;
XX
XXtag		:	IDENTIFIER					{ $$ = node1 (IDENTIFIER, (NodePtr) $1); }
XX		;
====End of SHAR parse.y====
if	[ "`wc -c <'parse.y'`" != '    8625' ]
then	echo 'Unpack for parse.y failed!'
	exit 1
else	echo 'Unpacked parse.y'
fi
sed -e 's/^XX//' <<'====End of SHAR symbol.c====' >symbol.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*	This module handles a symbol/type hash table			*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#ifdef	BSD
XX#include	<strings.h>
XX#define		memset(p,c,n)	{register int i;for(i=0;i<(n);i++)(p)[i]=(c);}
XX#else
XX#include	<string.h>
XX#include	<memory.h>
XX#endif
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XX#define		HASH_SIZE	1000  /*  Allow for 750 names at 75% full  */
XX
XXunsigned hsize;		/* Size of the hash table */
XXSymbol	**htable;	/* Pointer to an array of ponters to hash entries */
XX
XX#ifdef	DEBUG
XXunsigned *hcount;	/* Pointer to an array of hash counts  */
XX#endif
XX
XX
XXunsigned hash (key)
XXregister char	*key;
XX{
XX	/*  Hash a key string  */
XX
XX	register unsigned	hval = 0;
XX
XX	while  (*key)  hval = (hval << 3) + (hval >> 29) + *key++;
XX
XX	hval = (hval & 0x7fffffff) % hsize;
XX	return hval;
XX}
XX
XX
XXvoid mkhash (size)
XXunsigned	size;		/* Minimum size for hash table */
XX{
XX	/*  Create a hash table of size rounded up to next power of two-1  */
XX
XX	register unsigned tsize = size;
XX
XX	hsize = 1;		/* Actual hash table size  */
XX	while	(tsize)
XX	{
XX		tsize >>= 1;
XX		hsize <<= 1;
XX	}
XX	hsize--;
XX	if  (hsize == 0)  hsize++;   /* Silly, but it will work! */
XX
XX	htable = (Symbol **) emalloc (hsize * sizeof(Symbol *));
XX	memset ((char *) htable, 0, (int) (hsize * sizeof(Symbol *)));
XX
XX#ifdef	DEBUG
XX	Printf ("mkhash table size %d\n", hsize);
XX	hcount = (unsigned *) emalloc (hsize * sizeof(unsigned));
XX	memset ((char *) hcount, 0, (int) (hsize * sizeof(unsigned)));
XX#endif
XX}
XX
XX
XXvoid rmhash ()
XX{
XX	/*  Destroy hash table and all chained entries  */
XX
XX	register Symbol	**pp, *p;
XX	extern void	free();
XX
XX	for	(pp = htable; pp < htable + hsize; pp++)
XX	{
XX		while	(p = *pp)
XX		{
XX			*pp = p->next;
XX			free (p->name);
XX			free ((char *)p);
XX		}
XX	}
XX
XX	free((char *) htable);
XX	htable = 0;
XX}
XX
XX
XXSymbol *findsym (name, token, action)
XXchar	*name;		/* name to be inserted or found */
XXToken	token;		/* type of symbol */
XXAction	action;		/* Create or Find */
XX{
XX	/*  Search for, and create if required, an entry in the hash table  */
XX
XX	register Symbol		**pp;	/* address of pointer to entry	*/
XX	register Symbol		*p;	/* search through linked list	*/
XX	register unsigned	hval;	/* hash value from name		*/
XX	register int		res;	/* result of strcmp		*/
XX
XX	pp = &htable[hval = hash(name)];
XX	p = *pp;
XX
XX	while	(p != NULL)
XX	{
XX		if	((res = strcmp(name, p->name)) == 0)
XX		{
XX			return p;
XX		}
XX		elif	(res < 0)
XX		{
XX			/*  past point where name would be in sorted chain  */
XX			break;
XX		}
XX		pp = &(p->next);
XX		p = *pp;
XX	}
XX
XX	/* Item is not yet on list */
XX	if	(action == Find)
XX		return NilSym;
XX	else
XX	{
XX#ifdef	DEBUG
XX		/*  Accumulate hashing statistics  */
XX		hcount[hval]++;
XX#endif
XX		p = (Symbol *) emalloc (sizeof(Symbol));
XX		p->name = strcpy (emalloc ((unsigned) strlen(name)+1), name);
XX		p->token = token;
XX		p->next = *pp;
XX		*pp = p;
XX		return p;
XX	}
XX}
XX
XX
XXstatic struct Keyword {
XX	char	*name;
XX	Token	token;
XX} keywords [] = {
XX	"auto",		AUTO,
XX	"break",	BREAK,
XX	"case",		CASE,
XX	"char",		CHAR,
XX	"continue",	CONTINUE,
XX	"default",	DEFAULT,
XX	"do",		DO,
XX	"double",	DOUBLE,
XX	"else",		ELSE,
XX	"enum",		ENUM,
XX	"extern",	EXTERN,
XX	"float",	FLOAT,
XX	"for",		FOR,
XX	"goto",		GOTO,
XX	"if",		IF,
XX	"int",		INT,
XX	"long",		LONG,
XX	"register",	REGISTER,
XX	"return",	RETURN,
XX	"short",	SHORT,
XX	"sizeof",	SIZEOF,
XX	"static",	STATIC,
XX	"struct",	STRUCT,
XX	"switch",	SWITCH,
XX	"typedef",	TYPEDEF,
XX	"union",	UNION,
XX	"unsigned",	UNSIGNED,
XX	"void",		VOID,
XX	"while",	WHILE,
XX	0,		0
XX};
XX
XX
XXvoid sy_init ()
XX{
XX	/*  Initialise the Symbol Table with the reserved words  */
XX
XX	register struct Keyword *kw;
XX
XX	mkhash (HASH_SIZE);
XX	for	(kw = keywords; kw->name; kw++)
XX	{
XX		(void) findsym (kw->name, kw->token, Create);
XX	}
XX}
XX
XX
XXvoid sy_tidy ()
XX{
XX	/*  Remove all but reserved words from the Symbol Table  */
XX	/*  This is achieved by brute force; destroy & recreate! */
XX
XX#ifdef	DEBUG
XX	/*  print hash statistics  */
XX	register unsigned i, j, tot = 0, maxc = 0, unused = 0;
XX	for  (i=0; i<hsize; i++)
XX	{
XX		/*  print hash count if non-zero  */
XX		if	(j = hcount[i])
XX		{
XX			Printf ("hash %6d : %d\n", i, j);
XX			if  (j > maxc)  maxc = j;
XX			tot += j;
XX		}
XX		else	unused++;
XX	}
XX	Printf ("Total=%d, max chain length=%d, unused=%d\n",tot,maxc,unused);
XX#endif
XX	rmhash ();
XX	sy_init ();
XX}
====End of SHAR symbol.c====
if	[ "`wc -c <'symbol.c'`" != '    4271' ]
then	echo 'Unpack for symbol.c failed!'
	exit 1
else	echo 'Unpacked symbol.c'
fi
sed -e 's/^XX//' <<'====End of SHAR tables.sh====' >tables.sh
XX
XX#	Make tables.h from check.h and y.xxx.h
XX
XX(
XXsed <check.h -n -e '/	Parse/,$ s/^#define[ 	][ 	]*//p'
XXsed <y.xxx.h -n -e 's/^#[ 	]*define[ 	][ 	]*//p'
XX) |
XXawk -e '
XXBEGIN	{
XX		printf "typedef struct NodeName {\n"
XX		printf "\tint val;\n"
XX		printf "\tchar *str;\n"
XX		printf "} NodeName;\n\n"
XX		printf "NodeName\tnodenames[] = {\n"
XX	}
XX	{
XX		printf "\t%d, \"%s\",\n", $2, $1
XX	}
XXEND	{
XX		printf "	0, 0\n};\n"
XX	}' >tables.h
====End of SHAR tables.sh====
chmod +x tables.sh
if	[ "`wc -c <'tables.sh'`" != '     412' ]
then	echo 'Unpack for tables.sh failed!'
	exit 1
else	echo 'Unpacked tables.sh'
fi
sed -e 's/^XX//' <<'====End of SHAR test.c====' >test.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*		C Checker Main Entry Point				*/
XX/*	Modified version of 'main.c' to see messages from check.	*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#include	<ctype.h>
XX#include	"check.h"
XX#include	"y.tab.h"
XX
XX#define		MAXNAME		120
XX#define		CPP		"/lib/cpp -C %s %s"
XX
XXextern void	perror(), exit();
XX
XXint	errors = 0;			/*  count of number of errors	*/
XXchar	filebuf [MAXNAME];		/*  Buffer for file names	
XXchar	*filename;			/*  Pointer to file name	*/
XXchar	*progname;			/*  Name of this program	*/
XX
XX/*  Really should recode this to avoid using fixed buffer sizes;  */
XX/*  malloc and realloc are not difficult to use!                  */
XXchar	options [BUFSIZ];		/*  Buffer for cpp options	*/
XX
XX
XXint main (argc, argv)
XXint	argc;
XXchar	*argv[];
XX{
XX	/*	Main Entry Point to C Checker		*/
XX	/*  Check each file as specified by arguments	*/
XX
XX	char		command [BUFSIZ];    /*  for cpp command  */
XX	register char	*opts = options;
XX	register char	*opt_end = & options [BUFSIZ-2];
XX	register int	pnames;
XX	register int	ptree = FALSE;
XX	extern char	*strncat ();
XX	extern FILE	*popen ();
XX
XX	sy_init ();
XX	progname = *argv++;
XX
XX	/*  Extract C PreProcessor options  */
XX
XX	while	(--argc && **argv == '-')
XX	{
XX		register char *ap = *argv++;
XX
XX		if	(strcmp ("--", ap) == 0)
XX		{
XX			/*  End of Options  */
XX
XX			break;
XX		}
XX#if	YYDEBUG
XX		elif	(strcmp ("-yydebug", ap) == 0)
XX		{
XX			/*  Turn on Yacc Tracing  */
XX
XX			extern int yydebug;
XX			yydebug = 1;
XX		}
XX#endif
XX		elif	(strcmp ("-TP", ap) == 0)
XX		{
XX			/*  Print Parse Tree option (debug)  */
XX
XX			ptree = TRUE;
XX		}
XX		else
XX		{
XX			/*  C PreProcessor option  */
XX
XX			while	(*ap && (opts < opt_end)) *opts++ = *ap++;
XX			*opts++ = ' ';
XX		}
XX	}
XX	*opts++ = ' ';
XX	*opts++ = '\0';
XX
XX	pnames = argc - 1;
XX	while	(argc--)
XX	{
XX		/*  Open a pipe for reading from C PreProcessor  */
XX
XX		filename = *argv++;
XX		(void) sprintf (command, CPP, options, filename);
XX		if  (pnames > 0 && filename != NULL && othertests());
XX			Printf ("%s:\n", filename);
XX
XX		if  ((yyin = popen (command, "r")) == NULL)
XX			if  (yyin == NULL) perror ("cannot open pipe");
XX		else
XX		{
XX			/*  Analyse one C source file  */
XX
XX			lex_init ();
XX			met_init ();
XX			if  (yyparse ())  warn ("fatal syntax error");
XX
XX			/*  Report on results of analysis  */
XX
XX			if  (ptree)  treeprint ();
XX			check_prog ();
XX			metrics ((char *) 0);
XX
XX			/*  Tidy up, ready for another source file  */
XX
XX			(void) pclose (yyin);
XX			tidy_prog ();
XX			sy_tidy ();
XX		}
XX	}
XX
XX	return errors;
XX}
XX
XX
XXchar *emalloc (n)
XXunsigned n;
XX{
XX	/*====  Allocate Memory  ====*/
XX
XX	char	*p, *malloc ();
XX
XX	p = malloc (n);
XX
XX	if (p = 0)  error ("out of memory");
XX
XX	return p;
XX}
XX
XX
XXvoid yyerror (s)
XXchar	*s;
XX{
XX	/*  Syntax Error from Yacc  */
XX
XX	errors++;
XX	Fprintf (stderr, "%s: %s at or near line %d, token %d\n",
XX		filename, s, yylineno, yychar);
XX}
XX
XX
XXvoid warn (s)
XXchar	*s;
XX{
XX	/*  Generate a warning message  */
XX
XX	errors++;
XX	Fprintf (stderr, "%s: %s at or near line %d\n",
XX		filename, s, yylineno);
XX}
XX
XX
XXvoid error (s)
XXchar	*s;
XX{
XX	/*  Handle fatal errors  */
XX
XX	warn (s);
XX	exit (errors);
XX}
XX
XX
XXvoid setline (line)
XXchar	*line;
XX{
XX	/*  Handle a #line directive  */
XX
XX	register char	*cp  = line;
XX	register char	*fn  = filebuf;
XX	register int	lnum = 0;
XX
XX	if	(*cp != '#')		error ("invalid call to setline");
XX
XX	while	(*cp && !isdigit(*cp))	cp++;
XX	while	(*cp && isdigit (*cp))	lnum = lnum*10 + (*cp++ - '0');
XX	while	(*cp && *cp != '"')	cp++;
XX	if	(lnum)			yylineno = lnum;
XX
XX	if	(*cp++ == 0)		return;
XX	while	(*cp && *cp != '"')	*fn++ = *cp++;
XX	if	(fn == filename)	return;
XX
XX	*fn	 = '\0';
XX	filename = filebuf;
XX}
XX
XX/*  Unterminated comment here ...
XX
XXvoid uncompiled (not_passed)
XX{
XX	int	not_here;
XX
XX	not_called ();
XX}
====End of SHAR test.c====
if	[ "`wc -c <'test.c'`" != '    3731' ]
then	echo 'Unpack for test.c failed!'
	exit 1
else	echo 'Unpacked test.c'
fi
sed -e 's/^XX//' <<'====End of SHAR tree.c====' >tree.c
XX/*----------------------------------------------------------------------*/
XX/*									*/
XX/*	This module handles Parse Tree building and walking		*/
XX/*									*/
XX/*----------------------------------------------------------------------*/
XX
XX
XX#include	<stdio.h>
XX#include	<ctype.h>
XX#include	"check.h"
XX#include	"y.tab.h"
XX#include	"tables.h"
XX
XX#define		CHUNK		1024	/*  No. of nodes allocated	*/
XX
XXNodePtr		Tree;		/*  holds the base of the Parse Tree,	*/
XX				/*  built by the Yacc-generated Parser.	*/
XX
XXNodePtr		next_node,	/*  the next free node for allocation	*/
XX		last_node,	/*  node not available for allocation	*/
XX		node_list;	/*  head of list of blocks of nodes	*/
XX
XX
XXNodePtr new_node (nt, p1, p2, p3, p4)
XXint	nt;
XXNodePtr	p1, p2, p3, p4;
XX{
XX	/*  Build a node for the Parse Tree  */
XX
XX	register NodePtr	np;
XX
XX	if	(next_node == last_node)
XX	{
XX		/*  Allocate a new batch of nodes  */
XX
XX		next_node = (NodePtr) emalloc (sizeof (Node) * CHUNK);
XX		last_node = next_node + CHUNK;
XX
XX		next_node -> f1.ptr = node_list;
XX		node_list = next_node++;
XX	}
XX
XX	np = next_node++;
XX
XX	np -> type	=  nt;
XX	np -> line	=  yylineno;
XX	np -> f1.ptr	=  p1;
XX	np -> f2.ptr	=  p2;
XX	np -> f3.ptr	=  p3;
XX	np -> f4.ptr	=  p4;
XX
XX	return np;
XX}
XX
XX
XXvoid tidy_prog ()
XX{
XX	/*  Delete the parse tree and release its memory space;		*/
XX	/*  Leave just the first block of nodes for the	next program	*/
XX	/*  No attempt is made to return the freed space to the system	*/
XX	/*  by using (s)brk, but this could be added if desired.	*/
XX	/*  My own view is that if one program builds a huge tree,	*/
XX	/*  others in the same call to check may also, so let's keep	*/
XX	/*  the space.  But in that case, why even return it to malloc?	*/
XX	/*  Because it makes it easier to add the (s)brk, and it might	*/
XX	/*  alleviate heap fragmentation if other modules are also	*/
XX	/*  using malloc, and because it just feels neater to me!	*/
XX
XX	register NodePtr	np;
XX	extern void		free();
XX
XX	Tree = NilNode;
XX
XX	for  (np = node_list->f1.ptr; np; np = np->f1.ptr)  free ((char *) np);
XX	node_list -> f1.ptr = NilNode;
XX	next_node = node_list + 1;
XX	last_node = node_list + CHUNK;
XX}
XX
XX
XXvoid treewalk (depth, parent, branch, np, action)
XXint	depth, parent, branch;
XXNodePtr	np;
XXvoid	(*action) ();
XX{
XX	/*  Perform required action for this node, and all nodes below it  */
XX
XX	register int	here;
XX
XX	if  (np == NilNode)  return;
XX	yylineno = np -> line;
XX
XX	action (depth, parent, branch, np);
XX
XX	switch  (here = np -> type)
XX	{
XX		/* = = =  Terminal Nodes  = = = */
XX
XX		case IDENTIFIER:
XX		case CONSTANT:
XX		case DEFAULT:
XX		case BREAK:
XX		case CONTINUE:
XX		case ';':
XX		case Type:
XX		case Error:
XX		case 0:		break;
XX
XX		/* = = =  Unary Nodes  = = = */
XX
XX		case GOTO:
XX		case RETURN:
XX		case CASE:
XX		case Indirect:
XX		case Addr:
XX		case Uplus:
XX		case Uminus:
XX		case '!':
XX		case '~':
XX		case Pre_Inc:
XX		case Post_Inc:
XX		case Size_Type:
XX		case Size_Expr:
XX				treewalk (depth+1,here,1,np->f1.ptr,action);
XX				break;
XX
XX		/* = = =  Binary Nodes  = = = */
XX
XX		case Seq:
XX		case Label:
XX		case WHILE:
XX		case DO:
XX		case SWITCH:
XX		case ',':
XX		case Or:
XX		case And:
XX		case '|':
XX		case '^':
XX		case '&':
XX		case Eq_Op:
XX		case Rel_Op:
XX		case Shift:
XX		case '+':
XX		case '-':
XX		case '*':
XX		case '/':
XX		case '%':
XX		case Cast:
XX		case Point:
XX		case '.':
XX		case '(':
XX		case '[':
XX		case Asgn_Op:
XX				treewalk (depth+1,here,1,np->f1.ptr,action);
XX				treewalk (depth+1,here,2,np->f2.ptr,action);
XX				break;
XX
XX		/* = = =  Ternary Nodes  = = = */
XX
XX		case IF:
XX		case '?':
XX				treewalk (depth+1,here,1,np->f1.ptr,action);
XX				treewalk (depth+1,here,2,np->f2.ptr,action);
XX				treewalk (depth+1,here,3,np->f3.ptr,action);
XX				break;
XX
XX		/* = = =  Quaternary Nodes  = = = */
XX
XX		case FOR:
XX				treewalk (depth+1,here,1,np->f1.ptr,action);
XX				treewalk (depth+1,here,2,np->f2.ptr,action);
XX				treewalk (depth+1,here,3,np->f3.ptr,action);
XX				treewalk (depth+1,here,4,np->f4.ptr,action);
XX				break;
XX
XX
XX		default:	Printf ("node type %d encountered\n", here);
XX				error ("treewalk: parse tree corrupt");
XX	}
XX}
XX
XX
XXvoid walk_prog (action)
XXvoid	(*action) ();
XX{
XX	/*  Start off the Parse Tree walk  */
XX
XX	treewalk (0, 0, 0, Tree, action);
XX}
XX
XX
XX/*ARGSUSED*/
XXvoid printnode (depth, parent, branch, np)
XXint	depth, parent, branch;
XXNodePtr	np;
XX{
XX	/*  Print one node of the tree  */
XX
XX	register int type = np->type;
XX
XX	while  (depth--)  Printf ("  ");
XX	Printf ("(%d) line %d:  ", branch, np->line);
XX
XX	if	(type > 256)
XX	{
XX		register NodeName *q = nodenames;
XX
XX		while  (q->val != 0 && q->val != type)  q++;
XX		Printf ("%s\n", q->str);
XX	}
XX	elif	(type == 256)
XX		Printf ("ERROR/EOF\n");
XX	elif	(isprint(type))
XX		Printf ("%c\n", type);
XX	else	Printf ("type %d/0x%02x\n", type, type);
XX}
XX
XX
XXvoid treeprint ()
XX{
XX	/*  Print the Parse Tree  */
XX
XX	treewalk (0, 0, 0, Tree, printnode);
XX}
====End of SHAR tree.c====
if	[ "`wc -c <'tree.c'`" != '    4678' ]
then	echo 'Unpack for tree.c failed!'
	exit 1
else	echo 'Unpacked tree.c'
fi
exit
-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list