/*
**   CFLOW.C : find module call structure of c program
**   refer to cflow.doc for how to use
**
** 					Mark Ellington
**					05-27-84
**
**   Converted to MS-DOS 2 and C86, adding the following features:
**
**	1.  filename wildcard support
**	2.  -l flag to include line numbers in output
**	3.  -f flag to include file names in output
**	4.  #include support (with -h flag, same as C86 V2.20H)
**	5.  -x flag to generate input for CFLOWX
**	6.  #define macro() support
**	7.  -t flag to change to '\t' for indentation
**	8.  -s flag to allow specification of number of indentation spaces
**	       (default is 4 spaces)
**
**					Larry Steeger
**					06-12-85
*/

#include <stdio.h>		/* C86 header				*/

#define	VOID int

/*	external functions	*/

extern	VOID abort();
extern	char *alloc();
extern	VOID exit();
extern	int fclose();
extern	unsigned char *filewdir();
extern	FILE *fopen();
extern	VOID fprintf();
extern	int fputs();
extern	VOID free();
extern	int isalnum();
extern	int isdigit();
extern	int isspace();
extern	char *lower();
extern	char *makefile();
extern	char *makefnam();
extern	char *makepath();
extern	char *realloc();
extern	VOID setmem();
extern	VOID sprintf();
extern	char *strcat();
extern	unsigned char *strchr();
extern	char *strcpy();
extern	unsigned strlen();
extern	int strncmp();
extern	char *strncpy();
extern	int tolower();

#include "cflowx.h"		/* CFLOW/CFLOWX header			*/

#define	VERSION	85
#define	RELEASE	06
#define	MODIFIC	20

#define	outinfo(S) fputs(S,stdout)

/*	display CFLOW logo						*/

outlogo()
{
	outinfo("\nCFLOW --> function declarations and calls in C source");
	outinfo("\nby Mark Ellington");
	fprintf(stdout,
		"\n[V%02d.%02d.%02d for C86 by Lawrence R. Steeger]\n",
		VERSION, RELEASE, MODIFIC
		);
}

/*	CFLOW usage help						*/

outhelp()
{
	outinfo("\n  usage: cflow ");
	outinfo("[-[lft]|[x]] [-hsystem[,project]] ");
	outinfo("[input1[..inputn]] [>output]\n");
	outinfo("\n  flags: -hsystem[,project]");
	outinfo("\n              #include drive/pathname specification");
	outinfo("\n              (usage: C86 V2.20H)");
	outinfo("\n         -l   include line numbers in output");
	outinfo("\n         -f   include file names in output");
	outinfo("\n         -sn  number of indentation spaces");
	outinfo("\n         -t   tabs rather than spaces for indentation");
	outinfo("\n         -x   generate input for CFLOWX\n");
	outinfo("\ndefaults:     cflow -s4 [input] >stdout");
}

typedef	struct _cflow {		/* recursive execution structure	*/

	struct _cflow *_chain;	/* previous CFLOW structure		*/

	int _fnmbr,		/* current file name index		*/
	    _level,		/* level of open "{"s or #includes	*/
	    _prevchar,		/* previous character in input buffer	*/
	    _curchar,		/* current character in input buffer
				** array subscript
				*/
	    _curline,		/* current line in input dataset	*/
	    _define,		/* #define flag				*/
	    _defdcl;		/* name declaration offset		*/

	unsigned char _delimit,	/* #include file name delimiter		*/
		      *_defname,/* defined name buffer			*/
		      *_name,	/* module name buffer			*/
		      *_ins,	/* source input line buffer		*/
		      *_fname,	/* source file name buffer		*/
		      *_fpname;	/* source file/pathname buffer		*/

	FILE *_fptr;		/* input file pointer			*/

	} CFLOW;

/*	useability macros for CFLOW structure elements			*/

