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