/****************************************************************
*								*
*		CLIST (C Lister and Checker)			*
*								*
*  C Source Paren, Bracket, Comment, String, and Char Checker 	*
*								*
*	      V1.0 T. Jennings  -- Sometime in 1983		*
*								*
* Modifications Record:						*
* =====================						*
* V2.0	5-Sep-85 CJD						*
*	Translate to Whitesmiths' C, w/o using Unix-compatible	*
*	  subroutine calls (Whitesmiths' OTS alone is smaller).	*
*	Output page header giving filename, date, and time.	*
*	Output 3 blank lines at end of subroutines (when	*
*	  braces count becomes 0).				*
* V2.1	15-Jan-86 CJD						*
*	Ask for filename if not specified.			*
*	Don't count brackets inside strings and char constants.	*
*	Check brackets, quotes, and primes too.			*
*	Put messages into VMS standard format.			*
* V2.2	27-Jan-86 CJD						*
*	Accept 2nd filename (if any) as O/P file, w/o '>'.	*
*	Use stdin for input (& stdout for output), saving a FIO.*
* V2.3	16-May-86 CJD						*
*	Correct line number to start at 1 (was 0!!!).		*
*	Flag string continued over end of line without \.	*
*	Append an error report to listing if not to TT:		*
*								*
****************************************************************/

#include <std.h>

/* Very crude but very effective C source debugger. Counts the numbers of
matching braces, parentheses, brackets, comments, string quotes, and  character
quotes and displays them at the left edge of the screen. The best way to see
what it does is to do it to itself; try

	CLIST CLIST.C		! C List/Check CLIST.C

Properly handles parens and brackets inside comments, strings, and character
constants: they are ignored. 
*/

TEXT *_pname {"CLIST"};

int main(argc,argv)
int argc;
char **argv;
{
register char c,lastc;
register int parens,braces,brackets,comments,line,col,no_s_cont;
register BOOL endsub,in_string,in_char;
char *p;
char hdr[40];
extern FIO stdout,stdin;

	/* Make sure program name is in upper case.
	   CAUTION: Don't use toupper; it writes to *_pname, which may be the
		    default string in RO code, crashing program under VMS!    */

	for (p = _pname; *p != '\0'; p++) if(islower(*p)) *p = *p - ('a'-'A');

	if (argv[1] == NULL) {
		do
			putstr(STDERR,"_File: ",NULL);
		while ((argc=getlin(hdr,40)) == 1);
	if(argc==0) return(YES);		/* ctrl/Z typed to cancel CLIST */
	hdr[argc-1] = '\0';			/* Remove trailing LF */
	argv[1] = &hdr;
	}

	fclose(&stdin);
	if (!fopen(&stdin,argv[1],READ)) {
		errfmt("%%%p-F-OPNERR, Can't read %p\n",_pname,argv[1]);
		return(NO);
	}

	if (argv[2] != NULL) {
		fclose(&stdout);
		if (!fcreate(&stdout,argv[2],WRITE)) {
			errfmt("%%%p-F-OPNERR, Can't write %p\n",_pname,argv[2]);
			return(NO);
		}
	}

	date(&hdr); putfmt("CLIST V2.3. Listing of file %p on %p at ",argv[1],&hdr);
	time(&hdr); putfmt("%b\n\n",&hdr,8);

	braces=parens=brackets=comments=in_string=in_char=line=col=endsub=no_s_cont=0;
	lastc= '\0';

	while ((c=getch()) != EOF) {
		if (col == 0) {
			decode(hdr,19,"%2i%ac %2i%ac %2i%ac %2i%2as %ac%ac",
				abs(braces),(braces>=0)?'{':'}',
				abs(parens),(parens>=0)?'(':')',
				abs(brackets),(brackets>=0)?'[':']',
				abs(comments),(comments>=0)?0x2f2a:0x2a2f,
				(in_string)?'"':(in_char)?'\'':' ',
				(no_s_cont<0)?'\\':' ');
			putfmt("%b%4i ",hdr,19,++line);
			no_s_cont = abs(no_s_cont);
		}

/* Don't count parens and braces that are inside comments. This of course
assumes that comments are properly matched; in any case, that will be the
first thing to look for. */

		if (comments <= 0) {
			if (lastc != '\\') {
				if (c == '"' && !in_char) in_string = !in_string;
				if (c == '\'' && !in_string) in_char = !in_char;
			}
			if ((!in_string) && (!in_char)) {
				switch (c) {
					case '[': ++brackets;	break;
					case ']': --brackets;	break;
					case '{': ++braces;	break;
					case '}': if (--braces == 0) ++endsub; break;
					case '(': ++parens;	break;
					case ')': --parens;	break;
				}
			}
		}

/* Now do comments. This properly handles nested comments, whether or
not your compiler does is another matter (Whitesmiths' C doesn't).	 */

		if (!in_string) {
			if ((c == '*') && (lastc == '/')) ++comments;
			if ((c == '/') && (lastc == '*')) --comments;
		}

		++col;
		if (c == '\n') {		/* newline == New Line */
			col= 0;			/* set column 0 */
			if(in_string && lastc!='\\') no_s_cont = -(++no_s_cont);
		}
		putch(c);			/* display text */
		lastc= ((in_string || in_char) && (lastc=='\\') && (c=='\\'))
				? '\0' : c;	/* update last char */
		if(endsub && (c=='\n')) {
			putfmt("\n\n\n"); endsub=0;
		}
	}
	if (!(braces||parens||brackets||comments||in_string||in_char||no_s_cont)) return(YES);
	if (argv[2] == NULL) errfmt("\n\n");	/* Output to TT:. Separate */
	else {					/* Output to file. Include report */
		if (braces)	putfmt("\n\n%i unbalanced brace%ac\n",
					braces,(braces==1)?'\0':'s');
		if (parens)	putfmt("%i unbalanced parenthes%acs\n",
					parens,(parens==1)?'i':'e');
		if (brackets)	putfmt("%i unbalanced bracket%ac\n",
					brackets,(brackets==1)?'\0':'s');
		if (comments)	putfmt("%i unbalanced comment%ac\n",
					comments,(comments==1)?'\0':'s');
		if (in_string)	putfmt("Unclosed string\n");
		if (in_char)	putfmt("Unclosed character constant\n");
		if (no_s_cont)	putfmt("%i string%ac continued without \\\n",
					no_s_cont,(no_s_cont==1)?'\0':'s');
	}
	/* (Repeat) errors on errfmt in VAX standard format */
	if (braces)	errfmt("%%%p-W-BRACE, %i unbalanced brace%ac\n",
				_pname,braces,(braces==1)?'\0':'s');
	if (parens)	errfmt("%%%p-W-PAREN, %i unbalanced parenthes%acs\n",
				_pname,parens,(parens==1)?'i':'e');
	if (brackets)	errfmt("%%%p-W-BRACKET, %i unbalanced bracket%ac\n",
				_pname,brackets,(brackets==1)?'\0':'s');
	if (comments)	errfmt("%%%p-W-COMMENT, %i unbalanced comment%ac\n",
				_pname,comments,(comments==1)?'\0':'s');
	if (in_string)	errfmt("%%%p-W-STRING, Unclosed string\n",_pname);
	if (in_char)	errfmt("%%%p-W-CHAR, Unclosed character constant\n",_pname);
	if (no_s_cont)	errfmt("%%%p-W-CONSTR, %i string%ac continued without \\\n",
				_pname,no_s_cont,(no_s_cont==1)?'\0':'s');
	return(NO);
}
