#include "indent_c.h"
#include "indent_g.h"

extern int dec_ind;		/* current indentation for declarations */
extern int di_stack[20];	/* a stack of structure indentation levels */
extern int flushed_nl;		/* used when buffering up comments to
				 * remember that a newline was passed over */
extern int force_nl;		/* when true, code must be broken */
extern int hd_type;		/* used to store type of stmt for if (...),
				 * for (...), etc */
extern int scase;		/* set to true when we see a case, so we will
				 * know what to do with the following colon */
extern int sp_sw;		/* when true, we are in the expressin of
				 * if(...), while(...), etc. */
extern int squest;		/* when this is positive, we have seen a ?
				 * without the matching : in a <c>?<s>:<s>
				 * construct */
extern int type_code;		/* the type of token, returned by lexi */

extern int last_else;		/* true iff last keyword was an else */

extern int is_procname;


switch_on(type_code)
	int type_code;
{
	register int i;
	register char *t_ptr;	/* used for copying tokens */


	/*----------------------------------------------------*\
        |   do switch on type of token scanned
        \*----------------------------------------------------*/
	switch (type_code) {	/* now, decide what to do with the token */

	case form_feed:	/* found a form feed in line */
		ps.use_ff = true;	/* a form feed is treated much like a
					 * newline */
		dump_line();
		ps.want_blank = false;
		break;

	case newline:
		if (ps.last_token != comma || ps.p_l_follow > 0
		    || !ps.leave_comma || !break_comma || s_com != e_com) {
			dump_line();
			ps.want_blank = false;
		}
		++line_no;	/* keep track of input line number */
		break;

	case lparen:		/* got a '(' or '[' */
		++ps.p_l_follow;/* count parens to make Healy happy */
		if (ps.want_blank && *token != '[' &&
		    (ps.last_token != ident || proc_calls_space
		     || (ps.its_a_keyword && !ps.sizeof_keyword)))
			*e_code++ = ' ';
		if (ps.in_decl && !ps.block_init)
			if (troff && !ps.dumped_decl_indent) {
				ps.dumped_decl_indent = 1;
				sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
				e_code += strlen(e_code);
			}
			else {
				while ((e_code - s_code) < dec_ind)
					*e_code++ = ' ';
				*e_code++ = token[0];
			}
		else
			*e_code++ = token[0];
		ps.paren_indents[ps.p_l_follow - 1] = e_code - s_code;
		ps.want_blank = false;
		if (ps.in_or_st && *token == '(') {

			/*
			 * this is a kluge to make sure that declarations
			 * will be aligned right if proc decl has an explicit
			 * type on it, i.e. "int a(x) {..." 
			 */
			parse(semicolon);	/* I said this was a kluge... */
			ps.in_or_st = false;	/* turn off flag for
						 * structure decl or
						 * initialization */
		}
		if (ps.sizeof_keyword)
			ps.sizeof_mask |= 1 << ps.p_l_follow;
		break;

	case rparen:		/* got a ')' or ']' */
		if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.sizeof_mask) {
			ps.last_u_d = true;
			ps.cast_mask &= (1 << ps.p_l_follow) - 1;
		}
		ps.sizeof_mask &= (1 << ps.p_l_follow) - 1;
		if (--ps.p_l_follow < 0) {
			ps.p_l_follow = 0;
			diag(0, "Extra %c", *token);
		}
		if (e_code == s_code)	/* if the paren starts the line */
			ps.paren_level = ps.p_l_follow;	/* then indent it */

		*e_code++ = token[0];
		ps.want_blank = true;

		if (sp_sw && (ps.p_l_follow == 0)) {	/* check for end of if
							 * (...), or some such */
			sp_sw = false;
			force_nl = true;	/* must force newline after
						 * if */
			ps.last_u_d = true;	/* inform lexi that a
						 * following operator is
						 * unary */
			ps.in_stmt = false;	/* dont use stmt continuation
						 * indentation */

			parse(hd_type);	/* let parser worry about if, or
					 * whatever */
		}
		ps.search_brace = btype_2;	/* this should insure that
						 * constructs such as
						 * main(){...} and int[]{...}
						 * have their braces put in
						 * the right place */
		break;

	case unary_op:		/* this could be any unary operation */
		if (ps.want_blank)
			*e_code++ = ' ';

		if (troff && !ps.dumped_decl_indent && ps.in_decl) {
			sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
			ps.dumped_decl_indent = 1;
			e_code += strlen(e_code);
		}
		else {
			char *res = token;

			if (ps.in_decl && !ps.block_init) {	/* if this is a unary op
								 * in a declaration, we
								 * should indent this
								 * token */
				for (i = 0; token[i]; ++i);	/* find length of token */
				while ((e_code - s_code) < (dec_ind - i))
					*e_code++ = ' ';	/* pad it */
			}
			if (troff && token[0] == '-' && token[1] == '>')
				res = "\\(->";
			for (t_ptr = res; *t_ptr; ++t_ptr)
				*e_code++ = *t_ptr;
		}
		ps.want_blank = false;
		break;

	case binary_op:	/* any binary operation */
do_binary:
		if (ps.want_blank)
			*e_code++ = ' ';
		{
			char *res = token;

			if (troff)
				switch (token[0]) {
				case '<':
					if (token[1] == '=')
						res = "\\(<=";
					break;
				case '>':
					if (token[1] == '=')
						res = "\\(>=";
					break;
				case '!':
					if (token[1] == '=')
						res = "\\(!=";
					break;
				case '|':
					if (token[1] == '|')
						res = "\\(br\\(br";
					else if (token[1] == 0)
						res = "\\(br";
					break;
				case '-':
					if (token[1] == '>')
						res = "\\(->";
				}
			for (t_ptr = res; *t_ptr; ++t_ptr)
				*e_code++ = *t_ptr;	/* move the operator */
		}
		ps.want_blank = true;
		break;

	case postop:		/* got a trailing ++ or -- */
		*e_code++ = token[0];
		*e_code++ = token[1];
		ps.want_blank = true;
		break;

	case question:		/* got a ? */
		squest++;	/* this will be used when a later colon
				 * appears so we can distinguish the
				 * <c>?<n>:<n> construct */
		if (ps.want_blank)
			*e_code++ = ' ';
		*e_code++ = '?';
		ps.want_blank = true;
		break;

	case casestmt:		/* got word 'case' or 'default' */
		scase = true;	/* so we can process the later colon properly */
		goto copy_id;

	case colon:		/* got a ':' */
		if (squest > 0) {	/* it is part of the <c>?<n>: <n>
					 * construct */
			--squest;
			if (ps.want_blank)
				*e_code++ = ' ';
			*e_code++ = ':';
			ps.want_blank = true;
			break;
		}
		if (ps.in_decl) {
			*e_code++ = ':';
			ps.want_blank = false;
			break;
		}
		ps.in_stmt = false;	/* seeing a label does not imply we
					 * are in a stmt */
		for (t_ptr = s_code; *t_ptr; ++t_ptr)
			*e_lab++ = *t_ptr;	/* turn everything so far
						 * into a label */
		e_code = s_code;
		*e_lab++ = ':';
		*e_lab++ = ' ';
		*e_lab = '\0';

		force_nl = ps.pcase = scase;	/* ps.pcase will be used by
						 * dump_line to decide how to
						 * indent the label. force_nl
						 * will force a case n: to be
						 * on a line by itself */
		scase = false;
		ps.want_blank = false;
		break;

	case semicolon:	/* got a ';' */
		ps.in_or_st = false;	/* we are not in an initialization or
					 * structure declaration */
		scase = false;	/* these will only need resetting in a error */
		squest = 0;
		if (ps.last_token == rparen)
			ps.in_parameter_declaration = 0;
		ps.cast_mask = 0;
		ps.sizeof_mask = 0;
		ps.block_init = 0;
		ps.just_saw_decl--;

		if (ps.in_decl && s_code == e_code && !ps.block_init)
			while ((e_code - s_code) < (dec_ind - 1))
				*e_code++ = ' ';

		ps.in_decl = (ps.dec_nest > 0);	/* if we were in a first
						 * level structure
						 * declaration, we arent any
						 * more */

		if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) {

			/*
			 * This should be true iff there were unbalanced
			 * parens in the stmt.  It is a bit complicated,
			 * because the semicolon might be in a for stmt 
			 */
			diag(1, "Unbalanced parens");
			ps.p_l_follow = 0;
			if (sp_sw) {	/* this is a check for a if, while,
					 * etc. with unbalanced parens */
				sp_sw = false;
				parse(hd_type);	/* dont lose the if, or
						 * whatever */
			}
		}
		*e_code++ = ';';
		ps.want_blank = true;
		ps.in_stmt = (ps.p_l_follow > 0);	/* we are no longer in
							 * the middle of a stmt */

		if (!sp_sw) {	/* if not if for (;;) */
			parse(semicolon);	/* let parser know about end
						 * of stmt */
			force_nl = true;	/* force newline after a end
						 * of stmt */
		}
		break;

	case lbrace:		/* got a '{' */
		ps.in_stmt = false;	/* dont indent the {} */
		if (!ps.block_init)
			force_nl = true;	/* force other stuff on same
						 * line as '{' onto new line */

		if (s_code != e_code && !ps.block_init) {
			if (!btype_2) {
				dump_line();
				ps.want_blank = false;
			}
			else if (ps.in_parameter_declaration && !ps.in_or_st) {
				ps.i_l_follow = 0;
				dump_line();
				ps.want_blank = false;
			}
		}
		if (ps.in_parameter_declaration)
			prefix_blankline_requested = 0;

		if (ps.p_l_follow > 0) {	/* check for preceding
						 * unbalanced parens */
			diag(1, "Unbalanced parens");
			ps.p_l_follow = 0;
			if (sp_sw) {	/* check for unclosed if, for, etc. */
				sp_sw = false;
				parse(hd_type);
				ps.ind_level = ps.i_l_follow;
			}
		}
		if (s_code == e_code)
			ps.ind_stmt = false;	/* dont put extra indentation
						 * on line with '{' */
		if (ps.in_decl && ps.in_or_st) {	/* this is either a
							 * structure declaration
							 * or an init */
			di_stack[ps.dec_nest++] = dec_ind;
			dec_ind = 0;
		}
		else {
			ps.decl_on_line = false;	/* we cant be in the
							 * middle of a
							 * declaration, so dont
							 * do special
							 * indentation of
							 * comments */
			ps.in_parameter_declaration = 0;
		}
		parse(lbrace);	/* let parser know about this */
		if (ps.want_blank)	/* put a blank before '{' if '{' is
					 * not at start of line */
			*e_code++ = ' ';
		ps.want_blank = false;
		*e_code++ = '{';
		ps.just_saw_decl = 0;
		break;

	case rbrace:		/* got a '}' */
		if (ps.p_l_follow) {	/* check for unclosed if, for, else. */
			diag(1, "Unbalanced parens");
			ps.p_l_follow = 0;
			sp_sw = false;
		}
		ps.just_saw_decl = 0;
		if (s_code != e_code && !ps.block_init) {	/* '}' must be first on
								 * line */
			if (verbose)
				diag(0, "Line broken");
			dump_line();
		}
		*e_code++ = '}';
		ps.want_blank = true;
		ps.in_stmt = ps.ind_stmt = false;
		if (ps.dec_nest > 0) {	/* we are in multi-level structure
					 * declaration */
			dec_ind = di_stack[--ps.dec_nest];
			if (ps.dec_nest == 0 && !ps.in_parameter_declaration)
				ps.just_saw_decl = 2;
			ps.in_decl = true;
		}
		prefix_blankline_requested = 0;
		parse(rbrace);	/* let parser know about this */
		ps.search_brace = cuddle_else && ps.p_stack[ps.tos] == ifhead && ps.il[ps.tos] >= ps.ind_level;
		if (ps.tos <= 1 && blanklines_after_procs && ps.dec_nest <= 0)
			postfix_blankline_requested = 1;
		break;

	case swstmt:		/* got keyword "switch" */
		sp_sw = true;
		hd_type = swstmt;	/* keep this for when we have seen
					 * the expression */
		goto copy_id;	/* go move the token into buffer */

	case sp_paren:		/* token is if, while, for */
		sp_sw = true;	/* the interesting stuff is done after the
				 * expression is scanned */
		hd_type = (*token == 'i' ? ifstmt :
			   (*token == 'w' ? whilestmt : forstmt));

		/*
		 * remember the type of header for later use by parser 
		 */
		goto copy_id;	/* copy the token into line */

	case sp_nparen:	/* got else, do */
		ps.in_stmt = false;
		if (*token == 'e') {
			if (e_code != s_code && (!cuddle_else || e_code[-1] != '}')) {
				if (verbose)
					diag(0, "Line broken");
				dump_line();	/* make sure this starts a
						 * line */
				ps.want_blank = false;
			}
			force_nl = true;	/* also, following stuff must
						 * go onto new line */
			last_else = 1;
			parse(elselit);
		}
		else {
			if (e_code != s_code) {	/* make sure this starts a
						 * line */
				if (verbose)
					diag(0, "Line broken");
				dump_line();
				ps.want_blank = false;
			}
			force_nl = true;	/* also, following stuff must
						 * go onto new line */
			last_else = 0;
			parse(dolit);
		}
		goto copy_id;	/* move the token into line */

	case decl:		/* we have a declaration type (int, register,
				 * etc.) */
		parse(decl);	/* let parser worry about indentation */
		if (ps.last_token == rparen && ps.tos <= 1)
			ps.in_parameter_declaration = 1;
		if (ps.in_parameter_declaration && ps.indent_parameters && ps.dec_nest == 0) {
			ps.ind_level = ps.i_l_follow = 1;
			ps.ind_stmt = 0;
		}
		ps.in_or_st = true;	/* this might be a structure or
					 * initialization declaration */
		ps.in_decl = ps.decl_on_line = true;
		if ( /* !ps.in_or_st && */ ps.dec_nest <= 0)
			ps.just_saw_decl = 2;
		prefix_blankline_requested = 0;
		for (i = 0; token[i++];);	/* get length of token */

		/*
		 * dec_ind = e_code - s_code + (ps.decl_indent>i ?
		 * ps.decl_indent : i); 
		 */
		dec_ind = ps.decl_indent > 0 ? ps.decl_indent : i;
		goto copy_id;

	case ident:		/* got an identifier or constant */
		if (ps.in_decl) {	/* if we are in a declaration, we
					 * must indent identifier */
			if (ps.want_blank)
				*e_code++ = ' ';
			ps.want_blank = false;
			if (is_procname == 0 || !procnames_start_line) {
				if (!ps.block_init)
					if (troff && !ps.dumped_decl_indent) {
						sprintf(e_code, "\\c\n.De %dp+\200p\n", dec_ind * 7);
						ps.dumped_decl_indent = 1;
						e_code += strlen(e_code);
					}
					else
						while ((e_code - s_code) < dec_ind)
							*e_code++ = ' ';
			}
			else {
				if (dec_ind && s_code != e_code)
					dump_line();
				dec_ind = 0;
				ps.want_blank = false;
			}
		}
		else if (sp_sw && ps.p_l_follow == 0) {
			sp_sw = false;
			force_nl = true;
			ps.last_u_d = true;
			ps.in_stmt = false;
			parse(hd_type);
		}
