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