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