io.c revision 1.160 1 /* $NetBSD: io.c,v 1.160 2023/05/13 15:34:22 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.160 2023/05/13 15:34:22 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 (ps.blank_line_after_decl && ps.declaration == decl_no) {
292 ps.blank_line_after_decl = false;
293 if (lab.e != lab.s || code.e != code.s || com.e != com.s)
294 output_char('\n');
295 }
296
297 if (!inhibit_formatting) {
298 if (ps.ind_level == 0)
299 ps.in_stmt_cont = false; /* this is a class A kludge */
300
301 if (opt.blank_line_after_decl && ps.declaration == decl_end
302 && ps.tos > 1) {
303 ps.declaration = decl_no;
304 ps.blank_line_after_decl = true;
305 }
306
307 int ind = 0;
308 if (lab.e != lab.s)
309 ind = output_line_label();
310 if (code.e != code.s)
311 ind = output_line_code(ind);
312 if (com.e != com.s)
313 output_line_comment(ind);
314
315 output_char(line_terminator);
316 }
317
318 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */
319 ps.in_stmt_cont = ps.in_stmt_or_decl && !ps.in_decl;
320 ps.decl_indent_done = false;
321
322 *(lab.e = lab.s) = '\0'; /* reset buffers */
323 *(code.e = code.s) = '\0';
324 *(com.e = com.s = com.mem + 1) = '\0';
325
326 ps.ind_level = ps.ind_level_follow;
327 ps.line_start_nparen = ps.nparen;
328
329 if (ps.nparen > 0) {
330 /* TODO: explain what negative indentation means */
331 paren_indent = -1 - ps.paren[ps.nparen - 1].indent;
332 debug_println("paren_indent is now %d", paren_indent);
333 }
334 }
335
336 void
337 output_line(void)
338 {
339 output_complete_line('\n');
340 }
341
342 void
343 output_line_ff(void)
344 {
345 output_complete_line('\f');
346 }
347
348 static int
349 compute_code_indent_lineup(int base_ind)
350 {
351 int ti = paren_indent;
352 int overflow = ind_add(ti, code.s, code.e) - opt.max_line_length;
353 if (overflow < 0)
354 return ti;
355
356 if (ind_add(base_ind, code.s, code.e) < opt.max_line_length) {
357 ti -= overflow + 2;
358 if (ti > base_ind)
359 return ti;
360 return base_ind;
361 }
362
363 return ti;
364 }
365
366 int
367 compute_code_indent(void)
368 {
369 int base_ind = ps.ind_level * opt.indent_size;
370
371 if (ps.line_start_nparen == 0) {
372 if (ps.in_stmt_cont && ps.in_enum != in_enum_brace)
373 return base_ind + opt.continuation_indent;
374 return base_ind;
375 }
376
377 if (opt.lineup_to_parens) {
378 if (opt.lineup_to_parens_always)
379 return paren_indent;
380 return compute_code_indent_lineup(base_ind);
381 }
382
383 if (2 * opt.continuation_indent == opt.indent_size)
384 return base_ind + opt.continuation_indent;
385 else
386 return base_ind + opt.continuation_indent * ps.line_start_nparen;
387 }
388
389 int
390 compute_label_indent(void)
391 {
392 if (ps.is_case_label)
393 return (int)(case_ind * (float)opt.indent_size);
394 if (lab.s[0] == '#')
395 return 0;
396 return opt.indent_size * (ps.ind_level - 2);
397 }
398
399 static void
400 skip_blank(const char **pp)
401 {
402 while (ch_isblank(**pp))
403 (*pp)++;
404 }
405
406 static bool
407 skip_string(const char **pp, const char *s)
408 {
409 size_t len = strlen(s);
410 if (strncmp(*pp, s, len) == 0) {
411 *pp += len;
412 return true;
413 }
414 return false;
415 }
416
417 static void
418 parse_indent_comment(void)
419 {
420 bool on;
421
422 const char *p = inp.mem;
423
424 skip_blank(&p);
425 if (!skip_string(&p, "/*"))
426 return;
427 skip_blank(&p);
428 if (!skip_string(&p, "INDENT"))
429 return;
430
431 skip_blank(&p);
432 if (*p == '*' || skip_string(&p, "ON"))
433 on = true;
434 else if (skip_string(&p, "OFF"))
435 on = false;
436 else
437 return;
438
439 skip_blank(&p);
440 if (!skip_string(&p, "*/\n"))
441 return;
442
443 if (com.s != com.e || lab.s != lab.e || code.s != code.e)
444 output_line();
445
446 inhibit_formatting = !on;
447 }
448
449 void
450 inp_read_line(void)
451 {
452 inp_read_next_line(input);
453
454 parse_indent_comment();
455
456 if (inhibit_formatting)
457 output_range(inp.s, inp.e);
458 }
459