io.c revision 1.119 1 /* $NetBSD: io.c,v 1.119 2021/11/19 17:30:10 rillig Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-4-Clause
5 *
6 * Copyright (c) 1985 Sun Microsystems, Inc.
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40 #if 0
41 static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93";
42 #endif
43
44 #include <sys/cdefs.h>
45 #if defined(__NetBSD__)
46 __RCSID("$NetBSD: io.c,v 1.119 2021/11/19 17:30:10 rillig Exp $");
47 #elif defined(__FreeBSD__)
48 __FBSDID("$FreeBSD: head/usr.bin/indent/io.c 334927 2018-06-10 16:44:18Z pstef $");
49 #endif
50
51 #include <ctype.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <string.h>
55
56 #include "indent.h"
57
58 static int paren_indent;
59 static bool suppress_blanklines;
60
61
62 const char *
63 inp_p(void)
64 {
65 return inbuf.inp.s;
66 }
67
68 const char *
69 inp_line_end(void)
70 {
71 return inbuf.inp.e;
72 }
73
74 char
75 inp_peek(void)
76 {
77 return *inbuf.inp.s;
78 }
79
80 char
81 inp_lookahead(size_t i)
82 {
83 return inbuf.inp.s[i];
84 }
85
86 void
87 inp_skip(void)
88 {
89 inbuf.inp.s++;
90 if (inbuf.inp.s >= inbuf.inp.e)
91 inp_read_line();
92 }
93
94 char
95 inp_next(void)
96 {
97 char ch = inp_peek();
98 inp_skip();
99 return ch;
100 }
101
102
103 static void
104 output_char(char ch)
105 {
106 fputc(ch, output);
107 debug_vis_range("output_char '", &ch, &ch + 1, "'\n");
108 }
109
110 static void
111 output_range(const char *s, const char *e)
112 {
113 fwrite(s, 1, (size_t)(e - s), output);
114 debug_vis_range("output_range \"", s, e, "\"\n");
115 }
116
117 static inline void
118 output_string(const char *s)
119 {
120 output_range(s, s + strlen(s));
121 }
122
123 static int
124 output_indent(int old_ind, int new_ind)
125 {
126 int ind = old_ind;
127
128 if (opt.use_tabs) {
129 int tabsize = opt.tabsize;
130 int n = new_ind / tabsize - ind / tabsize;
131 if (n > 0)
132 ind -= ind % tabsize;
133 for (int i = 0; i < n; i++) {
134 fputc('\t', output);
135 ind += tabsize;
136 }
137 }
138
139 for (; ind < new_ind; ind++)
140 fputc(' ', output);
141
142 debug_println("output_indent %d", ind);
143 return ind;
144 }
145
146 static int
147 dump_line_label(void)
148 {
149 int ind;
150
151 while (lab.e > lab.s && ch_isblank(lab.e[-1]))
152 lab.e--;
153 *lab.e = '\0';
154
155 ind = output_indent(0, compute_label_indent());
156
157 if (lab.s[0] == '#' && (strncmp(lab.s, "#else", 5) == 0
158 || strncmp(lab.s, "#endif", 6) == 0)) {
159 const char *s = lab.s;
160 if (lab.e[-1] == '\n')
161 lab.e--;
162 do {
163 output_char(*s++);
164 } while (s < lab.e && 'a' <= *s && *s <= 'z');
165
166 while (s < lab.e && ch_isblank(*s))
167 s++;
168
169 if (s < lab.e) {
170 if (s[0] == '/' && s[1] == '*') {
171 output_char('\t');
172 output_range(s, lab.e);
173 } else {
174 output_string("\t/* ");
175 output_range(s, lab.e);
176 output_string(" */");
177 }
178 }
179 } else
180 output_range(lab.s, lab.e);
181 ind = ind_add(ind, lab.s, lab.e);
182
183 ps.is_case_label = false;
184 return ind;
185 }
186
187 static int
188 dump_line_code(int ind)
189 {
190
191 int target_ind = compute_code_indent();
192 for (int i = 0; i < ps.p_l_follow; i++) {
193 if (ps.paren_indents[i] >= 0) {
194 int paren_ind = ps.paren_indents[i];
195 ps.paren_indents[i] = (short)(-1 - (paren_ind + target_ind));
196 debug_println(
197 "setting paren_indents[%d] from %d to %d for column %d",
198 i, paren_ind, ps.paren_indents[i], target_ind + 1);
199 }
200 }
201
202 ind = output_indent(ind, target_ind);
203 output_range(code.s, code.e);
204 return ind_add(ind, code.s, code.e);
205 }
206
207 static void
208 dump_line_comment(int ind)
209 {
210 int target_ind = ps.com_ind;
211 const char *p = com.s;
212
213 target_ind += ps.comment_delta;
214
215 /* consider original indentation in case this is a box comment */
216 for (; *p == '\t'; p++)
217 target_ind += opt.tabsize;
218
219 for (; target_ind < 0; p++) {
220 if (*p == ' ')
221 target_ind++;
222 else if (*p == '\t')
223 target_ind = next_tab(target_ind);
224 else {
225 target_ind = 0;
226 break;
227 }
228 }
229
230 /* if comment can't fit on this line, put it on the next line */
231 if (ind > target_ind) {
232 output_char('\n');
233 ind = 0;
234 ps.stats.lines++;
235 }
236
237 while (com.e > p && isspace((unsigned char)com.e[-1]))
238 com.e--;
239
240 (void)output_indent(ind, target_ind);
241 output_range(p, com.e);
242
243 ps.comment_delta = ps.n_comment_delta;
244 ps.stats.comment_lines++;
245 }
246
247 /*
248 * Write a line of formatted source to the output file. The line consists of
249 * the label, the code and the comment.
250 */
251 static void
252 output_line(char line_terminator)
253 {
254 static bool first_line = true;
255
256 ps.procname[0] = '\0';
257
258 if (code.s == code.e && lab.s == lab.e && com.s == com.e) {
259 if (suppress_blanklines)
260 suppress_blanklines = false;
261 else
262 blank_lines_to_output++;
263
264 } else if (!inhibit_formatting) {
265 suppress_blanklines = false;
266 if (blank_line_before && !first_line) {
267 if (opt.swallow_optional_blanklines) {
268 if (blank_lines_to_output == 1)
269 blank_lines_to_output = 0;
270 } else {
271 if (blank_lines_to_output == 0)
272 blank_lines_to_output = 1;
273 }
274 }
275
276 for (; blank_lines_to_output > 0; blank_lines_to_output--)
277 output_char('\n');
278
279 if (ps.ind_level == 0)
280 ps.ind_stmt = false; /* this is a class A kludge. don't do
281 * additional statement indentation if
282 * we are at bracket level 0 */
283
284 if (lab.e != lab.s || code.e != code.s)
285 ps.stats.code_lines++;
286
287 int ind = 0;
288 if (lab.e != lab.s)
289 ind = dump_line_label();
290 if (code.e != code.s)
291 ind = dump_line_code(ind);
292 if (com.e != com.s)
293 dump_line_comment(ind);
294
295 output_char(line_terminator);
296 ps.stats.lines++;
297
298 if (ps.just_saw_decl == 1 && opt.blanklines_after_decl) {
299 blank_line_before = true;
300 ps.just_saw_decl = 0;
301 } else
302 blank_line_before = blank_line_after;
303 blank_line_after = false;
304 }
305
306 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */
307 ps.ind_stmt = ps.in_stmt && !ps.in_decl;
308 ps.decl_indent_done = false;
309
310 *(lab.e = lab.s) = '\0'; /* reset buffers */
311 *(code.e = code.s) = '\0';
312 *(com.e = com.s = com.buf + 1) = '\0';
313
314 ps.ind_level = ps.ind_level_follow;
315 ps.paren_level = ps.p_l_follow;
316
317 if (ps.paren_level > 0) {
318 /* TODO: explain what negative indentation means */
319 paren_indent = -1 - ps.paren_indents[ps.paren_level - 1];
320 debug_println("paren_indent is now %d", paren_indent);
321 }
322
323 first_line = false;
324 }
325
326 void
327 dump_line(void)
328 {
329 output_line('\n');
330 }
331
332 void
333 dump_line_ff(void)
334 {
335 output_line('\f');
336 }
337
338 static int
339 compute_code_indent_lineup(int base_ind)
340 {
341 int ti = paren_indent;
342 int overflow = ind_add(ti, code.s, code.e) - opt.max_line_length;
343 if (overflow < 0)
344 return ti;
345
346 if (ind_add(base_ind, code.s, code.e) < opt.max_line_length) {
347 ti -= overflow + 2;
348 if (ti > base_ind)
349 return ti;
350 return base_ind;
351 }
352
353 return ti;
354 }
355
356 int
357 compute_code_indent(void)
358 {
359 int base_ind = ps.ind_level * opt.indent_size;
360
361 if (ps.paren_level == 0) {
362 if (ps.ind_stmt)
363 return base_ind + opt.continuation_indent;
364 return base_ind;
365 }
366
367 if (opt.lineup_to_parens) {
368 if (opt.lineup_to_parens_always)
369 return paren_indent;
370 return compute_code_indent_lineup(base_ind);
371 }
372
373 if (2 * opt.continuation_indent == opt.indent_size)
374 return base_ind + opt.continuation_indent;
375 else
376 return base_ind + opt.continuation_indent * ps.paren_level;
377 }
378
379 int
380 compute_label_indent(void)
381 {
382 if (ps.is_case_label)
383 return (int)(case_ind * (float)opt.indent_size);
384 if (lab.s[0] == '#')
385 return 0;
386 return opt.indent_size * (ps.ind_level - 2);
387 }
388
389 static void
390 skip_blank(const char **pp)
391 {
392 while (ch_isblank(**pp))
393 (*pp)++;
394 }
395
396 static bool
397 skip_string(const char **pp, const char *s)
398 {
399 size_t len = strlen(s);
400 if (strncmp(*pp, s, len) == 0) {
401 *pp += len;
402 return true;
403 }
404 return false;
405 }
406
407 static void
408 parse_indent_comment(void)
409 {
410 bool on;
411
412 const char *p = inbuf.inp.buf;
413
414 skip_blank(&p);
415 if (!skip_string(&p, "/*"))
416 return;
417 skip_blank(&p);
418 if (!skip_string(&p, "INDENT"))
419 return;
420 skip_blank(&p);
421
422 if (*p == '*' || skip_string(&p, "ON"))
423 on = true;
424 else if (skip_string(&p, "OFF"))
425 on = false;
426 else
427 return;
428
429 skip_blank(&p);
430 if (!skip_string(&p, "*/\n"))
431 return;
432
433 if (com.s != com.e || lab.s != lab.e || code.s != code.e)
434 dump_line();
435
436 inhibit_formatting = !on;
437 if (on) {
438 blank_lines_to_output = 0;
439 blank_line_after = false;
440 blank_line_before = false;
441 suppress_blanklines = true;
442 }
443 }
444
445 /*
446 * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
447 *
448 * All rights reserved
449 */
450 void
451 inp_read_line(void)
452 {
453 char *p;
454 int ch;
455 FILE *f = input;
456
457 if (inbuf.saved_inp_s != NULL) { /* there is a partly filled input buffer left */
458 inbuf.inp.s = inbuf.saved_inp_s; /* do not read anything, just switch buffers */
459 inbuf.inp.e = inbuf.saved_inp_e;
460 inbuf.saved_inp_s = inbuf.saved_inp_e = NULL;
461 debug_println("switched inp.s back to saved_inp_s");
462 if (inbuf.inp.s < inbuf.inp.e)
463 return; /* only return if there is really something in
464 * this buffer */
465 }
466
467 for (p = inbuf.inp.buf;;) {
468 if (p >= inbuf.inp.l) {
469 size_t size = (size_t)(inbuf.inp.l - inbuf.inp.buf) * 2 + 10;
470 size_t offset = (size_t)(p - inbuf.inp.buf);
471 inbuf.inp.buf = xrealloc(inbuf.inp.buf, size);
472 p = inbuf.inp.buf + offset;
473 inbuf.inp.l = inbuf.inp.buf + size - 2;
474 }
475
476 if ((ch = getc(f)) == EOF) {
477 if (!inhibit_formatting) {
478 *p++ = ' ';
479 *p++ = '\n';
480 }
481 had_eof = true;
482 break;
483 }
484
485 if (ch != '\0')
486 *p++ = (char)ch;
487 if (ch == '\n')
488 break;
489 }
490
491 inbuf.inp.s = inbuf.inp.buf;
492 inbuf.inp.e = p;
493
494 if (p - inbuf.inp.s >= 3 && p[-3] == '*' && p[-2] == '/')
495 parse_indent_comment();
496
497 if (inhibit_formatting)
498 output_range(inbuf.inp.s, inbuf.inp.e);
499 }
500
501 int
502 ind_add(int ind, const char *start, const char *end)
503 {
504 for (const char *p = start; p != end; ++p) {
505 if (*p == '\n' || *p == '\f')
506 ind = 0;
507 else if (*p == '\t')
508 ind = next_tab(ind);
509 else if (*p == '\b')
510 --ind;
511 else
512 ++ind;
513 }
514 return ind;
515 }
516