#define	chain cflowp->_chain
#define	fnmbr cflowp->_fnmbr
#define	level cflowp->_level
#define prevchar cflowp->_prevchar
#define	curchar cflowp->_curchar
#define	curline cflowp->_curline
#define	define cflowp->_define
#define	defdcl cflowp->_defdcl
#define	delimit cflowp->_delimit
#define	defname cflowp->_defname
#define	name cflowp->_name
#define	ins cflowp->_ins
#define	fname cflowp->_fname
#define	fpname cflowp->_fpname
#define	fptr cflowp->_fptr

/*	runtime flags							*/

static	int hflag = FALSE,		/* -h  #include drive/pathname	*/
	    lflag = FALSE,		/* -l  show line numbers	*/
	    fflag = FALSE,		/* -f  show file names		*/
	    sflag = FALSE,		/* -s  space indentation count	*/
	    tflag = FALSE,		/* -t  tab indentation flag	*/
	    xflag = FALSE;		/* -x  generate CFLOWX input	*/

static	int fnumber = 0,		/* C source file counter	*/
	    flevel = 0,			/* C source file level		*/
	    mnumber = 0;		/* C source file main counter	*/

static	unsigned char *filepath = NULL,	/* default file/path name	*/
		      *hsystem = NULL,	/* -hsystem specification	*/
	              *hproject = NULL,	/* -h,project specification	*/
		      *tab = "    ";	/* -t indentation		*/

static	unsigned char newLine[] = {"\n"};

static	CFLOW *cflowp = NULL;		/* current CFLOW execution ptr	*/

/*	mainline							*/

main(argc,argv)
int argc;
unsigned char * argv[];
{
	unsigned char *fp,
		      *pp;		/* temporary file/path pointer	*/

	int fcount,			/* C source file counter	*/
	    i,
	    j;

	if (argc < 2) {			/* display CFLOW help		*/

		outlogo();
		outhelp();
                exit(1);
	}

	cflowp = alloc(sizeof(CFLOW));	/* 1st execution structure	*/

	name = alloc(MAXBUF);		/* module name buffer		*/
	ins = alloc(MAXBUF);		/* input line buffer		*/
	fname = alloc(MAXPATH);		/* current file name		*/
	fpname = alloc(MAXPATH);	/* current file/path name	*/

	filepath = alloc(1);		/* dummy file/path name		*/
	*filepath = EOS;

	for (i=1; i < argc; i++) {	/* process all flags		*/
		if (*argv[i] == '-') {
			flags(argv[i]);
			for (j = i--, --argc; j < argc; j++)
				argv[j] = argv[j+1];
		}
	}

	if (!xflag) outlogo();		/* display CFLOW logo		*/

	if (hsystem != NULL)		/* display -h system prefix	*/
		if (*hsystem)
			output(HDRSYST, hsystem);

	if (hproject != NULL)		/* display -h project prefix	*/
		if (*hproject)
			output(HDRPROJ, hproject);

	for (i=1; i < argc; i++) {	/* process all files		*/
		if (*argv[i]) {
			fnmbr = mnumber++;
			strcpy(fname, argv[i]);	/* get file name	*/
			lower(fname);

			free(filepath);		/* get file/path name	*/
			filepath = makepath(fname);

			if ((strchr(fname, '*') != NULL) /* wildcards	*/
			||  (strchr(fname, '?') != NULL)) {

				/*	process all matching file names	*/

				fcount = 0;
				while ((fp = filewdir(argv[i],0x00)) != NULL) {

					if (!fcount) {
						pp = makepath(fname);
						output(WILDPTH, pp);
						free(pp);
						pp = makefile(fname);
						output(WILDNME, pp);
						free(pp);
					}

					++fcount;
					lower(fp);
					strcpy(fname,fp);
					makefnam(fp, filepath, fpname);
					flevel = level = 0;
					modules();
					free(fp);
				}

				if (!fcount) output(WILDNF, fname);
			}
			else {				/* no wildcards	*/
				strcpy(fpname, fname);
				strcpy(fname, (pp=makefile(fname)));
				free(pp);
				flevel = level = 0;
				modules();
			}
		}
	}
	exit(0);
}

