io.c revision 1.164 1 /* $NetBSD: io.c,v 1.164 2023/05/14 14:14:07 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.164 2023/05/14 14:14:07 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 *lab.e = '\0';
194
195 ind = output_indent(0, compute_label_indent());
196 output_range(lab.s, lab.e);
197 ind = ind_add(ind, lab.s, lab.e);
198
199 ps.is_case_label = false;
200 return ind;
201 }
202
203 static int
204 output_line_code(int ind)
205 {
206
207 int target_ind = compute_code_indent();
208 for (int i = 0; i < ps.nparen; i++) {
209 if (ps.paren[i].indent >= 0) {
210 int paren_ind = ps.paren[i].indent;
211 ps.paren[i].indent = (short)(-1 - (paren_ind + target_ind));
212 debug_println(
213 "setting paren_indents[%d] from %d to %d for column %d",
214 i, paren_ind, ps.paren[i].indent, target_ind + 1);
215 }
216 }
217
218 ind = output_indent(ind, target_ind);
219 output_range(code.s, code.e);
220 return ind_add(ind, code.s, code.e);
221 }
222
223 static void
224 output_line_comment(int ind)
225 {
226 int target_ind = ps.com_ind;
227 const char *p = com.s;
228
229 target_ind += ps.comment_delta;
230
231 /* consider original indentation in case this is a box comment */
232 for (; *p == '\t'; p++)
233 target_ind += opt.tabsize;
234
235 for (; target_ind < 0; p++) {
236 if (*p == ' ')
237 target_ind++;
238 else if (*p == '\t')
239 target_ind = next_tab(target_ind);
240 else {
241 target_ind = 0;
242 break;
243 }
244 }
245
246 /* if comment can't fit on this line, put it on the next line */
247 if (ind > target_ind) {
248 output_char('\n');
249 ind = 0;
250 }
251
252 while (com.e > p && ch_isspace(com.e[-1]))
253 com.e--;
254
255 (void)output_indent(ind, target_ind);
256 output_range(p, com.e);
257
258 ps.comment_delta = ps.n_comment_delta;
259 }
260
261 /*
262 * Write a line of formatted source to the output file. The line consists of
263 * the label, the code and the comment.
264 *
265 * Comments are written directly, bypassing this function.
266 */
267 static void
268 output_complete_line(char line_terminator)
269 {
270 debug_printf("%s", __func__);
271 debug_buffers();
272 debug_println("%s", line_terminator == '\f' ? " form_feed" : "");
273
274 ps.is_function_definition = false;
275
276 if (ps.blank_line_after_decl && ps.declaration == decl_no) {
277 ps.blank_line_after_decl = false;
278 if (lab.e != lab.s || code.e != code.s || com.e != com.s)
279 output_char('\n');
280 }
281
282 if (!inhibit_formatting) {
283 if (ps.ind_level == 0)
284 ps.in_stmt_cont = false; /* this is a class A kludge */
285
286 if (opt.blank_line_after_decl && ps.declaration == decl_end
287 && ps.tos > 1) {
288 ps.declaration = decl_no;
289 ps.blank_line_after_decl = true;
290 }
291
292 int ind = 0;
293 if (lab.e != lab.s)
294 ind = output_line_label();
295 if (code.e != code.s)
296 ind = output_line_code(ind);
297 if (com.e != com.s)
298 output_line_comment(ind);
299
300 output_char(line_terminator);
301 }
302
303 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */
304 ps.in_stmt_cont = ps.in_stmt_or_decl && !ps.in_decl;
305 ps.decl_indent_done = false;
306
307 *(lab.e = lab.s) = '\0'; /* reset buffers */
308 *(code.e = code.s) = '\0';
309 *(com.e = com.s = com.mem + 1) = '\0';
310
311 ps.ind_level = ps.ind_level_follow;
312 ps.line_start_nparen = ps.nparen;
313
314 if (ps.nparen > 0) {
315 /* TODO: explain what negative indentation means */
316 paren_indent = -1 - ps.paren[ps.nparen - 1].indent;
317 debug_println("paren_indent is now %d", paren_indent);
318 }
319
320 ps.want_blank = false;
321 }
322
323 void
324 output_line(void)
325 {
326 output_complete_line('\n');
327 }
328
329 void
330 output_line_ff(void)
331 {
332 output_complete_line('\f');
333 }
334
335 static int
336 compute_code_indent_lineup(int base_ind)
337 {
338 int ind = paren_indent;
339 int overflow = ind_add(ind, code.s, code.e) - opt.max_line_length;
340 if (overflow < 0)
341 return ind;
342
343 if (ind_add(base_ind, code.s, code.e) < opt.max_line_length) {
344 ind -= overflow + 2;
345 if (ind > base_ind)
346 return ind;
347 return base_ind;
348 }
349
350 return ind;
351 }
352
353 int
354 compute_code_indent(void)
355 {
356 int base_ind = ps.ind_level * opt.indent_size;
357
358 if (ps.line_start_nparen == 0) {
359 if (ps.in_stmt_cont && ps.in_enum != in_enum_brace)
360 return base_ind + opt.continuation_indent;
361 return base_ind;
362 }
363
364 if (opt.lineup_to_parens) {
365 if (opt.lineup_to_parens_always)
366 return paren_indent;
367 return compute_code_indent_lineup(base_ind);
368 }
369
370 if (2 * opt.continuation_indent == opt.indent_size)
371 return base_ind + opt.continuation_indent;
372 else
373 return base_ind + opt.continuation_indent * ps.line_start_nparen;
374 }
375
376 int
377 compute_label_indent(void)
378 {
379 if (ps.is_case_label)
380 return (int)(case_ind * (float)opt.indent_size);
381 if (lab.s[0] == '#')
382 return 0;
383 return opt.indent_size * (ps.ind_level - 2);
384 }
385
386 static void
387 skip_blank(const char **pp)
388 {
389 while (ch_isblank(**pp))
390 (*pp)++;
391 }
392
393 static bool
394 skip_string(const char **pp, const char *s)
395 {
396 size_t len = strlen(s);
397 if (strncmp(*pp, s, len) == 0) {
398 *pp += len;
399 return true;
400 }
401 return false;
402 }
403
404 static void
405 parse_indent_comment(void)
406 {
407 bool on;
408
409 const char *p = inp.mem;
410
411 skip_blank(&p);
412 if (!skip_string(&p, "/*"))
413 return;
414 skip_blank(&p);
415 if (!skip_string(&p, "INDENT"))
416 return;
417
418 skip_blank(&p);
419 if (*p == '*' || skip_string(&p, "ON"))
420 on = true;
421 else if (skip_string(&p, "OFF"))
422 on = false;
423 else
424 return;
425
426 skip_blank(&p);
427 if (!skip_string(&p, "*/\n"))
428 return;
429
430 if (com.s != com.e || lab.s != lab.e || code.s != code.e)
431 output_line();
432
433 inhibit_formatting = !on;
434 }
435
436 void
437 inp_read_line(void)
438 {
439 inp_read_next_line(input);
440
441 parse_indent_comment();
442
443 if (inhibit_formatting)
444 output_range(inp.s, inp.e);
445 }
446