io.c revision 1.234 1 /* $NetBSD: io.c,v 1.234 2023/12/03 21:40:44 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 #include <sys/cdefs.h>
41 __RCSID("$NetBSD: io.c,v 1.234 2023/12/03 21:40:44 rillig Exp $");
42
43 #include <stdio.h>
44
45 #include "indent.h"
46
47 struct input_state in = {
48 .token_end_line = 1,
49 };
50
51 struct output_state out;
52 enum indent_enabled indent_enabled;
53 static int out_ind; /* width of the line that is being written */
54 static unsigned newlines = 2; /* the total of written and buffered newlines;
55 * 0 in the middle of a line, 1 after a single
56 * finished line, anything > 1 are trailing
57 * blank lines */
58 static unsigned buffered_newlines; /* not yet written */
59 static int paren_indent; /* total indentation when parenthesized */
60
61
62 static void
63 inp_read_next_line(void)
64 {
65 buf_clear(&inp);
66
67 for (;;) {
68 int ch = getc(input);
69 if (ch == EOF) {
70 if (indent_enabled == indent_on) {
71 buf_add_char(&inp, ' ');
72 buf_add_char(&inp, '\n');
73 }
74 had_eof = true;
75 break;
76 }
77
78 if (ch != '\0')
79 buf_add_char(&inp, (char)ch);
80 if (ch == '\n')
81 break;
82 }
83 buf_terminate(&inp);
84 inp_p = inp.s;
85 }
86
87 void
88 inp_read_line(void)
89 {
90 if (indent_enabled == indent_on)
91 buf_clear(&out.indent_off_text);
92 buf_add_chars(&out.indent_off_text, inp.s, inp.len);
93 inp_read_next_line();
94 }
95
96 void
97 inp_skip(void)
98 {
99 inp_p++;
100 if ((size_t)(inp_p - inp.s) >= inp.len)
101 inp_read_line();
102 }
103
104 char
105 inp_next(void)
106 {
107 char ch = inp_p[0];
108 inp_skip();
109 return ch;
110 }
111
112
113 static void
114 add_buffered_newline(void)
115 {
116 buffered_newlines++;
117 newlines++;
118 out_ind = 0;
119 }
120
121 static void
122 write_buffered_newlines(void)
123 {
124 for (; buffered_newlines > 0; buffered_newlines--) {
125 fputc('\n', output);
126 debug_println("write_newline");
127 }
128 }
129
130 static void
131 write_range(const char *s, size_t len)
132 {
133 write_buffered_newlines();
134 fwrite(s, 1, len, output);
135 debug_printf("write_range ");
136 debug_vis_range(s, len);
137 debug_println("");
138 for (size_t i = 0; i < len; i++)
139 newlines = s[i] == '\n' ? newlines + 1 : 0;
140 out_ind = ind_add(out_ind, s, len);
141 }
142
143 static void
144 write_indent(int new_ind)
145 {
146 write_buffered_newlines();
147
148 int ind = out_ind;
149
150 if (opt.use_tabs) {
151 int n = new_ind / opt.tabsize - ind / opt.tabsize;
152 if (n > 0) {
153 ind = ind - ind % opt.tabsize + n * opt.tabsize;
154 while (n-- > 0)
155 fputc('\t', output);
156 newlines = 0;
157 }
158 }
159
160 for (; ind < new_ind; ind++) {
161 fputc(' ', output);
162 newlines = 0;
163 }
164
165 debug_println("write_indent %d", ind);
166 out_ind = ind;
167 }
168
169 static bool
170 want_blank_line(void)
171 {
172 debug_println("%s: %s -> %s", __func__,
173 line_kind_name[out.prev_line_kind], line_kind_name[out.line_kind]);
174
175 if (((ps.blank_line_after_decl && ps.declaration == decl_no)
176 || ps.badp == badp_yes)
177 && (lab.len > 0 || code.len > 0)) {
178 ps.blank_line_after_decl = false;
179 ps.badp = badp_none;
180 return true;
181 }
182
183 if (opt.blank_line_around_conditional_compilation) {
184 if (out.prev_line_kind != lk_pre_if
185 && out.line_kind == lk_pre_if)
186 return true;
187 if (out.prev_line_kind == lk_pre_endif
188 && out.line_kind != lk_pre_endif)
189 return true;
190 }
191 if (opt.blank_line_after_proc && out.prev_line_kind == lk_func_end
192 && out.line_kind != lk_pre_endif && out.line_kind != lk_pre_other)
193 return true;
194 if (opt.blank_line_before_block_comment
195 && out.line_kind == lk_block_comment)
196 return true;
197 return false;
198 }
199
200 static bool
201 is_blank_line_optional(void)
202 {
203 if (out.prev_line_kind == lk_stmt_head)
204 return newlines >= 1;
205 if (ps.psyms.len >= 3)
206 return newlines >= 2;
207 return newlines >= 3;
208 }
209
210 static int
211 compute_case_label_indent(void)
212 {
213 size_t i = ps.psyms.len - 1;
214 while (i > 0 && ps.psyms.sym[i] != psym_switch_expr)
215 i--;
216 float case_ind = (float)ps.psyms.ind_level[i] + opt.case_indent;
217 // TODO: case_ind may become negative here.
218 return (int)(case_ind * (float)opt.indent_size);
219 }
220
221 int
222 compute_label_indent(void)
223 {
224 if (out.line_kind == lk_case_or_default)
225 return compute_case_label_indent();
226 if (lab.s[0] == '#')
227 return 0;
228 // TODO: the indentation may become negative here.
229 return opt.indent_size * (ps.ind_level - 2);
230 }
231
232 static void
233 output_line_label(void)
234 {
235 write_indent(compute_label_indent());
236 write_range(lab.s, lab.len);
237 }
238
239 static int
240 compute_lined_up_code_indent(int base_ind)
241 {
242 int ind = paren_indent;
243 int overflow = ind_add(ind, code.s, code.len) - opt.max_line_length;
244 if (overflow >= 0
245 && ind_add(base_ind, code.s, code.len) < opt.max_line_length) {
246 ind -= 2 + overflow;
247 if (ind < base_ind)
248 ind = base_ind;
249 }
250
251 if (ps.extra_expr_indent != eei_no
252 && ind == base_ind + opt.indent_size)
253 ind += opt.continuation_indent;
254 return ind;
255 }
256
257 int
258 compute_code_indent(void)
259 {
260 int base_ind = ps.ind_level * opt.indent_size;
261
262 if (ps.ind_paren_level == 0) {
263 if (ps.line_is_stmt_cont)
264 return base_ind + opt.continuation_indent;
265 return base_ind;
266 }
267
268 if (opt.lineup_to_parens) {
269 if (opt.lineup_to_parens_always)
270 return paren_indent;
271 return compute_lined_up_code_indent(base_ind);
272 }
273
274 int rel_ind = opt.continuation_indent * ps.ind_paren_level;
275 if (ps.extra_expr_indent != eei_no && rel_ind == opt.indent_size)
276 rel_ind += opt.continuation_indent;
277 return base_ind + rel_ind;
278 }
279
280 static void
281 output_line_code(void)
282 {
283 int target_ind = compute_code_indent();
284 for (size_t i = 0; i < ps.paren.len; i++) {
285 int paren_ind = ps.paren.item[i].indent;
286 if (paren_ind >= 0) {
287 ps.paren.item[i].indent =
288 -1 - (paren_ind + target_ind);
289 debug_println(
290 "setting paren_indents[%zu] from %d to %d "
291 "for column %d",
292 i, paren_ind,
293 ps.paren.item[i].indent, target_ind + 1);
294 }
295 }
296
297 if (lab.len > 0 && target_ind <= out_ind)
298 write_range(" ", 1);
299 write_indent(target_ind);
300 write_range(code.s, code.len);
301 }
302
303 static void
304 output_comment(void)
305 {
306 int target_ind = ps.comment_ind;
307 const char *p;
308
309 if (ps.comment_cont)
310 target_ind += ps.comment_shift;
311 ps.comment_cont = true;
312
313 /* consider the original indentation in case this is a box comment */
314 for (p = com.s; *p == '\t'; p++)
315 target_ind += opt.tabsize;
316
317 for (; target_ind < 0; p++) {
318 if (*p == ' ')
319 target_ind++;
320 else if (*p == '\t')
321 target_ind = next_tab(target_ind);
322 else {
323 target_ind = 0;
324 break;
325 }
326 }
327
328 if (out_ind > target_ind)
329 add_buffered_newline();
330
331 while (com.s + com.len > p && ch_isspace(com.s[com.len - 1]))
332 com.len--;
333 buf_terminate(&com);
334
335 write_indent(target_ind);
336 write_range(p, com.len - (size_t)(p - com.s));
337 }
338
339 /*
340 * Write a line of formatted source to the output file. The line consists of
341 * the label, the code and the comment.
342 */
343 static void
344 output_indented_line(void)
345 {
346 if (lab.len == 0 && code.len == 0 && com.len == 0)
347 out.line_kind = lk_blank;
348
349 if (want_blank_line() && newlines < 2 && out.line_kind != lk_blank)
350 add_buffered_newline();
351
352 /* This kludge aligns function definitions correctly. */
353 if (ps.ind_level == 0)
354 ps.line_is_stmt_cont = false;
355
356 if (opt.blank_line_after_decl && ps.declaration == decl_end
357 && ps.psyms.len > 2) {
358 ps.declaration = decl_no;
359 ps.blank_line_after_decl = true;
360 }
361
362 if (opt.swallow_optional_blank_lines
363 && out.line_kind == lk_blank
364 && is_blank_line_optional())
365 return;
366
367 if (lab.len > 0)
368 output_line_label();
369 if (code.len > 0)
370 output_line_code();
371 if (com.len > 0)
372 output_comment();
373 add_buffered_newline();
374 if (out.line_kind != lk_blank)
375 write_buffered_newlines();
376
377 out.prev_line_kind = out.line_kind;
378 }
379
380 static bool
381 is_stmt_cont(void)
382 {
383 if (ps.psyms.len >= 2
384 && ps.psyms.sym[ps.psyms.len - 2] == psym_lbrace_enum
385 && ps.prev_lsym == lsym_comma
386 && ps.paren.len == 0)
387 return false;
388 return ps.in_stmt_or_decl
389 && (!ps.in_decl || ps.in_init)
390 && ps.init_level == 0;
391 }
392
393 static void
394 prepare_next_line(void)
395 {
396 ps.line_has_decl = ps.in_decl;
397 ps.line_has_func_def = false;
398 ps.line_is_stmt_cont = is_stmt_cont();
399 ps.decl_indent_done = false;
400 if (ps.extra_expr_indent == eei_last)
401 ps.extra_expr_indent = eei_no;
402 if (!(ps.psyms.sym[ps.psyms.len - 1] == psym_if_expr_stmt_else
403 && ps.paren.len > 0))
404 ps.ind_level = ps.ind_level_follow;
405 ps.ind_paren_level = (int)ps.paren.len;
406 ps.want_blank = false;
407 if ((ps.badp == badp_seen_lbrace || ps.badp == badp_seen_decl)
408 && !ps.in_decl)
409 ps.badp = badp_yes;
410
411 if (ps.paren.len > 0) {
412 /* TODO: explain what negative indentation means */
413 paren_indent = -1 - ps.paren.item[ps.paren.len - 1].indent;
414 debug_println("paren_indent is now %d", paren_indent);
415 }
416
417 out.line_kind = lk_other;
418 }
419
420 void
421 output_line(void)
422 {
423 debug_blank_line();
424 debug_printf("%s", __func__);
425 debug_buffers();
426
427 if (indent_enabled == indent_on)
428 output_indented_line();
429 else if (indent_enabled == indent_last_off_line) {
430 indent_enabled = indent_on;
431 write_range(out.indent_off_text.s, out.indent_off_text.len);
432 buf_clear(&out.indent_off_text);
433 }
434
435 buf_clear(&lab);
436 buf_clear(&code);
437 buf_clear(&com);
438
439 prepare_next_line();
440 }
441
442 void
443 finish_output(void)
444 {
445 output_line();
446 if (indent_enabled != indent_on) {
447 indent_enabled = indent_last_off_line;
448 output_line();
449 }
450 fflush(output);
451 }
452