/*	process command line flags					*/

int flags(flag)
unsigned char *flag;
{
	unsigned char *hp;

	int i;

	for (i=1; i < strlen(flag); i++)
		switch (tolower(flag[i])) {

		case 'h':		/* -h header specifications	*/
			hflag = TRUE;
			i++;

			if (hsystem != NULL)
				free(hsystem);
			if ((hp = strchr(&flag[i], ',')) != NULL)
				*hp++ = EOS;
			hsystem = alloc((strlen(&flag[i]) + 1));
			strcpy(hsystem, lower(&flag[i]));

			if (hp) {
				if (hproject != NULL)
					free(hproject);
				hproject = alloc((strlen(hp) + 1));
				strcpy(hproject, lower(hp));
			}

			i=strlen(flag); /* force break in for loop	*/
			break;

		case 'l':		/* -l include line numbers	*/
			lflag = TRUE;
			break;

		case 'f':		/* -f include file names	*/
			fflag = TRUE;
			break;

		case 's':		/* -s spaces for indentation	*/
			if (tflag) {
				fprintf(stdout,
					"\n-s is mutually exclusive with -t");
				outhelp();
				exit(1);
			}
			if (!isdigit(flag[i+1])) {	/* -s only !	*/

				/*  note: -s  implies default		*/

				sflag = strlen(tab);
				break;
			}
			sflag = 0;
			while (isdigit(flag[++i]))
				sflag = (sflag * 10) + ((int)flag[i] - '0');
			--i;

			if (sflag) {
				tab = alloc(sflag + 1);
				setmem(tab, sflag, ' ');
				break;
			}

		/*	note:	-s0 implies -t				*/

		case 't':		/* -t tabs for indentation	*/
			if (sflag) {
				fprintf(stdout,
					"\n-t is mutually exclusive with -s");
				outhelp();
				exit(1);
			}
			tflag = TRUE;
			tab = "\t";
			break;

		case 'x':		/* -x generate CFLOWX file	*/
			xflag = TRUE;
			break;

		default:
			fprintf(stdout, "\nUnknown flag: \"%c\"", flag[i]);
			outhelp();
			exit(1);
		}
	flag[0] = EOS;			/* clear flag			*/
}

/*	find function declarations and calls				*/

