1 1.88 rillig /* $NetBSD: args.c,v 1.88 2024/12/12 05:51:50 rillig Exp $ */ 2 1.3 tls 3 1.14 kamil /*- 4 1.14 kamil * SPDX-License-Identifier: BSD-4-Clause 5 1.14 kamil * 6 1.14 kamil * Copyright (c) 1985 Sun Microsystems, Inc. 7 1.4 mrg * Copyright (c) 1980, 1993 8 1.4 mrg * The Regents of the University of California. All rights reserved. 9 1.1 cgd * All rights reserved. 10 1.1 cgd * 11 1.1 cgd * Redistribution and use in source and binary forms, with or without 12 1.1 cgd * modification, are permitted provided that the following conditions 13 1.1 cgd * are met: 14 1.1 cgd * 1. Redistributions of source code must retain the above copyright 15 1.1 cgd * notice, this list of conditions and the following disclaimer. 16 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 cgd * notice, this list of conditions and the following disclaimer in the 18 1.1 cgd * documentation and/or other materials provided with the distribution. 19 1.1 cgd * 3. All advertising materials mentioning features or use of this software 20 1.1 cgd * must display the following acknowledgement: 21 1.1 cgd * This product includes software developed by the University of 22 1.1 cgd * California, Berkeley and its contributors. 23 1.1 cgd * 4. Neither the name of the University nor the names of its contributors 24 1.1 cgd * may be used to endorse or promote products derived from this software 25 1.1 cgd * without specific prior written permission. 26 1.1 cgd * 27 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 1.1 cgd * SUCH DAMAGE. 38 1.1 cgd */ 39 1.1 cgd 40 1.5 lukem #include <sys/cdefs.h> 41 1.88 rillig __RCSID("$NetBSD: args.c,v 1.88 2024/12/12 05:51:50 rillig Exp $"); 42 1.1 cgd 43 1.50 rillig /* Read options from profile files and from the command line. */ 44 1.1 cgd 45 1.12 ginsbach #include <err.h> 46 1.14 kamil #include <limits.h> 47 1.84 rillig #include <stddef.h> 48 1.1 cgd #include <stdio.h> 49 1.1 cgd #include <stdlib.h> 50 1.1 cgd #include <string.h> 51 1.15 rillig 52 1.14 kamil #include "indent.h" 53 1.14 kamil 54 1.25 rillig #if __STDC_VERSION__ >= 201112L 55 1.84 rillig #define get_offset(name, type) \ 56 1.84 rillig _Generic((&opt.name), type *: offsetof(struct options, name)) 57 1.25 rillig #else 58 1.84 rillig #define get_offset(name, type) (offsetof(struct options, name)) 59 1.25 rillig #endif 60 1.54 rillig 61 1.25 rillig #define bool_option(name, value, var) \ 62 1.84 rillig {name, true, false, value, 0, 0, get_offset(var, bool)} 63 1.54 rillig #define bool_options(name, var) \ 64 1.84 rillig {name, true, true, false, 0, 0, get_offset(var, bool)} 65 1.56 rillig #define int_option(name, var, min, max) \ 66 1.84 rillig {name, false, false, false, min, max, get_offset(var, int)} 67 1.25 rillig 68 1.63 rillig /* See set_special_option for special options. */ 69 1.29 rillig static const struct pro { 70 1.78 rillig const char p_name[5]; /* e.g. "bl", "cli" */ 71 1.85 rillig bool p_is_bool:1; 72 1.85 rillig bool p_may_negate:1; 73 1.85 rillig bool p_bool_value:1; /* only relevant if !p_may_negate */ 74 1.78 rillig short i_min; 75 1.78 rillig short i_max; 76 1.84 rillig unsigned short opt_offset; /* the associated variable */ 77 1.60 rillig } pro[] = { 78 1.83 rillig bool_options("bacc", blank_line_around_conditional_compilation), 79 1.78 rillig bool_options("bad", blank_line_after_decl), 80 1.78 rillig bool_options("badp", blank_line_after_decl_at_top), 81 1.83 rillig bool_options("bap", blank_line_after_proc), 82 1.83 rillig bool_options("bbb", blank_line_before_block_comment), 83 1.78 rillig bool_options("bc", break_after_comma), 84 1.78 rillig bool_option("bl", false, brace_same_line), 85 1.78 rillig bool_option("br", true, brace_same_line), 86 1.78 rillig bool_options("bs", blank_after_sizeof), 87 1.78 rillig int_option("c", comment_column, 1, 999), 88 1.78 rillig int_option("cd", decl_comment_column, 1, 999), 89 1.83 rillig bool_options("cdb", comment_delimiter_on_blank_line), 90 1.78 rillig bool_options("ce", cuddle_else), 91 1.78 rillig int_option("ci", continuation_indent, 0, 999), 92 1.78 rillig /* "cli" is special */ 93 1.78 rillig bool_options("cs", space_after_cast), 94 1.78 rillig int_option("d", unindent_displace, -999, 999), 95 1.78 rillig int_option("di", decl_indent, 0, 999), 96 1.81 rillig bool_options("dj", left_justify_decl), 97 1.78 rillig bool_options("eei", extra_expr_indent), 98 1.81 rillig bool_options("ei", else_if_in_same_line), 99 1.78 rillig bool_options("fbs", function_brace_split), 100 1.78 rillig bool_options("fc1", format_col1_comments), 101 1.78 rillig bool_options("fcb", format_block_comments), 102 1.78 rillig int_option("i", indent_size, 1, 80), 103 1.78 rillig bool_options("ip", indent_parameters), 104 1.78 rillig int_option("l", max_line_length, 1, 999), 105 1.78 rillig int_option("lc", block_comment_max_line_length, 1, 999), 106 1.78 rillig int_option("ldi", local_decl_indent, 0, 999), 107 1.78 rillig bool_options("lp", lineup_to_parens), 108 1.78 rillig bool_options("lpl", lineup_to_parens_always), 109 1.78 rillig /* "npro" is special */ 110 1.78 rillig /* "P" is special */ 111 1.78 rillig bool_options("pcs", proc_calls_space), 112 1.78 rillig bool_options("psl", procnames_start_line), 113 1.78 rillig bool_options("sc", star_comment_cont), 114 1.83 rillig bool_options("sob", swallow_optional_blank_lines), 115 1.78 rillig /* "st" is special */ 116 1.78 rillig bool_option("ta", true, auto_typedefs), 117 1.78 rillig /* "T" is special */ 118 1.78 rillig int_option("ts", tabsize, 1, 80), 119 1.78 rillig /* "U" is special */ 120 1.78 rillig bool_options("ut", use_tabs), 121 1.78 rillig bool_options("v", verbose), 122 1.78 rillig /* "-version" is special */ 123 1.1 cgd }; 124 1.14 kamil 125 1.14 kamil 126 1.53 rillig static void 127 1.53 rillig add_typedefs_from_file(const char *fname) 128 1.53 rillig { 129 1.78 rillig FILE *file; 130 1.78 rillig char line[BUFSIZ]; 131 1.53 rillig 132 1.78 rillig if ((file = fopen(fname, "r")) == NULL) { 133 1.88 rillig (void)fprintf(stderr, "indent: cannot open file %s\n", fname); 134 1.78 rillig exit(1); 135 1.78 rillig } 136 1.78 rillig while ((fgets(line, sizeof(line), file)) != NULL) { 137 1.78 rillig /* Only keep the first word of the line. */ 138 1.78 rillig line[strcspn(line, " \t\n\r")] = '\0'; 139 1.78 rillig register_typename(line); 140 1.78 rillig } 141 1.78 rillig (void)fclose(file); 142 1.53 rillig } 143 1.53 rillig 144 1.32 rillig static bool 145 1.49 rillig set_special_option(const char *arg, const char *option_source) 146 1.32 rillig { 147 1.78 rillig const char *arg_end; 148 1.78 rillig 149 1.78 rillig if (strcmp(arg, "-version") == 0) { 150 1.78 rillig printf("NetBSD indent 2.1\n"); 151 1.78 rillig exit(0); 152 1.78 rillig } 153 1.78 rillig 154 1.78 rillig if (arg[0] == 'P' || strcmp(arg, "npro") == 0) 155 1.80 rillig return true; /* see load_profiles */ 156 1.78 rillig 157 1.78 rillig if (strncmp(arg, "cli", 3) == 0) { 158 1.78 rillig arg_end = arg + 3; 159 1.78 rillig if (arg_end[0] == '\0') 160 1.78 rillig goto need_arg; 161 1.78 rillig char *end; 162 1.78 rillig opt.case_indent = (float)strtod(arg_end, &end); 163 1.78 rillig if (*end != '\0') 164 1.79 rillig errx(1, "%s: argument \"%s\" to option \"-%.*s\" " 165 1.79 rillig "must be numeric", 166 1.78 rillig option_source, arg_end, (int)(arg_end - arg), arg); 167 1.78 rillig return true; 168 1.78 rillig } 169 1.78 rillig 170 1.78 rillig if (strcmp(arg, "st") == 0) { 171 1.86 rillig if (in.f == NULL) 172 1.86 rillig in.f = stdin; 173 1.78 rillig if (output == NULL) 174 1.78 rillig output = stdout; 175 1.78 rillig return true; 176 1.78 rillig } 177 1.32 rillig 178 1.78 rillig if (arg[0] == 'T') { 179 1.78 rillig arg_end = arg + 1; 180 1.78 rillig if (arg_end[0] == '\0') 181 1.78 rillig goto need_arg; 182 1.78 rillig register_typename(arg_end); 183 1.78 rillig return true; 184 1.78 rillig } 185 1.78 rillig 186 1.78 rillig if (arg[0] == 'U') { 187 1.78 rillig arg_end = arg + 1; 188 1.78 rillig if (arg_end[0] == '\0') 189 1.78 rillig goto need_arg; 190 1.78 rillig add_typedefs_from_file(arg_end); 191 1.78 rillig return true; 192 1.78 rillig } 193 1.32 rillig 194 1.78 rillig return false; 195 1.32 rillig 196 1.72 rillig need_arg: 197 1.78 rillig errx(1, "%s: option \"-%.*s\" requires an argument", 198 1.78 rillig option_source, (int)(arg_end - arg), arg); 199 1.78 rillig /* NOTREACHED */ 200 1.32 rillig } 201 1.32 rillig 202 1.62 rillig static const char * 203 1.62 rillig skip_over(const char *s, bool may_negate, const char *prefix) 204 1.62 rillig { 205 1.78 rillig if (may_negate && s[0] == 'n') 206 1.78 rillig s++; 207 1.78 rillig while (*prefix != '\0') { 208 1.78 rillig if (*prefix++ != *s++) 209 1.78 rillig return NULL; 210 1.78 rillig } 211 1.78 rillig return s; 212 1.62 rillig } 213 1.62 rillig 214 1.5 lukem void 215 1.49 rillig set_option(const char *arg, const char *option_source) 216 1.1 cgd { 217 1.78 rillig const struct pro *p; 218 1.78 rillig const char *arg_arg; 219 1.1 cgd 220 1.78 rillig arg++; /* skip leading '-' */ 221 1.78 rillig if (set_special_option(arg, option_source)) 222 1.78 rillig return; 223 1.78 rillig 224 1.79 rillig for (p = pro + array_length(pro); p-- != pro;) { 225 1.79 rillig arg_arg = skip_over(arg, p->p_may_negate, p->p_name); 226 1.79 rillig if (arg_arg != NULL) 227 1.78 rillig goto found; 228 1.79 rillig } 229 1.78 rillig errx(1, "%s: unknown option \"-%s\"", option_source, arg); 230 1.64 rillig found: 231 1.30 rillig 232 1.78 rillig if (p->p_is_bool) { 233 1.78 rillig if (arg_arg[0] != '\0') 234 1.79 rillig errx(1, "%s: unknown option \"-%s\"", 235 1.79 rillig option_source, arg); 236 1.78 rillig 237 1.84 rillig *(bool *)((unsigned char *)(void *)&opt + p->opt_offset) = 238 1.79 rillig p->p_may_negate ? arg[0] != 'n' : p->p_bool_value; 239 1.78 rillig return; 240 1.78 rillig } 241 1.78 rillig 242 1.78 rillig char *end; 243 1.78 rillig long num = strtol(arg_arg, &end, 10); 244 1.78 rillig if (*end != '\0') 245 1.79 rillig errx(1, "%s: argument \"%s\" to option \"-%s\" " 246 1.79 rillig "must be an integer", 247 1.78 rillig option_source, arg_arg, p->p_name); 248 1.78 rillig 249 1.78 rillig if (!(ch_isdigit(*arg_arg) && p->i_min <= num && num <= p->i_max)) 250 1.78 rillig errx(1, 251 1.79 rillig "%s: argument \"%s\" to option \"-%s\" " 252 1.79 rillig "must be between %d and %d", 253 1.78 rillig option_source, arg_arg, p->p_name, p->i_min, p->i_max); 254 1.64 rillig 255 1.84 rillig *(int *)((unsigned char *)(void *)&opt + p->opt_offset) = (int)num; 256 1.14 kamil } 257 1.62 rillig 258 1.62 rillig static void 259 1.62 rillig load_profile(const char *fname, bool must_exist) 260 1.62 rillig { 261 1.78 rillig FILE *f; 262 1.62 rillig 263 1.78 rillig if ((f = fopen(fname, "r")) == NULL) { 264 1.78 rillig if (must_exist) 265 1.78 rillig err(EXIT_FAILURE, "profile %s", fname); 266 1.78 rillig return; 267 1.78 rillig } 268 1.78 rillig 269 1.78 rillig for (;;) { 270 1.78 rillig char buf[BUFSIZ]; 271 1.78 rillig size_t n = 0; 272 1.78 rillig int ch, comment_ch = -1; 273 1.78 rillig 274 1.78 rillig while ((ch = getc(f)) != EOF) { 275 1.79 rillig if (ch == '*' && comment_ch == -1 276 1.79 rillig && n > 0 && buf[n - 1] == '/') { 277 1.78 rillig n--; 278 1.78 rillig comment_ch = '*'; 279 1.78 rillig } else if (comment_ch != -1) { 280 1.79 rillig comment_ch = ch == '/' && comment_ch == '*' 281 1.79 rillig ? -1 : ch; 282 1.78 rillig } else if (ch_isspace((char)ch)) { 283 1.78 rillig break; 284 1.78 rillig } else if (n >= array_length(buf) - 2) { 285 1.79 rillig errx(1, "buffer overflow in %s, " 286 1.79 rillig "starting with '%.10s'", 287 1.78 rillig fname, buf); 288 1.78 rillig } else 289 1.78 rillig buf[n++] = (char)ch; 290 1.78 rillig } 291 1.78 rillig 292 1.78 rillig if (n > 0) { 293 1.78 rillig buf[n] = '\0'; 294 1.78 rillig if (opt.verbose) 295 1.88 rillig (void)fprintf(stderr, "profile: %s\n", buf); 296 1.87 rillig if (buf[0] != '-') 297 1.87 rillig errx(1, 298 1.87 rillig "%s: option \"%s\" must start with '-'", 299 1.87 rillig fname, buf); 300 1.78 rillig set_option(buf, fname); 301 1.87 rillig } 302 1.87 rillig if (ch == EOF) 303 1.78 rillig break; 304 1.78 rillig } 305 1.78 rillig (void)fclose(f); 306 1.62 rillig } 307 1.62 rillig 308 1.62 rillig void 309 1.80 rillig load_profile_files(const char *path) 310 1.62 rillig { 311 1.80 rillig if (path != NULL) 312 1.80 rillig load_profile(path, true); 313 1.78 rillig else { 314 1.78 rillig const char *home = getenv("HOME"); 315 1.78 rillig if (home != NULL) { 316 1.80 rillig char fname[PATH_MAX]; 317 1.78 rillig snprintf(fname, sizeof(fname), "%s/.indent.pro", home); 318 1.78 rillig load_profile(fname, false); 319 1.78 rillig } 320 1.74 rillig } 321 1.78 rillig load_profile(".indent.pro", false); 322 1.62 rillig } 323