#ifdef	ournix
#include "ournix.h"
#endif
char sccsID[]	=
"@(#) brackets.c V2.14 Copyright Julian H. Stacey, Munich, 1988-12-29 - 2024-03-29\n";

#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>	// man 2 write
#include <sys/uio.h>	// man 2 write
#include <unistd.h>	// man 2 write

typedef	char	FLAG ;
FLAG flag_text		= 1 ;	/* non zero means display counts of all symbols
				encountered in file (whether in C text or code
				segments) */
FLAG flag_code		= 1 ;	/* non zero means display counts of symbols
				encountered in C code segment of file */
		/* JJLATER no flag yet for whether in html comment or code */
FLAG flag_warn		= 1 ;	/* 0 = turn off summary warnings */
FLAG flag_print		= 1 ;	/* 0 = do not print each line, just summary */
FLAG flag_verbose	= 0 ;	/* 1 = verbose (chat at end)*/
FLAG flag_angle		= 1 ;	/* 0 = do not warn or error if
				unbalanced '<' & '>'
			 */
int exit_code		= 0 ;
char	buffer[1000] ;
char	full_before[]	= "/*%d%c%d%c%c*/ {%d%c%d} (%d%c%d) [%d%c%d]" ;
char	full_angle[]	= " <%d%c%d>" ;
char	full_after[]	= " \"%d%c%d\" `%d%c%d` \'%d%c%d\'" ;
char	txt_count_neg[]	=	"%s %s %s count is negative at line %ld\n" ;
char	txt_bracket[]	=	"bracket" ;
char	txt_warning[]	=	"Warning: " ;
char	txt_erring[]	=	"Error: " ;
char	txt_comment[]	=	"comment" ;
char	txt_curly[]	=	"{} curly" ;
char	txt_round[]	=	"() round" ;
char	txt_square[]	=	"[] square" ;
char	txt_angle[]	=	"<> angle" ;
char	summarise[]	=	"%sOne or more symbols %s in %s is not matched.\n";
char	summarise_inc_angle[]	= "(inc. <>)";
char	summarise_exc_angle[]	= "(exc. <>)";

char	**ARGV ;

	int
