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