1 /* $NetBSD: io.c,v 1.187 2023/05/23 18:16:28 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.187 2023/05/23 18:16:28 rillig Exp $"); 42 43 #include <stdio.h> 44 45 #include "indent.h" 46 47 struct buffer inp; 48 struct output_state out; 49 static unsigned wrote_newlines = 2; /* 0 in the middle of a line, 1 after a 50 * single '\n', > 1 means there were (n 51 * - 1) blank lines above */ 52 static int paren_indent; 53 54 55 void 56 inp_skip(void) 57 { 58 inp.st++; 59 if ((size_t)(inp.st - inp.mem) >= inp.len) 60 inp_read_line(); 61 } 62 63 char 64 inp_next(void) 65 { 66 char ch = inp.st[0]; 67 inp_skip(); 68 return ch; 69 } 70 71 static void 72 inp_read_next_line(FILE *f) 73 { 74 inp.st = inp.mem; 75 inp.len = 0; 76 77 for (;;) { 78 int ch = getc(f); 79 if (ch == EOF) { 80 if (indent_enabled == indent_on) { 81 buf_add_char(&inp, ' '); 82 buf_add_char(&inp, '\n'); 83 } 84 had_eof = true; 85 break; 86 } 87 88 if (ch != '\0') 89 buf_add_char(&inp, (char)ch); 90 if (ch == '\n') 91 break; 92 } 93 } 94 95 static void 96 output_newline(void) 97 { 98 fputc('\n', output); 99 debug_println("output_newline"); 100 wrote_newlines++; 101 } 102 103 static void 104 output_range(const char *s, size_t len) 105 { 106 fwrite(s, 1, len, output); 107 debug_vis_range("output_range \"", s, len, "\"\n"); 108 for (size_t i = 0; i < len; i++) 109 wrote_newlines = s[i] == '\n' ? wrote_newlines + 1 : 0; 110 } 111 112 static int 113 output_indent(int old_ind, int new_ind) 114 { 115 int ind = old_ind; 116 117 if (opt.use_tabs) { 118 int tabsize = opt.tabsize; 119 int n = new_ind / tabsize - ind / tabsize; 120 if (n > 0) 121 ind -= ind % tabsize; 122 for (int i = 0; i < n; i++) { 123 fputc('\t', output); 124 ind += tabsize; 125 wrote_newlines = 0; 126 } 127 } 128 129 for (; ind < new_ind; ind++) { 130 fputc(' ', output); 131 wrote_newlines = 0; 132 } 133 134 debug_println("output_indent %d", ind); 135 return ind; 136 } 137 138 static bool 139 want_blank_line(void) 140 { 141 debug_println("%s: %s -> %s", __func__, 142 line_kind_name[out.prev_line_kind], line_kind_name[out.line_kind]); 143 144 if (ps.blank_line_after_decl && ps.declaration == decl_no) { 145 ps.blank_line_after_decl = false; 146 return true; 147 } 148 if (opt.blanklines_around_conditional_compilation) { 149 if (out.prev_line_kind != lk_if && out.line_kind == lk_if) 150 return true; 151 if (out.prev_line_kind == lk_endif 152 && out.line_kind != lk_endif) 153 return true; 154 } 155 if (opt.blanklines_after_procs && out.prev_line_kind == lk_func_end 156 && out.line_kind != lk_endif) 157 return true; 158 if (opt.blanklines_before_block_comments 159 && out.line_kind == lk_block_comment) 160 return true; 161 return false; 162 } 163 164 static bool 165 is_blank_line_optional(void) 166 { 167 if (out.prev_line_kind == lk_stmt_head) 168 return wrote_newlines >= 1; 169 if (ps.tos >= 2) 170 return wrote_newlines >= 2; 171 return wrote_newlines >= 3; 172 } 173 174 static int 175 output_line_label(void) 176 { 177 int ind; 178 179 while (lab.len > 0 && ch_isblank(lab.mem[lab.len - 1])) 180 lab.len--; 181 182 ind = output_indent(0, compute_label_indent()); 183 output_range(lab.st, lab.len); 184 ind = ind_add(ind, lab.st, lab.len); 185 186 ps.is_case_label = false; 187 return ind; 188 } 189 190 static int 191 output_line_code(int ind) 192 { 193 194 int target_ind = compute_code_indent(); 195 for (int i = 0; i < ps.nparen; i++) { 196 int paren_ind = ps.paren[i].indent; 197 if (paren_ind >= 0) { 198 ps.paren[i].indent = -1 - (paren_ind + target_ind); 199 debug_println( 200 "setting paren_indents[%d] from %d to %d " 201 "for column %d", 202 i, paren_ind, ps.paren[i].indent, target_ind + 1); 203 } 204 } 205 206 ind = output_indent(ind, target_ind); 207 output_range(code.st, code.len); 208 return ind_add(ind, code.st, code.len); 209 } 210 211 static void 212 output_line_comment(int ind) 213 { 214 int target_ind = ps.com_ind; 215 const char *p = com.st; 216 217 target_ind += ps.comment_delta; 218 219 /* consider original indentation in case this is a box comment */ 220 for (; *p == '\t'; p++) 221 target_ind += opt.tabsize; 222 223 for (; target_ind < 0; p++) { 224 if (*p == ' ') 225 target_ind++; 226 else if (*p == '\t') 227 target_ind = next_tab(target_ind); 228 else { 229 target_ind = 0; 230 break; 231 } 232 } 233 234 /* if comment can't fit on this line, put it on the next line */ 235 if (ind > target_ind) { 236 output_newline(); 237 ind = 0; 238 } 239 240 while (com.mem + com.len > p && ch_isspace(com.mem[com.len - 1])) 241 com.len--; 242 243 (void)output_indent(ind, target_ind); 244 output_range(p, com.len - (size_t)(p - com.mem)); 245 246 ps.comment_delta = ps.n_comment_delta; 247 } 248 249 /* 250 * Write a line of formatted source to the output file. The line consists of 251 * the label, the code and the comment. 252 * 253 * Comments are written directly, bypassing this function. 254 */ 255 void 256 output_line(void) 257 { 258 debug_blank_line(); 259 debug_printf("%s", __func__); 260 debug_buffers(); 261 262 ps.is_function_definition = false; 263 264 if (indent_enabled == indent_on) { 265 if (lab.len == 0 && code.len == 0 && com.len == 0) 266 out.line_kind = lk_blank; 267 268 if (want_blank_line() && wrote_newlines < 2 269 && out.line_kind != lk_blank) 270 output_newline(); 271 272 if (ps.ind_level == 0) 273 ps.in_stmt_cont = false; /* this is a class A 274 * kludge */ 275 276 if (opt.blank_line_after_decl && ps.declaration == decl_end 277 && ps.tos > 1) { 278 ps.declaration = decl_no; 279 ps.blank_line_after_decl = true; 280 } 281 282 if (opt.swallow_optional_blanklines 283 && out.line_kind == lk_blank 284 && is_blank_line_optional()) 285 goto dont_write_line; 286 287 int ind = 0; 288 if (lab.len > 0) 289 ind = output_line_label(); 290 if (code.len > 0) 291 ind = output_line_code(ind); 292 if (com.len > 0) 293 output_line_comment(ind); 294 295 output_newline(); 296 out.prev_line_kind = out.line_kind; 297 } 298 299 if (indent_enabled == indent_last_off_line) { 300 indent_enabled = indent_on; 301 output_range(out.indent_off_text.st, out.indent_off_text.len); 302 out.indent_off_text.len = 0; 303 } 304 305 dont_write_line: 306 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */ 307 ps.in_stmt_cont = ps.in_stmt_or_decl && !ps.in_decl; 308 ps.decl_indent_done = false; 309 if (ps.extra_expr_indent == eei_last) 310 ps.extra_expr_indent = eei_no; 311 312 lab.len = 0; 313 code.len = 0; 314 com.len = 0; 315 316 ps.ind_level = ps.ind_level_follow; 317 ps.line_start_nparen = ps.nparen; 318 319 if (ps.nparen > 0) { 320 /* TODO: explain what negative indentation means */ 321 paren_indent = -1 - ps.paren[ps.nparen - 1].indent; 322 debug_println("paren_indent is now %d", paren_indent); 323 } 324 325 ps.want_blank = false; 326 out.line_kind = lk_other; 327 } 328 329 static int 330 compute_code_indent_lineup(int base_ind) 331 { 332 int ind = paren_indent; 333 int overflow = ind_add(ind, code.st, code.len) - opt.max_line_length; 334 if (overflow < 0) 335 return ind; 336 337 if (ind_add(base_ind, code.st, code.len) < opt.max_line_length) { 338 ind -= overflow + 2; 339 if (ind > base_ind) 340 return ind; 341 return base_ind; 342 } 343 344 return ind; 345 } 346 347 int 348 compute_code_indent(void) 349 { 350 int base_ind = ps.ind_level * opt.indent_size; 351 352 if (ps.line_start_nparen == 0) { 353 if (ps.in_enum == in_enum_brace) 354 return base_ind; 355 if (ps.in_stmt_cont) 356 return base_ind + opt.continuation_indent; 357 return base_ind; 358 } 359 360 if (opt.lineup_to_parens) { 361 if (opt.lineup_to_parens_always) 362 return paren_indent; 363 return compute_code_indent_lineup(base_ind); 364 } 365 366 if (ps.extra_expr_indent != eei_no) 367 return base_ind + 2 * opt.continuation_indent; 368 369 if (2 * opt.continuation_indent == opt.indent_size) 370 return base_ind + opt.continuation_indent; 371 else 372 return base_ind + 373 opt.continuation_indent * ps.line_start_nparen; 374 } 375 376 int 377 compute_label_indent(void) 378 { 379 if (ps.is_case_label) 380 return (int)(case_ind * (float)opt.indent_size); 381 if (lab.st[0] == '#') 382 return 0; 383 return opt.indent_size * (ps.ind_level - 2); 384 } 385 386 void 387 inp_read_line(void) 388 { 389 if (indent_enabled == indent_on) 390 out.indent_off_text.len = 0; 391 buf_add_chars(&out.indent_off_text, inp.mem, inp.len); 392 inp_read_next_line(input); 393 } 394