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