1 /* $NetBSD: io.c,v 1.108 2021/10/30 11:49:38 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 #if 0 41 static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93"; 42 #endif 43 44 #include <sys/cdefs.h> 45 #if defined(__NetBSD__) 46 __RCSID("$NetBSD: io.c,v 1.108 2021/10/30 11:49:38 rillig Exp $"); 47 #elif defined(__FreeBSD__) 48 __FBSDID("$FreeBSD: head/usr.bin/indent/io.c 334927 2018-06-10 16:44:18Z pstef $"); 49 #endif 50 51 #include <ctype.h> 52 #include <stdarg.h> 53 #include <stdio.h> 54 #include <string.h> 55 56 #include "indent.h" 57 58 static int paren_indent; 59 static bool suppress_blanklines; 60 61 static void 62 output_char(char ch) 63 { 64 fputc(ch, output); 65 debug_vis_range("output_char '", &ch, &ch + 1, "'\n"); 66 } 67 68 static void 69 output_range(const char *s, const char *e) 70 { 71 fwrite(s, 1, (size_t)(e - s), output); 72 debug_vis_range("output_range \"", s, e, "\"\n"); 73 } 74 75 static inline void 76 output_string(const char *s) 77 { 78 output_range(s, s + strlen(s)); 79 } 80 81 static int 82 output_indent(int old_ind, int new_ind) 83 { 84 int ind = old_ind; 85 86 if (opt.use_tabs) { 87 int tabsize = opt.tabsize; 88 int n = new_ind / tabsize - ind / tabsize; 89 if (n > 0) 90 ind -= ind % tabsize; 91 for (int i = 0; i < n; i++) { 92 fputc('\t', output); 93 ind += tabsize; 94 } 95 } 96 97 for (; ind < new_ind; ind++) 98 fputc(' ', output); 99 100 debug_println("output_indent %d", ind); 101 return ind; 102 } 103 104 static int 105 dump_line_label(void) 106 { 107 int ind; 108 109 while (lab.e > lab.s && ch_isblank(lab.e[-1])) 110 lab.e--; 111 *lab.e = '\0'; 112 113 ind = output_indent(0, compute_label_indent()); 114 115 if (lab.s[0] == '#' && (strncmp(lab.s, "#else", 5) == 0 116 || strncmp(lab.s, "#endif", 6) == 0)) { 117 const char *s = lab.s; 118 if (lab.e[-1] == '\n') 119 lab.e--; 120 do { 121 output_char(*s++); 122 } while (s < lab.e && 'a' <= *s && *s <= 'z'); 123 124 while (s < lab.e && ch_isblank(*s)) 125 s++; 126 127 if (s < lab.e) { 128 if (s[0] == '/' && s[1] == '*') { 129 output_char('\t'); 130 output_range(s, lab.e); 131 } else { 132 output_string("\t/* "); 133 output_range(s, lab.e); 134 output_string(" */"); 135 } 136 } 137 } else 138 output_range(lab.s, lab.e); 139 ind = indentation_after(ind, lab.s); 140 141 ps.is_case_label = false; 142 return ind; 143 } 144 145 static int 146 dump_line_code(int ind) 147 { 148 149 int target_ind = compute_code_indent(); 150 for (int i = 0; i < ps.p_l_follow; i++) { 151 if (ps.paren_indents[i] >= 0) { 152 int paren_ind = ps.paren_indents[i]; 153 /* XXX: the '+ 1' smells like an off-by-one error. */ 154 ps.paren_indents[i] = (short)-(paren_ind + target_ind + 1); 155 debug_println( 156 "setting paren_indents[%d] from %d to %d for column %d", 157 i, paren_ind, ps.paren_indents[i], target_ind + 1); 158 } 159 } 160 161 ind = output_indent(ind, target_ind); 162 output_range(code.s, code.e); 163 return indentation_after(ind, code.s); 164 } 165 166 static void 167 dump_line_comment(int ind) 168 { 169 int target_ind = ps.com_ind; 170 const char *p = com.s; 171 172 target_ind += ps.comment_delta; 173 174 /* consider original indentation in case this is a box comment */ 175 for (; *p == '\t'; p++) 176 target_ind += opt.tabsize; 177 178 for (; target_ind < 0; p++) { 179 if (*p == ' ') 180 target_ind++; 181 else if (*p == '\t') 182 target_ind = next_tab(target_ind); 183 else { 184 target_ind = 0; 185 break; 186 } 187 } 188 189 /* if comment can't fit on this line, put it on the next line */ 190 if (ind > target_ind) { 191 output_char('\n'); 192 ind = 0; 193 ps.stats.lines++; 194 } 195 196 while (com.e > p && isspace((unsigned char)com.e[-1])) 197 com.e--; 198 199 (void)output_indent(ind, target_ind); 200 output_range(p, com.e); 201 202 ps.comment_delta = ps.n_comment_delta; 203 ps.stats.comment_lines++; 204 } 205 206 /* 207 * Write a line of formatted source to the output file. The line consists of 208 * the label, the code and the comment. 209 */ 210 static void 211 output_line(char line_terminator) 212 { 213 static bool first_line = true; 214 215 ps.procname[0] = '\0'; 216 217 if (code.s == code.e && lab.s == lab.e && com.s == com.e) { 218 if (suppress_blanklines) 219 suppress_blanklines = false; 220 else 221 blank_lines_to_output++; 222 223 } else if (!inhibit_formatting) { 224 suppress_blanklines = false; 225 if (blank_line_before && !first_line) { 226 if (opt.swallow_optional_blanklines) { 227 if (blank_lines_to_output == 1) 228 blank_lines_to_output = 0; 229 } else { 230 if (blank_lines_to_output == 0) 231 blank_lines_to_output = 1; 232 } 233 } 234 235 for (; blank_lines_to_output > 0; blank_lines_to_output--) 236 output_char('\n'); 237 238 if (ps.ind_level == 0) 239 ps.ind_stmt = false; /* this is a class A kludge. don't do 240 * additional statement indentation if 241 * we are at bracket level 0 */ 242 243 if (lab.e != lab.s || code.e != code.s) 244 ps.stats.code_lines++; 245 246 int ind = 0; 247 if (lab.e != lab.s) 248 ind = dump_line_label(); 249 if (code.e != code.s) 250 ind = dump_line_code(ind); 251 if (com.e != com.s) 252 dump_line_comment(ind); 253 254 output_char(line_terminator); 255 ps.stats.lines++; 256 257 if (ps.just_saw_decl == 1 && opt.blanklines_after_decl) { 258 blank_line_before = true; 259 ps.just_saw_decl = 0; 260 } else 261 blank_line_before = blank_line_after; 262 blank_line_after = false; 263 } 264 265 ps.decl_on_line = ps.in_decl; /* for proper comment indentation */ 266 ps.ind_stmt = ps.in_stmt && !ps.in_decl; 267 ps.decl_indent_done = false; 268 269 *(lab.e = lab.s) = '\0'; /* reset buffers */ 270 *(code.e = code.s) = '\0'; 271 *(com.e = com.s = com.buf + 1) = '\0'; 272 273 ps.ind_level = ps.ind_level_follow; 274 ps.paren_level = ps.p_l_follow; 275 276 if (ps.paren_level > 0) { 277 /* TODO: explain what negative indentation means */ 278 paren_indent = -ps.paren_indents[ps.paren_level - 1]; 279 debug_println("paren_indent is now %d", paren_indent); 280 } 281 282 first_line = false; 283 } 284 285 void 286 dump_line(void) 287 { 288 output_line('\n'); 289 } 290 291 void 292 dump_line_ff(void) 293 { 294 output_line('\f'); 295 } 296 297 int 298 compute_code_indent(void) 299 { 300 int target_ind = opt.indent_size * ps.ind_level; 301 302 if (ps.paren_level != 0) { 303 if (!opt.lineup_to_parens) { 304 if (2 * opt.continuation_indent == opt.indent_size) 305 target_ind += opt.continuation_indent; 306 else 307 target_ind += opt.continuation_indent * ps.paren_level; 308 309 } else if (opt.lineup_to_parens_always) { 310 /* 311 * XXX: where does this '- 1' come from? It looks strange but is 312 * nevertheless needed for proper indentation, as demonstrated in 313 * the test opt-lpl.0. 314 */ 315 target_ind = paren_indent - 1; 316 317 } else { 318 int w; 319 int t = paren_indent; 320 321 if ((w = 1 + indentation_after(t - 1, code.s) - opt.max_line_length) > 0 322 && 1 + indentation_after(target_ind, code.s) <= opt.max_line_length) { 323 t -= w + 1; 324 if (t > target_ind + 1) 325 target_ind = t - 1; 326 } else 327 target_ind = t - 1; 328 } 329 330 } else if (ps.ind_stmt) 331 target_ind += opt.continuation_indent; 332 333 return target_ind; 334 } 335 336 int 337 compute_label_indent(void) 338 { 339 if (ps.is_case_label) 340 return (int)(case_ind * (float)opt.indent_size); 341 if (lab.s[0] == '#') 342 return 0; 343 return opt.indent_size * (ps.ind_level - 2); 344 } 345 346 static void 347 skip_blank(const char **pp) 348 { 349 while (ch_isblank(**pp)) 350 (*pp)++; 351 } 352 353 static bool 354 skip_string(const char **pp, const char *s) 355 { 356 size_t len = strlen(s); 357 if (strncmp(*pp, s, len) == 0) { 358 *pp += len; 359 return true; 360 } 361 return false; 362 } 363 364 static void 365 parse_indent_comment(void) 366 { 367 bool on; 368 369 const char *p = inp.buf; 370 371 skip_blank(&p); 372 if (!skip_string(&p, "/*")) 373 return; 374 skip_blank(&p); 375 if (!skip_string(&p, "INDENT")) 376 return; 377 skip_blank(&p); 378 379 if (*p == '*' || skip_string(&p, "ON")) 380 on = true; 381 else if (skip_string(&p, "OFF")) 382 on = false; 383 else 384 return; 385 386 skip_blank(&p); 387 if (!skip_string(&p, "*/\n")) 388 return; 389 390 if (com.s != com.e || lab.s != lab.e || code.s != code.e) 391 dump_line(); 392 393 inhibit_formatting = !on; 394 if (on) { 395 blank_lines_to_output = 0; 396 blank_line_after = false; 397 blank_line_before = false; 398 suppress_blanklines = true; 399 } 400 } 401 402 /* 403 * Copyright (C) 1976 by the Board of Trustees of the University of Illinois 404 * 405 * All rights reserved 406 */ 407 void 408 inbuf_read_line(void) 409 { 410 char *p; 411 int ch; 412 FILE *f = input; 413 414 if (saved_inp_s != NULL) { /* there is a partly filled input buffer left */ 415 inp.s = saved_inp_s; /* do not read anything, just switch buffers */ 416 inp.e = saved_inp_e; 417 saved_inp_s = saved_inp_e = NULL; 418 debug_println("switched inp.s back to saved_inp_s"); 419 if (inp.s < inp.e) 420 return; /* only return if there is really something in 421 * this buffer */ 422 } 423 424 for (p = inp.buf;;) { 425 if (p >= inp.l) { 426 size_t size = (size_t)(inp.l - inp.buf) * 2 + 10; 427 size_t offset = (size_t)(p - inp.buf); 428 inp.buf = xrealloc(inp.buf, size); 429 p = inp.buf + offset; 430 inp.l = inp.buf + size - 2; 431 } 432 433 if ((ch = getc(f)) == EOF) { 434 if (!inhibit_formatting) { 435 *p++ = ' '; 436 *p++ = '\n'; 437 } 438 had_eof = true; 439 break; 440 } 441 442 if (ch != '\0') 443 *p++ = (char)ch; 444 if (ch == '\n') 445 break; 446 } 447 448 inp.s = inp.buf; 449 inp.e = p; 450 451 if (p - inp.s >= 3 && p[-3] == '*' && p[-2] == '/') { 452 if (strncmp(inp.s, "/**INDENT**", 11) == 0) 453 inbuf_read_line(); /* flush indent error message */ 454 else 455 parse_indent_comment(); 456 } 457 458 if (inhibit_formatting) 459 output_range(inp.s, inp.e); 460 } 461 462 int 463 indentation_after_range(int ind, const char *start, const char *end) 464 { 465 for (const char *p = start; *p != '\0' && p != end; ++p) { 466 if (*p == '\n' || *p == '\f') 467 ind = 0; 468 else if (*p == '\t') 469 ind = next_tab(ind); 470 else if (*p == '\b') 471 --ind; 472 else 473 ++ind; 474 } 475 return ind; 476 } 477 478 int 479 indentation_after(int ind, const char *s) 480 { 481 return indentation_after_range(ind, s, NULL); 482 } 483