modules()
{
	int breakc,		/* comment/quotes break character	*/
	    decl,		/* module declaration line flag		*/
	    header,		/* in function header (before 1st '{')	*/
	    j,			/* loop index				*/
	    lastlin,		/* last line of file flag		*/
	    modname();		/* module name extractor		*/

	unsigned char c,		/* current input character	*/
		      *pp;		/* temporary path name pointer	*/

	fnmbr = fnumber++;	/* set & increment C source file number	*/

	/*	the following switch() provides -h flag support
	**	as documented for the C86 V2.20H -h flag.
	*/

	switch (delimit) {	/* #include/command line file opens	*/

		case '"':		/* #include "filename"		*/
			if (*filepath) {
				fpname =
				alloc((strlen(filepath) + strlen(fname) + 1));
				strcpy(fpname, filepath);
				strcat(fpname, fname);
			}
			else {
				fpname = alloc((strlen(fname) + 1));
				strcpy(fpname, fname);
			}
			if ((fptr = fopen(fpname, "r+")) != NULL) break;
			free(fpname);

		case '>':		/* #include <filename>		*/
			if (hproject != NULL) {
				if (*hproject) {
				fpname =
				alloc((strlen(hproject) + strlen(fname) + 1));
				strcpy(fpname, hproject);
				strcat(fpname, fname);
				if ((fptr=fopen(fpname,"r+")) != NULL) break;
				free(fpname);
				}
			}

			if (hsystem != NULL) {
				if (*hsystem) {
				fpname =
				alloc((strlen(hsystem) + strlen(fname) + 1));
				strcpy(fpname, hsystem);
				strcat(fpname, fname);
				if ((fptr=fopen(fpname,"r+")) != NULL) break;
				free(fpname);
				}
			}

			if (hflag || delimit != '"') {
				output(FILEERR, fname);
				return;
			}

			/*	#include "filename" without -h		*/

			fpname = alloc((strlen(fname) + 1));
			strcpy(fpname, fname);

		default:		/* command line filename	*/
			if ((fptr = fopen(fpname, "r+")) == NULL) {
				output(FILEERR, fpname);
				return;
			}
			break;
	}

	/*	initialize flags/tokens					*/

	breakc = prevchar = lastlin = header = FALSE;

	curline = 0;			/* 1st character in buffer	*/

	/*	identify C source/#include file name			*/

	pp = makepath(fpname);		/* get path name		*/

	output(FILEPTH, pp);		/* show C source path name	*/
	output(FILENME, fname);		/* show C source file name	*/

	free(pp);			/* free path name		*/

	do {
		if (lastlin = fgets())	/* read a line of source	*/
			++curline;

		define = decl = FALSE;	/* assume nothing		*/
		curchar = 0;		/* start of line		*/
		defname = NULL;		/* clear defined name pointer	*/

		if (prevchar != '\\')	/* reset previous if not \	*/
			prevchar = FALSE;

		while (ins[curchar]) {	/* process all characters	*/

			if (breakc) {	/* comment/quote in progress	*/
				if (skipold(&breakc))
					break;
			}
				
			switch (ins[curchar]) {

			case '\"':	/* start quoted string		*/
			case '\'':	/* start quoted constant	*/
			case '/' :	/* start comment ?		*/
				skipnew(&breakc);
				break;

			case '#':	/* pre-processor tokens		*/
				if (includes())		/* #include	*/
					break;
				if (defines())		/* #define	*/
					break;
				goto notsupp;		/* unsupported	*/

			case '{':	/* compound statement start	*/
				level++;
				header = FALSE;
				break;

			case '}':	/* compound statement end	*/
				level--;
				if (level < 0) {	/* nest error	*/
					output(NESTERR, fpname);
					level = 0;
				}
				break;

			case '(':	/* function/macro call/declare	*/

				if (define) {	/* #define macro()	*/
					if (!lookmac(curchar))
						goto notfunc;
					macname();	/* macro name	*/
					break;
				}

				if (!lookmod(curchar))	/* module name	*/
					goto notfunc;
				j = modname();

				if (!j) goto notfunc;	/* not function	*/
	
				decl = TRUE;		/* function	*/

				if (j == 2)		/* declaration	*/
					header = TRUE;

				break;

			notsupp:
			notfunc:
  			default:		/* all other characters */
				break;
			}

			if (ins[curchar]) prevchar = (int)ins[curchar++];
		}

		if (header && !decl && !breakc && !define) {

			/* function declaration argument declarations	*/

			comout(ins);
			if (ins[0]) output(FUNCARG, ins);
		}

		if (defname != NULL) free(defname);

	} while (lastlin);		/* = FALSE if last line		*/

	fclose(fptr);

	if (!delimit) output(NOFUNC, newLine);

	return;
}

/*	process in progress comments and quoted character or strings	*/

int skipold(breakc)
int *breakc;
{
	while (ins[curchar] && *breakc) { /* !EOS & break in progress	*/

		switch (ins[curchar]) {

		case '\\':			/* escape sequence	*/
			if (ins[(curchar+1)])
				++curchar;	/* skip a character	*/
			break;

		case '\'':			/* end quoted character	*/
			if (*breakc != '\'') goto notbreak;
			*breakc = FALSE;
			break;

		case '"':			/* end quoted string	*/
			if (*breakc != '"') goto notbreak;
			*breakc = FALSE;
			break;

		case '*':			/* end comment ?	*/
			if (*breakc != '/') goto notbreak;
			if (ins[(curchar+1)] == '/') {
				++curchar;
				*breakc = FALSE;
				break;
			}

		notbreak:
		default:			/* other characters	*/
			break;
		}

		if (ins[curchar]) prevchar = (int)ins[curchar++];
	}

	return (*breakc);
}

