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