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