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