io.c revision 1.174 1 /* $NetBSD: io.c,v 1.174 2023/05/16 11:32:01 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.174 2023/05/16 11:32:01 rillig Exp $");
42
43 #include <stdio.h>
44 #include <string.h>
45
46 #include "indent.h"
47
48 /*
49 * The current line, ready to be split into tokens, terminated with '\n'. The
50 * current read position is inp.s, and the invariant inp.s < inp.e holds.
51 */
52 static struct buffer inp;
53 static struct buffer indent_off_text;
54
55 static int paren_indent;
56
57
58 const char *
59 inp_p(void)
60 {
61 return inp.st;
62 }
63
64 const char *
65 inp_line_start(void)
66 {
67 return inp.mem;
68 }
69
70 char
71 inp_peek(void)
72 {
73 return *inp.st;
74 }
75
76 char
77 inp_lookahead(size_t i)
78 {
79 return inp.st[i];
80 }
81
82 void
83 inp_skip(void)
84 {
85 inp.st++;
86 if ((size_t)(inp.st - inp.mem) >= inp.len)
87 inp_read_line();
88 }
89
90 char
91 inp_next(void)
92 {
93 char ch = inp_peek();
94 inp_skip();
95 return ch;
96 }
97
98 static void
99 inp_read_next_line(FILE *f)
100 {
101 inp.st = inp.mem;
102 inp.len = 0;
103
104 for (;;) {
105 int ch = getc(f);
106 if (ch == EOF) {
107 if (indent_enabled == indent_on) {
108 buf_add_char(&inp, ' ');
109 buf_add_char(&inp, '\n');
110 }
111 had_eof = true;
112 break;
113 }
114
115 if (ch != '\0')
116 buf_add_char(&inp, (char)ch);
117 if (ch == '\n')
118 break;
119 }
120 }
121
122 static void
123 output_char(char ch)
124 {
125 fputc(ch, output);
126 debug_vis_range("output_char '", &ch, 1, "'\n");
127 }
128
129 static void
130 output_range(const char *s, size_t len)
131 {
132 fwrite(s, 1, len, output);
133 debug_vis_range("output_range \"", s, len, "\"\n");
134 }
135
136 static int
137 output_indent(int old_ind, int new_ind)
138 {
139 int ind = old_ind;
140
141 if (opt.use_tabs) {
142 int tabsize = opt.tabsize;
143 int n = new_ind / tabsize - ind / tabsize;
144 if (n > 0)
145 ind -= ind % tabsize;
146 for (int i = 0; i < n; i++) {
147 fputc('\t', output);
148 ind += tabsize;
149 }
150 }
151
152 for (; ind < new_ind; ind++)
153 fputc(' ', output);
154
155 debug_println("output_indent %d", ind);
156 return ind;
157 }
158
159 static int
160 output_line_label(void)
161 {
162 int ind;
163
164 while (lab.len > 0 && ch_isblank(lab.mem[lab.len - 1]))
165 lab.len--;
166
167 ind = output_indent(0, compute_label_indent());
168 output_range(lab.st, lab.len);
169 ind = ind_add(ind, lab.st, lab.len);
170
171 ps.is_case_label = false;
172 return ind;
173 }
174
175 static int
176 output_line_code(int ind)
177 {
178
179 int target_ind = compute_code_indent();
180 for (int i = 0; i < ps.nparen; i++) {
181 int paren_ind = ps.paren[i].indent;
182 if (paren_ind >= 0) {
183 ps.paren[i].indent = (short)(-1 - (paren_ind + target_ind));
184 debug_println(
185 "setting paren_indents[%d] from %d to %d for column %d",
186 i, paren_ind, ps.paren[i].indent, target_ind + 1);
187 }
188 }
189
190 ind = output_indent(ind, target_ind);
191 output_range(code.st, code.len);
192 return ind_add(ind, code.st, code.len);
193 }
194
195 static void
196 output_line_comment(int ind)
197 {
198 int target_ind = ps.com_ind;
199 const char *p = com.st;
200
201 target_ind += ps.comment_delta;
202
203 /* consider original indentation in case this is a box comment */
204 for (; *p == '\t'; p++)
205 target_ind += opt.tabsize;
206
207 for (; target_ind < 0; p++) {
208 if (*p == ' ')
209 target_ind++;
210 else if (*p == '\t')
211 target_ind = next_tab(target_ind);
212 else {
213 target_ind = 0;
214 break;
215 }
216 }
217
218 /* if comment can't fit on this line, put it on the next line */
219 if (ind > target_ind) {
220 output_char('\n');
221 ind = 0;
222 }
223
224 while (com.mem + com.len > p && ch_isspace(com.mem[com.len - 1]))
225 com.len--;
226
227 (void)output_indent(ind, target_ind);
228 output_range(p, com.len - (size_t)(p - com.mem));
229
230 ps.comment_delta = ps.n_comment_delta;
231 }
232
233 /*
234 * Write a line of formatted source to the output file. The line consists of
235 * the label, the code and the comment.
236 *
237 * Comments are written directly, bypassing this function.
238 */
239 void
240 output_line(void)
241 {
242 debug_printf("%s", __func__);
243 debug_buffers();
244 debug_println("");
245
246 ps.is_function_definition = false;
247
248 if (ps.blank_line_after_decl && ps.declaration == decl_no) {
249 ps.blank_line_after_decl = false;
250 if (lab.len > 0 || code.len > 0 || com.len > 0)
251 output_char('\n');
252 }
253
254 if (indent_enabled == indent_on) {
255 if (ps.ind_level == 0)
256 ps.in_stmt_cont = false; /* this is a class A kludge */
257
258 if (opt.blank_line_after_decl && ps.declaration == decl_end
259 && ps.tos > 1) {
260 ps.declaration = decl_no;
261 ps.blank_line_after_decl = true;
262 }
263
264 int ind = 0;
265 if (lab.len > 0)
266 ind = output_line_label();
267 if (code.len > 0)
268 ind = output_line_code(ind);
269 if (com.len > 0)
270 output_line_comment(ind);
271
272 output_char('\n');
273 }
274
275 if (indent_enabled == indent_last_off_line) {
276 indent_enabled = indent_on;
277 output_range(indent_off_text.st, indent_off_text.len);
278 indent_off_text.len = 0;
279 }
280
281 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */
282 ps.in_stmt_cont = ps.in_stmt_or_decl && !ps.in_decl;
283 ps.decl_indent_done = false;
284 if (ps.extra_expr_indent == eei_last)
285 ps.extra_expr_indent = eei_no;
286
287 lab.len = 0;
288 code.len = 0;
289 com.len = 0;
290
291 ps.ind_level = ps.ind_level_follow;
292 ps.line_start_nparen = ps.nparen;
293
294 if (ps.nparen > 0) {
295 /* TODO: explain what negative indentation means */
296 paren_indent = -1 - ps.paren[ps.nparen - 1].indent;
297 debug_println("paren_indent is now %d", paren_indent);
298 }
299
300 ps.want_blank = false;
301 }
302
303 static int
304 compute_code_indent_lineup(int base_ind)
305 {
306 int ind = paren_indent;
307 int overflow = ind_add(ind, code.st, code.len) - opt.max_line_length;
308 if (overflow < 0)
309 return ind;
310
311 if (ind_add(base_ind, code.st, code.len) < opt.max_line_length) {
312 ind -= overflow + 2;
313 if (ind > base_ind)
314 return ind;
315 return base_ind;
316 }
317
318 return ind;
319 }
320
321 int
322 compute_code_indent(void)
323 {
324 int base_ind = ps.ind_level * opt.indent_size;
325
326 if (ps.line_start_nparen == 0) {
327 if (ps.in_stmt_cont && ps.in_enum != in_enum_brace)
328 return base_ind + opt.continuation_indent;
329 return base_ind;
330 }
331
332 if (opt.lineup_to_parens) {
333 if (opt.lineup_to_parens_always)
334 return paren_indent;
335 return compute_code_indent_lineup(base_ind);
336 }
337
338 if (ps.extra_expr_indent != eei_no)
339 return base_ind + 2 * opt.continuation_indent;
340
341 if (2 * opt.continuation_indent == opt.indent_size)
342 return base_ind + opt.continuation_indent;
343 else
344 return base_ind + opt.continuation_indent * ps.line_start_nparen;
345 }
346
347 int
348 compute_label_indent(void)
349 {
350 if (ps.is_case_label)
351 return (int)(case_ind * (float)opt.indent_size);
352 if (lab.st[0] == '#')
353 return 0;
354 return opt.indent_size * (ps.ind_level - 2);
355 }
356
357 void
358 inp_read_line(void)
359 {
360 if (indent_enabled == indent_on)
361 indent_off_text.len = 0;
362 buf_add_chars(&indent_off_text, inp.mem, inp.len);
363 inp_read_next_line(input);
364 }
365
366 void
367 clear_indent_off_text(void)
368 {
369 indent_off_text.len = 0;
370 }
371