do_file(fp)
	register FILE	*fp ;
	{
	register int	this_ch ;
	char	last_ch = '\0' ;
	char	*buf_p ;
	int	buf_len = 0 ;
	int	tmp ;
	char	seperator = ':' ;
	long	line = 1 ;
	char	code = 1 ;	/* / * * /
				a memory of scanning the comment open &
				close delimeters found within the text
				1 = compiler considers this bit as code
				*/
	char	dbl_quoted = 0 ; /* 1 = we are in a double quoted string */

	/* counts of all symbols seen within total file */
	int	cmt_comment = 0 ;	/* / * * / a count of the literal
					number of comment open & close
					delimeters found within the text */
	int	cmt_curly = 0 ; 	/* { } */
	int	cmt_round = 0 ; 	/* ( ) */
	int	cmt_square = 0 ;	/* [ ] */
	int	cmt_angle = 0 ;		/* < > */
	int	cmt_d_quote = 0 ;	/* double quote */
	int	cmt_o_quote = 0 ;	/* open single quote */
	int	cmt_c_quote = 0 ;	/* close single quote */

	/* counts of active symbols seen within code segment only */
	int	code_comment = 0 ;	/* / * * / */
	int	code_curly = 0 ;	/* { } */
	int	code_round = 0 ;	/* ( ) */
	int	code_square = 0 ;	/* [ ] */
	int	code_angle = 0 ;	/* < > */
	int	code_d_quote = 0 ;	/* double quote */
	int	code_o_quote = 0 ;	/* open single quote */
	int	code_c_quote = 0 ;	/* close single quote */
	/* make a note the first time something such as code_curly goes -ve */
	long	err_comment = 0L ;	/* / * * / */
	long	err_curly = 0L ;	/* { } */
	long	err_round = 0L ;	/* ( ) */
	long	err_square = 0L ;	/* [ ] */
	long	err_angle = 0L ;	/* < > */
	/* make a note the first time something such as txt_curly goes -ve */
	long	warn_comment = 0L ;	/* / * * / */
	long	warn_curly = 0L ;	/* { } */
	long	warn_round = 0L ;	/* ( ) */
	long	warn_square = 0L ;	/* [ ] */
	long	warn_angle = 0L ;	/* < > */

	if (!(flag_text || flag_code) ) exit(1);
	buf_p = buffer ;
	while((this_ch = getc(fp)) != EOF)
		{
		if ( this_ch == '\n')
			{
			line++ ;
			*buf_p = '\n' ; buf_len++ ;
			if (flag_print)
				{
				if (flag_text && flag_code)
					{
					printf(full_before,
						cmt_comment,seperator,
						code_comment,seperator,
						(code)? 'C' : 'T',
						cmt_curly, seperator,
							code_curly,
						cmt_round, seperator,
							code_round,
						cmt_square, seperator,
							code_square) ;
					if (flag_angle) printf(full_angle,
						cmt_angle, seperator,
						code_angle) ;
					printf(full_after,
						cmt_d_quote, seperator,
							code_d_quote,
						cmt_o_quote, seperator,
							code_o_quote,
						cmt_c_quote, seperator,
							code_c_quote) ;
					}
				else	{
					if (flag_text) printf(
					"/*%d*/{%d}(%d)[%d]<%d>\"%d\"`%d`\'%d\'"
						, cmt_comment, cmt_curly,
						cmt_round, cmt_square,
						cmt_angle, cmt_d_quote,
						cmt_o_quote, cmt_c_quote) ;
					else printf(
					"/*%d%c%c*/{%d}(%d)[%d]<%d>\"%d\"`%d`\'%d\'",
						code_comment, seperator,
						(code)? 'C' : 'T', code_curly,
						code_round, code_square,							code_angle, code_d_quote,
						code_o_quote, code_c_quote) ;
					}
				printf(" ");
				(void) fflush(stdout) ;
				if (write(1,buffer,buf_len) != buf_len)
					{
					perror(*ARGV) ;
					exit(1) ;
					}
				}
			buf_p = buffer ; buf_len = 0 ;
			}
		else	{
			tmp = 0 ;
			switch(this_ch)
				{
				case '{': tmp = 2 ;
				case '}': tmp -=1 ;
					if ( ( (cmt_curly += tmp) < 0)
						&& !warn_curly )
						warn_curly = line ;
					if (code && !dbl_quoted &&
						!code_c_quote)
						{
						if ( ( (code_curly += tmp) < 0)
							&& !err_curly )
							err_curly = line ;
						}
					break ;
				case '[': tmp = 2 ;
				case ']': tmp -= 1 ;
					if ( ( (cmt_square += tmp) < 0) &&
						!warn_square )
						warn_square = line ;
					if (code && !dbl_quoted &&
						!code_c_quote)
						{
						if ( ( (code_square += tmp) < 0)
							&& !err_square )
							err_square = line ;
						}
					break ;
				case '<': tmp = 2 ;
				case '>': tmp -= 1 ;
					if ( ( (cmt_angle += tmp) < 0) &&
						!warn_angle )
						warn_angle = line ;
					if (code && !dbl_quoted &&
						!code_c_quote)
						{
						if ( ( (code_angle += tmp) < 0)
							&& !err_angle )
							err_angle = line ;
						}
					break ;
				case '(': tmp = 2 ;
				case ')': tmp -= 1 ;
					if ( ( (cmt_round += tmp) < 0) &&
						!warn_round )
						warn_round = line ;
					if (code && !dbl_quoted &&
						!code_c_quote)
						{
						if ( ( (code_round += tmp) < 0)
							&& !err_round )
							err_round = line ;
						}
					break ;
				case '*': if ( last_ch == '/')
						{
						cmt_comment++ ;
						if (!dbl_quoted)
							{
							code_comment++ ;
							code = 0 ;
							}
						}
					break ;
				case '/': if ( last_ch == '*')
						{
						if ( (--cmt_comment < 0) &&
							!warn_comment )
							warn_comment = line ;
						if (!dbl_quoted)
							{
							code = 1 ;
							if ( (--code_comment <0)
								&& !err_comment)
								err_comment =
								line ;
							}
						}
					break ;
				case '`':	/*`*/
					if ( ++cmt_o_quote == 2 )
						cmt_o_quote = 0 ;
					if (code && !dbl_quoted && !code_c_quote
						&& ( ++code_o_quote == 2 ) )
						code_o_quote = 0 ;
					break ;
				case '"':	/*"*/
					if ( ++cmt_d_quote == 2 )
						cmt_d_quote = 0 ;
					if (code && !code_c_quote &&
						(last_ch != '\\') )
						{
						if ( ++code_d_quote == 2 )
							code_d_quote = 0 ;
						dbl_quoted =
							(code_d_quote==1) ?1:0;
						}
					break ;
				case '\'':	/*'*/
					if ( ++cmt_c_quote == 2 )
						cmt_c_quote = 0 ;
					if (code && !dbl_quoted &&
						(last_ch!='\\'))
						{
						if ( ++code_c_quote == 2 )
							code_c_quote = 0 ;
						}
					break ;
				default :
					break ; /* nothing special */
				}
			*buf_p++ = this_ch ; buf_len++ ;
#ifdef	DEBUG	/* { */
			*buf_p = '\0' ;
			printf("\nDEBUG: %s\n",buffer);
			printf(full_before,
				cmt_comment,seperator,code_comment,
				seperator, (code)? 'C' : 'T',
				cmt_curly, seperator, code_curly,
				cmt_round, seperator, code_round,
				cmt_square, seperator, code_square
				) ;
			if (flag_angle) printf(full_angle,
				cmt_angle, seperator, code_angle) ;
			printf(full_after,
				cmt_d_quote, seperator, code_d_quote,
				cmt_o_quote, seperator, code_o_quote,
				cmt_c_quote, seperator, code_c_quote) ;
			printf("\nLINE:%ld  DOUBLE:%d  SINGLE:%d  CODE:%d \n",
				line, dbl_quoted, code_c_quote, code);
#endif		/* } */
			}
		/* allow for occurences such as '\\' and "hallo\\" */
		if ((last_ch == '\\') && (this_ch =='\\')) this_ch = '\0' ;
		last_ch = this_ch ;
		}
	if (flag_warn)
		{
		if (warn_comment) fprintf(stderr,txt_count_neg,
			txt_warning,txt_comment,
					"delimeter", warn_comment) ;
		if (warn_curly)	fprintf(stderr,txt_count_neg,
			txt_warning,txt_curly,
					txt_bracket, warn_curly) ;
		if (warn_round)	fprintf(stderr,txt_count_neg,
			txt_warning,txt_round,
					txt_bracket, warn_round) ;
		if (warn_square) fprintf(stderr,txt_count_neg,
			txt_warning,txt_square,
					txt_bracket, warn_square) ;
		if (warn_angle) fprintf(stderr,txt_count_neg,
			txt_warning,txt_angle,
					txt_bracket, warn_angle) ;
		if ( cmt_comment || cmt_curly || cmt_round || cmt_square 
				|| (flag_angle && cmt_angle) 
				|| cmt_d_quote || cmt_o_quote || cmt_c_quote )
			fprintf(stderr,summarise, txt_warning,
				(flag_angle ? summarise_inc_angle :
				summarise_exc_angle ) , "Text" ) ;
		if (err_comment) fprintf(stderr,txt_count_neg,
			 txt_erring,txt_comment,
					"delimeter", err_comment) ;
		if (err_curly)	fprintf(stderr,txt_count_neg,
			 txt_erring,txt_curly,
					txt_bracket, err_curly) ;
		if (err_round)	fprintf(stderr,txt_count_neg,
			 txt_erring,txt_round,
					txt_bracket, err_round) ;
		if (err_square) fprintf(stderr,txt_count_neg,
			 txt_erring,txt_square,
					txt_bracket, err_square) ;
		if (flag_angle && err_angle) fprintf(stderr,txt_count_neg,
			 txt_erring,txt_angle,
					txt_bracket, err_angle) ;
		if (code_comment || code_curly || code_round
			|| code_square || ( flag_angle && code_angle)
			|| code_d_quote || code_o_quote
			|| code_c_quote )
			fprintf(stderr,summarise, txt_erring,
				(flag_angle ? summarise_inc_angle :
                        	summarise_exc_angle ) ,
				"Code" ) ;
		}
	if (flag_verbose)
		{
		printf("%s\n\t1st count: %s\n\t2nd count: %s %s\n",
			"Count pairs represent end of adjacent lines:",
			"Whole file as arbitrary text, ignoring /* & */.",
			"Only C code areas of file,",
			"recognising /* & */."
			/* I havent bothered to check for C++ style \\ */
			) ;
		}
	(void) fflush(stdout) ;
	return( ( code_comment || code_curly || code_round || code_square ||
		( flag_angle && code_angle ) ||
		code_d_quote || code_o_quote || code_c_quote ) ? 1 : 0 ) ;
	}

	int