/*	start new comment or quoted character/string process		*/

int skipnew(breakc)
int *breakc;
{
	switch (ins[curchar]) {		/* not EOS			*/

	case '/':			/* start comment ?		*/
		if (ins[++curchar] != '*') {
			break;
		}

		*breakc = '/';		/* comment started		*/

		if (ins[curchar]) prevchar = (int)ins[curchar];
		break;

	case '\'':			/* start quoted constant	*/
	case '"':			/* start quoted string		*/
		*breakc = (int)ins[curchar];
		break;

	default:			/* unknown ?			*/
		break;
	}

	return (*breakc);
}

/*	process #include						*/

includes()
{
	unsigned char delim, *fp;
	int filecur, fnamel;
	CFLOW *cflows;

	filecur = curchar;

	if (strncmp("#include", &ins[filecur], 8) != 0)
		return (FALSE);			/* not #include		*/

	for (filecur += 8; ins[filecur]; filecur++)
		if (!isspace(ins[filecur])) break;

	if (!ins[filecur]) return (FALSE);	/* #include.. only ?!	*/

	delim = ins[filecur++];			/* file name delimiter	*/

	if (delim != '\"' && delim != '<')
		return (FALSE);			/* #include.. what ?!	*/

	if (delim == '<') delim = '>';		/* #include <...>	*/

	if ((fp = strchr(&ins[filecur], delim)) == NULL)
		return (FALSE);			/* #include.. what ?!	*/

	if ((fnamel = (fp - &ins[filecur])) == 0)
		return (FALSE);			/* #include.. what ?!	*/

	fp = alloc(++fnamel);
	lower(strncpy(fp, &ins[filecur], (fnamel-1)));

	filecur += fnamel;		/* skip file name + 1		*/

	cflows = cflowp;		/* current CFLOW ptr		*/

	cflowp = alloc(sizeof(CFLOW));	/* allocate new CFLOW		*/

	chain = cflows;			/* build new CFLOW		*/
	name = alloc(MAXBUF);
	ins = alloc(MAXBUF);
	fname = fp;
	if ((strchr(fp, ':') != NULL)	/* no -h if drive/path used	*/
	||  (strchr(fp, '\\') != NULL)) {
		delimit = 1;		/* include with drive/path	*/
		fpname = alloc((strlen(fp) + 1));
		strcpy(fpname, fp);	/* fpname = fname		*/
		fname = makefile(fname);
		free(fp);
	}
	else {
		delimit = delim;
		fpname = NULL;		/* dynamic fpname at open	*/
	}
	fnmbr = cflows->_fnmbr + 1;
	flevel++;
	level = 0;

	modules();			/* recursive call		*/

	--flevel;

	cflows = chain;			/* dechain CFLOW structure	*/
	free(name);
	free(ins);
	free(fname);
	if (fpname != NULL) free(fpname);
	free((char *)cflowp);

	cflowp = cflows;		/* restore previous CFLOW	*/

	curchar = filecur;		/* update buffer index		*/
}

/*	process #defines						*/

int defines()
{
	if (strncmp("define", &ins[(curchar+1)], 6) != 0) {
		define = 0;
		return (FALSE);		/* not #define			*/
	}

	define = 1;			/* #define 1st level		*/
	curchar += 6;			/* skip #define			*/
	return (TRUE);			/* #define found		*/
}

/*	look back from position n in string.  called with n indicating '('.
	determine macro name.
*/

