args.c revision 1.25 1 /* $NetBSD: args.c,v 1.25 2021/09/25 17:11:23 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.25 2021/09/25 17:11:23 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 /* profile types */
68 #define PRO_SPECIAL 1 /* special case */
69 #define PRO_BOOL 2 /* boolean */
70 #define PRO_INT 3 /* integer */
71
72 /* profile specials for booleans */
73 #define ON 1 /* turn it on */
74 #define OFF 0 /* turn it off */
75
76 /* profile specials for specials */
77 #define IGN 1 /* ignore it */
78 #define CLI 2 /* case label indent (float) */
79 #define STDIN 3 /* use stdin */
80 #define KEY 4 /* type (keyword) */
81
82 static void scan_profile(FILE *);
83
84 #define KEY_FILE 5 /* only used for args */
85 #define VERSION 6 /* only used for args */
86
87 const char *option_source = "?";
88
89 void add_typedefs_from_file(const char *str);
90
91 #if __STDC_VERSION__ >= 201112L
92 #define assert_type(expr, type) _Generic((expr), type : (expr))
93 #else
94 #define assert_type(expr, type) (expr)
95 #endif
96 #define bool_option(name, value, var) \
97 {name, PRO_BOOL, value, assert_type(&(var), ibool *)}
98 #define int_option(name, value, var) \
99 {name, PRO_INT, value, assert_type(&(var), int *)}
100 #define special_option(name, value) \
101 {name, PRO_SPECIAL, assert_type(value, int), NULL}
102
103 /*
104 * N.B.: because of the way the table here is scanned, options whose names are
105 * substrings of other options must occur later; that is, with -lp vs -l, -lp
106 * must be first.
107 */
108 const struct pro {
109 const char *p_name; /* name, e.g. -bl, -cli */
110 int p_type; /* type (int, bool, special) */
111 int p_special; /* depends on type */
112 void *p_obj; /* the associated variable */
113 } pro[] = {
114 special_option("T", KEY),
115 special_option("U", KEY_FILE),
116 special_option("-version", VERSION),
117 special_option("P", IGN),
118 bool_option("bacc", ON, opt.blanklines_around_conditional_compilation),
119 bool_option("badp", ON, opt.blanklines_after_declarations_at_proctop),
120 bool_option("bad", ON, opt.blanklines_after_declarations),
121 bool_option("bap", ON, opt.blanklines_after_procs),
122 bool_option("bbb", ON, opt.blanklines_before_blockcomments),
123 bool_option("bc", OFF, opt.leave_comma),
124 bool_option("bl", OFF, opt.btype_2),
125 bool_option("br", ON, opt.btype_2),
126 bool_option("bs", ON, opt.Bill_Shannon),
127 bool_option("cdb", ON, opt.comment_delimiter_on_blankline),
128 int_option("cd", 0, opt.decl_comment_column),
129 bool_option("ce", ON, opt.cuddle_else),
130 int_option("ci", 0, opt.continuation_indent),
131 special_option("cli", CLI),
132 bool_option("cs", ON, opt.space_after_cast),
133 int_option("c", 0, opt.comment_column),
134 int_option("di", 0, opt.decl_indent),
135 bool_option("dj", ON, opt.ljust_decl),
136 int_option("d", 0, opt.unindent_displace),
137 bool_option("eei", ON, opt.extra_expression_indent),
138 bool_option("ei", ON, opt.else_if),
139 bool_option("fbs", ON, opt.function_brace_split),
140 bool_option("fc1", ON, opt.format_col1_comments),
141 bool_option("fcb", ON, opt.format_block_comments),
142 bool_option("ip", ON, opt.indent_parameters),
143 int_option("i", 0, opt.indent_size),
144 int_option("lc", 0, opt.block_comment_max_line_length),
145 int_option("ldi", 0, opt.local_decl_indent),
146 bool_option("lpl", ON, opt.lineup_to_parens_always),
147 bool_option("lp", ON, opt.lineup_to_parens),
148 int_option("l", 0, opt.max_line_length),
149 bool_option("nbacc", OFF, opt.blanklines_around_conditional_compilation),
150 bool_option("nbadp", OFF, opt.blanklines_after_declarations_at_proctop),
151 bool_option("nbad", OFF, opt.blanklines_after_declarations),
152 bool_option("nbap", OFF, opt.blanklines_after_procs),
153 bool_option("nbbb", OFF, opt.blanklines_before_blockcomments),
154 bool_option("nbc", ON, opt.leave_comma),
155 bool_option("nbs", OFF, opt.Bill_Shannon),
156 bool_option("ncdb", OFF, opt.comment_delimiter_on_blankline),
157 bool_option("nce", OFF, opt.cuddle_else),
158 bool_option("ncs", OFF, opt.space_after_cast),
159 bool_option("ndj", OFF, opt.ljust_decl),
160 bool_option("neei", OFF, opt.extra_expression_indent),
161 bool_option("nei", OFF, opt.else_if),
162 bool_option("nfbs", OFF, opt.function_brace_split),
163 bool_option("nfc1", OFF, opt.format_col1_comments),
164 bool_option("nfcb", OFF, opt.format_block_comments),
165 bool_option("nip", OFF, opt.indent_parameters),
166 bool_option("nlpl", OFF, opt.lineup_to_parens_always),
167 bool_option("nlp", OFF, opt.lineup_to_parens),
168 bool_option("npcs", OFF, opt.proc_calls_space),
169 special_option("npro", IGN),
170 bool_option("npsl", OFF, opt.procnames_start_line),
171 bool_option("nsc", OFF, opt.star_comment_cont),
172 bool_option("nsob", OFF, opt.swallow_optional_blanklines),
173 bool_option("nut", OFF, opt.use_tabs),
174 bool_option("nv", OFF, opt.verbose),
175 bool_option("pcs", ON, opt.proc_calls_space),
176 bool_option("psl", ON, opt.procnames_start_line),
177 bool_option("sc", ON, opt.star_comment_cont),
178 bool_option("sob", ON, opt.swallow_optional_blanklines),
179 special_option("st", STDIN),
180 bool_option("ta", ON, opt.auto_typedefs),
181 int_option("ts", 0, opt.tabsize),
182 bool_option("ut", ON, opt.use_tabs),
183 bool_option("v", ON, opt.verbose),
184 /* whew! */
185 {0, 0, 0, 0}
186 };
187
188 /*
189 * set_profile reads $HOME/.indent.pro and ./.indent.pro and handles arguments
190 * given in these files.
191 */
192 void
193 set_profile(const char *profile_name)
194 {
195 FILE *f;
196 char fname[PATH_MAX];
197 static char prof[] = ".indent.pro";
198
199 if (profile_name == NULL)
200 snprintf(fname, sizeof(fname), "%s/%s", getenv("HOME"), prof);
201 else
202 snprintf(fname, sizeof(fname), "%s", profile_name + 2);
203 if ((f = fopen(option_source = fname, "r")) != NULL) {
204 scan_profile(f);
205 (void) fclose(f);
206 }
207 if ((f = fopen(option_source = prof, "r")) != NULL) {
208 scan_profile(f);
209 (void) fclose(f);
210 }
211 option_source = "Command line";
212 }
213
214 static void
215 scan_profile(FILE *f)
216 {
217 int comment_index, i;
218 char *p;
219 char buf[BUFSIZ];
220
221 for (;;) {
222 p = buf;
223 comment_index = 0;
224 while ((i = getc(f)) != EOF) {
225 if (i == '*' && comment_index == 0 && p > buf && p[-1] == '/') {
226 comment_index = (int)(p - buf);
227 *p++ = i;
228 } else if (i == '/' && comment_index != 0 && p > buf && p[-1] == '*') {
229 p = buf + comment_index - 1;
230 comment_index = 0;
231 } else if (isspace((unsigned char)i)) {
232 if (p > buf && comment_index == 0)
233 break;
234 } else {
235 *p++ = i;
236 }
237 }
238 if (p != buf) {
239 *p++ = 0;
240 if (opt.verbose)
241 printf("profile: %s\n", buf);
242 set_option(buf);
243 } else if (i == EOF)
244 return;
245 }
246 }
247
248 static const char *
249 eqin(const char *s1, const char *s2)
250 {
251 while (*s1 != '\0') {
252 if (*s1++ != *s2++)
253 return NULL;
254 }
255 return s2;
256 }
257
258 void
259 set_option(char *arg)
260 {
261 const struct pro *p;
262 const char *param_start;
263
264 arg++; /* ignore leading "-" */
265 for (p = pro; p->p_name != NULL; p++)
266 if (*p->p_name == *arg && (param_start = eqin(p->p_name, arg)) != NULL)
267 goto found;
268 errx(1, "%s: unknown parameter \"%s\"", option_source, arg - 1);
269 found:
270 switch (p->p_type) {
271
272 case PRO_SPECIAL:
273 switch (p->p_special) {
274
275 case IGN:
276 break;
277
278 case CLI:
279 if (*param_start == 0)
280 goto need_param;
281 opt.case_indent = atof(param_start);
282 break;
283
284 case STDIN:
285 if (input == NULL)
286 input = stdin;
287 if (output == NULL)
288 output = stdout;
289 break;
290
291 case KEY:
292 if (*param_start == 0)
293 goto need_param;
294 add_typename(param_start);
295 break;
296
297 case KEY_FILE:
298 if (*param_start == 0)
299 goto need_param;
300 add_typedefs_from_file(param_start);
301 break;
302
303 case VERSION:
304 printf("FreeBSD indent %s\n", INDENT_VERSION);
305 exit(0);
306 /*NOTREACHED*/
307
308 default:
309 errx(1, "set_option: internal error: p_special %d", p->p_special);
310 }
311 break;
312
313 case PRO_BOOL:
314 if (p->p_special == OFF)
315 *(ibool *)p->p_obj = false;
316 else
317 *(ibool *)p->p_obj = true;
318 break;
319
320 case PRO_INT:
321 if (!isdigit((unsigned char)*param_start)) {
322 need_param:
323 errx(1, "%s: ``%s'' requires a parameter", option_source, p->p_name);
324 }
325 *(int *)p->p_obj = atoi(param_start);
326 break;
327
328 default:
329 errx(1, "set_option: internal error: p_type %d", p->p_type);
330 }
331 }
332
333 void
334 add_typedefs_from_file(const char *str)
335 {
336 FILE *file;
337 char line[BUFSIZ];
338
339 if ((file = fopen(str, "r")) == NULL) {
340 fprintf(stderr, "indent: cannot open file %s\n", str);
341 exit(1);
342 }
343 while ((fgets(line, BUFSIZ, file)) != NULL) {
344 /* Remove trailing whitespace */
345 line[strcspn(line, " \t\n\r")] = '\0';
346 add_typename(line);
347 }
348 fclose(file);
349 }
350