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 47struct input_state in = { 48 .token_end_line = 1, 49}; 50 51struct output_state out; 52enum indent_enabled indent_enabled; 53static int out_ind; /* width of the line that is being written */ 54static 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 */ 58static unsigned buffered_newlines; /* not yet written */ 59static int paren_indent; /* total indentation when parenthesized */ 60 61 62static void 63inp_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 87void 88inp_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 96void 97inp_skip(void) 98{ 99 in.p++; 100 if ((size_t)(in.p - in.line.s) >= in.line.len) 101 inp_read_line(); 102} 103 104char 105inp_next(void) 106{ 107 char ch = in.p[0]; 108 inp_skip(); 109 return ch; 110} 111 112 113static void 114add_buffered_newline(void) 115{ 116 buffered_newlines++; 117 newlines++; 118 out_ind = 0; 119} 120 121static void 122write_buffered_newlines(void) 123{ 124 for (; buffered_newlines > 0; buffered_newlines--) { 125 fputc('\n', output); 126 debug_println("write_newline"); 127 } 128} 129 130static void 131write_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 143static void 144write_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 169static bool 170want_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 200static bool 201is_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 210static int 211compute_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 221int 222compute_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 232static void 233output_line_label(void) 234{ 235 write_indent(compute_label_indent()); 236 write_range(lab.s, lab.len); 237} 238 239static int 240compute_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 257int 258compute_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 280static void 281output_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 303static void 304output_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 */ 343static void 344output_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 380static bool 381is_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 393static void 394prepare_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 420void 421output_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 442void 443finish_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