pr_comment.c revision 1.141 1 1.141 rillig /* $NetBSD: pr_comment.c,v 1.141 2023/05/15 09:22:53 rillig Exp $ */
2 1.4 tls
3 1.11 kamil /*-
4 1.11 kamil * SPDX-License-Identifier: BSD-4-Clause
5 1.11 kamil *
6 1.11 kamil * Copyright (c) 1985 Sun Microsystems, Inc.
7 1.5 mrg * Copyright (c) 1980, 1993
8 1.5 mrg * The Regents of the University of California. All rights reserved.
9 1.1 cgd * All rights reserved.
10 1.1 cgd *
11 1.1 cgd * Redistribution and use in source and binary forms, with or without
12 1.1 cgd * modification, are permitted provided that the following conditions
13 1.1 cgd * are met:
14 1.1 cgd * 1. Redistributions of source code must retain the above copyright
15 1.1 cgd * notice, this list of conditions and the following disclaimer.
16 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
17 1.1 cgd * notice, this list of conditions and the following disclaimer in the
18 1.1 cgd * documentation and/or other materials provided with the distribution.
19 1.1 cgd * 3. All advertising materials mentioning features or use of this software
20 1.1 cgd * must display the following acknowledgement:
21 1.1 cgd * This product includes software developed by the University of
22 1.1 cgd * California, Berkeley and its contributors.
23 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
24 1.1 cgd * may be used to endorse or promote products derived from this software
25 1.1 cgd * without specific prior written permission.
26 1.1 cgd *
27 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 1.1 cgd * SUCH DAMAGE.
38 1.1 cgd */
39 1.1 cgd
40 1.6 lukem #include <sys/cdefs.h>
41 1.141 rillig __RCSID("$NetBSD: pr_comment.c,v 1.141 2023/05/15 09:22:53 rillig Exp $");
42 1.1 cgd
43 1.11 kamil #include <string.h>
44 1.12 rillig
45 1.11 kamil #include "indent.h"
46 1.12 rillig
47 1.14 rillig static void
48 1.71 rillig com_add_char(char ch)
49 1.14 rillig {
50 1.140 rillig buf_add_char(&com, ch);
51 1.71 rillig }
52 1.71 rillig
53 1.71 rillig static void
54 1.71 rillig com_add_delim(void)
55 1.71 rillig {
56 1.71 rillig if (!opt.star_comment_cont)
57 1.71 rillig return;
58 1.140 rillig buf_add_chars(&com, " * ", 3);
59 1.71 rillig }
60 1.71 rillig
61 1.77 rillig static bool
62 1.124 rillig fits_in_one_line(int com_ind, int max_line_length)
63 1.77 rillig {
64 1.134 rillig for (const char *start = inp_p(), *p = start; *p != '\n'; p++) {
65 1.134 rillig if (p[0] == '*' && p[1] == '/') {
66 1.140 rillig int len = ind_add(com_ind + 3, start, (size_t)(p - start));
67 1.140 rillig len += p == start || ch_isblank(p[-1]) ? 2 : 3;
68 1.134 rillig return len <= max_line_length;
69 1.134 rillig }
70 1.77 rillig }
71 1.77 rillig return false;
72 1.77 rillig }
73 1.77 rillig
74 1.95 rillig static void
75 1.138 rillig analyze_comment(bool *p_may_wrap, bool *p_delim, int *p_line_length)
76 1.1 cgd {
77 1.75 rillig bool may_wrap = true;
78 1.138 rillig bool delim = opt.comment_delimiter_on_blankline;
79 1.138 rillig int ind;
80 1.138 rillig int line_length = opt.max_line_length;
81 1.11 kamil
82 1.93 rillig if (ps.curr_col_1 && !opt.format_col1_comments) {
83 1.75 rillig may_wrap = false;
84 1.138 rillig delim = false;
85 1.138 rillig ind = 0;
86 1.56 rillig
87 1.19 rillig } else {
88 1.119 rillig if (inp_peek() == '-' || inp_peek() == '*' ||
89 1.140 rillig token.mem[token.len - 1] == '/' ||
90 1.119 rillig (inp_peek() == '\n' && !opt.format_block_comments)) {
91 1.75 rillig may_wrap = false;
92 1.138 rillig delim = false;
93 1.11 kamil }
94 1.56 rillig
95 1.140 rillig if (com.len > 0)
96 1.137 rillig output_line();
97 1.140 rillig if (lab.len == 0 && code.len == 0) {
98 1.138 rillig ind = (ps.ind_level - opt.unindent_displace) * opt.indent_size;
99 1.138 rillig if (ind <= 0)
100 1.138 rillig ind = opt.format_col1_comments ? 0 : 1;
101 1.138 rillig line_length = opt.block_comment_max_line_length;
102 1.19 rillig } else {
103 1.138 rillig delim = false;
104 1.34 rillig
105 1.140 rillig int target_ind = code.len > 0
106 1.140 rillig ? ind_add(compute_code_indent(), code.st, code.len)
107 1.140 rillig : ind_add(compute_label_indent(), lab.st, lab.len);
108 1.34 rillig
109 1.138 rillig ind = ps.decl_on_line || ps.ind_level == 0
110 1.60 rillig ? opt.decl_comment_column - 1 : opt.comment_column - 1;
111 1.138 rillig if (ind <= target_ind)
112 1.138 rillig ind = next_tab(target_ind);
113 1.138 rillig if (ind + 25 > line_length)
114 1.138 rillig line_length = ind + 25;
115 1.11 kamil }
116 1.11 kamil }
117 1.56 rillig
118 1.138 rillig ps.com_ind = ind;
119 1.78 rillig
120 1.75 rillig if (!may_wrap) {
121 1.6 lukem /*
122 1.11 kamil * Find out how much indentation there was originally, because that
123 1.126 rillig * much will have to be ignored by output_complete_line.
124 1.25 rillig */
125 1.140 rillig size_t len = (size_t)(inp_p() - 2 - inp_line_start());
126 1.140 rillig ps.n_comment_delta = -ind_add(0, inp_line_start(), len);
127 1.19 rillig } else {
128 1.11 kamil ps.n_comment_delta = 0;
129 1.136 rillig if (!(inp_peek() == '\t' && !ch_isblank(inp_lookahead(1))))
130 1.136 rillig while (ch_isblank(inp_peek()))
131 1.136 rillig inp_skip();
132 1.11 kamil }
133 1.56 rillig
134 1.11 kamil ps.comment_delta = 0;
135 1.71 rillig com_add_char('/');
136 1.140 rillig com_add_char(token.mem[token.len - 1]); /* either '*' or '/' */
137 1.116 rillig
138 1.136 rillig if (may_wrap && !ch_isblank(inp_peek()))
139 1.71 rillig com_add_char(' ');
140 1.11 kamil
141 1.138 rillig if (delim && fits_in_one_line(ind, line_length))
142 1.138 rillig delim = false;
143 1.1 cgd
144 1.138 rillig if (delim) {
145 1.126 rillig output_line();
146 1.75 rillig com_add_delim();
147 1.11 kamil }
148 1.11 kamil
149 1.138 rillig *p_line_length = line_length;
150 1.138 rillig *p_delim = delim;
151 1.95 rillig *p_may_wrap = may_wrap;
152 1.95 rillig }
153 1.95 rillig
154 1.110 rillig /*
155 1.110 rillig * Copy characters from 'inp' to 'com'. Try to keep comments from going over
156 1.110 rillig * the maximum line length. To do that, remember where the last blank, tab, or
157 1.110 rillig * newline was. When a line is filled, print up to the last blank and continue
158 1.110 rillig * copying.
159 1.110 rillig */
160 1.95 rillig static void
161 1.138 rillig copy_comment_wrap(int line_length, bool delim)
162 1.95 rillig {
163 1.131 rillig ssize_t last_blank = -1; /* index of the last blank in com.mem */
164 1.99 rillig
165 1.99 rillig for (;;) {
166 1.119 rillig switch (inp_peek()) {
167 1.99 rillig case '\f':
168 1.126 rillig output_line_ff();
169 1.101 rillig last_blank = -1;
170 1.101 rillig com_add_delim();
171 1.120 rillig inp_skip();
172 1.119 rillig while (ch_isblank(inp_peek()))
173 1.120 rillig inp_skip();
174 1.99 rillig break;
175 1.99 rillig
176 1.99 rillig case '\n':
177 1.99 rillig if (had_eof) {
178 1.99 rillig diag(1, "Unterminated comment");
179 1.126 rillig output_line();
180 1.99 rillig return;
181 1.99 rillig }
182 1.99 rillig
183 1.99 rillig last_blank = -1;
184 1.101 rillig if (ps.next_col_1) {
185 1.140 rillig if (com.len == 0)
186 1.116 rillig com_add_char(' '); /* force empty line of output */
187 1.140 rillig if (com.len > 3) {
188 1.126 rillig output_line();
189 1.99 rillig com_add_delim();
190 1.99 rillig }
191 1.126 rillig output_line();
192 1.101 rillig com_add_delim();
193 1.99 rillig
194 1.99 rillig } else {
195 1.99 rillig ps.next_col_1 = true;
196 1.140 rillig if (!(com.len > 0 && ch_isblank(com.mem[com.len - 1])))
197 1.99 rillig com_add_char(' ');
198 1.140 rillig last_blank = (int)com.len - 1;
199 1.99 rillig }
200 1.99 rillig ++line_no;
201 1.101 rillig
202 1.101 rillig bool skip_asterisk = true;
203 1.101 rillig do { /* flush any blanks and/or tabs at start of
204 1.99 rillig * next line */
205 1.101 rillig inp_skip();
206 1.119 rillig if (inp_peek() == '*' && skip_asterisk) {
207 1.101 rillig skip_asterisk = false;
208 1.99 rillig inp_skip();
209 1.119 rillig if (inp_peek() == '/')
210 1.101 rillig goto end_of_comment;
211 1.101 rillig }
212 1.119 rillig } while (ch_isblank(inp_peek()));
213 1.101 rillig
214 1.99 rillig break; /* end of case for newline */
215 1.99 rillig
216 1.99 rillig case '*':
217 1.99 rillig inp_skip();
218 1.119 rillig if (inp_peek() == '/') {
219 1.99 rillig end_of_comment:
220 1.99 rillig inp_skip();
221 1.99 rillig
222 1.138 rillig if (delim) {
223 1.140 rillig if (com.len > 3)
224 1.126 rillig output_line();
225 1.99 rillig else
226 1.140 rillig com.len = 0;
227 1.99 rillig com_add_char(' ');
228 1.99 rillig }
229 1.99 rillig
230 1.140 rillig if (!(com.len > 0 && ch_isblank(com.mem[com.len - 1])))
231 1.99 rillig com_add_char(' ');
232 1.111 rillig com_add_char('*');
233 1.111 rillig com_add_char('/');
234 1.99 rillig return;
235 1.99 rillig
236 1.99 rillig } else /* handle isolated '*' */
237 1.99 rillig com_add_char('*');
238 1.99 rillig break;
239 1.99 rillig
240 1.139 rillig default:
241 1.99 rillig ;
242 1.140 rillig int now_len = ind_add(ps.com_ind, com.st, com.len);
243 1.99 rillig for (;;) {
244 1.99 rillig char ch = inp_next();
245 1.99 rillig if (ch_isblank(ch))
246 1.140 rillig last_blank = (ssize_t)com.len;
247 1.99 rillig com_add_char(ch);
248 1.99 rillig now_len++;
249 1.119 rillig if (memchr("*\n\r\b\t", inp_peek(), 6) != NULL)
250 1.99 rillig break;
251 1.138 rillig if (now_len >= line_length && last_blank != -1)
252 1.99 rillig break;
253 1.99 rillig }
254 1.99 rillig
255 1.99 rillig ps.next_col_1 = false;
256 1.99 rillig
257 1.138 rillig if (now_len <= line_length)
258 1.99 rillig break;
259 1.140 rillig if (ch_isspace(com.mem[com.len - 1]))
260 1.99 rillig break;
261 1.99 rillig
262 1.99 rillig if (last_blank == -1) { /* only a single word in this line */
263 1.126 rillig output_line();
264 1.99 rillig com_add_delim();
265 1.99 rillig break;
266 1.99 rillig }
267 1.99 rillig
268 1.131 rillig const char *last_word_s = com.mem + last_blank + 1;
269 1.140 rillig size_t last_word_len = com.len - (size_t)(last_blank + 1);
270 1.140 rillig com.len = (size_t)last_blank;
271 1.126 rillig output_line();
272 1.99 rillig com_add_delim();
273 1.99 rillig
274 1.140 rillig /*
275 1.141 rillig * Assume that output_line and com_add_delim don't invalidate the
276 1.141 rillig * "unused" part of the buffer beyond com.mem + com.len.
277 1.140 rillig */
278 1.140 rillig memmove(com.mem + com.len, last_word_s, last_word_len);
279 1.140 rillig com.len += last_word_len;
280 1.99 rillig last_blank = -1;
281 1.99 rillig }
282 1.99 rillig }
283 1.99 rillig }
284 1.99 rillig
285 1.99 rillig static void
286 1.103 rillig copy_comment_nowrap(void)
287 1.99 rillig {
288 1.81 rillig for (;;) {
289 1.119 rillig if (inp_peek() == '\n') {
290 1.140 rillig if (token.mem[token.len - 1] == '/')
291 1.128 rillig return;
292 1.56 rillig
293 1.54 rillig if (had_eof) {
294 1.67 rillig diag(1, "Unterminated comment");
295 1.126 rillig output_line();
296 1.11 kamil return;
297 1.11 kamil }
298 1.56 rillig
299 1.140 rillig if (com.len == 0)
300 1.100 rillig com_add_char(' '); /* force output of an empty line */
301 1.126 rillig output_line();
302 1.54 rillig ++line_no;
303 1.100 rillig inp_skip();
304 1.104 rillig continue;
305 1.104 rillig }
306 1.1 cgd
307 1.106 rillig com_add_char(inp_next());
308 1.140 rillig if (com.mem[com.len - 2] == '*' && com.mem[com.len - 1] == '/'
309 1.140 rillig && token.mem[token.len - 1] == '*')
310 1.128 rillig return;
311 1.11 kamil }
312 1.1 cgd }
313 1.95 rillig
314 1.95 rillig /*
315 1.95 rillig * Scan, reformat and output a single comment, which is either a block comment
316 1.95 rillig * starting with '/' '*' or an end-of-line comment starting with '//'.
317 1.95 rillig */
318 1.95 rillig void
319 1.95 rillig process_comment(void)
320 1.95 rillig {
321 1.138 rillig int line_length;
322 1.138 rillig bool may_wrap, delim;
323 1.95 rillig
324 1.138 rillig analyze_comment(&may_wrap, &delim, &line_length);
325 1.99 rillig if (may_wrap)
326 1.138 rillig copy_comment_wrap(line_length, delim);
327 1.99 rillig else
328 1.103 rillig copy_comment_nowrap();
329 1.95 rillig }
330