pr_comment.c revision 1.173 1 /* $NetBSD: pr_comment.c,v 1.173 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: pr_comment.c,v 1.173 2023/12/03 21:44:42 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_chars(&com, " * ", 3);
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_chars(&com, "*/", 2);
272 else
273 buf_add_chars(&com, " */", 3);
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