xref: /original-bsd/usr.bin/indent/indent.c (revision 7211505a)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * Copyright (c) 1976 Board of Trustees of the University of Illinois.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by the University of California, Berkeley and the University
12  * of Illinois, Urbana.  The name of either
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 
20 #ifndef lint
21 char copyright[] =
22 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
23 Copyright (c) 1976 Board of Trustees of the University of Illinois.\n\
24  All rights reserved.\n";
25 #endif /* not lint */
26 
27 #ifndef lint
28 static char sccsid[] = "@(#)indent.c	5.8 (Berkeley) 06/29/88";
29 #endif /* not lint */
30 
31 /*
32 NAME:
33 indent main program
34 
35 FUNCTION:
36 This is the main program of the indent program.  Indent will take a C
37 program source and reformat it into a semi-reasonable form.
38 
39 ALGORITHM:
40 The routine lexi scans tokens and passes them back one at a time to the
41 main routine.  The subroutine parse takes care of much of the work of
42 figuring indentation level.
43 
44 1) Call lexi
45 2) Enter a monster switch statement on the code returned by lexi.  If
46 the indentation level for the line yet to be printed should be
47 changed, set the variable ps.ind_level.  If the indentation level for
48 the following line should be changed, set the variable ps.i_l_follow.
49 
50 */
51 #include "indent_globs.h"
52 #include "indent_codes.h"
53 
54 char       *in_name = "Standard Input";	/* will always point to name of
55 					 * input file */
56 char       *out_name = "Standard Output";	/* will always point to
57 						 * name of output file */
58 char        bakfile[32] = "";
59 
60 main(argc, argv)
61     int         argc;
62     char      **argv;
63 {
64     extern int	found_err;	/* if any error occurred */
65 
66     int         dec_ind;	/* current indentation for declarations */
67     int         di_stack[20];	/* a stack of structure indentation levels */
68     int         flushed_nl;	/* used when buffering up comments to
69 				 * remember that a newline was passed over */
70     int         force_nl;	/* when true, code must be broken */
71     int         hd_type;	/* used to store type of stmt for if
72 				 * (...), for (...), etc */
73     register int i;		/* local loop counter */
74     register int j;		/* local loop counter */
75     int         scase;		/* set to true when we see a case, so we
76 				 * will know what to do with the following
77 				 * colon */
78     int         sp_sw;		/* when true, we are in the expressin of
79 				 * if(...), while(...), etc. */
80     int         squest;		/* when this is positive, we have seen a ?
81 				 * without the matching : in a <c>?<s>:<s>
82 				 * construct */
83     register char *t_ptr;	/* used for copying tokens */
84     int         type_code;	/* the type of token, returned by lexi */
85 
86     int         last_else = 0;	/* true iff last keyword was an else */
87 
88 
89     /*-----------------------------------------------*\
90     |		      INITIALIZATION		      |
91     \*-----------------------------------------------*/
92 
93 
94     ps.p_stack[0] = stmt;	/* this is the parser's stack */
95     ps.last_nl = true;		/* this is true if the last thing scanned
96 				 * was a newline */
97     ps.last_token = semicolon;
98     combuf[0] = codebuf[0] = labbuf[0] = ' ';	/* set up code, label, and
99 						 * comment buffers */
100     combuf[1] = codebuf[1] = labbuf[1] = '\0';
101     s_lab = e_lab = labbuf + 1;
102     s_code = e_code = codebuf + 1;
103     s_com = e_com = combuf + 1;
104 
105     buf_ptr = buf_end = in_buffer;
106     line_no = 1;
107     had_eof = ps.in_decl = ps.decl_on_line = break_comma = false;
108     sp_sw = force_nl = false;
109     ps.in_or_st = false;
110     ps.bl_line = true;
111     dec_ind = 0;
112     di_stack[ps.dec_nest = 0] = 0;
113     ps.want_blank = ps.in_stmt = ps.ind_stmt = false;
114 
115 
116     scase = ps.pcase = false;
117     squest = 0;
118     sc_end = 0;
119     bp_save = 0;
120     be_save = 0;
121 
122     output = 0;
123 
124 
125 
126     /*--------------------------------------------------*\
127     |   COMMAND LINE SCAN
128     \*--------------------------------------------------*/
129 
130     set_defaults();
131 
132     /*
133      * Unfortunately, we must look for -npro here because the profiles
134      * are read before the command line arguments.
135      */
136     for (i = 1; i < argc; ++i)
137 	if (strcmp(argv[i], "-npro") == 0)
138 	    break;
139     if (i >= argc)
140 	set_profile();
141 
142     input = 0;			/* cancel -st if it was in the profiles, */
143     output = 0;			/* as it doesn't make any sense there. */
144 
145     for (i = 1; i < argc; ++i) {
146 
147 	/*
148 	 * look thru args (if any) for changes to defaults
149 	 */
150 	if (argv[i][0] != '-') {/* no flag on parameter */
151 	    if (input == 0) {	/* we must have the input file */
152 		in_name = argv[i];	/* remember name of input file */
153 		input = fopen(in_name, "r");
154 		if (input == 0) {	/* check for open error */
155 		    fprintf(stderr, "indent: can't open %s\n", argv[i]);
156 		    exit(1);
157 		}
158 		continue;
159 	    } else if (output == 0) {	/* we have the output file */
160 		out_name = argv[i];	/* remember name of output file */
161 		if (strcmp(in_name, out_name) == 0) {	/* attempt to overwrite
162 							 * the file */
163 		    fprintf(stderr, "indent: input and output files must be different\n");
164 		    exit(1);
165 		}
166 		output = fopen(out_name, "w");
167 		if (output == 0) {	/* check for create error */
168 		    fprintf(stderr, "indent: can't create %s\n", argv[i]);
169 		    exit(1);
170 		}
171 		continue;
172 	    }
173 	    fprintf(stderr, "indent: unknown parameter: %s\n", argv[i]);
174 	    exit(1);
175 	} else
176 	    set_option(argv[i]);
177 
178     }				/* end of for */
179     if (input == 0) {
180 	printf("Usage: indent file [ outfile ] [ options ]\n");
181 	exit(1);
182     }
183     if (output == 0)
184 	if (troff)
185 	    output = stdout;
186 	else {
187 	    out_name = in_name;
188 	    bakcopy();
189 	}
190 
191     /*
192      * Adjust parameters that are out of range, or set defaults if
193      * no values were specified.
194      */
195     if (ps.com_ind <= 1)
196 	ps.com_ind = 2;		/* dont put normal comments before column
197 				 * 2 */
198     if (block_comment_max_col <= 0)
199 	block_comment_max_col = max_col;
200     if (ps.decl_com_ind <= 0)	/* if not specified by user, set this */
201 	ps.decl_com_ind = ps.ljust_decl ? ps.com_ind - 8 : ps.com_ind;
202     if (ps.decl_com_ind <= 1)
203 	ps.decl_com_ind = 2;
204     if (continuation_indent == 0)
205 	continuation_indent = ps.ind_size;
206     fill_buffer();		/* get first batch of stuff into input
207 				 * buffer */
208 
209     parse(semicolon);
210     {
211 	register char *p = buf_ptr;
212 	register    col = 1;
213 
214 	while (1) {
215 	    if (*p == ' ')
216 		col++;
217 	    else if (*p == '\t')
218 		col = ((col - 1) & ~7) + 9;
219 	    else
220 		break;
221 	    p++;
222 	};
223 	if (col > ps.ind_size)
224 	    ps.ind_level = ps.i_l_follow = col / ps.ind_size;
225     }
226     if (troff) {
227 	register char *p = in_name,
228 	           *beg = in_name;
229 
230 	while (*p)
231 	    if (*p++ == '/')
232 		beg = p;
233 	fprintf(output, ".Fn \"%s\"\n", beg);
234     }
235 
236     /*
237      * START OF MAIN LOOP
238      */
239 
240     while (1) {			/* this is the main loop.  it will go
241 				 * until we reach eof */
242 	int         is_procname;
243 
244 	type_code = lexi();	/* lexi reads one token.  The actual
245 				 * characters read are stored in "token".
246 				 * lexi returns a code indicating the type
247 				 * of token */
248 	is_procname = ps.procname[0];
249 
250 	/*
251 	 * The following code moves everything following an if (), while
252 	 * (), else, etc. up to the start of the following stmt to a
253 	 * buffer.  This allows proper handling of both kinds of brace
254 	 * placement.
255 	 */
256 
257 	flushed_nl = false;
258 	while (ps.search_brace) {	/* if we scanned an if(), while(),
259 					 * etc., we might need to copy
260 					 * stuff into a buffer we must
261 					 * loop, copying stuff into
262 					 * save_com, until we find the
263 					 * start of the stmt which follows
264 					 * the if, or whatever */
265 	    switch (type_code) {
266 		case newline:
267 		    ++line_no;
268 		    flushed_nl = true;
269 		case form_feed:
270 		    break;	/* form feeds and newlines found here will
271 				 * be ignored */
272 
273 		case lbrace:	/* this is a brace that starts the
274 				 * compound stmt */
275 		    if (sc_end == 0) {	/* ignore buffering if a comment
276 					 * wasnt stored up */
277 			ps.search_brace = false;
278 			goto check_type;
279 		    }
280 		    if (btype_2) {
281 			save_com[0] = '{';	/* we either want to put
282 						 * the brace right after
283 						 * the if */
284 			goto sw_buffer;	/* go to common code to get out of
285 					 * this loop */
286 		    }
287 		case comment:	/* we have a comment, so we must copy it
288 				 * into the buffer */
289 		    if (!flushed_nl) {
290 			if (sc_end == 0) {	/* if this is the first
291 						 * comment, we must set up
292 						 * the buffer */
293 			    save_com[0] = save_com[1] = ' ';
294 			    sc_end = &(save_com[2]);
295 			} else {
296 			    *sc_end++ = '\n';	/* add newline between
297 						 * comments */
298 			    *sc_end++ = ' ';
299 			    --line_no;
300 			}
301 			*sc_end++ = '/';	/* copy in start of
302 						 * comment */
303 			*sc_end++ = '*';
304 
305 			for (;;) {	/* loop until we get to the end of
306 					 * the comment */
307 			    *sc_end = *buf_ptr++;
308 			    if (buf_ptr >= buf_end)
309 				fill_buffer();
310 
311 			    if (*sc_end++ == '*' && *buf_ptr == '/')
312 				break;	/* we are at end of comment */
313 
314 			    if (sc_end >= &(save_com[sc_size])) {	/* check for temp buffer
315 									 * overflow */
316 				diag(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever.");
317 				fflush(output);
318 				exit(1);
319 			    }
320 			}
321 			*sc_end++ = '/';	/* add ending slash */
322 			if (++buf_ptr >= buf_end)	/* get past / in buffer */
323 			    fill_buffer();
324 			break;
325 		    }
326 		default:	/* it is the start of a normal statment */
327 		    if (flushed_nl)	/* if we flushed a newline, make
328 					 * sure it is put back */
329 			force_nl = true;
330 		    if (type_code == sp_paren && *token == 'i'
331 			&& last_else && ps.else_if
332 			|| type_code == sp_nparen && *token == 'e'
333 			&& e_code != s_code && e_code[-1] == '}')
334 			force_nl = false;
335 
336 		    if (sc_end == 0) {	/* ignore buffering if comment
337 					 * wasnt saved up */
338 			ps.search_brace = false;
339 			goto check_type;
340 		    }
341 		    if (force_nl) {	/* if we should insert a nl here,
342 					 * put it into the buffer */
343 			force_nl = false;
344 			--line_no;	/* this will be re-increased when
345 					 * the nl is read from the buffer */
346 			*sc_end++ = '\n';
347 			*sc_end++ = ' ';
348 			if (verbose && !flushed_nl)	/* print error msg if
349 							 * the line was not
350 							 * already broken */
351 			    diag(0, "Line broken");
352 			flushed_nl = false;
353 		    }
354 		    for (t_ptr = token; *t_ptr; ++t_ptr)
355 			*sc_end++ = *t_ptr;	/* copy token into temp
356 						 * buffer */
357 
358 	    sw_buffer:
359 		    ps.search_brace = false;	/* stop looking for start
360 						 * of stmt */
361 		    bp_save = buf_ptr;	/* save current input buffer */
362 		    be_save = buf_end;
363 		    buf_ptr = save_com;	/* fix so that subsequent calls to
364 					 * lexi will take tokens out of
365 					 * save_com */
366 		    *sc_end++ = ' ';	/* add trailing blank, just in
367 					 * case */
368 		    buf_end = sc_end;
369 		    sc_end = 0;
370 		    break;
371 	    }			/* end of switch */
372 	    if (type_code != 0)	/* we must make this check, just in case
373 				 * there was an unexpected EOF */
374 		type_code = lexi();	/* read another token */
375 	    is_procname = ps.procname[0];
376 	}			/* end of while (serach_brace) */
377 	last_else = 0;
378 check_type:
379 	if (type_code == 0) {	/* we got eof */
380 	    if (s_lab != e_lab || s_code != e_code
381 		|| s_com != e_com)	/* must dump end of line */
382 		dump_line();
383 	    if (ps.tos > 1)	/* check for balanced braces */
384 		diag(1, "Stuff missing from end of file.");
385 
386 	    if (verbose) {
387 		printf("There were %d output lines and %d comments\n",
388 		       ps.out_lines, ps.out_coms);
389 		printf("(Lines with comments)/(Lines with code): %6.3f\n",
390 		       (1.0 * ps.com_lines) / code_lines);
391 	    }
392 	    fflush(output);
393 	    exit(ps.tos > 1 || found_err);
394 	}
395 	if (
396 	    (type_code != comment) &&
397 	    (type_code != newline) &&
398 	    (type_code != preesc) &&
399 	    (type_code != form_feed)) {
400 	    if (
401 		force_nl
402 		&&
403 		(type_code != semicolon) &&
404 		(
405 		 type_code != lbrace
406 		 ||
407 		 !btype_2
408 		 )) {		/* we should force a broken line here */
409 		if (verbose && !flushed_nl)
410 		    diag(0, "Line broken");
411 		flushed_nl = false;
412 		dump_line();
413 		ps.want_blank = false;	/* dont insert blank at line start */
414 		force_nl = false;
415 	    }
416 	    ps.in_stmt = true;	/* turn on flag which causes an extra
417 				 * level of indentation. this is turned
418 				 * off by a ; or '}' */
419 	    if (s_com != e_com) {	/* the turkey has embedded a
420 					 * comment in a line. fix it */
421 		*e_code++ = ' ';
422 		for (t_ptr = s_com; *t_ptr; ++t_ptr)
423 		    *e_code++ = *t_ptr;
424 		*e_code++ = ' ';
425 		*e_code = '\0';	/* null terminate code sect */
426 		ps.want_blank = false;
427 		e_com = s_com;
428 	    }
429 	} else if (type_code != comment)	/* preserve force_nl thru
430 						 * a comment */
431 	    force_nl = false;
432 
433 	/*
434 	 * cancel forced newline after newline, form feed, etc
435 	 */
436 
437 
438 
439 	/*----------------------------------------------------*\
440 	|   do switch on type of token scanned
441 	\*----------------------------------------------------*/
442 	switch (type_code) {	/* now, decide what to do with the token */
443 
444 	    case form_feed:	/* found a form feed in line */
445 		ps.use_ff = true;	/* a form feed is treated much
446 					 * like a newline */
447 		dump_line();
448 		ps.want_blank = false;
449 		break;
450 
451 	    case newline:
452 		if (ps.last_token != comma || ps.p_l_follow > 0
453 		    || !ps.leave_comma || !break_comma || s_com != e_com) {
454 		    dump_line();
455 		    ps.want_blank = false;
456 		}
457 		++line_no;	/* keep track of input line number */
458 		break;
459 
460 	    case lparen:	/* got a '(' or '[' */
461 		++ps.p_l_follow;/* count parens to make Healy happy */
462 		if (ps.want_blank && *token != '[' &&
463 		    (ps.last_token != ident || proc_calls_space
464 		     || (ps.its_a_keyword && !ps.sizeof_keyword)))
465 		    *e_code++ = ' ';
466 		if (ps.in_decl && !ps.block_init)
467 		    if (troff && !ps.dumped_decl_indent) {
468 			ps.dumped_decl_indent = 1;
469 			sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
470 			e_code += strlen(e_code);
471 		    } else {
472 			while ((e_code - s_code) < dec_ind)
473 			    *e_code++ = ' ';
474 			*e_code++ = token[0];
475 		} else
476 		    *e_code++ = token[0];
477 		ps.paren_indents[ps.p_l_follow - 1] = e_code - s_code;
478 		ps.want_blank = false;
479 		if (ps.in_or_st && *token == '(') {
480 
481 		    /*
482 		     * this is a kluge to make sure that declarations will
483 		     * be aligned right if proc decl has an explicit type
484 		     * on it, i.e. "int a(x) {..."
485 		     */
486 		    parse(semicolon);	/* I said this was a kluge... */
487 		    ps.in_or_st = false;	/* turn off flag for
488 						 * structure decl or
489 						 * initialization */
490 		}
491 		if (ps.sizeof_keyword) ps.sizeof_mask |= 1<<ps.p_l_follow;
492 		break;
493 
494 	    case rparen:	/* got a ')' or ']' */
495 		if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.sizeof_mask) {
496 		    ps.last_u_d = true;
497 		    ps.cast_mask &= (1 << ps.p_l_follow) - 1;
498 		}
499 		ps.sizeof_mask &= (1 << ps.p_l_follow) - 1;
500 		if (--ps.p_l_follow < 0) {
501 		    ps.p_l_follow = 0;
502 		    diag(0, "Extra %c", *token);
503 		}
504 		if (e_code == s_code)	/* if the paren starts the line */
505 		    ps.paren_level = ps.p_l_follow;	/* then indent it */
506 
507 		*e_code++ = token[0];
508 		ps.want_blank = true;
509 
510 		if (sp_sw && (ps.p_l_follow == 0)) {	/* check for end of if
511 							 * (...), or some such */
512 		    sp_sw = false;
513 		    force_nl = true;	/* must force newline after if */
514 		    ps.last_u_d = true;	/* inform lexi that a following
515 					 * operator is unary */
516 		    ps.in_stmt = false;	/* dont use stmt continuation
517 					 * indentation */
518 
519 		    parse(hd_type);	/* let parser worry about if, or
520 					 * whatever */
521 		}
522 		ps.search_brace = btype_2;	/* this should insure that
523 						 * constructs such as
524 						 * main(){...} and
525 						 * int[]{...} have their
526 						 * braces put in the right
527 						 * place */
528 		break;
529 
530 	    case unary_op:	/* this could be any unary operation */
531 		if (ps.want_blank)
532 		    *e_code++ = ' ';
533 
534 		if (troff && !ps.dumped_decl_indent && ps.in_decl) {
535 		    sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
536 		    ps.dumped_decl_indent = 1;
537 		    e_code += strlen(e_code);
538 		} else {
539 		    char       *res = token;
540 
541 		    if (ps.in_decl && !ps.block_init) {	/* if this is a unary op
542 							 * in a declaration, we
543 							 * should indent this
544 							 * token */
545 			for (i = 0; token[i]; ++i);	/* find length of token */
546 			while ((e_code - s_code) < (dec_ind - i))
547 			    *e_code++ = ' ';	/* pad it */
548 		    }
549 		    if (troff && token[0] == '-' && token[1] == '>')
550 			res = "\\(->";
551 		    for (t_ptr = res; *t_ptr; ++t_ptr)
552 			*e_code++ = *t_ptr;
553 		}
554 		ps.want_blank = false;
555 		break;
556 
557 	    case binary_op:	/* any binary operation */
558 	do_binary:
559 		if (ps.want_blank)
560 		    *e_code++ = ' ';
561 		{
562 		    char       *res = token;
563 
564 		    if (troff)
565 			switch (token[0]) {
566 			    case '<':
567 				if (token[1] == '=')
568 				    res = "\\(<=";
569 				break;
570 			    case '>':
571 				if (token[1] == '=')
572 				    res = "\\(>=";
573 				break;
574 			    case '!':
575 				if (token[1] == '=')
576 				    res = "\\(!=";
577 				break;
578 			    case '|':
579 				if (token[1] == '|')
580 				    res = "\\(br\\(br";
581 				else if (token[1] == 0)
582 				    res = "\\(br";
583 				break;
584 			    case '-':
585 				if (token[1] == '>')
586 				    res = "\\(->";
587 			}
588 		    for (t_ptr = res; *t_ptr; ++t_ptr)
589 			*e_code++ = *t_ptr;	/* move the operator */
590 		}
591 		ps.want_blank = true;
592 		break;
593 
594 	    case postop:	/* got a trailing ++ or -- */
595 		*e_code++ = token[0];
596 		*e_code++ = token[1];
597 		ps.want_blank = true;
598 		break;
599 
600 	    case question:	/* got a ? */
601 		squest++;	/* this will be used when a later colon
602 				 * appears so we can distinguish the
603 				 * <c>?<n>:<n> construct */
604 		if (ps.want_blank)
605 		    *e_code++ = ' ';
606 		*e_code++ = '?';
607 		ps.want_blank = true;
608 		break;
609 
610 	    case casestmt:	/* got word 'case' or 'default' */
611 		scase = true;	/* so we can process the later colon
612 				 * properly */
613 		goto copy_id;
614 
615 	    case colon:	/* got a ':' */
616 		if (squest > 0) {	/* it is part of the <c>?<n>: <n>
617 					 * construct */
618 		    --squest;
619 		    if (ps.want_blank)
620 			*e_code++ = ' ';
621 		    *e_code++ = ':';
622 		    ps.want_blank = true;
623 		    break;
624 		}
625 		if (ps.in_decl) {
626 		    *e_code++ = ':';
627 		    ps.want_blank = false;
628 		    break;
629 		}
630 		ps.in_stmt = false;	/* seeing a label does not imply
631 					 * we are in a stmt */
632 		for (t_ptr = s_code; *t_ptr; ++t_ptr)
633 		    *e_lab++ = *t_ptr;	/* turn everything so far into a
634 					 * label */
635 		e_code = s_code;
636 		*e_lab++ = ':';
637 		*e_lab++ = ' ';
638 		*e_lab = '\0';
639 
640 		force_nl = ps.pcase = scase;	/* ps.pcase will be used
641 						 * by dump_line to decide
642 						 * how to indent the
643 						 * label. force_nl will
644 						 * force a case n: to be
645 						 * on a line by itself */
646 		scase = false;
647 		ps.want_blank = false;
648 		break;
649 
650 	    case semicolon:	/* got a ';' */
651 		ps.in_or_st = false;	/* we are not in an initialization
652 					 * or structure declaration */
653 		scase = false;	/* these will only need resetting in a
654 				 * error */
655 		squest = 0;
656 		if (ps.last_token == rparen)
657 		    ps.in_parameter_declaration = 0;
658 		ps.cast_mask = 0;
659 		ps.sizeof_mask = 0;
660 		ps.block_init = 0;
661 		ps.just_saw_decl--;
662 
663 		if (ps.in_decl && s_code == e_code && !ps.block_init)
664 		    while ((e_code - s_code) < (dec_ind - 1))
665 			*e_code++ = ' ';
666 
667 		ps.in_decl = (ps.dec_nest > 0);	/* if we were in a first
668 						 * level structure
669 						 * declaration, we arent
670 						 * any more */
671 
672 		if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) {
673 
674 		    /*
675 		     * This should be true iff there were unbalanced
676 		     * parens in the stmt.  It is a bit complicated,
677 		     * because the semicolon might be in a for stmt
678 		     */
679 		    diag(1, "Unbalanced parens");
680 		    ps.p_l_follow = 0;
681 		    if (sp_sw) {/* this is a check for a if, while, etc.
682 				 * with unbalanced parens */
683 			sp_sw = false;
684 			parse(hd_type);	/* dont lose the if, or whatever */
685 		    }
686 		}
687 		*e_code++ = ';';
688 		ps.want_blank = true;
689 		ps.in_stmt = (ps.p_l_follow > 0);	/* we are no longer in
690 							 * the middle of a stmt */
691 
692 		if (!sp_sw) {	/* if not if for (;;) */
693 		    parse(semicolon);	/* let parser know about end of
694 					 * stmt */
695 		    force_nl = true;	/* force newline after a end of
696 					 * stmt */
697 		}
698 		break;
699 
700 	    case lbrace:	/* got a '{' */
701 		ps.in_stmt = false;	/* dont indent the {} */
702 		if (!ps.block_init)
703 		    force_nl = true;	/* force other stuff on same line
704 					 * as '{' onto new line */
705 
706 		if (s_code != e_code && !ps.block_init) {
707 		    if (!btype_2) {
708 			dump_line();
709 			ps.want_blank = false;
710 		    } else if (ps.in_parameter_declaration && !ps.in_or_st) {
711 			ps.i_l_follow = 0;
712 			dump_line();
713 			ps.want_blank = false;
714 		    }
715 		}
716 		if (ps.in_parameter_declaration)
717 		    prefix_blankline_requested = 0;
718 
719 		if (ps.p_l_follow > 0) {	/* check for preceding
720 						 * unbalanced parens */
721 		    diag(1, "Unbalanced parens");
722 		    ps.p_l_follow = 0;
723 		    if (sp_sw) {/* check for unclosed if, for, etc. */
724 			sp_sw = false;
725 			parse(hd_type);
726 			ps.ind_level = ps.i_l_follow;
727 		    }
728 		}
729 		if (s_code == e_code)
730 		    ps.ind_stmt = false;	/* dont put extra
731 						 * indentation on line
732 						 * with '{' */
733 		if (ps.in_decl && ps.in_or_st) {	/* this is either a
734 							 * structure declaration
735 							 * or an init */
736 		    di_stack[ps.dec_nest++] = dec_ind;
737 		    dec_ind = 0;
738 		} else {
739 		    ps.decl_on_line = false;	/* we cant be in the
740 						 * middle of a
741 						 * declaration, so dont do
742 						 * special indentation of
743 						 * comments */
744 		    ps.in_parameter_declaration = 0;
745 		}
746 		parse(lbrace);	/* let parser know about this */
747 		if (ps.want_blank)	/* put a blank before '{' if '{'
748 					 * is not at start of line */
749 		    *e_code++ = ' ';
750 		ps.want_blank = false;
751 		*e_code++ = '{';
752 		ps.just_saw_decl = 0;
753 		break;
754 
755 	    case rbrace:	/* got a '}' */
756 		if (ps.p_l_follow) {	/* check for unclosed if, for,
757 					 * else. */
758 		    diag(1, "Unbalanced parens");
759 		    ps.p_l_follow = 0;
760 		    sp_sw = false;
761 		}
762 		ps.just_saw_decl = 0;
763 		if (s_code != e_code && !ps.block_init) {	/* '}' must be first on
764 								 * line */
765 		    if (verbose)
766 			diag(0, "Line broken");
767 		    dump_line();
768 		}
769 		*e_code++ = '}';
770 		ps.want_blank = true;
771 		ps.in_stmt = ps.ind_stmt = false;
772 		if (ps.dec_nest > 0) {	/* we are in multi-level structure
773 					 * declaration */
774 		    dec_ind = di_stack[--ps.dec_nest];
775 		    if (ps.dec_nest == 0 && !ps.in_parameter_declaration)
776 			ps.just_saw_decl = 2;
777 		    ps.in_decl = true;
778 		}
779 		prefix_blankline_requested = 0;
780 		parse(rbrace);	/* let parser know about this */
781 		ps.search_brace = cuddle_else && ps.p_stack[ps.tos] == ifhead && ps.il[ps.tos] >= ps.ind_level;
782 		if (ps.tos <= 1 && blanklines_after_procs && ps.dec_nest <= 0)
783 		    postfix_blankline_requested = 1;
784 		break;
785 
786 	    case swstmt:	/* got keyword "switch" */
787 		sp_sw = true;
788 		hd_type = swstmt;	/* keep this for when we have seen
789 					 * the expression */
790 		goto copy_id;	/* go move the token into buffer */
791 
792 	    case sp_paren:	/* token is if, while, for */
793 		sp_sw = true;	/* the interesting stuff is done after the
794 				 * expression is scanned */
795 		hd_type = (*token == 'i' ? ifstmt :
796 			   (*token == 'w' ? whilestmt : forstmt));
797 
798 		/*
799 		 * remember the type of header for later use by parser
800 		 */
801 		goto copy_id;	/* copy the token into line */
802 
803 	    case sp_nparen:	/* got else, do */
804 		ps.in_stmt = false;
805 		if (*token == 'e') {
806 		    if (e_code != s_code && (!cuddle_else || e_code[-1] != '}')) {
807 			if (verbose)
808 			    diag(0, "Line broken");
809 			dump_line();	/* make sure this starts a line */
810 			ps.want_blank = false;
811 		    }
812 		    force_nl = true;	/* also, following stuff must go
813 					 * onto new line */
814 		    last_else = 1;
815 		    parse(elselit);
816 		} else {
817 		    if (e_code != s_code) {	/* make sure this starts a
818 						 * line */
819 			if (verbose)
820 			    diag(0, "Line broken");
821 			dump_line();
822 			ps.want_blank = false;
823 		    }
824 		    force_nl = true;	/* also, following stuff must go
825 					 * onto new line */
826 		    last_else = 0;
827 		    parse(dolit);
828 		}
829 		goto copy_id;	/* move the token into line */
830 
831 	    case decl:		/* we have a declaration type (int,
832 				 * register, etc.) */
833 		parse(decl);	/* let parser worry about indentation */
834 		if (ps.last_token == rparen && ps.tos <= 1)
835 		    ps.in_parameter_declaration = 1;
836 		if (ps.in_parameter_declaration && ps.indent_parameters && ps.dec_nest == 0) {
837 		    ps.ind_level = ps.i_l_follow = 1;
838 		    ps.ind_stmt = 0;
839 		}
840 		ps.in_or_st = true;	/* this might be a structure or
841 					 * initialization declaration */
842 		ps.in_decl = ps.decl_on_line = true;
843 		if ( /* !ps.in_or_st && */ ps.dec_nest <= 0)
844 		    ps.just_saw_decl = 2;
845 		prefix_blankline_requested = 0;
846 		for (i = 0; token[i++];);	/* get length of token */
847 
848 		/*
849 		 * dec_ind = e_code - s_code + (ps.decl_indent>i ?
850 		 * ps.decl_indent : i);
851 		 */
852 		dec_ind = ps.decl_indent > 0 ? ps.decl_indent : i;
853 		goto copy_id;
854 
855 	    case ident:	/* got an identifier or constant */
856 		if (ps.in_decl) {	/* if we are in a declaration, we
857 					 * must indent identifier */
858 		    if (ps.want_blank)
859 			*e_code++ = ' ';
860 		    ps.want_blank = false;
861 		    if (is_procname == 0 || !procnames_start_line) {
862 			if (!ps.block_init)
863 			    if (troff && !ps.dumped_decl_indent) {
864 				sprintf(e_code, "\\c\n.De %dp+\200p\n", dec_ind * 7);
865 				ps.dumped_decl_indent = 1;
866 				e_code += strlen(e_code);
867 			    } else
868 				while ((e_code - s_code) < dec_ind)
869 				    *e_code++ = ' ';
870 		    } else {
871 			if (dec_ind && s_code != e_code)
872 			    dump_line();
873 			dec_ind = 0;
874 			ps.want_blank = false;
875 		    }
876 		} else if (sp_sw && ps.p_l_follow == 0) {
877 		    sp_sw = false;
878 		    force_nl = true;
879 		    ps.last_u_d = true;
880 		    ps.in_stmt = false;
881 		    parse(hd_type);
882 		}
883 	copy_id:
884 		if (ps.want_blank)
885 		    *e_code++ = ' ';
886 		if (troff && ps.its_a_keyword) {
887 		    *e_code++ = BACKSLASH;
888 		    *e_code++ = 'f';
889 		    *e_code++ = 'B';
890 		}
891 		for (t_ptr = token; *t_ptr; ++t_ptr)
892 		    *e_code++ = *t_ptr;
893 		if (troff && ps.its_a_keyword) {
894 		    *e_code++ = BACKSLASH;
895 		    *e_code++ = 'f';
896 		    *e_code++ = 'R';
897 		}
898 		ps.want_blank = true;
899 		break;
900 
901 	    case period:	/* treat a period kind of like a binary
902 				 * operation */
903 		*e_code++ = '.';/* move the period into line */
904 		ps.want_blank = false;	/* dont put a blank after a period */
905 		break;
906 
907 	    case comma:
908 		ps.want_blank = (s_code != e_code);	/* only put blank after
909 							 * comma if comma does
910 							 * not start the line */
911 		if (ps.in_decl && is_procname == 0 && !ps.block_init)
912 		    while ((e_code - s_code) < (dec_ind - 1))
913 			*e_code++ = ' ';
914 
915 		*e_code++ = ',';
916 		if (ps.p_l_follow == 0) {
917 		    ps.block_init = 0;
918 		    if (break_comma && !ps.leave_comma)
919 			force_nl = true;
920 		}
921 		break;
922 
923 	    case preesc:	/* got the character '#' */
924 		if ((s_com != e_com) ||
925 		    (s_lab != e_lab) ||
926 		    (s_code != e_code))
927 		    dump_line();
928 		*e_lab++ = '#';	/* move whole line to 'label' buffer */
929 		{
930 		    int         in_comment = 0;
931 		    char       *com_start = 0;
932 		    char        quote = 0;
933 		    char       *com_end = 0;
934 
935 		    while (*buf_ptr != '\n' || in_comment) {
936 			*e_lab = *buf_ptr++;
937 			if (buf_ptr >= buf_end)
938 			    fill_buffer();
939 			switch (*e_lab++) {
940 			    case BACKSLASH:
941 				if (troff)
942 				    *e_lab++ = BACKSLASH;
943 				if (!in_comment) {
944 				    *e_lab++ = *buf_ptr++;
945 				    if (buf_ptr >= buf_end)
946 					fill_buffer();
947 				}
948 				break;
949 			    case '/':
950 				if (*buf_ptr == '*' && !in_comment && !quote) {
951 				    in_comment = 1;
952 				    *e_lab++ = *buf_ptr++;
953 				    com_start = e_lab - 2;
954 				}
955 				break;
956 			    case '"':
957 				if (quote == '"')
958 				    quote = 0;
959 				break;
960 			    case '\'':
961 				if (quote == '\'')
962 				    quote = 0;
963 				break;
964 			    case '*':
965 				if (*buf_ptr == '/' && in_comment) {
966 				    in_comment = 0;
967 				    *e_lab++ = *buf_ptr++;
968 				    com_end = e_lab;
969 				}
970 				break;
971 			}
972 		    }
973 		    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
974 			e_lab--;
975 		    if (e_lab == com_end && bp_save == 0) {	/* comment on
976 								 * preprocessor line */
977 			if (sc_end == 0)	/* if this is the first
978 						 * comment, we must set up
979 						 * the buffer */
980 			    sc_end = &(save_com[0]);
981 			else {
982 			    *sc_end++ = '\n';	/* add newline between
983 						 * comments */
984 			    *sc_end++ = ' ';
985 			    --line_no;
986 			}
987 			bcopy(com_start, sc_end, com_end - com_start);
988 			sc_end += com_end - com_start;
989 			e_lab = com_start;
990 			while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
991 			    e_lab--;
992 			bp_save = buf_ptr;	/* save current input
993 						 * buffer */
994 			be_save = buf_end;
995 			buf_ptr = save_com;	/* fix so that subsequent
996 						 * calls to lexi will take
997 						 * tokens out of save_com */
998 			*sc_end++ = ' ';	/* add trailing blank,
999 						 * just in case */
1000 			buf_end = sc_end;
1001 			sc_end = 0;
1002 		    }
1003 		    *e_lab = '\0';	/* null terminate line */
1004 		    ps.pcase = false;
1005 		}
1006 		if (strncmp(s_lab, "#if", 3) == 0)
1007 		    if (ifdef_level < sizeof state_stack / sizeof state_stack[0]) {
1008 			match_state[ifdef_level].tos = -1;
1009 			state_stack[ifdef_level++] = ps;
1010 		    } else
1011 			diag(1, "#if stack overflow");
1012 		else if (strncmp(s_lab, "#else", 5) == 0)
1013 		    if (ifdef_level <= 0)
1014 			diag(1, "Unmatched #else");
1015 		    else {
1016 			match_state[ifdef_level - 1] = ps;
1017 			ps = state_stack[ifdef_level - 1];
1018 		} else if (strncmp(s_lab, "#endif", 6) == 0)
1019 		    if (ifdef_level <= 0)
1020 			diag(1, "Unmatched #endif");
1021 		    else {
1022 			ifdef_level--;
1023 #ifdef undef
1024 
1025 			/*
1026 			 * This match needs to be more intelligent before
1027 			 * the message is useful
1028 			 */
1029 			if (match_state[ifdef_level].tos >= 0
1030 			    && bcmp(&ps, &match_state[ifdef_level], sizeof ps))
1031 			    diag(0, "Syntactically inconsistant #ifdef alternatives.");
1032 #endif
1033 		    }
1034 		break;		/* subsequent processing of the newline
1035 				 * character will cause the line to be
1036 				 * printed */
1037 
1038 	    case comment:	/* we have gotten a /*  this is a biggie */
1039 	proc_comment:
1040 		if (flushed_nl) {	/* we should force a broken line
1041 					 * here */
1042 		    flushed_nl = false;
1043 		    dump_line();
1044 		    ps.want_blank = false;	/* dont insert blank at
1045 						 * line start */
1046 		    force_nl = false;
1047 		}
1048 		pr_comment();
1049 		break;
1050 	}			/* end of big switch stmt */
1051 	*e_code = '\0';		/* make sure code section is null
1052 				 * terminated */
1053 	if (type_code != comment && type_code != newline && type_code != preesc)
1054 	    ps.last_token = type_code;
1055     }				/* end of main while (1) loop */
1056 };
1057 
1058 /*
1059  * copy input file to backup file.  If in_name is /blah/blah/blah/file, then
1060  * backup file will be "file.BAK".  Then make the backup file the input and
1061  * original input file the output.
1062  */
1063 bakcopy()
1064 {
1065     int         n,
1066                 bakchn;
1067     char        buff[BUFSIZ];
1068     register char *p;
1069     char *rindex();
1070 
1071     if ((p = rindex(in_name, '/')) != NULL)
1072 	p++;
1073     else
1074 	p = in_name;
1075     sprintf(bakfile, "%s.BAK", p);
1076 
1077     /* copy in_name to backup file */
1078     bakchn = creat(bakfile, 0600);
1079     if (bakchn < 0) {
1080 	fprintf(stderr, "indent: can't create backup file \"%s\"\n", bakfile);
1081 	exit(1);
1082     }
1083     while ((n = read(fileno(input), buff, sizeof buff)) > 0)
1084 	if (write(bakchn, buff, n) != n) {
1085 	    fprintf(stderr, "indent: error writing backup file \"%s\"\n",
1086 		bakfile);
1087 	    exit(1);
1088 	}
1089     if (n < 0) {
1090 	fprintf(stderr, "indent: error reading input file \"%s\"\n", in_name);
1091 	exit(1);
1092     }
1093     close(bakchn);
1094     fclose(input);
1095 
1096     /* re-open backup file as the input file */
1097     input = fopen(bakfile, "r");
1098     if (input == NULL) {
1099 	fprintf(stderr, "indent: can't re-open backup file\n");
1100 	exit(1);
1101     }
1102     /* now the original input file will be the output */
1103     output = fopen(in_name, "w");
1104     if (output == NULL) {
1105 	fprintf(stderr, "indent: can't create %s\n", in_name);
1106 	unlink(bakfile);
1107 	exit(1);
1108     }
1109 }
1110