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