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