pr_comment.c revision 1.143 1 /* $NetBSD: pr_comment.c,v 1.143 2023/05/16 11:32:01 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.143 2023/05/16 11:32:01 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 '\n':
168 if (had_eof) {
169 diag(1, "Unterminated comment");
170 output_line();
171 return;
172 }
173
174 last_blank = -1;
175 if (ps.next_col_1) {
176 if (com.len == 0)
177 com_add_char(' '); /* force empty line of output */
178 if (com.len > 3) {
179 output_line();
180 com_add_delim();
181 }
182 output_line();
183 com_add_delim();
184
185 } else {
186 ps.next_col_1 = true;
187 if (!(com.len > 0 && ch_isblank(com.mem[com.len - 1])))
188 com_add_char(' ');
189 last_blank = (int)com.len - 1;
190 }
191 ++line_no;
192
193 bool skip_asterisk = true;
194 do { /* flush any blanks and/or tabs at start of
195 * next line */
196 inp_skip();
197 if (inp_peek() == '*' && skip_asterisk) {
198 skip_asterisk = false;
199 inp_skip();
200 if (inp_peek() == '/')
201 goto end_of_comment;
202 }
203 } while (ch_isblank(inp_peek()));
204
205 break; /* end of case for newline */
206
207 case '*':
208 inp_skip();
209 if (inp_peek() == '/') {
210 end_of_comment:
211 inp_skip();
212
213 if (delim) {
214 if (com.len > 3)
215 output_line();
216 else
217 com.len = 0;
218 com_add_char(' ');
219 } else {
220 size_t trimmed_len = com.len;
221 while (ch_isblank(com.mem[trimmed_len - 1]))
222 trimmed_len--;
223 int now_len = ind_add(ps.com_ind, com.st, trimmed_len);
224 if (now_len + 3 /* ' ' '*' '/' */ > line_length)
225 output_line();
226 }
227
228 if (!(com.len > 0 && ch_isblank(com.mem[com.len - 1])))
229 com_add_char(' ');
230 com_add_char('*');
231 com_add_char('/');
232 return;
233
234 } else /* handle isolated '*' */
235 com_add_char('*');
236 break;
237
238 default:
239 ;
240 int now_len = ind_add(ps.com_ind, com.st, com.len);
241 for (;;) {
242 char ch = inp_next();
243 if (ch_isblank(ch))
244 last_blank = (ssize_t)com.len;
245 com_add_char(ch);
246 now_len++;
247 if (memchr("*\n\r\b\t", inp_peek(), 6) != NULL)
248 break;
249 if (now_len >= line_length && last_blank != -1)
250 break;
251 }
252
253 ps.next_col_1 = false;
254
255 if (now_len <= line_length)
256 break;
257 if (ch_isspace(com.mem[com.len - 1]))
258 break;
259
260 if (last_blank == -1) { /* only a single word in this line */
261 output_line();
262 com_add_delim();
263 break;
264 }
265
266 const char *last_word_s = com.mem + last_blank + 1;
267 size_t last_word_len = com.len - (size_t)(last_blank + 1);
268 com.len = (size_t)last_blank;
269 output_line();
270 com_add_delim();
271
272 /*
273 * Assume that output_line and com_add_delim don't invalidate the
274 * "unused" part of the buffer beyond com.mem + com.len.
275 */
276 memmove(com.mem + com.len, last_word_s, last_word_len);
277 com.len += last_word_len;
278 last_blank = -1;
279 }
280 }
281 }
282
283 static void
284 copy_comment_nowrap(void)
285 {
286 for (;;) {
287 if (inp_peek() == '\n') {
288 if (token.mem[token.len - 1] == '/')
289 return;
290
291 if (had_eof) {
292 diag(1, "Unterminated comment");
293 output_line();
294 return;
295 }
296
297 if (com.len == 0)
298 com_add_char(' '); /* force output of an empty line */
299 output_line();
300 ++line_no;
301 inp_skip();
302 continue;
303 }
304
305 com_add_char(inp_next());
306 if (com.mem[com.len - 2] == '*' && com.mem[com.len - 1] == '/'
307 && token.mem[token.len - 1] == '*')
308 return;
309 }
310 }
311
312 /*
313 * Scan, reformat and output a single comment, which is either a block comment
314 * starting with '/' '*' or an end-of-line comment starting with '//'.
315 */
316 void
317 process_comment(void)
318 {
319 int line_length;
320 bool may_wrap, delim;
321
322 analyze_comment(&may_wrap, &delim, &line_length);
323 if (may_wrap)
324 copy_comment_wrap(line_length, delim);
325 else
326 copy_comment_nowrap();
327 }
328