pr_comment.c revision 1.121 1 /* $NetBSD: pr_comment.c,v 1.121 2021/11/19 20:23: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 #if 0
41 static char sccsid[] = "@(#)pr_comment.c 8.1 (Berkeley) 6/6/93";
42 #endif
43
44 #include <sys/cdefs.h>
45 #if defined(__NetBSD__)
46 __RCSID("$NetBSD: pr_comment.c,v 1.121 2021/11/19 20:23:17 rillig Exp $");
47 #elif defined(__FreeBSD__)
48 __FBSDID("$FreeBSD: head/usr.bin/indent/pr_comment.c 334927 2018-06-10 16:44:18Z pstef $");
49 #endif
50
51 #include <assert.h>
52 #include <stdio.h>
53 #include <string.h>
54
55 #include "indent.h"
56
57 static void
58 com_add_char(char ch)
59 {
60 if (1 >= com.l - com.e)
61 buf_expand(&com, 1);
62 *com.e++ = ch;
63 }
64
65 static void
66 com_add_delim(void)
67 {
68 if (!opt.star_comment_cont)
69 return;
70 size_t len = 3;
71 if (len >= (size_t)(com.l - com.e))
72 buf_expand(&com, len);
73 memcpy(com.e, " * ", len);
74 com.e += len;
75 }
76
77 static void
78 com_terminate(void)
79 {
80 if (1 >= com.l - com.e)
81 buf_expand(&com, 1);
82 *com.e = '\0';
83 }
84
85 static bool
86 fits_in_one_line(int max_line_length)
87 {
88 for (const char *p = inp_p(); *p != '\n'; p++) {
89 assert(*p != '\0');
90 assert(inp_line_end() - p >= 2);
91 if (!(p[0] == '*' && p[1] == '/'))
92 continue;
93
94 int len = ind_add(ps.com_ind + 3, inp_p(), p);
95 len += ch_isblank(p[-1]) ? 2 : 3;
96 return len <= max_line_length;
97 }
98 return false;
99 }
100
101 static void
102 analyze_comment(bool *p_may_wrap, bool *p_break_delim,
103 int *p_adj_max_line_length)
104 {
105 int adj_max_line_length; /* Adjusted max_line_length for comments that
106 * spill over the right margin */
107 bool break_delim = opt.comment_delimiter_on_blankline;
108 int com_ind;
109
110 adj_max_line_length = opt.max_line_length;
111 bool may_wrap = true;
112
113 if (ps.curr_col_1 && !opt.format_col1_comments) {
114 may_wrap = false;
115 break_delim = false;
116 com_ind = 0;
117
118 } else {
119 if (inp_peek() == '-' || inp_peek() == '*' ||
120 token.e[-1] == '/' ||
121 (inp_peek() == '\n' && !opt.format_block_comments)) {
122 may_wrap = false;
123 break_delim = false;
124 }
125
126 if (lab.s == lab.e && code.s == code.e) {
127 adj_max_line_length = opt.block_comment_max_line_length;
128 com_ind = (ps.ind_level - opt.unindent_displace) * opt.indent_size;
129 if (com_ind <= 0)
130 com_ind = opt.format_col1_comments ? 0 : 1;
131
132 } else {
133 break_delim = false;
134
135 int target_ind = code.s != code.e
136 ? ind_add(compute_code_indent(), code.s, code.e)
137 : ind_add(compute_label_indent(), lab.s, lab.e);
138
139 com_ind = ps.decl_on_line || ps.ind_level == 0
140 ? opt.decl_comment_column - 1 : opt.comment_column - 1;
141 if (com_ind <= target_ind)
142 com_ind = next_tab(target_ind);
143 if (com_ind + 25 > adj_max_line_length)
144 adj_max_line_length = com_ind + 25;
145 }
146 }
147
148 ps.com_ind = com_ind;
149
150 if (!may_wrap) {
151 /*
152 * Find out how much indentation there was originally, because that
153 * much will have to be ignored by dump_line().
154 */
155 const char *start = inp_line_start();
156 ps.n_comment_delta = -ind_add(0, start, inp_p() - 2);
157 } else {
158 ps.n_comment_delta = 0;
159 while (ch_isblank(inp_peek()))
160 inp_skip();
161 }
162
163 ps.comment_delta = 0;
164 com_add_char('/');
165 com_add_char(token.e[-1]); /* either '*' or '/' */
166
167 /* TODO: Maybe preserve a single '\t' as well. */
168 if (inp_peek() != ' ' && may_wrap)
169 com_add_char(' ');
170
171 if (break_delim && fits_in_one_line(adj_max_line_length))
172 break_delim = false;
173
174 if (break_delim) {
175 com.e = com.s + 2;
176 *com.e = '\0';
177 if (opt.blanklines_before_block_comments &&
178 ps.prev_token != lsym_lbrace)
179 blank_line_before = true;
180 dump_line();
181 com_add_delim();
182 }
183
184 *p_adj_max_line_length = adj_max_line_length;
185 *p_break_delim = break_delim;
186 *p_may_wrap = may_wrap;
187 }
188
189 /*
190 * Copy characters from 'inp' to 'com'. Try to keep comments from going over
191 * the maximum line length. To do that, remember where the last blank, tab, or
192 * newline was. When a line is filled, print up to the last blank and continue
193 * copying.
194 */
195 static void
196 copy_comment_wrap(int adj_max_line_length, bool break_delim)
197 {
198 ssize_t last_blank = -1; /* index of the last blank in com.buf */
199
200 for (;;) {
201 switch (inp_peek()) {
202 case '\f':
203 dump_line_ff();
204 last_blank = -1;
205 com_add_delim();
206 inp_skip();
207 while (ch_isblank(inp_peek()))
208 inp_skip();
209 break;
210
211 case '\n':
212 if (had_eof) {
213 diag(1, "Unterminated comment");
214 dump_line();
215 return;
216 }
217
218 last_blank = -1;
219 if (ps.next_col_1) {
220 if (com.s == com.e)
221 com_add_char(' '); /* force empty line of output */
222 if (com.e - com.s > 3) {
223 dump_line();
224 com_add_delim();
225 }
226 dump_line();
227 com_add_delim();
228
229 } else {
230 ps.next_col_1 = true;
231 if (!(com.e > com.s && ch_isblank(com.e[-1])))
232 com_add_char(' ');
233 last_blank = com.e - 1 - com.buf;
234 }
235 ++line_no;
236
237 bool skip_asterisk = true;
238 do { /* flush any blanks and/or tabs at start of
239 * next line */
240 inp_skip();
241 if (inp_peek() == '*' && skip_asterisk) {
242 skip_asterisk = false;
243 inp_skip();
244 if (inp_peek() == '/')
245 goto end_of_comment;
246 }
247 } while (ch_isblank(inp_peek()));
248
249 break; /* end of case for newline */
250
251 case '*':
252 inp_skip();
253 if (inp_peek() == '/') {
254 end_of_comment:
255 inp_skip();
256
257 if (break_delim) {
258 if (com.e - com.s > 3)
259 dump_line();
260 else
261 com.e = com.s;
262 com_add_char(' ');
263 }
264
265 if (!(com.e > com.s && ch_isblank(com.e[-1])))
266 com_add_char(' ');
267 com_add_char('*');
268 com_add_char('/');
269 com_terminate();
270 return;
271
272 } else /* handle isolated '*' */
273 com_add_char('*');
274 break;
275
276 default: /* we have a random char */
277 ;
278 int now_len = ind_add(ps.com_ind, com.s, com.e);
279 for (;;) {
280 char ch = inp_next();
281 if (ch_isblank(ch))
282 last_blank = com.e - com.buf;
283 com_add_char(ch);
284 now_len++;
285 if (memchr("*\n\r\b\t", inp_peek(), 6) != NULL)
286 break;
287 if (now_len >= adj_max_line_length && last_blank != -1)
288 break;
289 }
290
291 ps.next_col_1 = false;
292
293 if (now_len <= adj_max_line_length)
294 break;
295 if (ch_isspace(com.e[-1]))
296 break;
297
298 if (last_blank == -1) { /* only a single word in this line */
299 dump_line();
300 com_add_delim();
301 break;
302 }
303
304 const char *last_word_s = com.buf + last_blank + 1;
305 size_t last_word_len = (size_t)(com.e - last_word_s);
306 com.e = com.buf + last_blank;
307 dump_line();
308 com_add_delim();
309
310 memcpy(com.e, last_word_s, last_word_len);
311 com.e += last_word_len;
312 last_blank = -1;
313 }
314 }
315 }
316
317 static void
318 copy_comment_nowrap(void)
319 {
320 for (;;) {
321 if (inp_peek() == '\n') {
322 if (token.e[-1] == '/')
323 goto finish;
324
325 if (had_eof) {
326 diag(1, "Unterminated comment");
327 dump_line();
328 return;
329 }
330
331 if (com.s == com.e)
332 com_add_char(' '); /* force output of an empty line */
333 dump_line();
334 ++line_no;
335 inp_skip();
336 continue;
337 }
338
339 com_add_char(inp_next());
340 if (com.e[-2] == '*' && com.e[-1] == '/' && token.e[-1] == '*')
341 goto finish;
342 }
343
344 finish:
345 com_terminate();
346 }
347
348 /*
349 * Scan, reformat and output a single comment, which is either a block comment
350 * starting with '/' '*' or an end-of-line comment starting with '//'.
351 */
352 void
353 process_comment(void)
354 {
355 int adj_max_line_length;
356 bool may_wrap, break_delim;
357
358 ps.just_saw_decl = 0;
359 ps.stats.comments++;
360
361 int saved_just_saw_decl = ps.just_saw_decl;
362 analyze_comment(&may_wrap, &break_delim, &adj_max_line_length);
363 if (may_wrap)
364 copy_comment_wrap(adj_max_line_length, break_delim);
365 else
366 copy_comment_nowrap();
367 ps.just_saw_decl = saved_just_saw_decl;
368 }
369