1 /* $NetBSD: pr_comment.c,v 1.174 2025/01/04 10:28:08 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: pr_comment.c,v 1.174 2025/01/04 10:28:08 rillig Exp $"); 42 43 #include <string.h> 44 45 #include "indent.h" 46 47 static void 48 com_add_char(char ch) 49 { 50 buf_add_char(&com, ch); 51 } 52 53 static void 54 com_add_star(void) 55 { 56 if (opt.star_comment_cont) 57 buf_add_str(&com, " * "); 58 } 59 60 static bool 61 fits_in_one_line(int max_line_length) 62 { 63 for (const char *start = in.p, *p = start; *p != '\n'; p++) { 64 if (p[0] == '*' && p[1] == '/') { 65 while (p - in.p >= 2 66 && ch_isblank(p[-1]) 67 && ch_isblank(p[-2])) 68 p--; 69 int ind = ind_add(ps.comment_ind + 3, 70 start, (size_t)(p - start)); 71 ind += p == start || ch_isblank(p[-1]) ? 2 : 3; 72 return ind <= max_line_length; 73 } 74 } 75 return false; 76 } 77 78 static bool 79 is_block_comment(void) 80 { 81 const char *p = in.p; 82 while (*p == '*') 83 p++; 84 return *p == '\n'; 85 } 86 87 static void 88 analyze_comment(bool *p_may_wrap, bool *p_delim, int *p_line_length) 89 { 90 bool may_wrap = true; 91 bool delim = false; // only relevant if may_wrap 92 int ind; 93 int line_length = opt.max_line_length; 94 95 if (in.p - in.line.s == 2 && !opt.format_col1_comments) { 96 may_wrap = false; 97 ind = 0; 98 } else { 99 if (in.p[0] == '-' || in.p[0] == '*' || 100 token.s[token.len - 1] == '/' || 101 (in.p[0] == '\n' && !opt.format_block_comments)) 102 may_wrap = false; 103 104 if (com.len > 0) 105 output_line(); 106 if (lab.len == 0 && code.len == 0) { 107 if (is_block_comment()) 108 out.line_kind = lk_block_comment; 109 ind = (ps.ind_level - opt.unindent_displace) 110 * opt.indent_size; 111 if (ind <= 0) 112 ind = opt.format_col1_comments ? 0 : 1; 113 line_length = opt.block_comment_max_line_length; 114 if (may_wrap && in.p[0] == '\n') 115 delim = true; 116 if (may_wrap && opt.comment_delimiter_on_blank_line) 117 delim = true; 118 } else { 119 int min_ind = code.len > 0 120 ? ind_add(compute_code_indent(), code.s, code.len) 121 : ind_add(compute_label_indent(), lab.s, lab.len); 122 123 ind = ps.line_has_decl || ps.ind_level == 0 124 ? opt.decl_comment_column - 1 125 : opt.comment_column - 1; 126 if (ind <= min_ind) 127 ind = next_tab(min_ind); 128 if (ind + 25 > line_length) 129 line_length = ind + 25; 130 } 131 } 132 133 if (!may_wrap) { 134 /* Find out how much indentation there was originally, because 135 * that much will have to be ignored by output_line. */ 136 size_t len = (size_t)(in.p - 2 - in.line.s); 137 ps.comment_shift = -ind_add(0, in.line.s, len); 138 } else { 139 ps.comment_shift = 0; 140 if (!(in.p[0] == '\t' && !ch_isblank(in.p[1]))) 141 while (ch_isblank(in.p[0])) 142 in.p++; 143 } 144 145 ps.comment_ind = ind; 146 *p_may_wrap = may_wrap; 147 *p_delim = delim; 148 *p_line_length = line_length; 149 } 150 151 static void 152 copy_comment_start(bool may_wrap, bool *delim, int line_length) 153 { 154 ps.comment_cont = false; 155 buf_add_chars(&com, token.s, token.len); // "/*" or "//" 156 157 if (may_wrap) { 158 if (!ch_isblank(in.p[0])) 159 com_add_char(' '); 160 161 if (*delim && fits_in_one_line(line_length)) 162 *delim = false; 163 if (*delim) { 164 output_line(); 165 com_add_star(); 166 } 167 } 168 } 169 170 static void 171 copy_comment_wrap_text(int line_length, ssize_t *last_blank) 172 { 173 int ind = ind_add(ps.comment_ind, com.s, com.len); 174 for (;;) { 175 char ch = inp_next(); 176 if (ch_isblank(ch)) 177 *last_blank = (ssize_t)com.len; 178 com_add_char(ch); 179 ind++; 180 if (memchr("*\n\r\t", in.p[0], 5) != NULL) 181 break; 182 if (ind >= line_length && *last_blank != -1) 183 break; 184 } 185 186 if (ind <= line_length) 187 return; 188 if (ch_isspace(com.s[com.len - 1])) 189 return; 190 191 if (*last_blank == -1) { /* only a single word in this line */ 192 output_line(); 193 com_add_star(); 194 return; 195 } 196 197 // Move the overlong word to the next line. 198 const char *last_word = com.s + *last_blank + 1; 199 size_t last_word_len = com.len - (size_t)(*last_blank + 1); 200 com.len = (size_t)*last_blank; 201 buf_terminate(&com); 202 output_line(); 203 com_add_star(); 204 205 /* Assume that output_line and com_add_delim left the "unused" part of 206 * the now truncated buffer beyond com.s + com.len as-is. */ 207 memmove(com.s + com.len, last_word, last_word_len); 208 com.len += last_word_len; 209 buf_terminate(&com); 210 *last_blank = -1; 211 } 212 213 /* In a comment that is re-wrapped, handle a single newline character. */ 214 static bool 215 copy_comment_wrap_newline(ssize_t *last_blank, bool seen_newline) 216 { 217 *last_blank = -1; 218 if (seen_newline) { 219 if (com.len > 3) { 220 output_line(); 221 com_add_star(); 222 } 223 output_line(); 224 com_add_star(); 225 } else { 226 if (!(com.len > 0 && ch_isblank(com.s[com.len - 1]))) 227 com_add_char(' '); 228 *last_blank = (int)com.len - 1; 229 } 230 in.token_end_line++; 231 232 /* flush any blanks and/or tabs at start of next line */ 233 inp_skip(); /* '\n' */ 234 while (ch_isblank(in.p[0])) 235 in.p++; 236 if (in.p[0] == '*' && in.p[1] == '/') 237 return false; 238 if (in.p[0] == '*') { 239 in.p++; 240 while (ch_isblank(in.p[0])) 241 in.p++; 242 } 243 244 return true; 245 } 246 247 static void 248 copy_comment_wrap_finish(int line_length, bool delim) 249 { 250 if (delim) { 251 if (com.len > 3) 252 output_line(); 253 buf_clear(&com); 254 } else { 255 size_t len = com.len; 256 // XXX: This loop differs from the one below. 257 while (ch_isblank(com.s[len - 1])) 258 len--; 259 if (ind_add(ps.comment_ind, com.s, len) + 3 > line_length) 260 output_line(); 261 } 262 263 while (com.len >= 2 264 && ch_isblank(com.s[com.len - 1]) 265 && ch_isblank(com.s[com.len - 2])) 266 com.len--; 267 buf_terminate(&com); 268 269 in.p += 2; 270 if (com.len > 0 && ch_isblank(com.s[com.len - 1])) 271 buf_add_str(&com, "*/"); 272 else 273 buf_add_str(&com, " */"); 274 } 275 276 static void 277 copy_comment_wrap(int line_length, bool delim) 278 { 279 ssize_t last_blank = -1; /* index of the last blank in 'com' */ 280 bool seen_newline = false; 281 282 for (;;) { 283 if (in.p[0] == '\n') { 284 if (had_eof) 285 goto unterminated_comment; 286 if (!copy_comment_wrap_newline(&last_blank, 287 seen_newline)) 288 break; 289 seen_newline = true; 290 } else if (in.p[0] == '*' && in.p[1] == '/') 291 break; 292 else { 293 copy_comment_wrap_text(line_length, &last_blank); 294 seen_newline = false; 295 } 296 } 297 298 copy_comment_wrap_finish(line_length, delim); 299 return; 300 301 unterminated_comment: 302 in.token_start_line = in.token_end_line; 303 diag(1, "Unterminated comment"); 304 output_line(); 305 } 306 307 static void 308 copy_comment_nowrap(void) 309 { 310 char kind = token.s[token.len - 1]; 311 312 for (;;) { 313 if (in.p[0] == '\n') { 314 if (kind == '/') 315 return; 316 317 if (had_eof) { 318 in.token_start_line = in.token_end_line; 319 diag(1, "Unterminated comment"); 320 output_line(); 321 return; 322 } 323 324 output_line(); 325 in.token_end_line++; 326 inp_skip(); 327 continue; 328 } 329 330 if (kind == '*' && in.p[0] == '*' && in.p[1] == '/') { 331 com_add_char(*in.p++); 332 com_add_char(*in.p++); 333 return; 334 } 335 336 com_add_char(*in.p++); 337 } 338 } 339 340 void 341 process_comment(void) 342 { 343 bool may_wrap, delim; 344 int line_length; 345 346 analyze_comment(&may_wrap, &delim, &line_length); 347 copy_comment_start(may_wrap, &delim, line_length); 348 if (may_wrap) 349 copy_comment_wrap(line_length, delim); 350 else 351 copy_comment_nowrap(); 352 } 353