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