/**
 * Copyright (c) 1985 Sun Microsystems, Inc.
 * Copyright (c) 1980 The Regents of the University of California.
 * Copyright (c) 1976 Board of Trustees of the University of Illinois.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley, the University of Illinois,
 * Urbana, and Sun Microsystems, Inc.  The name of either University
 * or Sun Microsystems may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* ported to MINIX by: Robert R. Hall <hall@pnet01.cts.com>  */

#define _POSIX_SOURCE 1
#define	PUBLIC
#define NAME_SIZE _POSIX_NAME_MAX

#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "globs.h"
#include "codes.h"
#include "proto.h"

char           *in_name = "Standard Input";	/* will always point to
						   name of input file */
char           *out_name = "Standard Output";	/* will always point to
						   name of output file */
char            bakfile[60];

void main(argc, argv)
   int             argc;
   char          **argv;
{

   extern int      found_err;		/* flag set in diag() on error */
   int             dec_ind;		/* current indentation for
					   declarations */
   int             di_stack[20];	/* a stack of structure
					   indentation levels */
   int             flushed_nl;		/* used when buffering up
					   comments to remember that a
					   newline was passed over */
   int             force_nl;		/* when true, code must be
					   broken */
   int             hd_type;		/* used to store type of stmt
					   for if (...), for (...), etc */
   register int    i;			/* local loop counter */
   int             scase;		/* set to true when we see a
					   case, so we will know what
					   to do with the following
					   colon */
   int             sp_sw;		/* when true, we are in the
					   expressin of if(...),
					   while(...), etc. */
   int             squest;		/* when this is positive, we
					   have seen a ? without the
					   matching : in a <c>?<s>:<s>
					   construct */
   register char  *t_ptr;		/* used for copying tokens */
   int             type_code;		/* the type of token, returned
					   by lexi */

   int             last_else = 0;	/* true iff last keyword was an
					   else */


   /*-----------------------------------------------*\
    |		      INITIALIZATION		      |
    \*-----------------------------------------------*/


   ps.p_stack[0] = stmt;		/* this is the parser's stack */
   ps.last_nl = true;			/* this is true if the last
					   thing scanned was a newline */
   ps.last_token = semicolon;
   combuf = (char *) malloc(bufsize);
   labbuf = (char *) malloc(bufsize);
   codebuf = (char *) malloc(bufsize);
   l_com = combuf + bufsize - 5;
   l_lab = labbuf + bufsize - 5;
   l_code = codebuf + bufsize - 5;
   combuf[0] = codebuf[0] = labbuf[0] = ' ';	/* set up code, label,
						   and comment buffers */
   combuf[1] = codebuf[1] = labbuf[1] = '\0';
   ps.else_if = 1;			/* Default else-if special
					   processing to on */
   s_lab = e_lab = labbuf + 1;
   s_code = e_code = codebuf + 1;
   s_com = e_com = combuf + 1;

   buf_ptr = buf_end = in_buffer;
   line_no = 1;
   had_eof = ps.in_decl = ps.decl_on_line = break_comma = false;
   sp_sw = force_nl = false;
   ps.in_or_st = false;
   ps.bl_line = true;
   dec_ind = 0;
   di_stack[ps.dec_nest = 0] = 0;
   ps.want_blank = ps.in_stmt = ps.ind_stmt = false;


   scase = ps.pcase = false;
   squest = 0;
   sc_end = 0;
   bp_save = 0;
   be_save = 0;

   output = 0;



   /*--------------------------------------------------*\
    |   		COMMAND LINE SCAN		 |
    \*--------------------------------------------------*/

#ifdef undef
   max_col = 78;			/* -l78 */
   lineup_to_parens = 1;		/* -lp */
   ps.ljust_decl = 0;			/* -ndj */
   ps.com_ind = 33;			/* -c33 */
   star_comment_cont = 1;		/* -sc */
   ps.ind_size = 8;			/* -i8 */
   verbose = 0;
   ps.decl_indent = 16;			/* -di16 */
   ps.indent_parameters = 1;		/* -ip */
   ps.decl_com_ind = 0;			/* if this is not set to some
					   positive value by an arg, we
					   will set this equal to
					   ps.com_ind */
   btype_2 = 1;				/* -br */
   cuddle_else = 1;			/* -ce */
   ps.unindent_displace = 0;		/* -d0 */
   ps.case_indent = 0;			/* -cli0 */
   format_col1_comments = 1;		/* -fc1 */
   procnames_start_line = 1;		/* -psl */
   proc_calls_space = 0;		/* -npcs */
   comment_delimiter_on_blankline = 1;	/* -cdb */
   ps.leave_comma = 1;			/* -nbc */
#endif

   for (i = 1; i < argc; ++i)
      if (strcmp(argv[i], "-npro") == 0)
	 break;
   set_defaults();
   if (i >= argc)
      set_profile();

   for (i = 1; i < argc; ++i)
   {

      /* look thru args (if any) for changes to defaults */
      if (argv[i][0] != '-')
      {					/* no flag on parameter */
	 if (input == 0)
	 {				/* we must have the input file */
	    in_name = argv[i];		/* remember name of input file */
	    input = fopen(in_name, "r");
	    if (input == 0)
	    {				/* check for open error */
	       fprintf(stderr, "indent: can't open %s\n", argv[i]);
	       exit(1);
	    }
	    continue;
	 } else if (output == 0)
	 {				/* we have the output file */
	    out_name = argv[i];		/* remember name of output file */
	    if (strcmp(in_name, out_name) == 0)
	    {				/* attempt to overwrite the
					   file */
	       fprintf(stderr, "indent: input and output files must be different\n");
	       exit(1);
	    }
	    output = fopen(out_name, "w");
	    if (output == 0)
	    {				/* check for create error */
	       fprintf(stderr, "indent: can't create %s\n", argv[i]);
	       exit(1);
	    }
	    continue;
	 }
	 fprintf(stderr, "indent: unknown parameter: %s\n", argv[i]);
	 exit(1);
      } else
	 set_option(argv[i]);
   }					/* end of for */
   if (input == 0)
   {
      fprintf(stderr, "indent: usage: indent file [ outfile ] [ options ]\n");
      exit(1);
   }
   if (output == 0)
      if (troff)
	 output = stdout;
      else
      {
	 out_name = in_name;
	 bakcopy();
      }
   if (ps.com_ind <= 1)
      ps.com_ind = 2;			/* dont put normal comments
					   before column 2 */
   if (troff)
   {
      if (bodyf.font[0] == 0)
	 parsefont(&bodyf, "R");
      if (scomf.font[0] == 0)
	 parsefont(&scomf, "I");
      if (blkcomf.font[0] == 0)
	 blkcomf = scomf, blkcomf.size += 2;
      if (boxcomf.font[0] == 0)
	 boxcomf = blkcomf;
      if (stringf.font[0] == 0)
	 parsefont(&stringf, "L");
      if (keywordf.font[0] == 0)
	 parsefont(&keywordf, "B");
      writefdef(&bodyf, 'B');
      writefdef(&scomf, 'C');
      writefdef(&blkcomf, 'L');
      writefdef(&boxcomf, 'X');
      writefdef(&stringf, 'S');
      writefdef(&keywordf, 'K');
   }
   if (bk_max_col <= 0)
      bk_max_col = max_col;
   if (ps.decl_com_ind <= 0)		/* if not specified by user,
					   set this */
      ps.decl_com_ind = ps.ljust_decl ? (ps.com_ind <= 10 ? 2 : ps.com_ind - 8) : ps.com_ind;
   if (continuation_indent == 0)
      continuation_indent = ps.ind_size;
   fill_buffer();			/* get first batch of stuff
					   into input buffer */

   parse(semicolon);
   {
      register char  *p = buf_ptr;
      register        col = 1;

      while (1)
      {
	 if (*p == ' ')
	    col++;
	 else if (*p == '\t')
	    col = ((col - 1) & ~7) + 9;
	 else
	    break;
	 p++;
      };
      if (col > ps.ind_size)
	 ps.ind_level = ps.i_l_follow = col / ps.ind_size;
   }
   if (troff)
   {
      register char  *p = in_name, *beg = in_name;

      while (*p)
	 if (*p++ == '/')
	    beg = p;
      fprintf(output, ".Fn \"%s\"\n", beg);
   }
   /* START OF MAIN LOOP */

   while (1)
   {					/* this is the main loop.  it
					   will go until we reach eof */
      int             is_procname;

      type_code = lexi();		/* lexi reads one token.  The
					   actual characters read are
					   stored in "token". lexi
					   returns a code indicating
					   the type of token */
      is_procname = ps.procname[0];

      /* The following code moves everything following an if (), while
         (), else, etc. up to the start of the following stmt to a
         buffer. This allows proper handling of both kinds of brace
         placement. */

      flushed_nl = false;
      while (ps.search_brace)
      {					/* if we scanned an if(),
					   while(), etc., we might need
					   to copy stuff into a buffer
					   we must loop, copying stuff
					   into save_com, until we find
					   the start of the stmt which
					   follows the if, or whatever */
	 switch (type_code)
	 {
	 case newline:
	    ++line_no;
	    flushed_nl = true;
	 case form_feed:
	    break;			/* form feeds and newlines
					   found here will be ignored */

	 case lbrace:			/* this is a brace that starts
					   the compound stmt */
	    if (sc_end == 0)
	    {				/* ignore buffering if a
					   comment wasnt stored up */
	       ps.search_brace = false;
	       goto check_type;
	    }
	    if (btype_2)
	    {
	       save_com[0] = '{';	/* we either want to put the
					   brace right after the if */
	       goto sw_buffer;		/* go to common code to get out
					   of this loop */
	    }
/*
 * Something is buffered up in save_com, and -bl processing is in effect.
 * Add the brace after the comment so it will come out on the next line.
 */
	    flushed_nl = 0;	/* comment can start on the same line */
	    *sc_end++ = '\n';	/* break line after comment */
	    *sc_end++ = '{';
	    goto sw_buffer;

	 case comment:			/* we have a comment, so we
					   must copy it into the buffer */
	    if (!flushed_nl || sc_end != 0)
	    {
	       if (sc_end == 0)
	       {			/* if this is the first
					   comment, we must set up the
					   buffer */
		  save_com[0] = save_com[1] = ' ';
		  sc_end = &(save_com[2]);
	       } else
	       {
		  *sc_end++ = '\n';	/* add newline between comments */
		  *sc_end++ = ' ';
		  --line_no;
	       }
	       *sc_end++ = '/';		/* copy in start of comment */
	       *sc_end++ = '*';

	       for (;;)
	       {			/* loop until we get to the end
					   of the comment */
		  *sc_end = *buf_ptr++;
		  if (buf_ptr >= buf_end)
		     fill_buffer();

		  if (*sc_end++ == '*' && *buf_ptr == '/')
		     break;		/* we are at end of comment */

		  if (sc_end >= &(save_com[sc_size]))
		  {			/* check for temp buffer
					   overflow */
		     diag(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever.");
		     fflush(output);
		     exit(1);
		  }
	       }
	       *sc_end++ = '/';		/* add ending slash */
	       if (++buf_ptr >= buf_end)/* get past / in buffer */
		  fill_buffer();
	       break;
	    }
	 default:			/* it is the start of a normal
					   statment */
	    if (flushed_nl)		/* if we flushed a newline,
					   make sure it is put back */
	       force_nl = true;
	    if ((type_code == sp_paren && *token == 'i'
		    && last_else && ps.else_if)
		|| (type_code == sp_nparen && *token == 'e'
		    && e_code != s_code && e_code[-1] == '}'))
	       force_nl = false;

	    if (sc_end == 0)
	    {				/* ignore buffering if comment
					   wasnt saved up */
	       ps.search_brace = false;
	       goto check_type;
	    }
	    if (force_nl)
	    {				/* if we should insert a nl
					   here, put it into the buffer */
	       force_nl = false;
	       --line_no;		/* this will be re-increased
					   when the nl is read from the
					   buffer */
	       *sc_end++ = '\n';
	       *sc_end++ = ' ';
	       if (verbose && !flushed_nl)	/* print error msg if
						   the line was not
						   already broken */
		  diag(0, "Line broken");
	       flushed_nl = false;
	    }
	    for (t_ptr = token; *t_ptr; ++t_ptr)
	       *sc_end++ = *t_ptr;	/* copy token into temp buffer */
	    ps.procname[0] = 0;

      sw_buffer:
	    ps.search_brace = false;	/* stop looking for start of
					   stmt */
	    bp_save = buf_ptr;		/* save current input buffer */
	    be_save = buf_end;
	    buf_ptr = save_com;		/* fix so that subsequent calls
					   to lexi will take tokens out
					   of save_com */
	    *sc_end++ = ' ';		/* add trailing blank, just in
					   case */
	    buf_end = sc_end;
	    sc_end = 0;
	    break;
	 }				/* end of switch */
	 if (type_code != 0)		/* we must make this check,
					   just in case there was an
					   unexpected EOF */
	    type_code = lexi();		/* read another token */
	 /* if (ps.search_brace) ps.procname[0] = 0; */
	 if ((is_procname = ps.procname[0]) && flushed_nl
	     && !proc_str_line && ps.in_decl
	     && type_code == ident)
	    flushed_nl = 0;
      }					/* end of while (search_brace) */
      last_else = 0;
check_type:
      if (type_code == 0)
      {					/* we got eof */
	 if (s_lab != e_lab || s_code != e_code
	     || s_com != e_com)		/* must dump end of line */
	    dump_line();
	 if (ps.tos > 1)		/* check for balanced braces */
	    diag(1, "Stuff missing from end of file.");

	 if (verbose)
	 {
	    printf("There were %d output lines and %d comments\n",
		   ps.out_lines, ps.out_coms);
	    printf("(Lines with comments)/(Lines with code): %6d\n",
		   ps.com_lines / code_lines);
	 }
	 fflush(output);
	 exit(found_err);
      }
      if (
	  (type_code != comment) &&
	  (type_code != newline) &&
	  (type_code != preesc) &&
	  (type_code != form_feed))
      {
	 if (force_nl &&
	     (type_code != semicolon) &&
	     (type_code != lbrace || !btype_2))
	 {
	    /* we should force a broken line here */
	    if (verbose && !flushed_nl)
	       diag(0, "Line broken");
	    flushed_nl = false;
	    dump_line();
	    ps.want_blank = false;	/* dont insert blank at line
					   start */
	    force_nl = false;
	 }
	 ps.in_stmt = true;		/* turn on flag which causes an
					   extra level of indentation.
					   this is turned off by a ; or
					   '}' */
	 if (s_com != e_com)
	 {				/* the turkey has embedded a
					   comment in a line. fix it */
	    *e_code++ = ' ';
	    for (t_ptr = s_com; *t_ptr; ++t_ptr)
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *e_code++ = *t_ptr;
	    }
	    *e_code++ = ' ';
	    *e_code = '\0';		/* null terminate code sect */
	    ps.want_blank = false;
	    e_com = s_com;
	 }
      } else if (type_code != comment)	/* preserve force_nl thru a
					   comment */
	 force_nl = false;		/* cancel forced newline after
					   newline, form feed, etc */



      /*-----------------------------------------------------*\
        |	   do switch on type of token scanned		|
        \*-----------------------------------------------------*/
      if (e_code >= l_code)
      {
	 register        nsize = l_code - s_code + 400;
	 codebuf = (char *) realloc(codebuf, nsize);
	 e_code = codebuf + (e_code - s_code) + 1;
	 l_code = codebuf + nsize - 5;
	 s_code = codebuf + 1;
      }
      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 || ps.block_init || !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 || Bill_Shannon))))
	    *e_code++ = ' ';
	 if (ps.in_decl && !ps.block_init)
	    if (troff && !ps.dumped_decl_indent && !is_procname && ps.last_token == decl)
	    {
	       ps.dumped_decl_indent = 1;
	       sprintf(e_code, "\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
	       e_code += strlen(e_code);
	    } else
	    {
	       while ((e_code - s_code) < dec_ind)
	       {
		  if (e_code >= l_code)
		  {
		     register        nsize = l_code - s_code + 400;
		     codebuf = (char *) realloc(codebuf, nsize);
		     e_code = codebuf + (e_code - s_code) + 1;
		     l_code = codebuf + nsize - 5;
		     s_code = codebuf + 1;
		  }
		  *e_code++ = ' ';
	       }
	       *e_code++ = token[0];
	    }
	 else
	    *e_code++ = token[0];
	 ps.paren_indents[ps.p_l_follow - 1] = e_code - s_code;
	 if (sp_sw && ps.p_l_follow == 1 && ex_expr_indent
	     && ps.paren_indents[0] < 2 * ps.ind_size)
	    ps.paren_indents[0] = 2 * ps.ind_size;
	 ps.want_blank = false;
	 if (ps.in_or_st && *token == '(' && ps.tos <= 2)
	 {
	    /* 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 && !is_procname)
	 {
	    sprintf(e_code, "\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))
	       {
		  if (e_code >= l_code)
		  {
		     register        nsize = l_code - s_code + 400;
		     codebuf = (char *) realloc(codebuf, nsize);
		     e_code = codebuf + (e_code - s_code) + 1;
		     l_code = codebuf + nsize - 5;
		     s_code = codebuf + 1;
		  }
		  *e_code++ = ' ';	/* pad it */
	       }
	    }
	    if (troff && token[0] == '-' && token[1] == '>')
	       res = "\\(->";
	    for (t_ptr = res; *t_ptr; ++t_ptr)
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *e_code++ = *t_ptr;
	    }
	 }
	 ps.want_blank = false;
	 break;

      case binary_op:			/* any binary operation */
	 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;
	       }
	    for (t_ptr = res; *t_ptr; ++t_ptr)
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *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_par_decl = 0;
	 ps.cast_mask = 0;
	 ps.sizeof_mask = 0;
	 ps.block_init = 0;
	 ps.block_init_level = 0;
	 ps.just_saw_decl--;

	 if (ps.in_decl && s_code == e_code && !ps.block_init)
	    while ((e_code - s_code) < (dec_ind - 1))
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 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 */
	 else if (ps.block_init_level <= 0)
	    ps.block_init_level = 1;
	 else
	    ps.block_init_level++;

	 if (s_code != e_code && !ps.block_init)
	 {
	    if (!btype_2)
	    {
	       dump_line();
	       ps.want_blank = false;
	    } else if (ps.in_par_decl && !ps.in_or_st)
	    {
	       ps.i_l_follow = 0;
	       dump_line();
	       ps.want_blank = false;
	    }
	 }
	 if (ps.in_par_decl)
	    prefix_blankline_requested = 0;

	 if (ps.p_l_follow > 0)
	 {				/* check for preceeding
					   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 */
	    if (bl_at_proctop
		&& ps.in_par_decl)
	       postfix_blankline_requested = 1;
	    ps.in_par_decl = 0;
	 }
	 dec_ind = 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_stack[ps.tos] == decl && !ps.block_init)	/* semicolons can be
								   omitted in
								   declarations */
	    parse(semicolon);
	 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;
	 ps.block_init_level--;
	 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_par_decl)
	       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 && bl_a_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_par_decl = 1;
	    if (s_code != e_code)
	    {
	       dump_line();
	       ps.want_blank = 0;
	    }
	 }
	 if (ps.in_par_decl && 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 || !proc_str_line)
	    {
	       if (!ps.block_init)
		  if (troff && !ps.dumped_decl_indent)
		  {
		     sprintf(e_code, "\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)
		     {
			if (e_code >= l_code)
			{
			   register        nsize = l_code - s_code + 400;
			   codebuf = (char *) realloc(codebuf, nsize);
			   e_code = codebuf + (e_code - s_code) + 1;
			   l_code = codebuf + nsize - 5;
			   s_code = codebuf + 1;
			}
			*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 = chfont(&bodyf, &keywordf, e_code);
	    for (t_ptr = token; *t_ptr; ++t_ptr)
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *e_code++ = keywordf.allcaps && islower(*t_ptr)
		  ? toupper(*t_ptr) : *t_ptr;
	    }
	    e_code = chfont(&keywordf, &bodyf, e_code);
	 } else
	    for (t_ptr = token; *t_ptr; ++t_ptr)
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *e_code++ = *t_ptr;
	    }
	 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))
	    {
	       if (e_code >= l_code)
	       {
		  register        nsize = l_code - s_code + 400;
		  codebuf = (char *) realloc(codebuf, nsize);
		  e_code = codebuf + (e_code - s_code) + 1;
		  l_code = codebuf + nsize - 5;
		  s_code = codebuf + 1;
	       }
	       *e_code++ = ' ';
	    }

	 *e_code++ = ',';
	 if (ps.p_l_follow == 0)
	 {
	    if (ps.block_init_level <= 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 */
	 {
	    int             in_comment = 0;
	    int             com_start = 0;
	    char            quote = 0;
	    int             com_end = 0;

	    while (*buf_ptr != '\n' || in_comment)
	    {
	       if (e_lab >= l_lab)
	       {
		  register        nsize = l_lab - s_lab + 400;
		  labbuf = (char *) realloc(labbuf, nsize);
		  e_lab = labbuf + (e_lab - s_lab) + 1;
		  l_lab = labbuf + nsize - 5;
		  s_lab = labbuf + 1;
	       }
	       *e_lab = *buf_ptr++;
	       if (buf_ptr >= buf_end)
		  fill_buffer();
	       switch (*e_lab++)
	       {
	       case BACKSLASH:
		  if (troff)
		     *e_lab++ = BACKSLASH;
		  if (!in_comment)
		  {
		     *e_lab++ = *buf_ptr++;
		     if (buf_ptr >= buf_end)
			fill_buffer();
		  }
		  break;
	       case '/':
		  if (*buf_ptr == '*' && !in_comment && !quote)
		  {
		     in_comment = 1;
		     *e_lab++ = *buf_ptr++;
		     com_start = e_lab - s_lab - 2;
		  }
		  break;
	       case '"':
		  if (quote == '"')
		     quote = 0;
		  break;
	       case '\'':
		  if (quote == '\'')
		     quote = 0;
		  break;
	       case '*':
		  if (*buf_ptr == '/' && in_comment)
		  {
		     in_comment = 0;
		     *e_lab++ = *buf_ptr++;
		     com_end = e_lab - s_lab;
		  }
		  break;
	       }
	    }

	    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
	       e_lab--;
	    if (e_lab - s_lab == com_end && bp_save == 0)
	    {				/* comment on preprocessor line */
	       if (sc_end == 0)		/* if this is the first
					   comment, we must set up the
					   buffer */
		  sc_end = &(save_com[0]);
	       else
	       {
		  *sc_end++ = '\n';	/* add newline between comments */
		  *sc_end++ = ' ';
		  --line_no;
	       }
	       memcpy(sc_end, s_lab + com_start, com_end - com_start);
	       sc_end += com_end - com_start;
	       if (sc_end >= &save_com[sc_size])
		  abort();
	       e_lab = s_lab + com_start;
	       while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
		  e_lab--;
	       bp_save = buf_ptr;	/* save current input buffer */
	       be_save = buf_end;
	       buf_ptr = save_com;	/* fix so that subsequent calls
					   to lexi will take tokens out
					   of save_com */
	       *sc_end++ = ' ';		/* add trailing blank, just in
					   case */
	       buf_end = sc_end;
	       sc_end = 0;
	    }
	    *e_lab = '\0';		/* null terminate line */
	    ps.pcase = false;
	 }

	 if (strncmp(s_lab, "#if", 3) == 0)
	 {
	    if (bl_around)
	    {
	       register        c;
	       prefix_blankline_requested++;
	       while ((c = getc(input)) == '\n');
	       ungetc(c, input);
	    }
	    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
	    }
	    if (bl_around)
	    {
	       postfix_blankline_requested++;
	       n_real_blanklines = 0;
	    }
	 }
	 break;				/* subsequent processing of the
					   newline character will cause
					   the line to be printed */

      case comment:			/* we have gotten a / *  this is
					   a biggie */
	 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 */

      *e_code = '\0';			/* make sure code section is
					   null terminated */
      if (type_code != comment && type_code != newline && type_code != preesc)
	 ps.last_token = type_code;
   }					/* end of main while (1) loop */
}