int lookmac(n)
int n;
{
	int i;

	i = 0;

	if (n) {			/* ignore white space		*/
		--n;
		while (isspace(ins[n])) --n;
	}

	if (isalnum(ins[n])) {		/* find beginning of macro name */

		while (n && isalnum(ins[n-1])) --n;

		/*	save name
			include definition if macro declaration
		*/

		if (define < 2) defdcl = n;	/* macro name offset	*/

		while (isalnum(ins[n]))		/* macro definition	*/
			name[i++] = ins[n++];
	}

	name[i] = EOS;

	if (i) {
		comout(name);	/* remove any comment from name string	*/
		if (define < 2) {		/* save macro name	*/
			defname = alloc((strlen(name) + 1));
			strcpy(defname, name);
		}
		return (TRUE);			/* macro name found	*/
	}
	else
		return (FALSE);			/* macro name not found	*/
}

/*	look back from position n in string.  called with n indicating '('.
	determine function name.
*/

int lookmod(n)
int n;
{
	int i, j, k;

	i = 0;

	if (n) {			/* ignore white space		*/
		--n;
		while (isspace(ins[n])) --n;
	}

	if (isalnum(ins[n])) {		/* find beginning of name	*/

		while (n && isalnum(ins[n-1])) --n;

		/* 	save name
			include variable declarations if module declaration
		*/

		if (level == 0) {		/* function declaration */
			defdcl = n;
			j = 0;
			while (ins[j])		/* copy full definition	*/
				name[i++] = ins[j++];
			defname = alloc((strlen(&ins[defdcl]) + 1));
			j = defdcl;
			k = 0;
			while (isalnum(ins[j]))	/* copy name only	*/
				defname[k++] = ins[j++];
			defname[k] = EOS;
		}
		else				/* function call	*/
			while (isalnum(ins[n]))
				name[i++] = ins[n++];
	}

	name[i] = EOS;

	if (i) {
		comout(name);	/* remove any comment from name string	*/
		return (TRUE);	/* module name found			*/
	}
	else
		return (FALSE);	/* module name not found		*/
}

/*	terminate string at comment, stripping trailing white space	*/

comout(s)
unsigned char *s;
{
	unsigned char c;

	while (c = *s++) {
		if (c == '\n') {
			*--s = EOS;
			break;
		}
		if (c == '/')
			if (*s == '*') {
				*--s = EOS;
				break;
			}
	}
	for (--s; isspace(*s);) *s-- = EOS;
	if (*s == '{') {
		*s = EOS;
		for (--s; isspace(*s);) *s-- = EOS;
	}
}

/*	display macro name						*/

macname()
{
	char *cp;

    	if (define < 2) {			/* macro start		*/
		define++;			/* update macro level	*/
	}
	else {
		output(MACNME, defname);	/* macro name		*/
		cp = alloc((strlen(&ins[defdcl]) + 1));
		strcpy(cp, &ins[defdcl]);
		comout(cp);
		output(MACDCL, cp);		/* macro definition	*/
		output(MACEQU, name);		/* macro operand	*/
		free(cp);
	}
}

/*	display module name with indentation according to { level

	returns 0 if not module,
		1 if call within module,
		2 if module declaration
*/

modname()
{
	char *cp;
	int i, result;

	if (unreserved() && !external()) { /* builtin/extern entries	*/
	    	if (level == 0) {
			output(FUNCNME, defname);/* module name		*/
			output(FUNCDCL, name);	 /* module function	*/
			return (2);
                }
		else {
			output(FUNCREF, name);	 /* module reference	*/
			return (1);
		}
        }
	return (0);
}

/*	generate output based upon type of data and execution flags	*/

