1 /* $NetBSD: io.c,v 1.237 2025/01/04 10:28:08 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.237 2025/01/04 10:28:08 rillig Exp $"); 42 43 #include <err.h> 44 #include <stdio.h> 45 46 #include "indent.h" 47 48 struct input_state in = { 49 .token_end_line = 1, 50 }; 51 52 struct output_state out; 53 enum indent_enabled indent_enabled; 54 static int out_ind; /* width of the line that is being written */ 55 static unsigned newlines = 2; /* the total of written and buffered newlines; 56 * 0 in the middle of a line, 1 after a single 57 * finished line, anything > 1 are trailing 58 * blank lines */ 59 static unsigned buffered_newlines; /* not yet written */ 60 static int paren_indent; /* total indentation when parenthesized */ 61 62 63 static void 64 inp_read_next_line(void) 65 { 66 buf_clear(&in.line); 67 68 for (;;) { 69 int ch = getc(in.f); 70 if (ch == EOF) { 71 if (indent_enabled == indent_on) { 72 buf_add_char(&in.line, ' '); 73 buf_add_char(&in.line, '\n'); 74 } 75 had_eof = true; 76 break; 77 } 78 79 if (ch != '\0') 80 buf_add_char(&in.line, (char)ch); 81 if (ch == '\n') 82 break; 83 } 84 buf_terminate(&in.line); 85 in.p = in.line.s; 86 } 87 88 void 89 inp_read_line(void) 90 { 91 if (indent_enabled == indent_on) 92 buf_clear(&out.indent_off_text); 93 buf_add_chars(&out.indent_off_text, in.line.s, in.line.len); 94 inp_read_next_line(); 95 } 96 97 void 98 inp_skip(void) 99 { 100 in.p++; 101 if ((size_t)(in.p - in.line.s) >= in.line.len) 102 inp_read_line(); 103 } 104 105 char 106 inp_next(void) 107 { 108 char ch = in.p[0]; 109 inp_skip(); 110 return ch; 111 } 112 113 114 static void 115 add_buffered_newline(void) 116 { 117 buffered_newlines++; 118 newlines++; 119 out_ind = 0; 120 } 121 122 static void 123 write_buffered_newlines(void) 124 { 125 for (; buffered_newlines > 0; buffered_newlines--) { 126 if (fputc('\n', output) == EOF) 127 err(1, "cannot write output"); 128 debug_println("write_newline"); 129 } 130 } 131 132 static void 133 write_range(const char *s, size_t len) 134 { 135 write_buffered_newlines(); 136 if (fwrite(s, 1, len, output) != len) 137 err(1, "cannot write output"); 138 debug_printf("write_range "); 139 debug_vis_range(s, len); 140 debug_println(""); 141 for (size_t i = 0; i < len; i++) 142 newlines = s[i] == '\n' ? newlines + 1 : 0; 143 out_ind = ind_add(out_ind, s, len); 144 } 145 146 static void 147 write_indent(int new_ind) 148 { 149 write_buffered_newlines(); 150 151 int ind = out_ind; 152 153 if (opt.use_tabs) { 154 int n = new_ind / opt.tabsize - ind / opt.tabsize; 155 if (n > 0) { 156 ind = ind - ind % opt.tabsize + n * opt.tabsize; 157 while (n-- > 0) 158 if (fputc('\t', output) == EOF) 159 err(1, "cannot write output"); 160 newlines = 0; 161 } 162 } 163 164 for (; ind < new_ind; ind++) { 165 if (fputc(' ', output) == EOF) 166 err(1, "cannot write output"); 167 newlines = 0; 168 } 169 170 debug_println("write_indent %d", ind); 171 out_ind = ind; 172 } 173 174 static bool 175 want_blank_line(void) 176 { 177 debug_println("%s: %s -> %s", __func__, 178 line_kind_name[out.prev_line_kind], line_kind_name[out.line_kind]); 179 debug_blank_line(); 180 181 if (((ps.blank_line_after_decl && ps.declaration == decl_no) 182 || ps.badp == badp_yes) 183 && (lab.len > 0 || code.len > 0)) { 184 ps.blank_line_after_decl = false; 185 ps.badp = badp_none; 186 return true; 187 } 188 189 if (opt.blank_line_around_conditional_compilation) { 190 if (out.prev_line_kind != lk_pre_if 191 && out.line_kind == lk_pre_if) 192 return true; 193 if (out.prev_line_kind == lk_pre_endif 194 && out.line_kind != lk_pre_endif) 195 return true; 196 } 197 if (opt.blank_line_after_proc && out.prev_line_kind == lk_func_end 198 && out.line_kind != lk_pre_endif && out.line_kind != lk_pre_other) 199 return true; 200 if (opt.blank_line_before_block_comment 201 && out.line_kind == lk_block_comment) 202 return true; 203 return false; 204 } 205 206 static bool 207 is_blank_line_optional(void) 208 { 209 if (out.prev_line_kind == lk_stmt_head) 210 return newlines >= 1; 211 if (ps.psyms.len >= 3) 212 return newlines >= 2; 213 return newlines >= 3; 214 } 215 216 static int 217 compute_case_label_indent(void) 218 { 219 size_t i = ps.psyms.len - 1; 220 while (i > 0 && ps.psyms.sym[i] != psym_switch_expr) 221 i--; 222 float case_ind = (float)ps.psyms.ind_level[i] + opt.case_indent; 223 // TODO: case_ind may become negative here. 224 return (int)(case_ind * (float)opt.indent_size); 225 } 226 227 int 228 compute_label_indent(void) 229 { 230 if (out.line_kind == lk_case_or_default) 231 return compute_case_label_indent(); 232 if (lab.s[0] == '#') 233 return 0; 234 // TODO: the indentation may become negative here. 235 return opt.indent_size * (ps.ind_level - 2); 236 } 237 238 static void 239 output_line_label(void) 240 { 241 write_indent(compute_label_indent()); 242 write_range(lab.s, lab.len); 243 } 244 245 static int 246 compute_lined_up_code_indent(int base_ind) 247 { 248 int ind = paren_indent; 249 int overflow = ind_add(ind, code.s, code.len) - opt.max_line_length; 250 if (overflow >= 0 251 && ind_add(base_ind, code.s, code.len) < opt.max_line_length) { 252 ind -= 2 + overflow; 253 if (ind < base_ind) 254 ind = base_ind; 255 } 256 257 if (ps.extra_expr_indent != eei_no 258 && ind == base_ind + opt.indent_size) 259 ind += opt.continuation_indent; 260 return ind; 261 } 262 263 int 264 compute_code_indent(void) 265 { 266 int base_ind = ps.ind_level * opt.indent_size; 267 268 if (ps.ind_paren_level == 0) { 269 if (ps.line_is_stmt_cont) 270 return base_ind + opt.continuation_indent; 271 return base_ind; 272 } 273 274 if (opt.lineup_to_parens) { 275 if (opt.lineup_to_parens_always) 276 return paren_indent; 277 return compute_lined_up_code_indent(base_ind); 278 } 279 280 int rel_ind = opt.continuation_indent * ps.ind_paren_level; 281 if (ps.extra_expr_indent != eei_no && rel_ind == opt.indent_size) 282 rel_ind += opt.continuation_indent; 283 return base_ind + rel_ind; 284 } 285 286 static void 287 output_line_code(void) 288 { 289 int target_ind = compute_code_indent(); 290 for (size_t i = 0; i < ps.paren.len; i++) { 291 int paren_ind = ps.paren.item[i].indent; 292 if (paren_ind >= 0) { 293 ps.paren.item[i].indent = 294 -1 - (paren_ind + target_ind); 295 debug_println( 296 "setting paren_indents[%zu] from %d to %d " 297 "for column %d", 298 i, paren_ind, 299 ps.paren.item[i].indent, target_ind + 1); 300 } 301 } 302 303 if (lab.len > 0 && target_ind <= out_ind) 304 write_range(" ", 1); 305 write_indent(target_ind); 306 write_range(code.s, code.len); 307 } 308 309 static void 310 output_comment(void) 311 { 312 int target_ind = ps.comment_ind; 313 const char *p; 314 315 if (ps.comment_cont) 316 target_ind += ps.comment_shift; 317 ps.comment_cont = true; 318 319 /* consider the original indentation in case this is a box comment */ 320 for (p = com.s; *p == '\t'; p++) 321 target_ind += opt.tabsize; 322 323 for (; target_ind < 0; p++) { 324 if (*p == ' ') 325 target_ind++; 326 else if (*p == '\t') 327 target_ind = next_tab(target_ind); 328 else { 329 target_ind = 0; 330 break; 331 } 332 } 333 334 if (out_ind > target_ind) 335 add_buffered_newline(); 336 337 while (com.s + com.len > p && ch_isspace(com.s[com.len - 1])) 338 com.len--; 339 buf_terminate(&com); 340 341 write_indent(target_ind); 342 write_range(p, com.len - (size_t)(p - com.s)); 343 } 344 345 /* 346 * Write a line of formatted source to the output file. The line consists of 347 * the label, the code and the comment. 348 */ 349 static void 350 output_indented_line(void) 351 { 352 if (lab.len == 0 && code.len == 0 && com.len == 0) 353 out.line_kind = lk_blank; 354 355 if (want_blank_line() && newlines < 2 && out.line_kind != lk_blank) 356 add_buffered_newline(); 357 358 /* This kludge aligns function definitions correctly. */ 359 if (ps.ind_level == 0) 360 ps.line_is_stmt_cont = false; 361 362 if (opt.blank_line_after_decl && ps.declaration == decl_end 363 && ps.psyms.len > 2) { 364 ps.declaration = decl_no; 365 ps.blank_line_after_decl = true; 366 } 367 368 if (opt.swallow_optional_blank_lines 369 && out.line_kind == lk_blank 370 && is_blank_line_optional()) 371 return; 372 373 if (lab.len > 0) 374 output_line_label(); 375 if (code.len > 0) 376 output_line_code(); 377 if (com.len > 0) 378 output_comment(); 379 add_buffered_newline(); 380 if (out.line_kind != lk_blank) 381 write_buffered_newlines(); 382 383 out.prev_line_kind = out.line_kind; 384 } 385 386 static bool 387 is_stmt_cont(void) 388 { 389 if (ps.psyms.len >= 2 390 && ps.psyms.sym[ps.psyms.len - 2] == psym_lbrace_enum 391 && ps.prev_lsym == lsym_comma 392 && ps.paren.len == 0) 393 return false; 394 return ps.in_stmt_or_decl 395 && (!ps.in_decl || ps.in_init) 396 && ps.init_level == 0; 397 } 398 399 static void 400 prepare_next_line(void) 401 { 402 ps.line_has_decl = ps.in_decl; 403 ps.line_has_func_def = false; 404 ps.line_is_stmt_cont = is_stmt_cont(); 405 ps.decl_indent_done = false; 406 if (ps.extra_expr_indent == eei_last) 407 ps.extra_expr_indent = eei_no; 408 if (!(ps.psyms.sym[ps.psyms.len - 1] == psym_if_expr_stmt_else 409 && ps.paren.len > 0)) 410 ps.ind_level = ps.ind_level_follow; 411 ps.ind_paren_level = (int)ps.paren.len; 412 ps.want_blank = false; 413 if ((ps.badp == badp_seen_lbrace || ps.badp == badp_seen_decl) 414 && !ps.in_decl) 415 ps.badp = badp_yes; 416 417 if (ps.paren.len > 0) { 418 /* TODO: explain what negative indentation means */ 419 paren_indent = -1 - ps.paren.item[ps.paren.len - 1].indent; 420 debug_println("paren_indent is now %d", paren_indent); 421 } 422 423 out.line_kind = lk_other; 424 } 425 426 void 427 output_line(void) 428 { 429 debug_blank_line(); 430 debug_buffers(__func__); 431 432 if (indent_enabled == indent_on) 433 output_indented_line(); 434 else if (indent_enabled == indent_last_off_line) { 435 indent_enabled = indent_on; 436 write_range(out.indent_off_text.s, out.indent_off_text.len); 437 buf_clear(&out.indent_off_text); 438 } 439 440 buf_clear(&lab); 441 buf_clear(&code); 442 buf_clear(&com); 443 444 prepare_next_line(); 445 } 446 447 void 448 finish_output(void) 449 { 450 output_line(); 451 if (indent_enabled != indent_on) { 452 indent_enabled = indent_last_off_line; 453 output_line(); 454 } 455 if (fflush(output) != 0) 456 err(1, "output file"); 457 } 458