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