pr_comment.c revision 1.154 1 /* $NetBSD: pr_comment.c,v 1.154 2023/06/06 07:14:20 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.154 2023/06/06 07:14:20 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_delim(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 com_ind, int max_line_length)
62 {
63 for (const char *start = inp_p, *p = start; *p != '\n'; p++) {
64 if (p[0] == '*' && p[1] == '/') {
65 int len = ind_add(com_ind + 3,
66 start, (size_t)(p - start));
67 len += p == start || ch_isblank(p[-1]) ? 2 : 3;
68 return len <= max_line_length;
69 }
70 }
71 return false;
72 }
73
74 static void
75 analyze_comment(bool *p_may_wrap, bool *p_delim,
76 int *p_ind, int *p_line_length)
77 {
78 bool may_wrap = true;
79 bool delim = opt.comment_delimiter_on_blankline;
80 int ind;
81 int line_length = opt.max_line_length;
82
83 if (ps.curr_col_1 && !opt.format_col1_comments) {
84 may_wrap = false;
85 delim = false;
86 ind = 0;
87
88 } else {
89 if (inp_p[0] == '-' || inp_p[0] == '*' ||
90 token.s[token.len - 1] == '/' ||
91 (inp_p[0] == '\n' && !opt.format_block_comments)) {
92 may_wrap = false;
93 delim = false;
94 }
95 if (code.len == 0 && inp_p[strspn(inp_p, "*")] == '\n')
96 out.line_kind = lk_block_comment;
97
98 if (com.len > 0)
99 output_line();
100 if (lab.len == 0 && code.len == 0) {
101 ind = (ps.ind_level - opt.unindent_displace)
102 * opt.indent_size;
103 if (ind <= 0)
104 ind = opt.format_col1_comments ? 0 : 1;
105 line_length = opt.block_comment_max_line_length;
106 } else {
107 delim = false;
108
109 int target_ind = code.len > 0
110 ? ind_add(compute_code_indent(), code.s, code.len)
111 : ind_add(compute_label_indent(), lab.s, lab.len);
112
113 ind = ps.decl_on_line || ps.ind_level == 0
114 ? opt.decl_comment_column - 1
115 : opt.comment_column - 1;
116 if (ind <= target_ind)
117 ind = next_tab(target_ind);
118 if (ind + 25 > line_length)
119 line_length = ind + 25;
120 }
121 }
122
123 ps.com_ind = ind;
124
125 if (!may_wrap) {
126 /* Find out how much indentation there was originally, because
127 * that much will have to be ignored by output_line. */
128 size_t len = (size_t)(inp_p - 2 - inp.s);
129 ps.n_comment_delta = -ind_add(0, inp.s, len);
130 } else {
131 ps.n_comment_delta = 0;
132 if (!(inp_p[0] == '\t' && !ch_isblank(inp_p[1])))
133 while (ch_isblank(inp_p[0]))
134 inp_p++;
135 }
136
137 *p_may_wrap = may_wrap;
138 *p_delim = delim;
139 *p_ind = ind;
140 *p_line_length = line_length;
141 }
142
143 static void
144 copy_comment_start(bool may_wrap, bool *delim, int ind, int line_length)
145 {
146 ps.comment_delta = 0;
147 com_add_char('/');
148 com_add_char(token.s[token.len - 1]); /* either '*' or '/' */
149
150 if (may_wrap && !ch_isblank(inp_p[0]))
151 com_add_char(' ');
152
153 if (*delim && fits_in_one_line(ind, line_length))
154 *delim = false;
155
156 if (*delim) {
157 output_line();
158 com_add_delim();
159 }
160 }
161
162 static void
163 copy_comment_wrap_text(int line_length, ssize_t *last_blank)
164 {
165 int now_len = ind_add(ps.com_ind, com.s, com.len);
166 for (;;) {
167 char ch = inp_next();
168 if (ch_isblank(ch))
169 *last_blank = (ssize_t)com.len;
170 com_add_char(ch);
171 now_len++;
172 if (memchr("*\n\r\b\t", inp_p[0], 6) != NULL)
173 break;
174 if (now_len >= line_length && *last_blank != -1)
175 break;
176 }
177
178 ps.next_col_1 = false;
179
180 if (now_len <= line_length)
181 return;
182 if (ch_isspace(com.s[com.len - 1]))
183 return;
184
185 if (*last_blank == -1) {
186 /* only a single word in this line */
187 output_line();
188 com_add_delim();
189 return;
190 }
191
192 const char *last_word_s = com.s + *last_blank + 1;
193 size_t last_word_len = com.len - (size_t)(*last_blank + 1);
194 com.len = (size_t)*last_blank;
195 output_line();
196 com_add_delim();
197
198 /* Assume that output_line and com_add_delim don't
199 * invalidate the "unused" part of the buffer beyond
200 * com.s + com.len. */
201 memmove(com.s + com.len, last_word_s, last_word_len);
202 com.len += last_word_len;
203 *last_blank = -1;
204 }
205
206 static bool
207 copy_comment_wrap_newline(ssize_t *last_blank)
208 {
209 *last_blank = -1;
210 if (ps.next_col_1) {
211 if (com.len == 0)
212 com_add_char(' '); /* force empty output line */
213 if (com.len > 3) {
214 output_line();
215 com_add_delim();
216 }
217 output_line();
218 com_add_delim();
219 } else {
220 ps.next_col_1 = true;
221 if (!(com.len > 0 && ch_isblank(com.s[com.len - 1])))
222 com_add_char(' ');
223 *last_blank = (int)com.len - 1;
224 }
225 ++line_no;
226
227 /* flush any blanks and/or tabs at start of next line */
228 inp_skip(); /* '\n' */
229 while (ch_isblank(inp_p[0]))
230 inp_p++;
231 if (inp_p[0] == '*' && inp_p[1] == '/')
232 return false;
233 if (inp_p[0] == '*') {
234 inp_p++;
235 while (ch_isblank(inp_p[0]))
236 inp_p++;
237 }
238
239 return true;
240 }
241
242 static void
243 copy_comment_wrap_finish(int line_length, bool delim)
244 {
245 if (delim) {
246 if (com.len > 3)
247 output_line();
248 else
249 com.len = 0;
250 com_add_char(' ');
251 } else {
252 size_t len = com.len;
253 while (ch_isblank(com.s[len - 1]))
254 len--;
255 int end_ind = ind_add(ps.com_ind, com.s, len);
256 if (end_ind + 3 > line_length)
257 output_line();
258 }
259
260 inp_p += 2;
261 if (com.len > 0 && ch_isblank(com.s[com.len - 1]))
262 buf_add_chars(&com, "*/", 2);
263 else
264 buf_add_chars(&com, " */", 3);
265 }
266
267 /*
268 * Copy characters from 'inp' to 'com'. Try to keep comments from going over
269 * the maximum line length. To do that, remember where the last blank, tab, or
270 * newline was. When a line is filled, print up to the last blank and continue
271 * copying.
272 */
273 static void
274 copy_comment_wrap(int line_length, bool delim)
275 {
276 ssize_t last_blank = -1; /* index of the last blank in 'com' */
277
278 for (;;) {
279 if (inp_p[0] == '\n') {
280 if (had_eof)
281 goto unterminated_comment;
282 if (!copy_comment_wrap_newline(&last_blank))
283 goto end_of_comment;
284 } else if (inp_p[0] == '*' && inp_p[1] == '/')
285 goto end_of_comment;
286 else
287 copy_comment_wrap_text(line_length, &last_blank);
288 }
289
290 end_of_comment:
291 copy_comment_wrap_finish(line_length, delim);
292 return;
293
294 unterminated_comment:
295 diag(1, "Unterminated comment");
296 output_line();
297 }
298
299 static void
300 copy_comment_nowrap(void)
301 {
302 char kind = token.s[token.len - 1];
303
304 for (;;) {
305 if (inp_p[0] == '\n') {
306 if (kind == '/')
307 return;
308
309 if (had_eof) {
310 diag(1, "Unterminated comment");
311 output_line();
312 return;
313 }
314
315 if (com.len == 0)
316 com_add_char(' '); /* force output of an
317 * empty line */
318 output_line();
319 ++line_no;
320 inp_skip();
321 continue;
322 }
323
324 com_add_char(*inp_p++);
325 if (com.len >= 2
326 && com.s[com.len - 2] == '*'
327 && com.s[com.len - 1] == '/'
328 && kind == '*')
329 return;
330 }
331 }
332
333 /*
334 * Scan, reformat and output a single comment, which is either a block comment
335 * starting with '/' '*' or an end-of-line comment starting with '//'.
336 */
337 void
338 process_comment(void)
339 {
340 bool may_wrap, delim;
341 int ind, line_length;
342
343 analyze_comment(&may_wrap, &delim, &ind, &line_length);
344 copy_comment_start(may_wrap, &delim, ind, line_length);
345 if (may_wrap)
346 copy_comment_wrap(line_length, delim);
347 else
348 copy_comment_nowrap();
349 }
350