/*
 * copy input file to backup file if in_name is /blah/blah/blah/file, then
 * backup file will be ".Bfile" then make the backup file the input and
 * original input file the output
 */
void bakcopy()
{
   int             n, bakchn;
   char            buff[8 * 1024];
   register char  *p;

   /* construct file name .Bfile */
   for (p = in_name; *p; p++);		/* skip to end of string */
   while (p > in_name && *p != '/')	/* find last '/' */
      p--;
   if (*p == '/')
      p++;
   sprintf(bakfile, "%s.BAK", p);
   if (strlen(p) >= NAME_SIZE) *bakfile ^= 040;	/* toggle char */

   /* copy in_name to backup file */
   bakchn = creat(bakfile, 0600);
   if (bakchn < 0)
   {
      fprintf(stderr, "indent: can't create backup file \"%s\"\n", bakfile);
      exit(1);
   }
   while (n = read(fileno(input), buff, sizeof buff))
      if (write(bakchn, buff, n) != n)
      {
	 fprintf(stderr, "indent: error writing backup file \"%s\"\n",
		 bakfile);
	 exit(1);
      }
   if (n < 0)
   {
      fprintf(stderr, "indent: error reading input file \"%s\"\n", in_name);
      exit(1);
   }
   close(bakchn);
   fclose(input);

   /* re-open backup file as the input file */
   input = fopen(bakfile, "r");
   if (input == 0)
   {
      fprintf(stderr, "indent: can't re-open backup file\n");
      exit(1);
   }
   /* now the original input file will be the output */
   output = fopen(in_name, "w");
   if (output == 0)
   {
      fprintf(stderr, "indent: can't create %s\n", in_name);
      unlink(bakfile);
      exit(1);
   }
}
