xref: /freebsd/lib/libdpv/dialogrc.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * Copyright (c) 2013-2015 Devin Teske <dteske@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 #include <sys/types.h>
29 
30 #include <err.h>
31 #include <errno.h>
32 #include <figpar.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <string_m.h>
38 
39 #include "dialogrc.h"
40 
41 #define STR_BUFSIZE 255
42 
43 /* dialog(1) `.dialogrc' characteristics */
44 uint8_t use_colors = 1;
45 uint8_t use_shadow = 1;
46 char gauge_color[STR_BUFSIZE]	= "47b"; /* (BLUE,WHITE,ON) */
47 char separator[STR_BUFSIZE]	= "";
48 
49 /* Function prototypes */
50 static int setattr(struct figpar_config *, uint32_t, char *, char *);
51 static int setbool(struct figpar_config *, uint32_t, char *, char *);
52 static int setnum(struct figpar_config *, uint32_t, char *, char *);
53 static int setstr(struct figpar_config *, uint32_t, char *, char *);
54 
55 /*
56  * Anatomy of DIALOGRC (~/.dialogrc by default)
57  * NOTE: Must appear after private function prototypes (above)
58  * NB: Brace-initialization of union requires cast to *first* member of union
59  */
60 static struct figpar_config dialogrc_config[] = {
61     /* TYPE            DIRECTIVE                      DEFAULT        HANDLER */
62     {FIGPAR_TYPE_INT,  "aspect",                      {(void *)0},   &setnum},
63     {FIGPAR_TYPE_STR,  "separate_widget",             {separator},   &setstr},
64     {FIGPAR_TYPE_INT,  "tab_len",                     {(void *)0},   &setnum},
65     {FIGPAR_TYPE_BOOL, "visit_items",                 {(void *)0},   &setbool},
66     {FIGPAR_TYPE_BOOL, "use_shadow",                  {(void *)1},   &setbool},
67     {FIGPAR_TYPE_BOOL, "use_colors",                  {(void *)1},   &setbool},
68     {FIGPAR_TYPE_STR,  "screen_color",                {NULL},        &setattr},
69     {FIGPAR_TYPE_STR,  "shadow_color",                {NULL},        &setattr},
70     {FIGPAR_TYPE_STR,  "dialog_color",                {NULL},        &setattr},
71     {FIGPAR_TYPE_STR,  "title_color",                 {NULL},        &setattr},
72     {FIGPAR_TYPE_STR,  "border_color",                {NULL},        &setattr},
73     {FIGPAR_TYPE_STR,  "button_active_color",         {NULL},        &setattr},
74     {FIGPAR_TYPE_STR,  "button_inactive_color",       {NULL},        &setattr},
75     {FIGPAR_TYPE_STR,  "button_key_active_color",     {NULL},        &setattr},
76     {FIGPAR_TYPE_STR,  "button_key_inactive_color",   {NULL},        &setattr},
77     {FIGPAR_TYPE_STR,  "button_label_active_color",   {NULL},        &setattr},
78     {FIGPAR_TYPE_STR,  "button_label_inactive_color", {NULL},        &setattr},
79     {FIGPAR_TYPE_STR,  "inputbox_color",              {NULL},        &setattr},
80     {FIGPAR_TYPE_STR,  "inputbox_border_color",       {NULL},        &setattr},
81     {FIGPAR_TYPE_STR,  "searchbox_color",             {NULL},        &setattr},
82     {FIGPAR_TYPE_STR,  "searchbox_title_color",       {NULL},        &setattr},
83     {FIGPAR_TYPE_STR,  "searchbox_border_color",      {NULL},        &setattr},
84     {FIGPAR_TYPE_STR,  "position_indicator_color",    {NULL},        &setattr},
85     {FIGPAR_TYPE_STR,  "menubox_color",               {NULL},        &setattr},
86     {FIGPAR_TYPE_STR,  "menubox_border_color",        {NULL},        &setattr},
87     {FIGPAR_TYPE_STR,  "item_color",                  {NULL},        &setattr},
88     {FIGPAR_TYPE_STR,  "item_selected_color",         {NULL},        &setattr},
89     {FIGPAR_TYPE_STR,  "tag_color",                   {NULL},        &setattr},
90     {FIGPAR_TYPE_STR,  "tag_selected_color",          {NULL},        &setattr},
91     {FIGPAR_TYPE_STR,  "tag_key_color",               {NULL},        &setattr},
92     {FIGPAR_TYPE_STR,  "tag_key_selected_color",      {NULL},        &setattr},
93     {FIGPAR_TYPE_STR,  "check_color",                 {NULL},        &setattr},
94     {FIGPAR_TYPE_STR,  "check_selected_color",        {NULL},        &setattr},
95     {FIGPAR_TYPE_STR,  "uarrow_color",                {NULL},        &setattr},
96     {FIGPAR_TYPE_STR,  "darrow_color",                {NULL},        &setattr},
97     {FIGPAR_TYPE_STR,  "itemhelp_color",              {NULL},        &setattr},
98     {FIGPAR_TYPE_STR,  "form_active_text_color",      {NULL},        &setattr},
99     {FIGPAR_TYPE_STR,  "form_text_color",             {NULL},        &setattr},
100     {FIGPAR_TYPE_STR,  "form_item_readonly_color",    {NULL},        &setattr},
101     {FIGPAR_TYPE_STR,  "gauge_color",                 {gauge_color}, &setattr},
102     {0, NULL, {0}, NULL}
103 };
104 
105 /*
106  * figpar call-back for interpreting value as .dialogrc `Attribute'
107  */
108 static int
109 setattr(struct figpar_config *option, uint32_t line __unused,
110     char *directive __unused, char *value)
111 {
112 	char *cp = value;
113 	char *val;
114 	size_t len;
115 	char attrbuf[4];
116 
117 	if (option == NULL) {
118 		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
119 		    __LINE__, __func__);
120 		return (-1); /* Abort processing */
121 	}
122 
123 	/* Allocate memory for the data if not already done */
124 	if (option->value.str == NULL) {
125 		if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
126 			return (-1);
127 	}
128 
129 	/*
130 	 * If the first character is left-parenthesis, the format is
131 	 * `(background,foreground,highlight)' otherwise, we should take it
132 	 * as a reference to another color.
133 	 */
134 	if (*cp != '(') {
135 		/* Copy the [current] value from the referenced color */
136 		val = dialogrc_config_option(cp)->value.str;
137 		if (val != NULL)
138 			snprintf(option->value.str, STR_BUFSIZE, "%s", val);
139 
140 		return (0);
141 	} else
142 		cp++;
143 
144 	strtolower(cp);
145 
146 	/* Initialize the attrbuf (fg,bg,hi,NUL) */
147 	attrbuf[0] = '0';
148 	attrbuf[1] = '0';
149 	attrbuf[2] = 'B'; /* \ZB = disable; \Zb = enable (see dialog(1)) */
150 	attrbuf[3] = '\0';
151 
152 	/* Interpret the foreground color */
153 	if      (strncmp(cp, "red,",     4) == 0) attrbuf[0] = '1';
154 	else if (strncmp(cp, "green,",   6) == 0) attrbuf[0] = '2';
155 	else if (strncmp(cp, "yellow,",  7) == 0) attrbuf[0] = '3';
156 	else if (strncmp(cp, "blue,",    5) == 0) attrbuf[0] = '4';
157 	else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[0] = '5';
158 	else if (strncmp(cp, "cyan,",    5) == 0) attrbuf[0] = '6';
159 	else if (strncmp(cp, "white,",   6) == 0) attrbuf[0] = '7';
160 	else if (strncmp(cp, "black,",   6) == 0) attrbuf[0] = '8';
161 
162 	/* Advance to the background color */
163 	cp = strchr(cp, ',');
164 	if (cp == NULL)
165 		goto write_attrbuf;
166 	else
167 		cp++;
168 
169 	/* Interpret the background color */
170 	if      (strncmp(cp, "red,",     4) == 0) attrbuf[1] = '1';
171 	else if (strncmp(cp, "green,",   6) == 0) attrbuf[1] = '2';
172 	else if (strncmp(cp, "yellow,",  7) == 0) attrbuf[1] = '3';
173 	else if (strncmp(cp, "blue,",    5) == 0) attrbuf[1] = '4';
174 	else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[1] = '5';
175 	else if (strncmp(cp, "cyan,",    5) == 0) attrbuf[1] = '6';
176 	else if (strncmp(cp, "white,",   6) == 0) attrbuf[1] = '7';
177 	else if (strncmp(cp, "black,",   6) == 0) attrbuf[1] = '8';
178 
179 	/* Advance to the highlight */
180 	cp = strchr(cp, ',');
181 	if (cp == NULL)
182 		goto write_attrbuf;
183 	else
184 		cp++;
185 
186 	/* Trim trailing parenthesis */
187 	len = strlen(cp);
188 	if (cp[len - 1] == ')')
189 		cp[len - 1] = '\0';
190 
191 	/* Interpret the highlight (initialized to off above) */
192 	if (strcmp(cp, "on") == 0 || strncmp(cp, "on,", 3) == 0)
193 		attrbuf[2] = 'b'; /* \Zb = enable bold (see dialog(1)) */
194 
195 write_attrbuf:
196 	sprintf(option->value.str, "%s", attrbuf);
197 
198 	return (0);
199 }
200 
201 /*
202  * figpar call-back for interpreting value as .dialogrc `Boolean'
203  */
204 static int
205 setbool(struct figpar_config *option, uint32_t line __unused,
206     char *directive __unused, char *value)
207 {
208 
209 	if (option == NULL) {
210 		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
211 		    __LINE__, __func__);
212 		return (-1); /* Abort processing */
213 	}
214 
215 	/* Assume ON, check for OFF (case-insensitive) */
216 	option->value.boolean = 1;
217 	strtolower(value);
218 	if (strcmp(value, "off") == 0)
219 		option->value.boolean = 0;
220 
221 	return (0);
222 }
223 
224 /*
225  * figpar call-back for interpreting value as .dialogrc `Number'
226  */
227 static int
228 setnum(struct figpar_config *option, uint32_t line __unused,
229     char *directive __unused, char *value)
230 {
231 
232 	if (option == NULL) {
233 		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
234 		    __LINE__, __func__);
235 		return (-1); /* Abort processing */
236 	}
237 
238 	/* Convert the string to a 32-bit signed integer */
239 	option->value.num = (int32_t)strtol(value, (char **)NULL, 10);
240 
241 	return (0);
242 }
243 
244 /*
245  * figpar call-back for interpreting value as .dialogrc `String'
246  */
247 static int
248 setstr(struct figpar_config *option, uint32_t line __unused,
249     char *directive __unused, char *value)
250 {
251 	size_t len;
252 
253 	if (option == NULL) {
254 		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
255 		    __LINE__, __func__);
256 		return (-1); /* Abort processing */
257 	}
258 
259 	/* Allocate memory for the data if not already done */
260 	if (option->value.str == NULL) {
261 		if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
262 			return (-1);
263 	}
264 
265 	/* Trim leading quote */
266 	if (*value == '"')
267 		value++;
268 
269 	/* Write the data into the buffer */
270 	snprintf(option->value.str, STR_BUFSIZE, "%s", value);
271 
272 	/* Trim trailing quote */
273 	len = strlen(option->value.str);
274 	if (option->value.str[len - 1] == '"')
275 		option->value.str[len - 1] = '\0';
276 
277 	return (0);
278 }
279 
280 /*
281  * Parse (in order of preference) $DIALOGRC or `$HOME/.dialogrc'. Returns zero
282  * on success, -1 on failure (and errno should be consulted).
283  */
284 int
285 parse_dialogrc(void)
286 {
287 	char *cp;
288 	int res;
289 	size_t len;
290 	char path[PATH_MAX];
291 
292 	/* Allow $DIALOGRC to override `$HOME/.dialogrc' default */
293 	if ((cp = getenv(ENV_DIALOGRC)) != NULL && *cp != '\0')
294 		snprintf(path, PATH_MAX, "%s", cp);
295 	else if ((cp = getenv(ENV_HOME)) != NULL) {
296 		/* Copy $HOME into buffer and append trailing `/' if missing */
297 		snprintf(path, PATH_MAX, "%s", cp);
298 		len = strlen(path);
299 		cp = path + len;
300 		if (len > 0 && len < (PATH_MAX - 1) && *(cp - 1) != '/') {
301 			*cp++ = '/';
302 			*cp = '\0';
303 			len++;
304 		}
305 
306 		/* If we still have room, shove in the name of rc file */
307 		if (len < (PATH_MAX - 1))
308 			snprintf(cp, PATH_MAX - len, "%s", DIALOGRC);
309 	} else {
310 		/* Like dialog(1), don't process a file if $HOME is unset */
311 		errno = ENOENT;
312 		return (-1);
313 	}
314 
315 	/* Process file (either $DIALOGRC if set, or `$HOME/.dialogrc') */
316 	res = parse_config(dialogrc_config,
317 		path, NULL, FIGPAR_BREAK_ON_EQUALS);
318 
319 	/* Set some globals based on what we parsed */
320 	use_shadow = dialogrc_config_option("use_shadow")->value.boolean;
321 	use_colors = dialogrc_config_option("use_colors")->value.boolean;
322 	snprintf(gauge_color, STR_BUFSIZE, "%s",
323 	    dialogrc_config_option("gauge_color")->value.str);
324 
325 	return (res);
326 }
327 
328 /*
329  * Return a pointer to the `.dialogrc' config option specific to `directive' or
330  * static figpar_dummy_config (full of NULLs) if none found (see
331  * get_config_option(3); part of figpar(3)).
332  */
333 struct figpar_config *
334 dialogrc_config_option(const char *directive)
335 {
336 	return (get_config_option(dialogrc_config, directive));
337 }
338 
339 /*
340  * Free allocated items initialized by setattr() (via parse_config() callback
341  * matrix [dialogrc_config] used in parse_dialogrc() above).
342  */
343 void
344 dialogrc_free(void)
345 {
346 	char *value;
347 	uint32_t n;
348 
349 	for (n = 0; dialogrc_config[n].directive != NULL; n++) {
350 		if (dialogrc_config[n].action != &setattr)
351 			continue;
352 		value = dialogrc_config[n].value.str;
353 		if (value != NULL && value != gauge_color) {
354 			free(dialogrc_config[n].value.str);
355 			dialogrc_config[n].value.str = NULL;
356 		}
357 	}
358 }
359