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