args.c revision 1.61 1 /* $NetBSD: args.c,v 1.61 2021/10/28 20:31: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[] = "@(#)args.c 8.1 (Berkeley) 6/6/93";
42 #endif
43
44 #include <sys/cdefs.h>
45 #if defined(__NetBSD__)
46 __RCSID("$NetBSD: args.c,v 1.61 2021/10/28 20:31:17 rillig Exp $");
47 #elif defined(__FreeBSD__)
48 __FBSDID("$FreeBSD: head/usr.bin/indent/args.c 336318 2018-07-15 21:04:21Z pstef $");
49 #endif
50
51 /* Read options from profile files and from the command line. */
52
53 #include <ctype.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <limits.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60
61 #include "indent.h"
62
63 #if __STDC_VERSION__ >= 201112L
64 #define assert_type(expr, type) _Generic((expr), type : (expr))
65 #else
66 #define assert_type(expr, type) (expr)
67 #endif
68
69 #define bool_option(name, value, var) \
70 {name, true, value, false, 0, 0, assert_type(&(opt.var), bool *)}
71 #define bool_options(name, var) \
72 {name, true, false, true, 0, 0, assert_type(&(opt.var), bool *)}
73 #define int_option(name, var, min, max) \
74 {name, false, false, false, min, max, assert_type(&(opt.var), int *)}
75
76 /*
77 * N.B.: an option whose name is a prefix of another option must come earlier;
78 * for example, "l" must come before "lp".
79 *
80 * See set_special_option for special options.
81 */
82 static const struct pro {
83 const char p_name[5]; /* e.g. "bl", "cli" */
84 bool p_is_bool;
85 bool p_bool_value;
86 bool p_may_negate;
87 short i_min;
88 short i_max;
89 void *p_var; /* the associated variable */
90 } pro[] = {
91 bool_options("bacc", blanklines_around_conditional_compilation),
92 bool_options("bad", blanklines_after_decl),
93 bool_options("badp", blanklines_after_decl_at_top),
94 bool_options("bap", blanklines_after_procs),
95 bool_options("bbb", blanklines_before_block_comments),
96 bool_options("bc", break_after_comma),
97 bool_option("bl", false, brace_same_line),
98 bool_option("br", true, brace_same_line),
99 bool_options("bs", blank_after_sizeof),
100 int_option("c", comment_column, 1, 999),
101 int_option("cd", decl_comment_column, 1, 999),
102 bool_options("cdb", comment_delimiter_on_blankline),
103 bool_options("ce", cuddle_else),
104 int_option("ci", continuation_indent, 0, 999),
105 /* "cli" is special */
106 bool_options("cs", space_after_cast),
107 int_option("d", unindent_displace, -999, 999),
108 int_option("di", decl_indent, 0, 999),
109 bool_options("dj", ljust_decl),
110 bool_options("eei", extra_expr_indent),
111 bool_options("ei", else_if),
112 bool_options("fbs", function_brace_split),
113 bool_options("fc1", format_col1_comments),
114 bool_options("fcb", format_block_comments),
115 int_option("i", indent_size, 1, 80),
116 bool_options("ip", indent_parameters),
117 int_option("l", max_line_length, 1, 999),
118 int_option("lc", block_comment_max_line_length, 1, 999),
119 int_option("ldi", local_decl_indent, 0, 999),
120 bool_options("lp", lineup_to_parens),
121 bool_options("lpl", lineup_to_parens_always),
122 /* "npro" is special */
123 /* "P" is special */
124 bool_options("pcs", proc_calls_space),
125 bool_options("psl", procnames_start_line),
126 bool_options("sc", star_comment_cont),
127 bool_options("sob", swallow_optional_blanklines),
128 /* "st" is special */
129 bool_option("ta", true, auto_typedefs),
130 /* "T" is special */
131 int_option("ts", tabsize, 1, 80),
132 /* "U" is special */
133 bool_options("ut", use_tabs),
134 bool_options("v", verbose),
135 };
136
137 static void
138 load_profile(const char *fname, bool must_exist)
139 {
140 FILE *f;
141
142 if ((f = fopen(fname, "r")) == NULL) {
143 if (must_exist)
144 err(EXIT_FAILURE, "profile %s", fname);
145 return;
146 }
147
148 for (;;) {
149 char buf[BUFSIZ];
150 size_t n = 0;
151 int ch, comment_ch = -1;
152
153 while ((ch = getc(f)) != EOF) {
154 if (ch == '*' && comment_ch < 0 && n > 0 && buf[n - 1] == '/') {
155 n--;
156 comment_ch = ch;
157 } else if (comment_ch >= 0) {
158 comment_ch = ch == '/' && comment_ch == '*' ? -1 : ch;
159 } else if (isspace((unsigned char)ch)) {
160 break;
161 } else if (n >= array_length(buf) - 5) {
162 diag(1, "buffer overflow in %s, starting with '%.10s'",
163 fname, buf);
164 exit(1);
165 } else
166 buf[n++] = (char)ch;
167 }
168
169 if (n > 0) {
170 buf[n] = '\0';
171 if (opt.verbose)
172 printf("profile: %s\n", buf);
173 set_option(buf, fname);
174 } else if (ch == EOF)
175 break;
176 }
177 (void)fclose(f);
178 }
179
180 void
181 load_profiles(const char *profile_name)
182 {
183 char fname[PATH_MAX];
184
185 if (profile_name != NULL)
186 load_profile(profile_name, true);
187 else {
188 snprintf(fname, sizeof(fname), "%s/.indent.pro", getenv("HOME"));
189 load_profile(fname, false);
190 }
191 load_profile(".indent.pro", false);
192 }
193
194 static const char *
195 skip_over(const char *s, bool may_negate, const char *prefix)
196 {
197 if (may_negate && s[0] == 'n')
198 s++;
199 while (*prefix != '\0') {
200 if (*prefix++ != *s++)
201 return NULL;
202 }
203 return s;
204 }
205
206 static void
207 add_typedefs_from_file(const char *fname)
208 {
209 FILE *file;
210 char line[BUFSIZ];
211
212 if ((file = fopen(fname, "r")) == NULL) {
213 fprintf(stderr, "indent: cannot open file %s\n", fname);
214 exit(1);
215 }
216 while ((fgets(line, BUFSIZ, file)) != NULL) {
217 /* Remove trailing whitespace */
218 line[strcspn(line, " \t\n\r")] = '\0';
219 add_typename(line);
220 }
221 (void)fclose(file);
222 }
223
224 static bool
225 set_special_option(const char *arg, const char *option_source)
226 {
227 const char *arg_end;
228
229 if (strncmp(arg, "-version", 8) == 0) {
230 printf("NetBSD indent 2.1\n");
231 exit(0);
232 }
233
234 if (arg[0] == 'P' || strncmp(arg, "npro", 4) == 0)
235 return true;
236
237 if (strncmp(arg, "cli", 3) == 0) {
238 arg_end = arg + 3;
239 if (arg_end[0] == '\0')
240 goto need_param;
241 opt.case_indent = (float)atof(arg_end);
242 return true;
243 }
244
245 if (strncmp(arg, "st", 2) == 0) {
246 if (input == NULL)
247 input = stdin;
248 if (output == NULL)
249 output = stdout;
250 return true;
251 }
252
253 if (arg[0] == 'T') {
254 arg_end = arg + 1;
255 if (arg_end[0] == '\0')
256 goto need_param;
257 add_typename(arg_end);
258 return true;
259 }
260
261 if (arg[0] == 'U') {
262 arg_end = arg + 1;
263 if (arg_end[0] == '\0')
264 goto need_param;
265 add_typedefs_from_file(arg_end);
266 return true;
267 }
268
269 return false;
270
271 need_param:
272 errx(1, "%s: ``%.*s'' requires a parameter",
273 option_source, (int)(arg_end - arg), arg);
274 /* NOTREACHED */
275 }
276
277 void
278 set_option(const char *arg, const char *option_source)
279 {
280 const struct pro *p;
281 const char *param_start;
282
283 arg++; /* skip leading '-' */
284 if (set_special_option(arg, option_source))
285 return;
286
287 for (p = pro + array_length(pro); p-- != pro;) {
288 param_start = skip_over(arg, p->p_may_negate, p->p_name);
289 if (param_start != NULL)
290 goto found;
291 }
292 errx(1, "%s: unknown option \"-%s\"", option_source, arg);
293
294 found:
295 if (p->p_is_bool) {
296 if (param_start[0] != '\0')
297 errx(1, "%s: unknown option \"-%s\"", option_source, arg);
298 *(bool *)p->p_var = p->p_may_negate ? arg[0] != 'n' : p->p_bool_value;
299 } else {
300 if (!isdigit((unsigned char)*param_start))
301 errx(1, "%s: option \"-%s\" requires an integer parameter",
302 option_source, p->p_name);
303 errno = 0;
304 char *end;
305 long num = strtol(param_start, &end, 10);
306 if (!(errno == 0 && *end == '\0' &&
307 p->i_min <= num && num <= p->i_max))
308 errx(1, "%s: invalid argument \"%s\" for option \"-%s\"",
309 option_source, param_start, p->p_name);
310 *(int *)p->p_var = (int)num;
311 }
312 }
313