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