main(argc, argv)
	int	argc ;
	char	**argv ;
	{
	FILE	*fp ;
	char	*p ;
	char	*name ;

	ARGV = argv ;

#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */

	for (argc--, argv++ ; argc ; argv++, argc--)
		{
		if (**argv != '-') break ;
		p = *argv + 1 ;
		while(*p) switch(*p++)
			{
			case 't': /*turn off display for 'all file' area*/
				flag_text = 0 ;
				break ;
			case 'c': /*turn off display for 'code only' area*/
				flag_code = 0 ;
				break ;
			case 'w': /* turn off summary warnings */
				flag_warn = 0 ;
				break ;
			case 'p': /* turn off print of lines during analysis */
				flag_print = 0 ;
				break ;
			case 'v': /* turn on verbose chat at end */
				flag_verbose = 1 ;
				break ;
			case 'h': /* Enable checking of < & > in HTML,
					leave off for C programs */
				flag_angle = 0 ;
				break ;
			default:
				fprintf(stderr, "Unknown flag %c\n", *(p-1)) ;
				break ;
			}
		}
	if (argc == 0) exit(do_file(stdin)) ;
	else do {
		name = *argv++ ;
		fp = fopen(name, "r") ;
		if (fp == (FILE *)0)
			{
			fprintf(stderr, "Cannot open '%s'\n",name) ;
			exit_code |= 1 ;
			continue ;
			}
		exit_code |= do_file(fp) ;
		(void) fclose(fp) ;
		} while (--argc) ;
	exit(exit_code) ;
	}
