args.c revision 1.86 1 1.86 rillig /* $NetBSD: args.c,v 1.86 2023/12/03 21:44:42 rillig Exp $ */
2 1.3 tls
3 1.14 kamil /*-
4 1.14 kamil * SPDX-License-Identifier: BSD-4-Clause
5 1.14 kamil *
6 1.14 kamil * Copyright (c) 1985 Sun Microsystems, Inc.
7 1.4 mrg * Copyright (c) 1980, 1993
8 1.4 mrg * The Regents of the University of California. All rights reserved.
9 1.1 cgd * All rights reserved.
10 1.1 cgd *
11 1.1 cgd * Redistribution and use in source and binary forms, with or without
12 1.1 cgd * modification, are permitted provided that the following conditions
13 1.1 cgd * are met:
14 1.1 cgd * 1. Redistributions of source code must retain the above copyright
15 1.1 cgd * notice, this list of conditions and the following disclaimer.
16 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
17 1.1 cgd * notice, this list of conditions and the following disclaimer in the
18 1.1 cgd * documentation and/or other materials provided with the distribution.
19 1.1 cgd * 3. All advertising materials mentioning features or use of this software
20 1.1 cgd * must display the following acknowledgement:
21 1.1 cgd * This product includes software developed by the University of
22 1.1 cgd * California, Berkeley and its contributors.
23 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
24 1.1 cgd * may be used to endorse or promote products derived from this software
25 1.1 cgd * without specific prior written permission.
26 1.1 cgd *
27 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 1.1 cgd * SUCH DAMAGE.
38 1.1 cgd */
39 1.1 cgd
40 1.5 lukem #include <sys/cdefs.h>
41 1.86 rillig __RCSID("$NetBSD: args.c,v 1.86 2023/12/03 21:44:42 rillig Exp $");
42 1.1 cgd
43 1.50 rillig /* Read options from profile files and from the command line. */
44 1.1 cgd
45 1.12 ginsbach #include <err.h>
46 1.14 kamil #include <limits.h>
47 1.84 rillig #include <stddef.h>
48 1.1 cgd #include <stdio.h>
49 1.1 cgd #include <stdlib.h>
50 1.1 cgd #include <string.h>
51 1.15 rillig
52 1.14 kamil #include "indent.h"
53 1.14 kamil
54 1.25 rillig #if __STDC_VERSION__ >= 201112L
55 1.84 rillig #define get_offset(name, type) \
56 1.84 rillig _Generic((&opt.name), type *: offsetof(struct options, name))
57 1.25 rillig #else
58 1.84 rillig #define get_offset(name, type) (offsetof(struct options, name))
59 1.25 rillig #endif
60 1.54 rillig
61 1.25 rillig #define bool_option(name, value, var) \
62 1.84 rillig {name, true, false, value, 0, 0, get_offset(var, bool)}
63 1.54 rillig #define bool_options(name, var) \
64 1.84 rillig {name, true, true, false, 0, 0, get_offset(var, bool)}
65 1.56 rillig #define int_option(name, var, min, max) \
66 1.84 rillig {name, false, false, false, min, max, get_offset(var, int)}
67 1.25 rillig
68 1.63 rillig /* See set_special_option for special options. */
69 1.29 rillig static const struct pro {
70 1.78 rillig const char p_name[5]; /* e.g. "bl", "cli" */
71 1.85 rillig bool p_is_bool:1;
72 1.85 rillig bool p_may_negate:1;
73 1.85 rillig bool p_bool_value:1; /* only relevant if !p_may_negate */
74 1.78 rillig short i_min;
75 1.78 rillig short i_max;
76 1.84 rillig unsigned short opt_offset; /* the associated variable */
77 1.60 rillig } pro[] = {
78 1.83 rillig bool_options("bacc", blank_line_around_conditional_compilation),
79 1.78 rillig bool_options("bad", blank_line_after_decl),
80 1.78 rillig bool_options("badp", blank_line_after_decl_at_top),
81 1.83 rillig bool_options("bap", blank_line_after_proc),
82 1.83 rillig bool_options("bbb", blank_line_before_block_comment),
83 1.78 rillig bool_options("bc", break_after_comma),
84 1.78 rillig bool_option("bl", false, brace_same_line),
85 1.78 rillig bool_option("br", true, brace_same_line),
86 1.78 rillig bool_options("bs", blank_after_sizeof),
87 1.78 rillig int_option("c", comment_column, 1, 999),
88 1.78 rillig int_option("cd", decl_comment_column, 1, 999),
89 1.83 rillig bool_options("cdb", comment_delimiter_on_blank_line),
90 1.78 rillig bool_options("ce", cuddle_else),
91 1.78 rillig int_option("ci", continuation_indent, 0, 999),
92 1.78 rillig /* "cli" is special */
93 1.78 rillig bool_options("cs", space_after_cast),
94 1.78 rillig int_option("d", unindent_displace, -999, 999),
95 1.78 rillig int_option("di", decl_indent, 0, 999),
96 1.81 rillig bool_options("dj", left_justify_decl),
97 1.78 rillig bool_options("eei", extra_expr_indent),
98 1.81 rillig bool_options("ei", else_if_in_same_line),
99 1.78 rillig bool_options("fbs", function_brace_split),
100 1.78 rillig bool_options("fc1", format_col1_comments),
101 1.78 rillig bool_options("fcb", format_block_comments),
102 1.78 rillig int_option("i", indent_size, 1, 80),
103 1.78 rillig bool_options("ip", indent_parameters),
104 1.78 rillig int_option("l", max_line_length, 1, 999),
105 1.78 rillig int_option("lc", block_comment_max_line_length, 1, 999),
106 1.78 rillig int_option("ldi", local_decl_indent, 0, 999),
107 1.78 rillig bool_options("lp", lineup_to_parens),
108 1.78 rillig bool_options("lpl", lineup_to_parens_always),
109 1.78 rillig /* "npro" is special */
110 1.78 rillig /* "P" is special */
111 1.78 rillig bool_options("pcs", proc_calls_space),
112 1.78 rillig bool_options("psl", procnames_start_line),
113 1.78 rillig bool_options("sc", star_comment_cont),
114 1.83 rillig bool_options("sob", swallow_optional_blank_lines),
115 1.78 rillig /* "st" is special */
116 1.78 rillig bool_option("ta", true, auto_typedefs),
117 1.78 rillig /* "T" is special */
118 1.78 rillig int_option("ts", tabsize, 1, 80),
119 1.78 rillig /* "U" is special */
120 1.78 rillig bool_options("ut", use_tabs),
121 1.78 rillig bool_options("v", verbose),
122 1.78 rillig /* "-version" is special */
123 1.1 cgd };
124 1.14 kamil
125 1.14 kamil
126 1.53 rillig static void
127 1.53 rillig add_typedefs_from_file(const char *fname)
128 1.53 rillig {
129 1.78 rillig FILE *file;
130 1.78 rillig char line[BUFSIZ];
131 1.53 rillig
132 1.78 rillig if ((file = fopen(fname, "r")) == NULL) {
133 1.78 rillig fprintf(stderr, "indent: cannot open file %s\n", fname);
134 1.78 rillig exit(1);
135 1.78 rillig }
136 1.78 rillig while ((fgets(line, sizeof(line), file)) != NULL) {
137 1.78 rillig /* Only keep the first word of the line. */
138 1.78 rillig line[strcspn(line, " \t\n\r")] = '\0';
139 1.78 rillig register_typename(line);
140 1.78 rillig }
141 1.78 rillig (void)fclose(file);
142 1.53 rillig }
143 1.53 rillig
144 1.32 rillig static bool
145 1.49 rillig set_special_option(const char *arg, const char *option_source)
146 1.32 rillig {
147 1.78 rillig const char *arg_end;
148 1.78 rillig
149 1.78 rillig if (strcmp(arg, "-version") == 0) {
150 1.78 rillig printf("NetBSD indent 2.1\n");
151 1.78 rillig exit(0);
152 1.78 rillig }
153 1.78 rillig
154 1.78 rillig if (arg[0] == 'P' || strcmp(arg, "npro") == 0)
155 1.80 rillig return true; /* see load_profiles */
156 1.78 rillig
157 1.78 rillig if (strncmp(arg, "cli", 3) == 0) {
158 1.78 rillig arg_end = arg + 3;
159 1.78 rillig if (arg_end[0] == '\0')
160 1.78 rillig goto need_arg;
161 1.78 rillig char *end;
162 1.78 rillig opt.case_indent = (float)strtod(arg_end, &end);
163 1.78 rillig if (*end != '\0')
164 1.79 rillig errx(1, "%s: argument \"%s\" to option \"-%.*s\" "
165 1.79 rillig "must be numeric",
166 1.78 rillig option_source, arg_end, (int)(arg_end - arg), arg);
167 1.78 rillig return true;
168 1.78 rillig }
169 1.78 rillig
170 1.78 rillig if (strcmp(arg, "st") == 0) {
171 1.86 rillig if (in.f == NULL)
172 1.86 rillig in.f = stdin;
173 1.78 rillig if (output == NULL)
174 1.78 rillig output = stdout;
175 1.78 rillig return true;
176 1.78 rillig }
177 1.32 rillig
178 1.78 rillig if (arg[0] == 'T') {
179 1.78 rillig arg_end = arg + 1;
180 1.78 rillig if (arg_end[0] == '\0')
181 1.78 rillig goto need_arg;
182 1.78 rillig register_typename(arg_end);
183 1.78 rillig return true;
184 1.78 rillig }
185 1.78 rillig
186 1.78 rillig if (arg[0] == 'U') {
187 1.78 rillig arg_end = arg + 1;
188 1.78 rillig if (arg_end[0] == '\0')
189 1.78 rillig goto need_arg;
190 1.78 rillig add_typedefs_from_file(arg_end);
191 1.78 rillig return true;
192 1.78 rillig }
193 1.32 rillig
194 1.78 rillig return false;
195 1.32 rillig
196 1.72 rillig need_arg:
197 1.78 rillig errx(1, "%s: option \"-%.*s\" requires an argument",
198 1.78 rillig option_source, (int)(arg_end - arg), arg);
199 1.78 rillig /* NOTREACHED */
200 1.32 rillig }
201 1.32 rillig
202 1.62 rillig static const char *
203 1.62 rillig skip_over(const char *s, bool may_negate, const char *prefix)
204 1.62 rillig {
205 1.78 rillig if (may_negate && s[0] == 'n')
206 1.78 rillig s++;
207 1.78 rillig while (*prefix != '\0') {
208 1.78 rillig if (*prefix++ != *s++)
209 1.78 rillig return NULL;
210 1.78 rillig }
211 1.78 rillig return s;
212 1.62 rillig }
213 1.62 rillig
214 1.5 lukem void
215 1.49 rillig set_option(const char *arg, const char *option_source)
216 1.1 cgd {
217 1.78 rillig const struct pro *p;
218 1.78 rillig const char *arg_arg;
219 1.1 cgd
220 1.78 rillig arg++; /* skip leading '-' */
221 1.78 rillig if (set_special_option(arg, option_source))
222 1.78 rillig return;
223 1.78 rillig
224 1.79 rillig for (p = pro + array_length(pro); p-- != pro;) {
225 1.79 rillig arg_arg = skip_over(arg, p->p_may_negate, p->p_name);
226 1.79 rillig if (arg_arg != NULL)
227 1.78 rillig goto found;
228 1.79 rillig }
229 1.78 rillig errx(1, "%s: unknown option \"-%s\"", option_source, arg);
230 1.64 rillig found:
231 1.30 rillig
232 1.78 rillig if (p->p_is_bool) {
233 1.78 rillig if (arg_arg[0] != '\0')
234 1.79 rillig errx(1, "%s: unknown option \"-%s\"",
235 1.79 rillig option_source, arg);
236 1.78 rillig
237 1.84 rillig *(bool *)((unsigned char *)(void *)&opt + p->opt_offset) =
238 1.79 rillig p->p_may_negate ? arg[0] != 'n' : p->p_bool_value;
239 1.78 rillig return;
240 1.78 rillig }
241 1.78 rillig
242 1.78 rillig char *end;
243 1.78 rillig long num = strtol(arg_arg, &end, 10);
244 1.78 rillig if (*end != '\0')
245 1.79 rillig errx(1, "%s: argument \"%s\" to option \"-%s\" "
246 1.79 rillig "must be an integer",
247 1.78 rillig option_source, arg_arg, p->p_name);
248 1.78 rillig
249 1.78 rillig if (!(ch_isdigit(*arg_arg) && p->i_min <= num && num <= p->i_max))
250 1.78 rillig errx(1,
251 1.79 rillig "%s: argument \"%s\" to option \"-%s\" "
252 1.79 rillig "must be between %d and %d",
253 1.78 rillig option_source, arg_arg, p->p_name, p->i_min, p->i_max);
254 1.64 rillig
255 1.84 rillig *(int *)((unsigned char *)(void *)&opt + p->opt_offset) = (int)num;
256 1.14 kamil }
257 1.62 rillig
258 1.62 rillig static void
259 1.62 rillig load_profile(const char *fname, bool must_exist)
260 1.62 rillig {
261 1.78 rillig FILE *f;
262 1.62 rillig
263 1.78 rillig if ((f = fopen(fname, "r")) == NULL) {
264 1.78 rillig if (must_exist)
265 1.78 rillig err(EXIT_FAILURE, "profile %s", fname);
266 1.78 rillig return;
267 1.78 rillig }
268 1.78 rillig
269 1.78 rillig for (;;) {
270 1.78 rillig char buf[BUFSIZ];
271 1.78 rillig size_t n = 0;
272 1.78 rillig int ch, comment_ch = -1;
273 1.78 rillig
274 1.78 rillig while ((ch = getc(f)) != EOF) {
275 1.79 rillig if (ch == '*' && comment_ch == -1
276 1.79 rillig && n > 0 && buf[n - 1] == '/') {
277 1.78 rillig n--;
278 1.78 rillig comment_ch = '*';
279 1.78 rillig } else if (comment_ch != -1) {
280 1.79 rillig comment_ch = ch == '/' && comment_ch == '*'
281 1.79 rillig ? -1 : ch;
282 1.78 rillig } else if (ch_isspace((char)ch)) {
283 1.78 rillig break;
284 1.78 rillig } else if (n >= array_length(buf) - 2) {
285 1.79 rillig errx(1, "buffer overflow in %s, "
286 1.79 rillig "starting with '%.10s'",
287 1.78 rillig fname, buf);
288 1.78 rillig } else
289 1.78 rillig buf[n++] = (char)ch;
290 1.78 rillig }
291 1.78 rillig
292 1.78 rillig if (n > 0) {
293 1.78 rillig buf[n] = '\0';
294 1.78 rillig if (opt.verbose)
295 1.82 rillig fprintf(stderr, "profile: %s\n", buf);
296 1.78 rillig set_option(buf, fname);
297 1.78 rillig } else if (ch == EOF)
298 1.78 rillig break;
299 1.78 rillig }
300 1.78 rillig (void)fclose(f);
301 1.62 rillig }
302 1.62 rillig
303 1.62 rillig void
304 1.80 rillig load_profile_files(const char *path)
305 1.62 rillig {
306 1.80 rillig if (path != NULL)
307 1.80 rillig load_profile(path, true);
308 1.78 rillig else {
309 1.78 rillig const char *home = getenv("HOME");
310 1.78 rillig if (home != NULL) {
311 1.80 rillig char fname[PATH_MAX];
312 1.78 rillig snprintf(fname, sizeof(fname), "%s/.indent.pro", home);
313 1.78 rillig load_profile(fname, false);
314 1.78 rillig }
315 1.74 rillig }
316 1.78 rillig load_profile(".indent.pro", false);
317 1.62 rillig }
318