xref: /original-bsd/usr.bin/indent/indent.c (revision dd74897b)
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.2 (Berkeley) 08/28/85";
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 
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     ps.else_if = 1;		/* Default else-if special processing to
97 				 * on */
98     s_lab = e_lab = labbuf + 1;
99     s_code = e_code = codebuf + 1;
100     s_com = e_com = combuf + 1;
101 
102     buf_ptr = buf_end = in_buffer;
103     line_no = 1;
104     had_eof = ps.in_decl = ps.decl_on_line = break_comma = false;
105     sp_sw = force_nl = false;
106     ps.in_or_st = false;
107     ps.bl_line = true;
108     dec_ind = 0;
109     di_stack[ps.dec_nest = 0] = 0;
110     ps.want_blank = ps.in_stmt = ps.ind_stmt = false;
111 
112 
113     scase = ps.pcase = false;
114     squest = 0;
115     sc_end = 0;
116     bp_save = 0;
117     be_save = 0;
118 
119     output = 0;
120 
121 
122 
123     /*--------------------------------------------------*\
124 |   COMMAND LINE SCAN
125 \*--------------------------------------------------
126 */
127 
128     max_col = 78;		/* -l78 */
129     lineup_to_parens = 1;	/* -lp */
130     ps.ljust_decl = 0;		/* -ndj */
131     ps.com_ind = 33;		/* -c33 */
132     star_comment_cont = 1;	/* -sc */
133     ps.ind_size = 8;		/* -i8 */
134     verbose = 0;
135     ps.decl_indent = 16;	/* -di16 */
136     ps.indent_parameters = 1;	/* -ip */
137     ps.decl_com_ind = 0;	/* if this is not set to some positive
138 				 * value by an arg, we will set this equal
139 				 * to ps.com_ind */
140     btype_2 = 1;		/* -br */
141     cuddle_else = 1;		/* -ce */
142     ps.unindent_displace = 0;	/* -d0 */
143     ps.case_indent = 0;		/* -cli0 */
144     format_col1_comments = 1;	/* -fc1 */
145     procnames_start_line = 1;	/* -psl */
146     proc_calls_space = 0;	/* -npcs */
147     comment_delimiter_on_blankline = 1;	/* -cdb */
148     ps.leave_comma = 1;		/* -nbc */
149 
150     for (i = 1; i < argc; ++i)
151 	if (strcmp(argv[i], "-npro") == 0)
152 	    break;
153     if (i >= argc)
154 	set_profile();
155 
156     for (i = 1; i < argc; ++i) {
157 
158 	/*
159 	 * look thru args (if any) for changes to defaults
160 	 */
161 	if (argv[i][0] != '-') {/* no flag on parameter */
162 	    if (input == 0) {	/* we must have the input file */
163 		in_name = argv[i];	/* remember name of input file */
164 		input = fopen(in_name, "r");
165 		if (input == 0) {	/* check for open error */
166 		    printf("Can't open %s\n", argv[i]);
167 		    exit();
168 		}
169 		continue;
170 	    } else if (output == 0) {	/* we have the output file */
171 		out_name = argv[i];	/* remember name of output file */
172 		if (strcmp(in_name, out_name) == 0) {	/* attempt to overwrite
173 							 * the file */
174 		    printf("Input and output files must be different\n");
175 		    exit();
176 		}
177 		output = fopen(out_name, "w");
178 		if (output == 0) {	/* check for create error */
179 		    printf("Can't create %s\n", argv[i]);
180 		    exit();
181 		}
182 		continue;
183 	    }
184 	    printf("Unknown parameter: %s\n", argv[i]);
185 	    exit();
186 	} else
187 	    set_option(argv[i]);
188 
189     }				/* end of for */
190     if (input == 0) {
191 	printf("Usage: indent file [ outfile ] [ options ]\n");
192 	exit();
193     }
194     if (output == 0)
195 	if (troff)
196 	    output = stdout;
197 	else {
198 	    out_name = in_name;
199 	    bakcopy();
200 	}
201     if (ps.com_ind <= 1)
202 	ps.com_ind = 2;		/* dont put normal comments before column
203 				 * 2 */
204     if (block_comment_max_col <= 0)
205 	block_comment_max_col = max_col;
206     if (ps.decl_com_ind <= 0)	/* if not specified by user, set this */
207 	ps.decl_com_ind = ps.ljust_decl ? (ps.com_ind <= 10 ? 2 : ps.com_ind - 8) : ps.com_ind;
208     if (continuation_indent == 0)
209 	continuation_indent = ps.ind_size;
210     fill_buffer();		/* get first batch of stuff into input
211 				 * buffer */
212 
213     parse(semicolon);
214     {
215 	register char *p = buf_ptr;
216 	register    col = 1;
217 
218 	while (1) {
219 	    if (*p == ' ')
220 		col++;
221 	    else if (*p == '\t')
222 		col = ((col - 1) & ~7) + 9;
223 	    else
224 		break;
225 	    p++;
226 	};
227 	if (col > ps.ind_size)
228 	    ps.ind_level = ps.i_l_follow = col / ps.ind_size;
229     }
230     if (troff) {
231 	register char *p = in_name,
232 	           *beg = in_name;
233 
234 	while (*p)
235 	    if (*p++ == '/')
236 		beg = p;
237 	fprintf(output, ".Fn \"%s\"\n", beg);
238     }
239 
240     /*
241      * START OF MAIN LOOP
242      */
243 
244     while (1) {			/* this is the main loop.  it will go
245 				 * until we reach eof */
246 	int         is_procname;
247 
248 	type_code = lexi();	/* lexi reads one token.  The actual
249 				 * characters read are stored in "token".
250 				 * lexi returns a code indicating the type
251 				 * of token */
252 	is_procname = ps.procname[0];
253 
254 	/*
255 	 * The following code moves everything following an if (), while
256 	 * (), else, etc. up to the start of the following stmt to a
257 	 * buffer.  This allows proper handling of both kinds of brace
258 	 * placement.
259 	 */
260 
261 	flushed_nl = false;
262 	while (ps.search_brace) {	/* if we scanned an if(), while(),
263 					 * etc., we might need to copy
264 					 * stuff into a buffer we must
265 					 * loop, copying stuff into
266 					 * save_com, until we find the
267 					 * start of the stmt which follows
268 					 * the if, or whatever */
269 	    switch (type_code) {
270 		case newline:
271 		    ++line_no;
272 		    flushed_nl = true;
273 		case form_feed:
274 		    break;	/* form feeds and newlines found here will
275 				 * be ignored */
276 
277 		case lbrace:	/* this is a brace that starts the
278 				 * compound stmt */
279 		    if (sc_end == 0) {	/* ignore buffering if a comment
280 					 * wasnt stored up */
281 			ps.search_brace = false;
282 			goto check_type;
283 		    }
284 		    if (btype_2) {
285 			save_com[0] = '{';	/* we either want to put
286 						 * the brace right after
287 						 * the if */
288 			goto sw_buffer;	/* go to common code to get out of
289 					 * this loop */
290 		    }
291 		case comment:	/* we have a comment, so we must copy it
292 				 * into the buffer */
293 		    if (!flushed_nl) {
294 			if (sc_end == 0) {	/* if this is the first
295 						 * comment, we must set up
296 						 * the buffer */
297 			    save_com[0] = save_com[1] = ' ';
298 			    sc_end = &(save_com[2]);
299 			} else {
300 			    *sc_end++ = '\n';	/* add newline between
301 						 * comments */
302 			    *sc_end++ = ' ';
303 			    --line_no;
304 			}
305 			*sc_end++ = '/';	/* copy in start of
306 						 * comment */
307 			*sc_end++ = '*';
308 
309 			for (;;) {	/* loop until we get to the end of
310 					 * the comment */
311 			    *sc_end = *buf_ptr++;
312 			    if (buf_ptr >= buf_end)
313 				fill_buffer();
314 
315 			    if (*sc_end++ == '*' && *buf_ptr == '/')
316 				break;	/* we are at end of comment */
317 
318 			    if (sc_end >= &(save_com[sc_size])) {	/* check for temp buffer
319 									 * overflow */
320 				diag(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever.");
321 				fflush(output);
322 				exit();
323 			    }
324 			}
325 			*sc_end++ = '/';	/* add ending slash */
326 			if (++buf_ptr >= buf_end)	/* get past / in buffer */
327 			    fill_buffer();
328 			break;
329 		    }
330 		default:	/* it is the start of a normal statment */
331 		    if (flushed_nl)	/* if we flushed a newline, make
332 					 * sure it is put back */
333 			force_nl = true;
334 		    if (type_code == sp_paren && *token == 'i'
335 			&& last_else && ps.else_if
336 			|| type_code == sp_nparen && *token == 'e'
337 			&& e_code != s_code && e_code[-1] == '}')
338 			force_nl = false;
339 
340 		    if (sc_end == 0) {	/* ignore buffering if comment
341 					 * wasnt saved up */
342 			ps.search_brace = false;
343 			goto check_type;
344 		    }
345 		    if (force_nl) {	/* if we should insert a nl here,
346 					 * put it into the buffer */
347 			force_nl = false;
348 			--line_no;	/* this will be re-increased when
349 					 * the nl is read from the buffer */
350 			*sc_end++ = '\n';
351 			*sc_end++ = ' ';
352 			if (verbose && !flushed_nl)	/* print error msg if
353 							 * the line was not
354 							 * already broken */
355 			    diag(0, "Line broken");
356 			flushed_nl = false;
357 		    }
358 		    for (t_ptr = token; *t_ptr; ++t_ptr)
359 			*sc_end++ = *t_ptr;	/* copy token into temp
360 						 * buffer */
361 
362 	    sw_buffer:
363 		    ps.search_brace = false;	/* stop looking for start
364 						 * of stmt */
365 		    bp_save = buf_ptr;	/* save current input buffer */
366 		    be_save = buf_end;
367 		    buf_ptr = save_com;	/* fix so that subsequent calls to
368 					 * lexi will take tokens out of
369 					 * save_com */
370 		    *sc_end++ = ' ';	/* add trailing blank, just in
371 					 * case */
372 		    buf_end = sc_end;
373 		    sc_end = 0;
374 		    break;
375 	    }			/* end of switch */
376 	    if (type_code != 0)	/* we must make this check, just in case
377 				 * there was an unexpected EOF */
378 		type_code = lexi();	/* read another token */
379 	    is_procname = ps.procname[0];
380 	}			/* end of while (serach_brace) */
381 	last_else = 0;
382 check_type:
383 	if (type_code == 0) {	/* we got eof */
384 	    if (s_lab != e_lab || s_code != e_code
385 		|| s_com != e_com)	/* must dump end of line */
386 		dump_line();
387 	    if (ps.tos > 1)	/* check for balanced braces */
388 		diag(1, "Stuff missing from end of file.");
389 
390 	    if (verbose) {
391 		printf("There were %d output lines and %d comments\n",
392 		       ps.out_lines, ps.out_coms);
393 		printf("(Lines with comments)/(Lines with code): %6.3f\n",
394 		       (1.0 * ps.com_lines) / code_lines);
395 	    }
396 	    fflush(output);
397 	    exit();
398 	}
399 	if (
400 	    (type_code != comment) &&
401 	    (type_code != newline) &&
402 	    (type_code != preesc) &&
403 	    (type_code != form_feed)) {
404 	    if (
405 		force_nl
406 		&&
407 		(type_code != semicolon) &&
408 		(
409 		 type_code != lbrace
410 		 ||
411 		 !btype_2
412 		 )) {		/* we should force a broken line here */
413 		if (verbose && !flushed_nl)
414 		    diag(0, "Line broken");
415 		flushed_nl = false;
416 		dump_line();
417 		ps.want_blank = false;	/* dont insert blank at line start */
418 		force_nl = false;
419 	    }
420 	    ps.in_stmt = true;	/* turn on flag which causes an extra
421 				 * level of indentation. this is turned
422 				 * off by a ; or '}' */
423 	    if (s_com != e_com) {	/* the turkey has embedded a
424 					 * comment in a line. fix it */
425 		*e_code++ = ' ';
426 		for (t_ptr = s_com; *t_ptr; ++t_ptr)
427 		    *e_code++ = *t_ptr;
428 		*e_code++ = ' ';
429 		*e_code = '\0';	/* null terminate code sect */
430 		ps.want_blank = false;
431 		e_com = s_com;
432 	    }
433 	} else if (type_code != comment)	/* preserve force_nl thru
434 						 * a comment */
435 	    force_nl = false;
436 
437 	/*
438 	 * cancel forced newline after newline, form feed, etc
439 	 */
440 
441 
442 
443 	/*----------------------------------------------------*\
444 |   do switch on type of token scanned
445 \*----------------------------------------------------*/
446 	switch (type_code) {	/* now, decide what to do with the token */
447 
448 	    case form_feed:	/* found a form feed in line */
449 		ps.use_ff = true;	/* a form feed is treated much
450 					 * like a newline */
451 		dump_line();
452 		ps.want_blank = false;
453 		break;
454 
455 	    case newline:
456 		if (ps.last_token != comma || ps.p_l_follow > 0
457 		    || !ps.leave_comma || !break_comma || s_com != e_com) {
458 		    dump_line();
459 		    ps.want_blank = false;
460 		}
461 		++line_no;	/* keep track of input line number */
462 		break;
463 
464 	    case lparen:	/* got a '(' or '[' */
465 		++ps.p_l_follow;/* count parens to make Healy happy */
466 		if (ps.want_blank && *token != '[' &&
467 		    (ps.last_token != ident || proc_calls_space
468 		     || (ps.its_a_keyword && !ps.sizeof_keyword)))
469 		    *e_code++ = ' ';
470 		if (ps.in_decl && !ps.block_init)
471 		    if (troff && !ps.dumped_decl_indent) {
472 			ps.dumped_decl_indent = 1;
473 			sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
474 			e_code += strlen(e_code);
475 		    } else {
476 			while ((e_code - s_code) < dec_ind)
477 			    *e_code++ = ' ';
478 			*e_code++ = token[0];
479 		} else
480 		    *e_code++ = token[0];
481 		ps.paren_indents[ps.p_l_follow - 1] = e_code - s_code;
482 		ps.want_blank = false;
483 		if (ps.in_or_st && *token == '(') {
484 
485 		    /*
486 		     * this is a kluge to make sure that declarations will
487 		     * be aligned right if proc decl has an explicit type
488 		     * on it, i.e. "int a(x) {..."
489 		     */
490 		    parse(semicolon);	/* I said this was a kluge... */
491 		    ps.in_or_st = false;	/* turn off flag for
492 						 * structure decl or
493 						 * initialization */
494 		}
495 		if (ps.sizeof_keyword) ps.sizeof_mask |= 1<<ps.p_l_follow;
496 		break;
497 
498 	    case rparen:	/* got a ')' or ']' */
499 		if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.sizeof_mask) {
500 		    ps.last_u_d = true;
501 		    ps.cast_mask &= (1 << ps.p_l_follow) - 1;
502 		}
503 		ps.sizeof_mask &= (1 << ps.p_l_follow) - 1;
504 		if (--ps.p_l_follow < 0) {
505 		    ps.p_l_follow = 0;
506 		    diag(0, "Extra %c", *token);
507 		}
508 		if (e_code == s_code)	/* if the paren starts the line */
509 		    ps.paren_level = ps.p_l_follow;	/* then indent it */
510 
511 		*e_code++ = token[0];
512 		ps.want_blank = true;
513 
514 		if (sp_sw && (ps.p_l_follow == 0)) {	/* check for end of if
515 							 * (...), or some such */
516 		    sp_sw = false;
517 		    force_nl = true;	/* must force newline after if */
518 		    ps.last_u_d = true;	/* inform lexi that a following
519 					 * operator is unary */
520 		    ps.in_stmt = false;	/* dont use stmt continuation
521 					 * indentation */
522 
523 		    parse(hd_type);	/* let parser worry about if, or
524 					 * whatever */
525 		}
526 		ps.search_brace = btype_2;	/* this should insure that
527 						 * constructs such as
528 						 * main(){...} and
529 						 * int[]{...} have their
530 						 * braces put in the right
531 						 * place */
532 		break;
533 
534 	    case unary_op:	/* this could be any unary operation */
535 		if (ps.want_blank)
536 		    *e_code++ = ' ';
537 
538 		if (troff && !ps.dumped_decl_indent && ps.in_decl) {
539 		    sprintf(e_code, "\\c\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
540 		    ps.dumped_decl_indent = 1;
541 		    e_code += strlen(e_code);
542 		} else {
543 		    char       *res = token;
544 
545 		    if (ps.in_decl && !ps.block_init) {	/* if this is a unary op
546 							 * in a declaration, we
547 							 * should indent this
548 							 * token */
549 			for (i = 0; token[i]; ++i);	/* find length of token */
550 			while ((e_code - s_code) < (dec_ind - i))
551 			    *e_code++ = ' ';	/* pad it */
552 		    }
553 		    if (troff && token[0] == '-' && token[1] == '>')
554 			res = "\\(->";
555 		    for (t_ptr = res; *t_ptr; ++t_ptr)
556 			*e_code++ = *t_ptr;
557 		}
558 		ps.want_blank = false;
559 		break;
560 
561 	    case binary_op:	/* any binary operation */
562 	do_binary:
563 		if (ps.want_blank)
564 		    *e_code++ = ' ';
565 		{
566 		    char       *res = token;
567 
568 		    if (troff)
569 			switch (token[0]) {
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 = "\\(!=";
581 				break;
582 			    case '|':
583 				if (token[1] == '|')
584 				    res = "\\(br\\(br";
585 				else if (token[1] == 0)
586 				    res = "\\(br";
587 				break;
588 			}
589 		    for (t_ptr = res; *t_ptr; ++t_ptr)
590 			*e_code++ = *t_ptr;	/* move the operator */
591 		}
592 		ps.want_blank = true;
593 		break;
594 
595 	    case postop:	/* got a trailing ++ or -- */
596 		*e_code++ = token[0];
597 		*e_code++ = token[1];
598 		ps.want_blank = true;
599 		break;
600 
601 	    case question:	/* got a ? */
602 		squest++;	/* this will be used when a later colon
603 				 * appears so we can distinguish the
604 				 * <c>?<n>:<n> construct */
605 		if (ps.want_blank)
606 		    *e_code++ = ' ';
607 		*e_code++ = '?';
608 		ps.want_blank = true;
609 		break;
610 
611 	    case casestmt:	/* got word 'case' or 'default' */
612 		scase = true;	/* so we can process the later colon
613 				 * properly */
614 		goto copy_id;
615 
616 	    case colon:	/* got a ':' */
617 		if (squest > 0) {	/* it is part of the <c>?<n>: <n>
618 					 * construct */
619 		    --squest;
620 		    if (ps.want_blank)
621 			*e_code++ = ' ';
622 		    *e_code++ = ':';
623 		    ps.want_blank = true;
624 		    break;
625 		}
626 		if (ps.in_decl) {
627 		    *e_code++ = ':';
628 		    ps.want_blank = false;
629 		    break;
630 		}
631 		ps.in_stmt = false;	/* seeing a label does not imply
632 					 * we are in a stmt */
633 		for (t_ptr = s_code; *t_ptr; ++t_ptr)
634 		    *e_lab++ = *t_ptr;	/* turn everything so far into a
635 					 * label */
636 		e_code = s_code;
637 		*e_lab++ = ':';
638 		*e_lab++ = ' ';
639 		*e_lab = '\0';
640 
641 		force_nl = ps.pcase = scase;	/* ps.pcase will be used
642 						 * by dump_line to decide
643 						 * how to indent the
644 						 * label. force_nl will
645 						 * force a case n: to be
646 						 * on a line by itself */
647 		scase = false;
648 		ps.want_blank = false;
649 		break;
650 
651 	    case semicolon:	/* got a ';' */
652 		ps.in_or_st = false;	/* we are not in an initialization
653 					 * or structure declaration */
654 		scase = false;	/* these will only need resetting in a
655 				 * error */
656 		squest = 0;
657 		if (ps.last_token == rparen)
658 		    ps.in_parameter_declaration = 0;
659 		ps.cast_mask = 0;
660 		ps.sizeof_mask = 0;
661 		ps.block_init = 0;
662 		ps.just_saw_decl--;
663 
664 		if (ps.in_decl && s_code == e_code && !ps.block_init)
665 		    while ((e_code - s_code) < (dec_ind - 1))
666 			*e_code++ = ' ';
667 
668 		ps.in_decl = (ps.dec_nest > 0);	/* if we were in a first
669 						 * level structure
670 						 * declaration, we arent
671 						 * any more */
672 
673 		if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) {
674 
675 		    /*
676 		     * This should be true iff there were unbalanced
677 		     * parens in the stmt.  It is a bit complicated,
678 		     * because the semicolon might be in a for stmt
679 		     */
680 		    diag(1, "Unbalanced parens");
681 		    ps.p_l_follow = 0;
682 		    if (sp_sw) {/* this is a check for a if, while, etc.
683 				 * with unbalanced parens */
684 			sp_sw = false;
685 			parse(hd_type);	/* dont lose the if, or whatever */
686 		    }
687 		}
688 		*e_code++ = ';';
689 		ps.want_blank = true;
690 		ps.in_stmt = (ps.p_l_follow > 0);	/* we are no longer in
691 							 * the middle of a stmt */
692 
693 		if (!sp_sw) {	/* if not if for (;;) */
694 		    parse(semicolon);	/* let parser know about end of
695 					 * stmt */
696 		    force_nl = true;	/* force newline after a end of
697 					 * stmt */
698 		}
699 		break;
700 
701 	    case lbrace:	/* got a '{' */
702 		ps.in_stmt = false;	/* dont indent the {} */
703 		if (!ps.block_init)
704 		    force_nl = true;	/* force other stuff on same line
705 					 * as '{' onto new line */
706 
707 		if (s_code != e_code && !ps.block_init) {
708 		    if (!btype_2) {
709 			dump_line();
710 			ps.want_blank = false;
711 		    } else if (ps.in_parameter_declaration && !ps.in_or_st) {
712 			ps.i_l_follow = 0;
713 			dump_line();
714 			ps.want_blank = false;
715 		    }
716 		}
717 		if (ps.in_parameter_declaration)
718 		    prefix_blankline_requested = 0;
719 
720 		if (ps.p_l_follow > 0) {	/* check for preceeding
721 						 * unbalanced parens */
722 		    diag(1, "Unbalanced parens");
723 		    ps.p_l_follow = 0;
724 		    if (sp_sw) {/* check for unclosed if, for, etc. */
725 			sp_sw = false;
726 			parse(hd_type);
727 			ps.ind_level = ps.i_l_follow;
728 		    }
729 		}
730 		if (s_code == e_code)
731 		    ps.ind_stmt = false;	/* dont put extra
732 						 * indentation on line
733 						 * with '{' */
734 		if (ps.in_decl && ps.in_or_st) {	/* this is either a
735 							 * structure declaration
736 							 * or an init */
737 		    di_stack[ps.dec_nest++] = dec_ind;
738 		    dec_ind = 0;
739 		} else {
740 		    ps.decl_on_line = false;	/* we cant be in the
741 						 * middle of a
742 						 * declaration, so dont do
743 						 * special indentation of
744 						 * comments */
745 		    ps.in_parameter_declaration = 0;
746 		}
747 		parse(lbrace);	/* let parser know about this */
748 		if (ps.want_blank)	/* put a blank before '{' if '{'
749 					 * is not at start of line */
750 		    *e_code++ = ' ';
751 		ps.want_blank = false;
752 		*e_code++ = '{';
753 		ps.just_saw_decl = 0;
754 		break;
755 
756 	    case rbrace:	/* got a '}' */
757 		if (ps.p_l_follow) {	/* check for unclosed if, for,
758 					 * else. */
759 		    diag(1, "Unbalanced parens");
760 		    ps.p_l_follow = 0;
761 		    sp_sw = false;
762 		}
763 		ps.just_saw_decl = 0;
764 		if (s_code != e_code && !ps.block_init) {	/* '}' must be first on
765 								 * line */
766 		    if (verbose)
767 			diag(0, "Line broken");
768 		    dump_line();
769 		}
770 		*e_code++ = '}';
771 		ps.want_blank = true;
772 		ps.in_stmt = ps.ind_stmt = false;
773 		if (ps.dec_nest > 0) {	/* we are in multi-level structure
774 					 * declaration */
775 		    dec_ind = di_stack[--ps.dec_nest];
776 		    if (ps.dec_nest == 0 && !ps.in_parameter_declaration)
777 			ps.just_saw_decl = 2;
778 		    ps.in_decl = true;
779 		}
780 		prefix_blankline_requested = 0;
781 		parse(rbrace);	/* let parser know about this */
782 		ps.search_brace = cuddle_else && ps.p_stack[ps.tos] == ifhead && ps.il[ps.tos] >= ps.ind_level;
783 		if (ps.tos <= 1 && blanklines_after_procs && ps.dec_nest <= 0)
784 		    postfix_blankline_requested = 1;
785 		break;
786 
787 	    case swstmt:	/* got keyword "switch" */
788 		sp_sw = true;
789 		hd_type = swstmt;	/* keep this for when we have seen
790 					 * the expression */
791 		goto copy_id;	/* go move the token into buffer */
792 
793 	    case sp_paren:	/* token is if, while, for */
794 		sp_sw = true;	/* the interesting stuff is done after the
795 				 * expression is scanned */
796 		hd_type = (*token == 'i' ? ifstmt :
797 			   (*token == 'w' ? whilestmt : forstmt));
798 
799 		/*
800 		 * remember the type of header for later use by parser
801 		 */
802 		goto copy_id;	/* copy the token into line */
803 
804 	    case sp_nparen:	/* got else, do */
805 		ps.in_stmt = false;
806 		if (*token == 'e') {
807 		    if (e_code != s_code && (!cuddle_else || e_code[-1] != '}')) {
808 			if (verbose)
809 			    diag(0, "Line broken");
810 			dump_line();	/* make sure this starts a line */
811 			ps.want_blank = false;
812 		    }
813 		    force_nl = true;	/* also, following stuff must go
814 					 * onto new line */
815 		    last_else = 1;
816 		    parse(elselit);
817 		} else {
818 		    if (e_code != s_code) {	/* make sure this starts a
819 						 * line */
820 			if (verbose)
821 			    diag(0, "Line broken");
822 			dump_line();
823 			ps.want_blank = false;
824 		    }
825 		    force_nl = true;	/* also, following stuff must go
826 					 * onto new line */
827 		    last_else = 0;
828 		    parse(dolit);
829 		}
830 		goto copy_id;	/* move the token into line */
831 
832 	    case decl:		/* we have a declaration type (int,
833 				 * register, etc.) */
834 		parse(decl);	/* let parser worry about indentation */
835 		if (ps.last_token == rparen && ps.tos <= 1)
836 		    ps.in_parameter_declaration = 1;
837 		if (ps.in_parameter_declaration && ps.indent_parameters && ps.dec_nest == 0) {
838 		    ps.ind_level = ps.i_l_follow = 1;
839 		    ps.ind_stmt = 0;
840 		}
841 		ps.in_or_st = true;	/* this might be a structure or
842 					 * initialization declaration */
843 		ps.in_decl = ps.decl_on_line = true;
844 		if ( /* !ps.in_or_st && */ ps.dec_nest <= 0)
845 		    ps.just_saw_decl = 2;
846 		prefix_blankline_requested = 0;
847 		for (i = 0; token[i++];);	/* get length of token */
848 
849 		/*
850 		 * dec_ind = e_code - s_code + (ps.decl_indent>i ?
851 		 * ps.decl_indent : i);
852 		 */
853 		dec_ind = ps.decl_indent > 0 ? ps.decl_indent : i;
854 		goto copy_id;
855 
856 	    case ident:	/* got an identifier or constant */
857 		if (ps.in_decl) {	/* if we are in a declaration, we
858 					 * must indent identifier */
859 		    if (ps.want_blank)
860 			*e_code++ = ' ';
861 		    ps.want_blank = false;
862 		    if (is_procname == 0 || !procnames_start_line) {
863 			if (!ps.block_init)
864 			    if (troff && !ps.dumped_decl_indent) {
865 				sprintf(e_code, "\\c\n.De %dp+\200p\n", dec_ind * 7);
866 				ps.dumped_decl_indent = 1;
867 				e_code += strlen(e_code);
868 			    } else
869 				while ((e_code - s_code) < dec_ind)
870 				    *e_code++ = ' ';
871 		    } else {
872 			if (dec_ind && s_code != e_code)
873 			    dump_line();
874 			dec_ind = 0;
875 			ps.want_blank = false;
876 		    }
877 		} else if (sp_sw && ps.p_l_follow == 0) {
878 		    sp_sw = false;
879 		    force_nl = true;
880 		    ps.last_u_d = true;
881 		    ps.in_stmt = false;
882 		    parse(hd_type);
883 		}
884 	copy_id:
885 		if (ps.want_blank)
886 		    *e_code++ = ' ';
887 		if (troff && ps.its_a_keyword) {
888 		    *e_code++ = BACKSLASH;
889 		    *e_code++ = 'f';
890 		    *e_code++ = 'B';
891 		}
892 		for (t_ptr = token; *t_ptr; ++t_ptr)
893 		    *e_code++ = *t_ptr;
894 		if (troff && ps.its_a_keyword) {
895 		    *e_code++ = BACKSLASH;
896 		    *e_code++ = 'f';
897 		    *e_code++ = 'R';
898 		}
899 		ps.want_blank = true;
900 		break;
901 
902 	    case period:	/* treat a period kind of like a binary
903 				 * operation */
904 		*e_code++ = '.';/* move the period into line */
905 		ps.want_blank = false;	/* dont put a blank after a period */
906 		break;
907 
908 	    case comma:
909 		ps.want_blank = (s_code != e_code);	/* only put blank after
910 							 * comma if comma does
911 							 * not start the line */
912 		if (ps.in_decl && is_procname == 0 && !ps.block_init)
913 		    while ((e_code - s_code) < (dec_ind - 1))
914 			*e_code++ = ' ';
915 
916 		*e_code++ = ',';
917 		if (ps.p_l_follow == 0) {
918 		    ps.block_init = 0;
919 		    if (break_comma && !ps.leave_comma)
920 			force_nl = true;
921 		}
922 		break;
923 
924 	    case preesc:	/* got the character '#' */
925 		if ((s_com != e_com) ||
926 		    (s_lab != e_lab) ||
927 		    (s_code != e_code))
928 		    dump_line();
929 		*e_lab++ = '#';	/* move whole line to 'label' buffer */
930 		{
931 		    int         in_comment = 0;
932 		    char       *com_start = 0;
933 		    char        quote = 0;
934 		    char       *com_end = 0;
935 
936 		    while (*buf_ptr != '\n' || in_comment) {
937 			*e_lab = *buf_ptr++;
938 			if (buf_ptr >= buf_end)
939 			    fill_buffer();
940 			switch (*e_lab++) {
941 			    case BACKSLASH:
942 				if (troff)
943 				    *e_lab++ = BACKSLASH;
944 				if (!in_comment) {
945 				    *e_lab++ = *buf_ptr++;
946 				    if (buf_ptr >= buf_end)
947 					fill_buffer();
948 				}
949 				break;
950 			    case '/':
951 				if (*buf_ptr == '*' && !in_comment && !quote) {
952 				    in_comment = 1;
953 				    *e_lab++ = *buf_ptr++;
954 				    com_start = e_lab - 2;
955 				}
956 				break;
957 			    case '"':
958 				if (quote == '"')
959 				    quote = 0;
960 				break;
961 			    case '\'':
962 				if (quote == '\'')
963 				    quote = 0;
964 				break;
965 			    case '*':
966 				if (*buf_ptr == '/' && in_comment) {
967 				    in_comment = 0;
968 				    *e_lab++ = *buf_ptr++;
969 				    com_end = e_lab;
970 				}
971 				break;
972 			}
973 		    }
974 		    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
975 			e_lab--;
976 		    if (e_lab == com_end && bp_save == 0) {	/* comment on
977 								 * preprocessor line */
978 			if (sc_end == 0)	/* if this is the first
979 						 * comment, we must set up
980 						 * the buffer */
981 			    sc_end = &(save_com[0]);
982 			else {
983 			    *sc_end++ = '\n';	/* add newline between
984 						 * comments */
985 			    *sc_end++ = ' ';
986 			    --line_no;
987 			}
988 			bcopy(com_start, sc_end, com_end - com_start);
989 			sc_end += com_end - com_start;
990 			e_lab = com_start;
991 			while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
992 			    e_lab--;
993 			bp_save = buf_ptr;	/* save current input
994 						 * buffer */
995 			be_save = buf_end;
996 			buf_ptr = save_com;	/* fix so that subsequent
997 						 * calls to lexi will take
998 						 * tokens out of save_com */
999 			*sc_end++ = ' ';	/* add trailing blank,
1000 						 * just in case */
1001 			buf_end = sc_end;
1002 			sc_end = 0;
1003 		    }
1004 		    *e_lab = '\0';	/* null terminate line */
1005 		    ps.pcase = false;
1006 		}
1007 		if (strncmp(s_lab, "#if", 3) == 0)
1008 		    if (ifdef_level < sizeof state_stack / sizeof state_stack[0]) {
1009 			match_state[ifdef_level].tos = -1;
1010 			state_stack[ifdef_level++] = ps;
1011 		    } else
1012 			diag(1, "#if stack overflow");
1013 		else if (strncmp(s_lab, "#else", 5) == 0)
1014 		    if (ifdef_level <= 0)
1015 			diag(1, "Unmatched #else");
1016 		    else {
1017 			match_state[ifdef_level - 1] = ps;
1018 			ps = state_stack[ifdef_level - 1];
1019 		} else if (strncmp(s_lab, "#endif", 6) == 0)
1020 		    if (ifdef_level <= 0)
1021 			diag(1, "Unmatched #endif");
1022 		    else {
1023 			ifdef_level--;
1024 #ifdef undef
1025 
1026 			/*
1027 			 * This match needs to be more intelligent before
1028 			 * the message is useful
1029 			 */
1030 			if (match_state[ifdef_level].tos >= 0
1031 			    && bcmp(&ps, &match_state[ifdef_level], sizeof ps))
1032 			    diag(0, "Syntactically inconsistant #ifdef alternatives.");
1033 #endif
1034 		    }
1035 		break;		/* subsequent processing of the newline
1036 				 * character will cause the line to be
1037 				 * printed */
1038 
1039 	    case comment:	/* we have gotten a /*  this is a biggie */
1040 	proc_comment:
1041 		if (flushed_nl) {	/* we should force a broken line
1042 					 * here */
1043 		    flushed_nl = false;
1044 		    dump_line();
1045 		    ps.want_blank = false;	/* dont insert blank at
1046 						 * line start */
1047 		    force_nl = false;
1048 		}
1049 		pr_comment();
1050 		break;
1051 	}			/* end of big switch stmt */
1052 	*e_code = '\0';		/* make sure code section is null
1053 				 * terminated */
1054 	if (type_code != comment && type_code != newline && type_code != preesc)
1055 	    ps.last_token = type_code;
1056     }				/* end of main while (1) loop */
1057 };
1058 
1059 /*
1060  * copy input file to backup file if in_name is /blah/blah/blah/file, then
1061  * backup file will be ".Bfile" then make the backup file the input and
1062  * original input file the output
1063  */
1064 bakcopy()
1065 {
1066     int         n,
1067                 bakchn;
1068     char        buff[512];
1069     register char *p;
1070 
1071     /* construct file name .Bfile */
1072     for (p = in_name; *p; p++);	/* skip to end of string */
1073     while (p > in_name && *p != '/')	/* find last '/' */
1074 	p--;
1075     if (*p == '/')
1076 	p++;
1077     sprintf(bakfile, "%s.BAK", p);
1078 
1079     /* copy in_name to backup file */
1080     bakchn = creat(bakfile, 0600);
1081     if (bakchn < 0) {
1082 	printf("can't create backup file \"%s\"\n", bakfile);
1083 	exit();
1084     }
1085     while (n = read(fileno(input), buff, 512))
1086 	write(bakchn, buff, n);
1087     close(bakchn);
1088     fclose(input);
1089 
1090     /* re-open backup file as the input file */
1091     input = fopen(bakfile, "r");
1092     if (input < 0) {
1093 	printf("can't re-open backup file\n");
1094 	exit();
1095     }
1096     /* now the original input file will be the output */
1097     output = fopen(in_name, "w");
1098     if (output == 0) {
1099 	printf("can't create %s\n", in_name);
1100 	unlink(bakfile);
1101 	exit();
1102     }
1103 }
1104 
1105 
1106 char       *param_start;
1107 
1108 eqin(s1, s2)
1109     register char *s1;
1110     register char *s2;
1111 {
1112     while (*s1) {
1113 	if (*s1++ != *s2++)
1114 	    return (false);
1115     }
1116     param_start = s2;
1117     return (true);
1118 }
1119 
1120 
1121 set_option(arg)
1122     char       *arg;
1123 {
1124     if (!eqin("-npro", arg))
1125 	if (eqin("-lc", arg))	/* comment line length */
1126 	    block_comment_max_col = atoi(param_start);
1127 	else if (eqin("-lp", arg))
1128 	    lineup_to_parens = 1;
1129 	else if (eqin("-nlp", arg))
1130 	    lineup_to_parens = 0;
1131 	else if (eqin("-l", arg))	/* line length */
1132 	    max_col = atoi(param_start);
1133 	else if (eqin("-psl", arg))	/* if true, the names of
1134 					 * procedures being defined get
1135 					 * placed in column 1 (ie. a
1136 					 * newline is placed between the
1137 					 * type of the procedure and its
1138 					 * name) */
1139 	    procnames_start_line = 1;
1140 	else if (eqin("-npsl", arg))
1141 	    procnames_start_line = 0;
1142 	else if (eqin("-fc1", arg))
1143 	    format_col1_comments = 1;
1144 	else if (eqin("-nfc1", arg))
1145 	    format_col1_comments = 0;
1146 	else if (eqin("-pcs", arg))	/* If true, procedure calls look
1147 					 * like: foo(bar) rather than foo
1148 					 * (bar) */
1149 	    proc_calls_space = 1;
1150 	else if (eqin("-npcs", arg))
1151 	    proc_calls_space = 0;
1152 	else if (eqin("-ip", arg))	/* indent parameters */
1153 	    ps.indent_parameters = 1;
1154 	else if (eqin("-nip", arg))	/* no indent parameters */
1155 	    ps.indent_parameters = 0;
1156 	else if (eqin("-cli", arg)) {	/* case label indent */
1157 	    extern float atof();
1158 
1159 	    ps.case_indent = atof(param_start);
1160 	}
1161 	else if (eqin("-ci",arg))
1162 	    continuation_indent = atoi(param_start);
1163 	else if (eqin("-cdb", arg))	/* comment delimiters should be on
1164 					 * lines by themselves */
1165 	    comment_delimiter_on_blankline = 1;
1166 	else if (eqin("-ncdb", arg))	/* comment delimiters shouldnt be
1167 					 * on lines by themselves */
1168 	    comment_delimiter_on_blankline = 0;
1169 	else if (eqin("-i", arg))	/* indent width */
1170 	    ps.ind_size = atoi(param_start);
1171 	else if (eqin("-cd", arg))	/* indent for comments on
1172 					 * declarations */
1173 	    ps.decl_com_ind = atoi(param_start);
1174 	else if (eqin("-ce", arg))	/* true iff 'else' should cuddle
1175 					 * up to '}' */
1176 	    cuddle_else = 1;
1177 	else if (eqin("-c", arg))	/* comment indent */
1178 	    ps.com_ind = atoi(param_start);
1179 	else if (eqin("-v", arg))	/* spew out rubbish */
1180 	    verbose = true;
1181 	else if (eqin("-nv", arg))	/* keep quiet */
1182 	    verbose = false;
1183 	else if (eqin("-dj", arg))
1184 	    ps.ljust_decl = true;
1185 	else if (eqin("-ndj", arg))
1186 	    ps.ljust_decl = false;
1187 	else if (eqin("-nbc", arg))	/* dont break after commas in
1188 					 * declarations */
1189 	    ps.leave_comma = true;
1190 	else if (eqin("-bc", arg))	/* break after commas in
1191 					 * declarations */
1192 	    ps.leave_comma = false;
1193 	else if (eqin("-di", arg))	/* indent from type to varname in
1194 					 * a declaration */
1195 	    ps.decl_indent = atoi(param_start);
1196 	else if (eqin("-d", arg))
1197 	    ps.unindent_displace = atoi(param_start);
1198 	else if (eqin("-br", arg))
1199 	    btype_2 = true;
1200 	else if (eqin("-bl", arg))
1201 	    btype_2 = false;
1202 	else if (eqin("-st", arg)) {	/* input and output on standard IO */
1203 	    if (input == 0)
1204 		input = stdin;
1205 	    if (output == 0)
1206 		output = stdout;
1207 	} else if (eqin("-ei", arg))	/* else-ifs should be stuck
1208 					 * together */
1209 	    ps.else_if = 1;
1210 	else if (eqin("-nei", arg))	/* else-ifs should be broken apart */
1211 	    ps.else_if = 0;
1212 	else if (eqin("-nce", arg))	/* else should always start a line */
1213 	    cuddle_else = 0;
1214 	else if (eqin("-sc", arg))	/* comment continuations should
1215 					 * start with a * */
1216 	    star_comment_cont = 1;
1217 	else if (eqin("-nsc", arg))
1218 	    star_comment_cont = 0;	/* comments shouldnt start with a
1219 					 * star */
1220 	else if (eqin("-bap", arg))	/* blanklines after procedures */
1221 	    blanklines_after_procs = 1;
1222 	else if (eqin("-nbap", arg))	/* blanklines after procedures */
1223 	    blanklines_after_procs = 0;
1224 	else if (eqin("-sob", arg))	/* swallow optional blanklines */
1225 	    swallow_optional_blanklines = 1;
1226 	else if (eqin("-nsob", arg))	/* swallow optional blanklines */
1227 	    swallow_optional_blanklines = 0;
1228 	else if (eqin("-bad", arg))	/* blanklines after declarations */
1229 	    blanklines_after_declarations = 1;
1230 	else if (eqin("-nbad", arg))	/* blanklines after declarations */
1231 	    blanklines_after_declarations = 0;
1232 	else if (eqin("-bbb", arg))	/* blanklines before blockcomments */
1233 	    blanklines_before_blockcomments = 1;
1234 	else if (eqin("-nbbb", arg))	/* blanklines before blockcomments */
1235 	    blanklines_before_blockcomments = 0;
1236 	else if (eqin("-troff", arg))
1237 	    troff = 1;
1238 	else if (arg[0] == '-' && arg[1] == 'T')	/* -Ttypename */
1239 	    addkey(arg + 2, 4);
1240 	else {			/* illegal arg given */
1241 	    printf("Unknown parameter: %s\n", arg);
1242 	    exit();
1243 	}
1244 }
1245 
1246 
1247 /*
1248  * GETPRO - get profile file profile file is max 127 characters
1249  */
1250 getpro(name, buf, len)
1251     char       *name,		/* profile file name, as in '.indent.pro' */
1252                *buf;		/* will receive contents of .pro file */
1253 {
1254     register    chn,
1255                 n;
1256     char        file[100];
1257 
1258     file[0] = 0;
1259     strcat(file, getenv("HOME"));
1260     strcat(file, "/");
1261     strcat(file, name);
1262     if ((chn = open(name, 0)) < 0)
1263 	chn = open(file, 0);
1264     if (chn < 0)
1265 	return (-1);
1266     n = read(chn, buf, len);
1267     if (n < 0)
1268 	return (-1);
1269     buf[n--] = 0;		/* null terminate line */
1270     if (buf[n] == '\n')
1271 	buf[n] = 0;
1272     close(chn);
1273     return (0);
1274 }
1275 
1276 
1277 /*
1278  * strip off arguments in a string: p is address of a character pointer
1279  * nextchr returns pointer to front of first arg arg is null terminated. p
1280  * is reset to after arg for subsequent calls
1281  */
1282 char       *
1283 nxtarg(p)
1284     char      **p;
1285 {
1286     register char *f,
1287                *b;
1288 
1289     f = *p;
1290     while (*f && *f <= ' ')
1291 	f++;
1292     b = f;
1293     while (*b > ' ')
1294 	b++;
1295     if (*b != 0)
1296 	*b++ = 0;
1297     *p = b;
1298     return (f);
1299 }
1300 
1301 
1302 set_profile()
1303 {
1304     char        line[1000],
1305                *b;
1306     register char *f;
1307     extern char *nxtarg();
1308 
1309     if (getpro(".indent.pro", line, sizeof line) < 0)
1310 	return;
1311     b = line;
1312     if (verbose)
1313 	printf("profile: %s\n", b);
1314     while (*(f = nxtarg(&b)))
1315 	set_option(f);
1316 }
1317