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