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