pr_comment.c revision 1.140 1 /* $NetBSD: pr_comment.c,v 1.140 2023/05/15 07:28:45 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.140 2023/05/15 07:28:45 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 }
229
230 if (!(com.len > 0 && ch_isblank(com.mem[com.len - 1])))
231 com_add_char(' ');
232 com_add_char('*');
233 com_add_char('/');
234 return;
235
236 } else /* handle isolated '*' */
237 com_add_char('*');
238 break;
239
240 default:
241 ;
242 int now_len = ind_add(ps.com_ind, com.st, com.len);
243 for (;;) {
244 char ch = inp_next();
245 if (ch_isblank(ch))
246 last_blank = (ssize_t)com.len;
247 com_add_char(ch);
248 now_len++;
249 if (memchr("*\n\r\b\t", inp_peek(), 6) != NULL)
250 break;
251 if (now_len >= line_length && last_blank != -1)
252 break;
253 }
254
255 ps.next_col_1 = false;
256
257 if (now_len <= line_length)
258 break;
259 if (ch_isspace(com.mem[com.len - 1]))
260 break;
261
262 if (last_blank == -1) { /* only a single word in this line */
263 output_line();
264 com_add_delim();
265 break;
266 }
267
268 const char *last_word_s = com.mem + last_blank + 1;
269 size_t last_word_len = com.len - (size_t)(last_blank + 1);
270 com.len = (size_t)last_blank;
271 output_line();
272 com_add_delim();
273
274 /*
275 * Assume that output_line and com_add_delim don't invalidate
276 * the "unused" part of the buffer beyond com.mem + com.len.
277 */
278 memmove(com.mem + com.len, last_word_s, last_word_len);
279 com.len += last_word_len;
280 last_blank = -1;
281 }
282 }
283 }
284
285 static void
286 copy_comment_nowrap(void)
287 {
288 for (;;) {
289 if (inp_peek() == '\n') {
290 if (token.mem[token.len - 1] == '/')
291 return;
292
293 if (had_eof) {
294 diag(1, "Unterminated comment");
295 output_line();
296 return;
297 }
298
299 if (com.len == 0)
300 com_add_char(' '); /* force output of an empty line */
301 output_line();
302 ++line_no;
303 inp_skip();
304 continue;
305 }
306
307 com_add_char(inp_next());
308 if (com.mem[com.len - 2] == '*' && com.mem[com.len - 1] == '/'
309 && token.mem[token.len - 1] == '*')
310 return;
311 }
312 }
313
314 /*
315 * Scan, reformat and output a single comment, which is either a block comment
316 * starting with '/' '*' or an end-of-line comment starting with '//'.
317 */
318 void
319 process_comment(void)
320 {
321 int line_length;
322 bool may_wrap, delim;
323
324 analyze_comment(&may_wrap, &delim, &line_length);
325 if (may_wrap)
326 copy_comment_wrap(line_length, delim);
327 else
328 copy_comment_nowrap();
329 }
330