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