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