xref: /freebsd/usr.bin/indent/io.c (revision b077aed33b7b6aefca7b17ddb250cf521f938613)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1985 Sun Microsystems, Inc.
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)io.c	8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42 #endif
43 
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46 
47 #include <ctype.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include "indent_globs.h"
53 #include "indent.h"
54 
55 /* Globals */
56 int	found_err;
57 int	n_real_blanklines;
58 int	prefix_blankline_requested, postfix_blankline_requested;
59 int	code_lines;
60 int	had_eof;
61 int	inhibit_formatting;
62 int	suppress_blanklines;
63 
64 int         comment_open;
65 static int  paren_target;
66 static int pad_output(int current, int target);
67 
68 void
69 dump_line(void)
70 {				/* dump_line is the routine that actually
71 				 * effects the printing of the new source. It
72 				 * prints the label section, followed by the
73 				 * code section with the appropriate nesting
74 				 * level, followed by any comments */
75     int cur_col,
76                 target_col = 1;
77     static int  not_first_line;
78 
79     if (ps.procname[0]) {
80 	ps.ind_level = 0;
81 	ps.procname[0] = 0;
82     }
83     if (s_code == e_code && s_lab == e_lab && s_com == e_com) {
84 	if (suppress_blanklines > 0)
85 	    suppress_blanklines--;
86 	else {
87 	    ps.bl_line = true;
88 	    n_real_blanklines++;
89 	}
90     }
91     else if (!inhibit_formatting) {
92 	suppress_blanklines = 0;
93 	ps.bl_line = false;
94 	if (prefix_blankline_requested && not_first_line) {
95 	    if (opt.swallow_optional_blanklines) {
96 		if (n_real_blanklines == 1)
97 		    n_real_blanklines = 0;
98 	    }
99 	    else {
100 		if (n_real_blanklines == 0)
101 		    n_real_blanklines = 1;
102 	    }
103 	}
104 	while (--n_real_blanklines >= 0)
105 	    putc('\n', output);
106 	n_real_blanklines = 0;
107 	if (ps.ind_level == 0)
108 	    ps.ind_stmt = 0;	/* this is a class A kludge. dont do
109 				 * additional statement indentation if we are
110 				 * at bracket level 0 */
111 
112 	if (e_lab != s_lab || e_code != s_code)
113 	    ++code_lines;	/* keep count of lines with code */
114 
115 
116 	if (e_lab != s_lab) {	/* print lab, if any */
117 	    if (comment_open) {
118 		comment_open = 0;
119 		fprintf(output, ".*/\n");
120 	    }
121 	    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
122 		e_lab--;
123 	    *e_lab = '\0';
124 	    cur_col = pad_output(1, compute_label_target());
125 	    if (s_lab[0] == '#' && (strncmp(s_lab, "#else", 5) == 0
126 				    || strncmp(s_lab, "#endif", 6) == 0)) {
127 		char *s = s_lab;
128 		if (e_lab[-1] == '\n') e_lab--;
129 		do putc(*s++, output);
130 		while (s < e_lab && 'a' <= *s && *s<='z');
131 		while ((*s == ' ' || *s == '\t') && s < e_lab)
132 		    s++;
133 		if (s < e_lab)
134 		    fprintf(output, s[0]=='/' && s[1]=='*' ? "\t%.*s" : "\t/* %.*s */",
135 			    (int)(e_lab - s), s);
136 	    }
137 	    else fprintf(output, "%.*s", (int)(e_lab - s_lab), s_lab);
138 	    cur_col = count_spaces(cur_col, s_lab);
139 	}
140 	else
141 	    cur_col = 1;	/* there is no label section */
142 
143 	ps.pcase = false;
144 
145 	if (s_code != e_code) {	/* print code section, if any */
146 	    char *p;
147 
148 	    if (comment_open) {
149 		comment_open = 0;
150 		fprintf(output, ".*/\n");
151 	    }
152 	    target_col = compute_code_target();
153 	    {
154 		int i;
155 
156 		for (i = 0; i < ps.p_l_follow; i++)
157 		    if (ps.paren_indents[i] >= 0)
158 			ps.paren_indents[i] = -(ps.paren_indents[i] + target_col);
159 	    }
160 	    cur_col = pad_output(cur_col, target_col);
161 	    for (p = s_code; p < e_code; p++)
162 		if (*p == (char) 0200)
163 		    fprintf(output, "%d", target_col * 7);
164 		else
165 		    putc(*p, output);
166 	    cur_col = count_spaces(cur_col, s_code);
167 	}
168 	if (s_com != e_com) {		/* print comment, if any */
169 	    int target = ps.com_col;
170 	    char *com_st = s_com;
171 
172 	    target += ps.comment_delta;
173 	    while (*com_st == '\t')	/* consider original indentation in
174 				     * case this is a box comment */
175 		com_st++, target += opt.tabsize;
176 	    while (target <= 0)
177 		if (*com_st == ' ')
178 		    target++, com_st++;
179 		else if (*com_st == '\t') {
180 		    target = opt.tabsize * (1 + (target - 1) / opt.tabsize) + 1;
181 		    com_st++;
182 		}
183 		else
184 		    target = 1;
185 	    if (cur_col > target) {	/* if comment can't fit on this line,
186 				     * put it on next line */
187 		putc('\n', output);
188 		cur_col = 1;
189 		++ps.out_lines;
190 	    }
191 	    while (e_com > com_st && isspace((unsigned char)e_com[-1]))
192 		e_com--;
193 	    (void)pad_output(cur_col, target);
194 	    fwrite(com_st, e_com - com_st, 1, output);
195 	    ps.comment_delta = ps.n_comment_delta;
196 	    ++ps.com_lines;	/* count lines with comments */
197 	}
198 	if (ps.use_ff)
199 	    putc('\014', output);
200 	else
201 	    putc('\n', output);
202 	++ps.out_lines;
203 	if (ps.just_saw_decl == 1 && opt.blanklines_after_declarations) {
204 	    prefix_blankline_requested = 1;
205 	    ps.just_saw_decl = 0;
206 	}
207 	else
208 	    prefix_blankline_requested = postfix_blankline_requested;
209 	postfix_blankline_requested = 0;
210     }
211     ps.decl_on_line = ps.in_decl;	/* if we are in the middle of a
212 					 * declaration, remember that fact for
213 					 * proper comment indentation */
214     ps.ind_stmt = ps.in_stmt & ~ps.in_decl;	/* next line should be
215 						 * indented if we have not
216 						 * completed this stmt and if
217 						 * we are not in the middle of
218 						 * a declaration */
219     ps.use_ff = false;
220     ps.dumped_decl_indent = 0;
221     *(e_lab = s_lab) = '\0';	/* reset buffers */
222     *(e_code = s_code) = '\0';
223     *(e_com = s_com = combuf + 1) = '\0';
224     ps.ind_level = ps.i_l_follow;
225     ps.paren_level = ps.p_l_follow;
226     if (ps.paren_level > 0)
227 	paren_target = -ps.paren_indents[ps.paren_level - 1];
228     not_first_line = 1;
229 }
230 
231 int
232 compute_code_target(void)
233 {
234     int target_col = opt.ind_size * ps.ind_level + 1;
235 
236     if (ps.paren_level)
237 	if (!opt.lineup_to_parens)
238 	    target_col += opt.continuation_indent *
239 		(2 * opt.continuation_indent == opt.ind_size ? 1 : ps.paren_level);
240 	else if (opt.lineup_to_parens_always)
241 	    target_col = paren_target;
242 	else {
243 	    int w;
244 	    int t = paren_target;
245 
246 	    if ((w = count_spaces(t, s_code) - opt.max_col) > 0
247 		    && count_spaces(target_col, s_code) <= opt.max_col) {
248 		t -= w + 1;
249 		if (t > target_col)
250 		    target_col = t;
251 	    }
252 	    else
253 		target_col = t;
254 	}
255     else if (ps.ind_stmt)
256 	target_col += opt.continuation_indent;
257     return target_col;
258 }
259 
260 int
261 compute_label_target(void)
262 {
263     return
264 	ps.pcase ? (int) (case_ind * opt.ind_size) + 1
265 	: *s_lab == '#' ? 1
266 	: opt.ind_size * (ps.ind_level - label_offset) + 1;
267 }
268 
269 
270 /*
271  * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
272  *
273  * All rights reserved
274  *
275  *
276  * NAME: fill_buffer
277  *
278  * FUNCTION: Reads one block of input into input_buffer
279  *
280  * HISTORY: initial coding 	November 1976	D A Willcox of CAC 1/7/77 A
281  * Willcox of CAC	Added check for switch back to partly full input
282  * buffer from temporary buffer
283  *
284  */
285 void
286 fill_buffer(void)
287 {				/* this routine reads stuff from the input */
288     char *p;
289     int i;
290     FILE *f = input;
291 
292     if (bp_save != NULL) {	/* there is a partly filled input buffer left */
293 	buf_ptr = bp_save;	/* do not read anything, just switch buffers */
294 	buf_end = be_save;
295 	bp_save = be_save = NULL;
296 	if (buf_ptr < buf_end)
297 	    return;		/* only return if there is really something in
298 				 * this buffer */
299     }
300     for (p = in_buffer;;) {
301 	if (p >= in_buffer_limit) {
302 	    int size = (in_buffer_limit - in_buffer) * 2 + 10;
303 	    int offset = p - in_buffer;
304 	    in_buffer = realloc(in_buffer, size);
305 	    if (in_buffer == NULL)
306 		errx(1, "input line too long");
307 	    p = in_buffer + offset;
308 	    in_buffer_limit = in_buffer + size - 2;
309 	}
310 	if ((i = getc(f)) == EOF) {
311 		*p++ = ' ';
312 		*p++ = '\n';
313 		had_eof = true;
314 		break;
315 	}
316 	if (i != '\0')
317 	    *p++ = i;
318 	if (i == '\n')
319 		break;
320     }
321     buf_ptr = in_buffer;
322     buf_end = p;
323     if (p - in_buffer > 2 && p[-2] == '/' && p[-3] == '*') {
324 	if (in_buffer[3] == 'I' && strncmp(in_buffer, "/**INDENT**", 11) == 0)
325 	    fill_buffer();	/* flush indent error message */
326 	else {
327 	    int         com = 0;
328 
329 	    p = in_buffer;
330 	    while (*p == ' ' || *p == '\t')
331 		p++;
332 	    if (*p == '/' && p[1] == '*') {
333 		p += 2;
334 		while (*p == ' ' || *p == '\t')
335 		    p++;
336 		if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E'
337 			&& p[4] == 'N' && p[5] == 'T') {
338 		    p += 6;
339 		    while (*p == ' ' || *p == '\t')
340 			p++;
341 		    if (*p == '*')
342 			com = 1;
343 		    else if (*p == 'O') {
344 			if (*++p == 'N')
345 			    p++, com = 1;
346 			else if (*p == 'F' && *++p == 'F')
347 			    p++, com = 2;
348 		    }
349 		    while (*p == ' ' || *p == '\t')
350 			p++;
351 		    if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) {
352 			if (s_com != e_com || s_lab != e_lab || s_code != e_code)
353 			    dump_line();
354 			if (!(inhibit_formatting = com - 1)) {
355 			    n_real_blanklines = 0;
356 			    postfix_blankline_requested = 0;
357 			    prefix_blankline_requested = 0;
358 			    suppress_blanklines = 1;
359 			}
360 		    }
361 		}
362 	    }
363 	}
364     }
365     if (inhibit_formatting) {
366 	p = in_buffer;
367 	do
368 	    putc(*p, output);
369 	while (*p++ != '\n');
370     }
371 }
372 
373 /*
374  * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
375  *
376  * All rights reserved
377  *
378  *
379  * NAME: pad_output
380  *
381  * FUNCTION: Writes tabs and spaces to move the current column up to the desired
382  * position.
383  *
384  * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf.
385  *
386  * PARAMETERS: current		integer		The current column target
387  * nteger		The desired column
388  *
389  * RETURNS: Integer value of the new column.  (If current >= target, no action is
390  * taken, and current is returned.
391  *
392  * GLOBALS: None
393  *
394  * CALLS: write (sys)
395  *
396  * CALLED BY: dump_line
397  *
398  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
399  *
400  */
401 static int
402 pad_output(int current, int target)
403 			        /* writes tabs and blanks (if necessary) to
404 				 * get the current output position up to the
405 				 * target column */
406     /* current: the current column value */
407     /* target: position we want it at */
408 {
409     int curr;			/* internal column pointer */
410 
411     if (current >= target)
412 	return (current);	/* line is already long enough */
413     curr = current;
414     if (opt.use_tabs) {
415 	int tcur;
416 
417 	while ((tcur = opt.tabsize * (1 + (curr - 1) / opt.tabsize) + 1) <= target) {
418 	    putc('\t', output);
419 	    curr = tcur;
420 	}
421     }
422     while (curr++ < target)
423 	putc(' ', output);	/* pad with final blanks */
424 
425     return (target);
426 }
427 
428 /*
429  * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
430  *
431  * All rights reserved
432  *
433  *
434  * NAME: count_spaces
435  *
436  * FUNCTION: Find out where printing of a given string will leave the current
437  * character position on output.
438  *
439  * ALGORITHM: Run thru input string and add appropriate values to current
440  * position.
441  *
442  * RETURNS: Integer value of position after printing "buffer" starting in column
443  * "current".
444  *
445  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
446  *
447  */
448 int
449 count_spaces_until(int cur, char *buffer, char *end)
450 /*
451  * this routine figures out where the character position will be after
452  * printing the text in buffer starting at column "current"
453  */
454 {
455     char *buf;		/* used to look thru buffer */
456 
457     for (buf = buffer; *buf != '\0' && buf != end; ++buf) {
458 	switch (*buf) {
459 
460 	case '\n':
461 	case 014:		/* form feed */
462 	    cur = 1;
463 	    break;
464 
465 	case '\t':
466 	    cur = opt.tabsize * (1 + (cur - 1) / opt.tabsize) + 1;
467 	    break;
468 
469 	case 010:		/* backspace */
470 	    --cur;
471 	    break;
472 
473 	default:
474 	    ++cur;
475 	    break;
476 	}			/* end of switch */
477     }				/* end of for loop */
478     return (cur);
479 }
480 
481 int
482 count_spaces(int cur, char *buffer)
483 {
484     return (count_spaces_until(cur, buffer, NULL));
485 }
486 
487 void
488 diag4(int level, const char *msg, int a, int b)
489 {
490     if (level)
491 	found_err = 1;
492     if (output == stdout) {
493 	fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
494 	fprintf(stdout, msg, a, b);
495 	fprintf(stdout, " */\n");
496     }
497     else {
498 	fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no);
499 	fprintf(stderr, msg, a, b);
500 	fprintf(stderr, "\n");
501     }
502 }
503 
504 void
505 diag3(int level, const char *msg, int a)
506 {
507     if (level)
508 	found_err = 1;
509     if (output == stdout) {
510 	fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
511 	fprintf(stdout, msg, a);
512 	fprintf(stdout, " */\n");
513     }
514     else {
515 	fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no);
516 	fprintf(stderr, msg, a);
517 	fprintf(stderr, "\n");
518     }
519 }
520 
521 void
522 diag2(int level, const char *msg)
523 {
524     if (level)
525 	found_err = 1;
526     if (output == stdout) {
527 	fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
528 	fprintf(stdout, "%s", msg);
529 	fprintf(stdout, " */\n");
530     }
531     else {
532 	fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no);
533 	fprintf(stderr, "%s", msg);
534 	fprintf(stderr, "\n");
535     }
536 }
537 
538