copy_id:
		if (ps.want_blank)
			*e_code++ = ' ';
		if (troff && ps.its_a_keyword) {
			*e_code++ = BACKSLASH;
			*e_code++ = 'f';
			*e_code++ = 'B';
		}
		for (t_ptr = token; *t_ptr; ++t_ptr)
			*e_code++ = *t_ptr;
		if (troff && ps.its_a_keyword) {
			*e_code++ = BACKSLASH;
			*e_code++ = 'f';
			*e_code++ = 'R';
		}
		ps.want_blank = true;
		break;

	case period:		/* treat a period kind of like a binary
				 * operation */
		*e_code++ = '.';/* move the period into line */
		ps.want_blank = false;	/* dont put a blank after a period */
		break;

	case comma:
		ps.want_blank = (s_code != e_code);	/* only put blank after
							 * comma if comma does
							 * not start the line */
		if (ps.in_decl && is_procname == 0 && !ps.block_init)
			while ((e_code - s_code) < (dec_ind - 1))
				*e_code++ = ' ';

		*e_code++ = ',';
		if (ps.p_l_follow == 0) {
			ps.block_init = 0;
			if (break_comma && !ps.leave_comma)
				force_nl = true;
		}
		break;

	case preesc:		/* got the character '#' */
		if ((s_com != e_com) ||
		    (s_lab != e_lab) ||
		    (s_code != e_code))
			dump_line();
		*e_lab++ = '#';	/* move whole line to 'label' buffer */


		/*
		 * a large section of code was removed here to break these
		 * files up into msc sized chunks 
		 */
		dopreesc();


		if (strncmp(s_lab, "#if", 3) == 0)
			if (ifdef_level < sizeof state_stack / sizeof state_stack[0]) {
				match_state[ifdef_level].tos = -1;
				state_stack[ifdef_level++] = ps;
			}
			else
				diag(1, "#if stack overflow");
		else if (strncmp(s_lab, "#else", 5) == 0)
			if (ifdef_level <= 0)
				diag(1, "Unmatched #else");
			else {
				match_state[ifdef_level - 1] = ps;
				ps = state_stack[ifdef_level - 1];
			}
		else if (strncmp(s_lab, "#endif", 6) == 0)
			if (ifdef_level <= 0)
				diag(1, "Unmatched #endif");
			else {
				ifdef_level--;
#ifdef undef

				/*
				 * This match needs to be more intelligent
				 * before the message is useful 
				 */
				if (match_state[ifdef_level].tos >= 0
				    && bcmp(&ps, &match_state[ifdef_level], sizeof ps))
					diag(0, "Syntactically inconsistant #ifdef alternatives.");
#endif
			}
		break;		/* subsequent processing of the newline
				 * character will cause the line to be
				 * printed */

	case comment:		/* we have gotten a /*  this is a biggie */
proc_comment:
		if (flushed_nl) {	/* we should force a broken line here */
			flushed_nl = false;
			dump_line();
			ps.want_blank = false;	/* dont insert blank at line
						 * start */
			force_nl = false;
		}
		pr_comment();
		break;
	}			/* end of big switch stmt */
}
