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