pr_comment.c revision 1.171 1 /* $NetBSD: pr_comment.c,v 1.171 2023/06/23 20:59:04 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.171 2023/06/23 20:59:04 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 = inp_p, *p = start; *p != '\n'; p++) {
64 if (p[0] == '*' && p[1] == '/') {
65 while (p - inp_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 = inp_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 (inp_p - inp.s == 2 && !opt.format_col1_comments) {
96 may_wrap = false;
97 ind = 0;
98 } else {
99 if (inp_p[0] == '-' || inp_p[0] == '*' ||
100 token.s[token.len - 1] == '/' ||
101 (inp_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 && inp_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)(inp_p - 2 - inp.s);
137 ps.comment_shift = -ind_add(0, inp.s, len);
138 } else {
139 ps.comment_shift = 0;
140 if (!(inp_p[0] == '\t' && !ch_isblank(inp_p[1])))
141 while (ch_isblank(inp_p[0]))
142 inp_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(inp_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", inp_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 line_no++;
231
232 /* flush any blanks and/or tabs at start of next line */
233 inp_skip(); /* '\n' */
234 while (ch_isblank(inp_p[0]))
235 inp_p++;
236 if (inp_p[0] == '*' && inp_p[1] == '/')
237 return false;
238 if (inp_p[0] == '*') {
239 inp_p++;
240 while (ch_isblank(inp_p[0]))
241 inp_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 inp_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 (inp_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 (inp_p[0] == '*' && inp_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 diag(1, "Unterminated comment");
303 output_line();
304 }
305
306 static void
307 copy_comment_nowrap(void)
308 {
309 char kind = token.s[token.len - 1];
310
311 for (;;) {
312 if (inp_p[0] == '\n') {
313 if (kind == '/')
314 return;
315
316 if (had_eof) {
317 diag(1, "Unterminated comment");
318 output_line();
319 return;
320 }
321
322 output_line();
323 line_no++;
324 inp_skip();
325 continue;
326 }
327
328 if (kind == '*' && inp_p[0] == '*' && inp_p[1] == '/') {
329 com_add_char(*inp_p++);
330 com_add_char(*inp_p++);
331 return;
332 }
333
334 com_add_char(*inp_p++);
335 }
336 }
337
338 void
339 process_comment(void)
340 {
341 bool may_wrap, delim;
342 int line_length;
343
344 analyze_comment(&may_wrap, &delim, &line_length);
345 copy_comment_start(may_wrap, &delim, line_length);
346 if (may_wrap)
347 copy_comment_wrap(line_length, delim);
348 else
349 copy_comment_nowrap();
350 }
351