pr_comment.c revision 1.147 1 /* $NetBSD: pr_comment.c,v 1.147 2023/05/20 11:19:17 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.147 2023/05/20 11:19:17 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 return;
58 buf_add_chars(&com, " * ", 3);
59 }
60
61 static bool
62 fits_in_one_line(int com_ind, int max_line_length)
63 {
64 for (const char *start = inp.st, *p = start; *p != '\n'; p++) {
65 if (p[0] == '*' && p[1] == '/') {
66 int len = ind_add(com_ind + 3,
67 start, (size_t)(p - start));
68 len += p == start || ch_isblank(p[-1]) ? 2 : 3;
69 return len <= max_line_length;
70 }
71 }
72 return false;
73 }
74
75 static void
76 analyze_comment(bool *p_may_wrap, bool *p_delim, 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.st[0] == '-' || inp.st[0] == '*' ||
90 token.mem[token.len - 1] == '/' ||
91 (inp.st[0] == '\n' && !opt.format_block_comments)) {
92 may_wrap = false;
93 delim = false;
94 }
95 if (code.len == 0 && inp.st[strspn(inp.st, "*")] == '\n')
96 ps.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.st, code.len)
111 : ind_add(compute_label_indent(), lab.st, 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_complete_line.
128 */
129 size_t len = (size_t)(inp.st - 2 - inp.mem);
130 ps.n_comment_delta = -ind_add(0, inp.mem, len);
131 } else {
132 ps.n_comment_delta = 0;
133 if (!(inp.st[0] == '\t' && !ch_isblank(inp.st[1])))
134 while (ch_isblank(inp.st[0]))
135 inp.st++;
136 }
137
138 ps.comment_delta = 0;
139 com_add_char('/');
140 com_add_char(token.mem[token.len - 1]); /* either '*' or '/' */
141
142 if (may_wrap && !ch_isblank(inp.st[0]))
143 com_add_char(' ');
144
145 if (delim && fits_in_one_line(ind, line_length))
146 delim = false;
147
148 if (delim) {
149 output_line();
150 com_add_delim();
151 }
152
153 *p_line_length = line_length;
154 *p_delim = delim;
155 *p_may_wrap = may_wrap;
156 }
157
158 /*
159 * Copy characters from 'inp' to 'com'. Try to keep comments from going over
160 * the maximum line length. To do that, remember where the last blank, tab, or
161 * newline was. When a line is filled, print up to the last blank and continue
162 * copying.
163 */
164 static void
165 copy_comment_wrap(int line_length, bool delim)
166 {
167 ssize_t last_blank = -1; /* index of the last blank in com.mem
168 */
169
170 for (;;) {
171 switch (inp.st[0]) {
172 case '\n':
173 if (had_eof) {
174 diag(1, "Unterminated comment");
175 output_line();
176 return;
177 }
178
179 last_blank = -1;
180 if (ps.next_col_1) {
181 if (com.len == 0) {
182 /* force empty line of output */
183 com_add_char(' ');
184 }
185 if (com.len > 3) {
186 output_line();
187 com_add_delim();
188 }
189 output_line();
190 com_add_delim();
191
192 } else {
193 ps.next_col_1 = true;
194 if (!(com.len > 0
195 && ch_isblank(com.mem[com.len - 1])))
196 com_add_char(' ');
197 last_blank = (int)com.len - 1;
198 }
199 ++line_no;
200
201 bool skip_asterisk = true;
202 do { /* flush any blanks and/or tabs at start of
203 * next line */
204 inp_skip();
205 if (inp.st[0] == '*' && skip_asterisk) {
206 skip_asterisk = false;
207 inp.st++;
208 if (inp.st[0] == '/')
209 goto end_of_comment;
210 }
211 } while (ch_isblank(inp.st[0]));
212
213 break; /* end of case for newline */
214
215 case '*':
216 inp.st++;
217 if (inp.st[0] == '/') {
218 end_of_comment:
219 inp.st++;
220
221 if (delim) {
222 if (com.len > 3)
223 output_line();
224 else
225 com.len = 0;
226 com_add_char(' ');
227 } else {
228 size_t len = com.len;
229 while (ch_isblank(com.mem[len - 1]))
230 len--;
231 int now_len = ind_add(
232 ps.com_ind, com.st, len);
233 if (now_len + 3 > line_length)
234 output_line();
235 }
236
237 if (!(com.len > 0
238 && ch_isblank(com.mem[com.len - 1])))
239 com_add_char(' ');
240 com_add_char('*');
241 com_add_char('/');
242 return;
243
244 } else /* handle isolated '*' */
245 com_add_char('*');
246 break;
247
248 default:
249 ;
250 int now_len = ind_add(ps.com_ind, com.st, com.len);
251 for (;;) {
252 char ch = inp_next();
253 if (ch_isblank(ch))
254 last_blank = (ssize_t)com.len;
255 com_add_char(ch);
256 now_len++;
257 if (memchr("*\n\r\b\t", inp.st[0], 6) != NULL)
258 break;
259 if (now_len >= line_length && last_blank != -1)
260 break;
261 }
262
263 ps.next_col_1 = false;
264
265 if (now_len <= line_length)
266 break;
267 if (ch_isspace(com.mem[com.len - 1]))
268 break;
269
270 if (last_blank == -1) {
271 /* only a single word in this line */
272 output_line();
273 com_add_delim();
274 break;
275 }
276
277 const char *last_word_s = com.mem + last_blank + 1;
278 size_t last_word_len = com.len
279 - (size_t)(last_blank + 1);
280 com.len = (size_t)last_blank;
281 output_line();
282 com_add_delim();
283
284 /* Assume that output_line and com_add_delim don't
285 * invalidate the "unused" part of the buffer beyond
286 * com.mem + com.len. */
287 memmove(com.mem + com.len, last_word_s, last_word_len);
288 com.len += last_word_len;
289 last_blank = -1;
290 }
291 }
292 }
293
294 static void
295 copy_comment_nowrap(void)
296 {
297 for (;;) {
298 if (inp.st[0] == '\n') {
299 if (token.mem[token.len - 1] == '/')
300 return;
301
302 if (had_eof) {
303 diag(1, "Unterminated comment");
304 output_line();
305 return;
306 }
307
308 if (com.len == 0)
309 com_add_char(' '); /* force output of an
310 * empty line */
311 output_line();
312 ++line_no;
313 inp_skip();
314 continue;
315 }
316
317 com_add_char(*inp.st++);
318 if (com.mem[com.len - 2] == '*' && com.mem[com.len - 1] == '/'
319 && token.mem[token.len - 1] == '*')
320 return;
321 }
322 }
323
324 /*
325 * Scan, reformat and output a single comment, which is either a block comment
326 * starting with '/' '*' or an end-of-line comment starting with '//'.
327 */
328 void
329 process_comment(void)
330 {
331 int line_length;
332 bool may_wrap, delim;
333
334 analyze_comment(&may_wrap, &delim, &line_length);
335 if (may_wrap)
336 copy_comment_wrap(line_length, delim);
337 else
338 copy_comment_nowrap();
339 }
340