output(type, string, operand)   
int type;
unsigned char *string, *operand;
{
	char *cptype;

	unsigned char outbuf[MAXBUF],
		      *filter();

	int j, slevel;

	slevel = level;			/* save current level		*/

	switch (type) {			/* generate output strings	*/

	case NOFUNC:
		if (strchr(string, '%') != NULL) {
			sprintf(outbuf, string, operand);
			cptype = outbuf;
		}
		else	cptype = string; 
		break;

	case FUNCREF:
		strcpy(outbuf, tab);
		for (j=0; j < level; ++j)
			strcat(outbuf, tab);
		sprintf(&outbuf[strlen(outbuf)], "%s()", string);
		cptype = outbuf;
		break;

	case FUNCARG:
		cptype = " * %s";
		string = filter(string);
		break;

	case FUNCDCL:
		cptype = "** %s";
		string = filter(string);
		break;

	case MACEQU:
		cptype = " = %s()";
		string = filter(string);
		break;

	case MACDCL:
		cptype = "== %s";
		string = filter(string);
		break;

	case FILENME:
		if (flevel) {
			cptype = "           C Include: %s";
			level = flevel;
		}
		else	cptype = "            C Source: %s";
		break;

	case FILEPTH:
		if (*string)
			if (flevel) {
				cptype = "C Include Drive/Path: %s";
				level = flevel;
			}
			else	cptype = " C Source Drive/Path: %s";
		else	cptype = "";
		break;

	case HDRSYST:
		cptype = "     C System Header: %s";
		break;

	case HDRPROJ:
		cptype = "    C Project Header: %s";
		break;

	case WILDPTH:
		if (*string)
			cptype = " WildCard Drive/Path: %s";
		else	cptype = "";
		break;

	case WILDNME:
		cptype = "      WildCard Files: %s";
		break;

	case WILDNF:
		cptype = "   No Files Matching: %s";
		break;

	case FILEERR:
		if (flevel)
			cptype = "Can't Open C Include: %s";
		else	cptype = " Can't Open C Source: %s";
		break;

	case NESTERR:
		cptype = "  {..} Nesting Error: %s";
		break;

	case MACNME:
	case FUNCNME:
		break;

	default:
		fprintf(stderr,
			"\noutput(%d): internal error\n", type);
		exit(1);
	}

	if (!xflag) {
		switch (type) {		/* perform spacing for types	*/

		case FUNCDCL:		/* function declaration		*/
		case MACDCL:		/* macro declaration		*/
		case WILDPTH:		/* wildcard path name		*/
		case FILEPTH:		/* file pathname		*/
			fputs(newLine, stdout);
			if (!*cptype) break;

		case WILDNME:		/* wildcard specification	*/
		case FILENME:		/* C source file name		*/
		case FUNCARG:		/* function argument		*/
		case FUNCREF:		/* function reference		*/
		case MACEQU:		/* macro equate			*/
		case NESTERR:		/* nesting error		*/
		case FILEERR:		/* file error			*/
		case HDRSYST:		/* -h system prefix		*/
		case HDRPROJ:		/* -h project prefix		*/
		case WILDNF:		/* no files match wildcard	*/
			fputs(newLine, stdout);
			break;

		case MACNME:		/* macro name			*/
		case FUNCNME:		/* function name		*/
			return;

		case NOFUNC:		/* non-function output		*/
		default:
			break;
		}

		if (*cptype) {
			outhdr(type);		/* generate line header	*/
			fprintf(stdout, cptype, string); /* output line	*/
		}
	}
	else {				/* generate CFLOWX output	*/
		if (type) {
			if (type == FILEERR) {
				output(FILEPTH, "", "");
				strcpy(outbuf, "* ");
				strcat(outbuf, string);
				output(FILENME, outbuf, "");
				level = slevel;
				return;
			}

			if (type == WILDNF) {
				output(WILDPTH, "", "");
				strcpy(outbuf, "# ");
				strcat(outbuf, string);
				output(WILDNME, outbuf, "");
				level = slevel;
				return;
			}

			fprintf(stdout,
				cxref(MAXFLDS),
				type,
				fnmbr,
				level,
				curline,
				string
				);
		}
	}

	level = slevel;			/* restore current level	*/
}

/*	output line heading						*/

