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