1 /*
2  * Copyright (c) 1985 Sun Microsystems, Inc.
3  * Copyright (c) 1980 The Regents of the University of California.
4  * Copyright (c) 1976 Board of Trustees of the University of Illinois.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by the University of California, Berkeley, the University of Illinois,
13  * Urbana, and Sun Microsystems, Inc.  The name of either University
14  * or Sun Microsystems may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20 
21 #ifndef lint
22 static char sccsid[] = "@(#)io.c	5.10 (Berkeley) 9/15/88";
23 #endif /* not lint */
24 
25 #include "indent_globs.h"
26 #include <ctype.h>
27 /* POSIX says that fcntl.h should exist.  Linking either sys/fcntl.h
28    or sys/file.h to fcntl.h should work fine if you don't have fcntl.h */
29 #include <fcntl.h>
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 int suppress_blanklines = 0;
35 int         comment_open;
36 
37 int paren_target;
38 
39 /* true if INDENT OFF is in effect */
40 static int inhibit_formatting;
41 
dump_line()42 dump_line()
43 {				/* dump_line is the routine that actually
44 				 * effects the printing of the new source. It
45 				 * prints the label section, followed by the
46 				 * code section with the appropriate nesting
47 				 * level, followed by any comments */
48     register int cur_col,
49                 target_col;
50     static      not_first_line;
51 
52     if (parser_state_tos->procname[0]) {
53 	if (troff) {
54 	    if (comment_open) {
55 		comment_open = 0;
56 		fprintf(output, ".*/\n");
57 	    }
58 	    fprintf(output, ".Pr \"%.*s\"\n", parser_state_tos->procname_end - parser_state_tos->procname,
59 		    parser_state_tos->procname);
60 	}
61 	parser_state_tos->ind_level = 0;
62 	parser_state_tos->procname = "\0";
63     }
64     if (s_code == e_code && s_lab == e_lab && s_com == e_com) {
65       /* If we have a formfeed on a blank line, we should just output it,
66 	 rather than treat it as a normal blank line.  */
67       if (parser_state_tos->use_ff)
68 	{
69 	  putc('\014',output);
70 	  parser_state_tos->use_ff = false;
71 	}
72       else
73 	{
74 	  if (suppress_blanklines > 0)
75 	    suppress_blanklines--;
76 	  else {
77 	    parser_state_tos->bl_line = true;
78 	    n_real_blanklines++;
79 	  }
80 	}
81     }
82     else if (!inhibit_formatting) {
83 	suppress_blanklines = 0;
84 	parser_state_tos->bl_line = false;
85 	if (prefix_blankline_requested && not_first_line)
86 	    if (swallow_optional_blanklines) {
87 		if (n_real_blanklines == 1)
88 		    n_real_blanklines = 0;
89 	    }
90 	    else {
91 		if (n_real_blanklines == 0)
92 		    n_real_blanklines = 1;
93 	    }
94 	while (--n_real_blanklines >= 0)
95 	    putc('\n', output);
96 	n_real_blanklines = 0;
97 	if (parser_state_tos->ind_level == 0)
98 	    parser_state_tos->ind_stmt = 0;	/* this is a class A kludge. dont do
99 				 * additional statement indentation if we are
100 				 * at bracket level 0 */
101 
102 	if (e_lab != s_lab || e_code != s_code)
103 	    ++code_lines;	/* keep count of lines with code */
104 
105 
106 	if (e_lab != s_lab) {	/* print lab, if any */
107 	    if (comment_open) {
108 		comment_open = 0;
109 		fprintf(output, ".*/\n");
110 	    }
111 	    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
112 		e_lab--;
113 	    cur_col = pad_output(1, compute_label_target());
114 	    fprintf(output, "%.*s", e_lab - s_lab, s_lab);
115 	    cur_col = count_spaces(cur_col, s_lab);
116 	}
117 	else
118 	    cur_col = 1;	/* there is no label section */
119 
120 	parser_state_tos->pcase = false;
121 
122 	if (s_code != e_code) {	/* print code section, if any */
123 	    register char *p;
124 
125 	    if (comment_open) {
126 		comment_open = 0;
127 		fprintf(output, ".*/\n");
128 	    }
129 	    target_col = compute_code_target();
130 	    /* If a line ends in an lparen character, the following
131 	       line should not line up with the parenthesis, but
132 	       should be indented by the usual amount.  */
133 	    if (parser_state_tos->last_token == lparen)
134 	      {
135 		parser_state_tos->paren_indents[parser_state_tos->p_l_follow-1]
136 		  += ind_size - 1;
137 	      }
138 	    {
139 		register    i;
140 
141 		for (i = 0; i < parser_state_tos->p_l_follow; i++)
142 		    if (parser_state_tos->paren_indents[i] >= 0)
143 			parser_state_tos->paren_indents[i] = -(parser_state_tos->paren_indents[i] + target_col);
144 	    }
145 	    cur_col = pad_output(cur_col, target_col);
146 	    for (p = s_code; p < e_code; p++)
147 		if (*p == (char) 0200)
148 		    fprintf(output, "%d", target_col * 7);
149 		else
150 		    putc(*p, output);
151 	    cur_col = count_spaces(cur_col, s_code);
152 	}
153 	if (s_com != e_com)
154 	  {
155 	    if (troff) {
156 		int         all_here = 0;
157 		register char *p;
158 
159 		if (e_com[-1] == '/' && e_com[-2] == '*')
160 		    e_com -= 2, all_here++;
161 		while (e_com > s_com && e_com[-1] == ' ')
162 		    e_com--;
163 		*e_com = 0;
164 		p = s_com;
165 		while (*p == ' ')
166 		    p++;
167 		if (p[0] == '/' && p[1] == '*')
168 		    p += 2, all_here++;
169 		else if (p[0] == '*')
170 		    p += p[1] == '/' ? 2 : 1;
171 		while (*p == ' ')
172 		    p++;
173 		if (*p == 0)
174 		    goto inhibit_newline;
175 		if (comment_open < 2 && parser_state_tos->box_com) {
176 		    comment_open = 0;
177 		    fprintf(output, ".*/\n");
178 		}
179 		if (comment_open == 0) {
180 		    if ('a' <= *p && *p <= 'z')
181 			*p = *p + 'A' - 'a';
182 		    if (e_com - p < 50 && all_here == 2) {
183 			register char *follow = p;
184 			fprintf(output, "\n.nr C! \\w\1");
185 			while (follow < e_com) {
186 			    switch (*follow) {
187 			    case '\n':
188 				putc(' ', output);
189 			    case 1:
190 				break;
191 			    case '\\':
192 				putc('\\', output);
193 			    default:
194 				putc(*follow, output);
195 			    }
196 			    follow++;
197 			}
198 			putc(1, output);
199 		    }
200 		    fprintf(output, "\n./* %dp %d %dp\n",
201 			    parser_state_tos->com_col * 7,
202 			    (s_code != e_code || s_lab != e_lab) - parser_state_tos->box_com,
203 			    target_col * 7);
204 		}
205 		comment_open = 1 + parser_state_tos->box_com;
206 		while (*p) {
207 		    if (*p == BACKSLASH)
208 			putc(BACKSLASH, output);
209 		    putc(*p++, output);
210 		}
211 	    }
212 	    else {		/* print comment, if any */
213 		register    target = parser_state_tos->com_col;
214 		register char *com_st = s_com;
215 
216 		target += parser_state_tos->comment_delta;
217 		while (*com_st == '\t')
218 		    com_st++, target += 8;	/* ? */
219 		while (target <= 0)
220 		    if (*com_st == ' ')
221 			target++, com_st++;
222 		    else if (*com_st == '\t')
223 			target = ((target - 1) & ~7) + 9, com_st++;
224 		    else
225 			target = 1;
226 		if (cur_col > target) {	/* if comment cant fit on this line,
227 					 * put it on next line */
228 		    putc('\n', output);
229 		    cur_col = 1;
230 		    ++parser_state_tos->out_lines;
231 		}
232 		while (e_com > com_st && isspace(e_com[-1]))
233 		    e_com--;
234 		cur_col = pad_output(cur_col, target);
235 		if (!parser_state_tos->box_com) {
236 		    if (star_comment_cont && (com_st[1] != '*' || e_com <= com_st + 1))
237 			if (com_st[1] == ' ' && com_st[0] == ' ' && e_com > com_st + 1)
238 			    com_st[1] = '*';
239 			else
240 			    fwrite(" * ", com_st[0] == '\t' ? 2 : com_st[0] == '*' ? 1 : 3, 1, output);
241 		}
242 		fwrite(com_st, e_com - com_st, 1, output);
243 		parser_state_tos->comment_delta = parser_state_tos->n_comment_delta;
244 		cur_col = count_spaces(cur_col, com_st);
245 		++parser_state_tos->com_lines;	/* count lines with comments */
246 	    }
247 	  }
248 	if (parser_state_tos->use_ff)
249 	  {
250 	    putc('\014', output);
251 	    parser_state_tos->use_ff = false;
252 	  }
253 	else
254 	    putc('\n', output);
255 inhibit_newline:
256 	++parser_state_tos->out_lines;
257 	if (parser_state_tos->just_saw_decl == 1 && blanklines_after_declarations) {
258 	    prefix_blankline_requested = 1;
259 	    parser_state_tos->just_saw_decl = 0;
260 	}
261 	else
262 	    prefix_blankline_requested = postfix_blankline_requested;
263 	postfix_blankline_requested = 0;
264     }
265     parser_state_tos->decl_on_line = parser_state_tos->in_decl;	/* if we are in the middle of a
266 					 * declaration, remember that fact for
267 					 * proper comment indentation */
268     parser_state_tos->ind_stmt = parser_state_tos->in_stmt & ~parser_state_tos->in_decl;	/* next line should be
269 						 * indented if we have not
270 						 * completed this stmt and if
271 						 * we are not in the middle of
272 						 * a declaration */
273     parser_state_tos->dumped_decl_indent = 0;
274     *(e_lab = s_lab) = '\0';	/* reset buffers */
275     *(e_code = s_code) = '\0';
276     *(e_com = s_com) = '\0';
277     parser_state_tos->ind_level = parser_state_tos->i_l_follow;
278     parser_state_tos->paren_level = parser_state_tos->p_l_follow;
279     paren_target = -parser_state_tos->paren_indents[parser_state_tos->paren_level - 1];
280     not_first_line = 1;
281     return;
282 }
283 
284 /* Figure out where we should put the code in codebuf.
285    Return the column number in spaces.  */
286 int
compute_code_target()287 compute_code_target()
288 {
289     register    target_col = parser_state_tos->ind_level + 1;
290 
291     if (parser_state_tos->paren_level)
292 	if (!lineup_to_parens)
293 	    target_col += continuation_indent * parser_state_tos->paren_level;
294 	else {
295 	    register    w;
296 	    register    t = paren_target;
297 
298 	    if ((w = count_spaces(t, s_code) - max_col) > 0
299 		    && count_spaces(target_col, s_code) <= max_col) {
300 		t -= w + 1;
301 		if (t > target_col)
302 		    target_col = t;
303 	    }
304 	    else
305 		target_col = t;
306 	}
307     else if (parser_state_tos->ind_stmt)
308 	target_col += continuation_indent;
309     return target_col;
310 }
311 
312 int
compute_label_target()313 compute_label_target()
314 {
315     return
316 	parser_state_tos->pcase ? case_ind + 1
317 	: *s_lab == '#' ? 1
318 	: parser_state_tos->ind_level - label_offset + 1;
319 }
320 
321 /* Allocate space for FILENAME, read in the file, and set in_prog and
322    in_prog_pos to point to the buffer.  If any errors occur, report an
323    error message and abort.  */
324 void
read_file(filename)325 read_file(filename)
326      char *filename;
327 {
328   /* File descriptor for the input file */
329   int in_fd;
330 
331   /* File status for the input file */
332   struct stat in_stat;
333 
334   /* buffer used for error messages */
335   char *errbuf;
336 
337   errbuf = (char *)xmalloc (strlen (filename) + 80);
338   sprintf(errbuf,"indent: %s",filename);
339 
340   in_fd = open(filename,O_RDONLY,0777);
341   if (in_fd < 0)
342     {
343       perror(errbuf);
344       exit(1);
345     }
346 
347   if (fstat(in_fd,&in_stat) < 0)
348     {
349       perror(errbuf);
350       exit(1);
351     }
352   in_prog_size = in_stat.st_size;
353   in_prog = xmalloc(in_prog_size + 3); /* 3 for ' ','\n','\0' */
354 
355   if (read(in_fd,in_prog,in_prog_size) < 0)
356     {
357       perror(errbuf);
358       exit(1);
359     }
360 
361   if (close(in_fd) < 0)
362     {
363       perror(errbuf);
364       exit(1);
365     }
366 
367   in_prog[in_prog_size] = ' ';
368   in_prog[in_prog_size+1] = '\n';
369   in_prog[in_prog_size+2] = 0;
370   in_prog_pos = in_prog;
371 
372   free (errbuf);
373 }
374 
375 /* Like read_file but read from stdin.  */
376 void
read_stdin()377 read_stdin()
378 {
379   unsigned int alloc_size = 10000;
380   char ch;
381   unsigned int pos;
382   unsigned i;
383 
384   in_prog_pos = in_prog = xmalloc(alloc_size);
385   in_prog_size = 0;
386   do
387     {
388       /* Copy up until 3 bytes before the end of the buffer,
389 	 (the 3 is for ' \n\0')...  */
390       for (; in_prog_size < alloc_size - 3; in_prog_size++)
391 	{
392 	  ch = getc(stdin);
393 	  if (ch == EOF)
394 	      break;
395 	  in_prog[in_prog_size] = ch;
396 	}
397       /* ...and if that's not enough allocate more.  */
398       if (ch != EOF) {
399 	alloc_size *= 2;
400 	in_prog = xrealloc(in_prog,alloc_size);
401       }
402     }
403   while (ch != EOF);
404 
405   in_prog[in_prog_size] = ' ';
406   in_prog[in_prog_size+1] = '\n';
407   in_prog[in_prog_size+2] = 0;
408   in_prog_pos = in_prog;
409 }
410 
411 /* Advance buf_ptr so that it points to the next line of input.  Skip
412    over indent errors (comments beginning with *INDENT**), ignoring
413    them.  Process INDENT ON and INDENT OFF. (Note: the name of this
414    function is a historical artifact from before the time that indent
415    kept the whole source file in memory). */
416 
fill_buffer()417 fill_buffer()
418 {
419   /* Point various places in the buffer.  */
420   char *p;
421 
422   /* Have we found INDENT ON or INDENT OFF ? */
423   enum {None,Indent_on,Indent_off} com;
424 
425   if (bp_save != 0)
426     {		/* there is a partly filled input buffer left */
427 	buf_ptr = bp_save;	/* dont read anything, just switch buffers */
428 	buf_end = be_save;
429 	bp_save = be_save = 0;
430 	if (buf_ptr < buf_end)
431 	    return;		/* only return if there is really something in
432 				 * this buffer */
433     }
434 
435   fill_it:
436 
437   cur_line = in_prog_pos;
438   buf_ptr = in_prog_pos;
439   for (p = buf_ptr; *p && *p++ != '\n';)
440     ;
441 
442   buf_end = p;
443   if (!*p)
444     had_eof = true;
445 
446   p = buf_ptr;
447   in_prog_pos = buf_end;
448 
449   while (*p == ' ' || *p == '\t')
450     p++;
451   if (*p == '/' && p[1] == '*')
452     {
453       p += 2;
454       if (p[1] == 'I' && strncmp(p,"*INDENT**",9) == 0)
455 	goto fill_it;
456       while (*p == ' ' || *p == '\t')
457 	p++;
458       com = None;
459       if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E'
460 			&& p[4] == 'N' && p[5] == 'T') {
461 		    p += 6;
462 		    while (*p == ' ' || *p == '\t')
463 			p++;
464 		    if (*p == '*')
465 			com = 1;
466 		    else if (*p == 'O')
467 			if (*++p == 'N')
468 			    p++, com = Indent_on;
469 			else if (*p == 'F' && *++p == 'F')
470 			    p++, com = Indent_off;
471 		    while (*p == ' ' || *p == '\t')
472 			p++;
473 		    if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) {
474 			if (s_com != e_com || s_lab != e_lab || s_code != e_code)
475 			    dump_line();
476 			if (!(inhibit_formatting = com - 1)) {
477 			    n_real_blanklines = 0;
478 			    postfix_blankline_requested = 0;
479 			    prefix_blankline_requested = 0;
480 			    suppress_blanklines = 1;
481 			}
482 		    }
483 		  }
484     }
485   if (inhibit_formatting)
486     {
487       p = buf_ptr;
488       do
489 	putc(*p,output);
490       while (*p++ != '\n');
491     }
492 
493 }
494 
495 
496 
497 /*
498  * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
499  *
500  * All rights reserved
501  *
502  *
503  * NAME: pad_output
504  *
505  * FUNCTION: Writes tabs and spaces to move the current column up to the desired
506  * position.
507  *
508  * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf.
509  *
510  * PARAMETERS: current		integer		The current column target
511  * nteger		The desired column
512  *
513  * RETURNS: Integer value of the new column.  (If current >= target, no action is
514  * taken, and current is returned.
515  *
516  * GLOBALS: None
517  *
518  * CALLS: write (sys)
519  *
520  * CALLED BY: dump_line
521  *
522  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
523  *
524  */
pad_output(current,target)525 pad_output(current, target)	/* writes tabs and blanks (if necessary) to
526 				 * get the current output position up to the
527 				 * target column */
528     int         current;	/* the current column value */
529     int         target;		/* position we want it at */
530 {
531     register int curr;		/* internal column pointer */
532     register int tcur;
533 
534     if (troff)
535 	fprintf(output, "\\h'|%dp'", (target - 1) * 7);
536     else {
537 	if (current >= target)
538 	    return (current);	/* line is already long enough */
539 	curr = current;
540 	while ((tcur = ((curr - 1) & tabmask) + tabsize + 1) <= target) {
541 	    putc('\t', output);
542 	    curr = tcur;
543 	}
544 	while (curr++ < target)
545 	    putc(' ', output);	/* pad with final blanks */
546     }
547     return (target);
548 }
549 
550 /*
551  * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
552  *
553  * All rights reserved
554  *
555  *
556  * NAME: count_spaces
557  *
558  * FUNCTION: Find out where printing of a given string will leave the current
559  * character position on output.
560  *
561  * ALGORITHM: Run thru input string and add appropriate values to current
562  * position.
563  *
564  * RETURNS: Integer value of position after printing "buffer" starting in column
565  * "current".
566  *
567  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
568  *
569  */
570 int
count_spaces(current,buffer)571 count_spaces(current, buffer)
572 /*
573  * this routine figures out where the character position will be after
574  * printing the text in buffer starting at column "current"
575  */
576     int         current;
577     char       *buffer;
578 {
579     register char *buf;		/* used to look thru buffer */
580     register int cur;		/* current character counter */
581 
582     cur = current;
583 
584     for (buf = buffer; *buf != '\0'; ++buf) {
585 	switch (*buf) {
586 
587 	case '\n':
588 	case 014:		/* form feed */
589 	    cur = 1;
590 	    break;
591 
592 	case '\t':
593 	    cur = ((cur - 1) & tabmask) + tabsize + 1;
594 	    break;
595 
596 	case '':		/* this is a backspace */
597 	    --cur;
598 	    break;
599 
600 	default:
601 	    ++cur;
602 	    break;
603 	}			/* end of switch */
604     }				/* end of for loop */
605     return (cur);
606 }
607 
608 /* Nonzero if we have found an error (not a warning).  */
609 int	found_err;
610 
611 /* Signal an error.  LEVEL is nonzero if it is an error (as opposed to
612    a warning.  MSG is a printf-style format string.  Additional
613    arguments are additional arguments for printf.  */
diag(level,msg,a,b)614 diag(level, msg, a, b)
615 {
616     if (level)
617 	found_err = 1;
618     if (output == stdout) {
619 	fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
620 	fprintf(stdout, (char *)msg, a, b);
621 	fprintf(stdout, " */\n");
622     }
623     else {
624 	fprintf(stderr, "%s: %d: ", in_name, line_no);
625 	fprintf(stderr, (char *)msg, a, b);
626 	fprintf(stderr, "\n");
627     }
628 }
629 
writefdef(f,nm)630 writefdef(f, nm)
631     register struct fstate *f;
632 {
633     fprintf(output, ".ds f%c %s\n.nr s%c %d\n",
634 	    nm, f->font, nm, f->size);
635 }
636 
637 /* Write characters starting at S to change the font from OF to NF.  Return
638    a pointer to the character after the last character written.
639    For troff mode only.  */
640 char *
chfont(of,nf,s)641 chfont(of, nf, s)
642     register struct fstate *of,
643                *nf;
644     char       *s;
645 {
646     if (of->font[0] != nf->font[0]
647 	    || of->font[1] != nf->font[1]) {
648 	*s++ = '\\';
649 	*s++ = 'f';
650 	if (nf->font[1]) {
651 	    *s++ = '(';
652 	    *s++ = nf->font[0];
653 	    *s++ = nf->font[1];
654 	}
655 	else
656 	    *s++ = nf->font[0];
657     }
658     if (nf->size != of->size) {
659 	*s++ = '\\';
660 	*s++ = 's';
661 	if (nf->size < of->size) {
662 	    *s++ = '-';
663 	    *s++ = '0' + of->size - nf->size;
664 	}
665 	else {
666 	    *s++ = '+';
667 	    *s++ = '0' + nf->size - of->size;
668 	}
669     }
670     return s;
671 }
672 
673 
parsefont(f,s0)674 parsefont(f, s0)
675     register struct fstate *f;
676     char       *s0;
677 {
678     register char *s = s0;
679     int         sizedelta = 0;
680     int i;
681 
682     f->size = 0;
683     f->allcaps = 1;
684     for (i = 0; i < 4; i++)
685       f->font[i] = 0;
686 
687     while (*s) {
688 	if (isdigit(*s))
689 	    f->size = f->size * 10 + *s - '0';
690 	else if (isupper(*s))
691 	    if (f->font[0])
692 		f->font[1] = *s;
693 	    else
694 		f->font[0] = *s;
695 	else if (*s == 'c')
696 	    f->allcaps = 1;
697 	else if (*s == '+')
698 	    sizedelta++;
699 	else if (*s == '-')
700 	    sizedelta--;
701 	else {
702 	    fprintf(stderr, "indent: bad font specification: %s\n", s0);
703 	    exit(1);
704 	}
705 	s++;
706     }
707     if (f->font[0] == 0)
708 	f->font[0] = 'R';
709     if (bodyf.size == 0)
710 	bodyf.size = 11;
711     if (f->size == 0)
712 	f->size = bodyf.size + sizedelta;
713     else if (sizedelta > 0)
714 	f->size += bodyf.size;
715     else
716 	f->size = bodyf.size - f->size;
717 }
718