outhdr(type)
int type;
{
	if (type && curline) {
		if (fflag) fprintf(stdout, "%s ", fpname);
		if (lflag) fprintf(stdout, "%5d: ", curline);
	}
}

/*	C string filter for extraneous white space			*/

unsigned char *filter(string)
unsigned char *string;
{
	int j;

	while (isspace(*string)) ++string;

	for (j=0; string[j]; j++)
		if (isspace(string[j])) string[j] = ' ';

	for (j=0; string[j] && string[(j+1)] != EOS; j++) {
		if (string[j] == ' ' && string[(j+1)] == ' ') {
			strcpy(&string[j], &string[(j+1)]);
			continue;
		}
		if (string[j] == ' ' && string[(j+1)] == '(') {
			strcpy(&string[j], &string[(j+1)]);
			continue;
		}
		if (string[j] == ',' && string[(j+1)] == ' ') {
			strcpy(&string[(j+1)], &string[(j+2)]);
			continue;
		}
		if (string[j] == '*' && string[(j+1)] == ' ')
			strcpy(&string[(j+1)], &string[(j+2)]);
	}

	return (string);
}

/*	test for C reserved words					*/

unreserved()
{
	     if (strncmp(name, "return", 6) == 0
	     &&  !iscname(name[6])) return (FALSE);
	else if (strncmp(name, "if",     2) == 0
	     &&  !iscname(name[2])) return (FALSE);
	else if (strncmp(name, "while",  5) == 0
	     &&  !iscname(name[5])) return (FALSE);
	else if (strncmp(name, "for",    3) == 0
	     &&  !iscname(name[3])) return (FALSE);
	else if (strncmp(name, "switch", 6) == 0
	     &&  !iscname(name[6])) return (FALSE);
	else if (strncmp(name, "sizeof", 6) == 0
	     &&  !iscname(name[6])) return (FALSE);

	else return (TRUE);
}

/*	detect "extern" function definitions outside of all functions	*/

external()
{
	char *cp;
	int i, rc;

	if (level) return (FALSE);		/* within a function	*/

	for (i=0; ins[i]; i++) if (isalnum(ins[i])) break;

	if (ins[i]) {		
		if (strncmp(&ins[i], "extern", 6) == 0
	        &&  !iscname(ins[6])) return (TRUE);
		if (strncmp(&ins[i], "typedef", 7) == 0
		&&  !iscname(ins[7])) return (TRUE);
	}

	cp = alloc(strlen(ins));
	strcpy(cp, ins);
	comout(cp);

	if (*cp)
		if (cp[(strlen(cp)-1)] != ')') {
			free(cp);
			return (TRUE);
		}

	free(cp);
	return (FALSE);
}

/*	read a line of source						*/

int fgets()
{
	unsigned char *s;
	int ch, count;

	s = ins;
	count = 0;

	while ((ch = getc(fptr)) != EOF) {

		buflim(count, MAXBUF);
		*s++ = (unsigned char)ch;
		++count;

		if (ch == '\n') {
			buflim(count, MAXBUF);
			*s = EOS;
			return (TRUE);		/* not EOF		*/
		}
	}

	buflim(count, MAXBUF);
	*s = EOS;
	return (FALSE);				/* EOF			*/
}

/*	test if buffer limit reached					*/

int buflim(current, limit)
int current, limit;
{
	if (current == limit) {
		fprintf(stderr,
			"\nfgets(): line %d in %s has more than %d characters",
			curline, fpname, MAXBUF);
		exit(1);
	}
}

/*	determine if character is valid in a C name or label		*/

int iscname(c)
unsigned char c;
{
	if (isalnum(c)) return (TRUE);	/* a-z and A-Z are valid	*/

	switch (c) {			/* plus 4 special characters	*/

	case '_':
	case '@':
	case '$':
	case '#':
		return (TRUE);
	default:
		return (FALSE);
	}
}

/*	end of cflow.c			*/

e