1274116Sdteske/*-
2293619Sdteske * Copyright (c) 2013-2015 Devin Teske <dteske@FreeBSD.org>
3274116Sdteske * All rights reserved.
4274116Sdteske *
5274116Sdteske * Redistribution and use in source and binary forms, with or without
6274116Sdteske * modification, are permitted provided that the following conditions
7274116Sdteske * are met:
8274116Sdteske * 1. Redistributions of source code must retain the above copyright
9274116Sdteske *    notice, this list of conditions and the following disclaimer.
10274116Sdteske * 2. Redistributions in binary form must reproduce the above copyright
11274116Sdteske *    notice, this list of conditions and the following disclaimer in the
12274116Sdteske *    documentation and/or other materials provided with the distribution.
13274116Sdteske *
14274116Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15274116Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16274116Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17274116Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18274116Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19274116Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20274116Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21274116Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22274116Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23274116Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24274116Sdteske * SUCH DAMAGE.
25274116Sdteske */
26274116Sdteske
27274116Sdteske#include <sys/cdefs.h>
28274116Sdteske__FBSDID("$FreeBSD: releng/10.3/lib/libdpv/dialogrc.c 293619 2016-01-09 23:33:44Z dteske $");
29274116Sdteske
30274116Sdteske#include <sys/types.h>
31274116Sdteske
32274116Sdteske#include <err.h>
33274116Sdteske#include <errno.h>
34274116Sdteske#include <figpar.h>
35274116Sdteske#include <limits.h>
36274116Sdteske#include <stdio.h>
37274116Sdteske#include <stdlib.h>
38274116Sdteske#include <string.h>
39274116Sdteske#include <string_m.h>
40274116Sdteske
41274116Sdteske#include "dialogrc.h"
42274116Sdteske
43274116Sdteske#define STR_BUFSIZE 255
44274116Sdteske
45274116Sdteske/* dialog(1) `.dialogrc' characteristics */
46274116Sdteskeuint8_t use_colors = 1;
47274116Sdteskeuint8_t use_shadow = 1;
48274116Sdteskechar gauge_color[STR_BUFSIZE]	= "47b"; /* (BLUE,WHITE,ON) */
49274116Sdteskechar separator[STR_BUFSIZE]	= "";
50274116Sdteske
51274116Sdteske/* Function prototypes */
52293619Sdteskestatic int setattr(struct figpar_config *, uint32_t, char *, char *);
53293619Sdteskestatic int setbool(struct figpar_config *, uint32_t, char *, char *);
54293619Sdteskestatic int setnum(struct figpar_config *, uint32_t, char *, char *);
55293619Sdteskestatic int setstr(struct figpar_config *, uint32_t, char *, char *);
56274116Sdteske
57274116Sdteske/*
58274116Sdteske * Anatomy of DIALOGRC (~/.dialogrc by default)
59274116Sdteske * NOTE: Must appear after private function prototypes (above)
60274116Sdteske * NB: Brace-initialization of union requires cast to *first* member of union
61274116Sdteske */
62293619Sdteskestatic struct figpar_config dialogrc_config[] = {
63293619Sdteske    /* TYPE            DIRECTIVE                      DEFAULT        HANDLER */
64293619Sdteske    {FIGPAR_TYPE_INT,  "aspect",                      {(void *)0},   &setnum},
65293619Sdteske    {FIGPAR_TYPE_STR,  "separate_widget",             {separator},   &setstr},
66293619Sdteske    {FIGPAR_TYPE_INT,  "tab_len",                     {(void *)0},   &setnum},
67293619Sdteske    {FIGPAR_TYPE_BOOL, "visit_items",                 {(void *)0},   &setbool},
68293619Sdteske    {FIGPAR_TYPE_BOOL, "use_shadow",                  {(void *)1},   &setbool},
69293619Sdteske    {FIGPAR_TYPE_BOOL, "use_colors",                  {(void *)1},   &setbool},
70293619Sdteske    {FIGPAR_TYPE_STR,  "screen_color",                {NULL},        &setattr},
71293619Sdteske    {FIGPAR_TYPE_STR,  "shadow_color",                {NULL},        &setattr},
72293619Sdteske    {FIGPAR_TYPE_STR,  "dialog_color",                {NULL},        &setattr},
73293619Sdteske    {FIGPAR_TYPE_STR,  "title_color",                 {NULL},        &setattr},
74293619Sdteske    {FIGPAR_TYPE_STR,  "border_color",                {NULL},        &setattr},
75293619Sdteske    {FIGPAR_TYPE_STR,  "button_active_color",         {NULL},        &setattr},
76293619Sdteske    {FIGPAR_TYPE_STR,  "button_inactive_color",       {NULL},        &setattr},
77293619Sdteske    {FIGPAR_TYPE_STR,  "button_key_active_color",     {NULL},        &setattr},
78293619Sdteske    {FIGPAR_TYPE_STR,  "button_key_inactive_color",   {NULL},        &setattr},
79293619Sdteske    {FIGPAR_TYPE_STR,  "button_label_active_color",   {NULL},        &setattr},
80293619Sdteske    {FIGPAR_TYPE_STR,  "button_label_inactive_color", {NULL},        &setattr},
81293619Sdteske    {FIGPAR_TYPE_STR,  "inputbox_color",              {NULL},        &setattr},
82293619Sdteske    {FIGPAR_TYPE_STR,  "inputbox_border_color",       {NULL},        &setattr},
83293619Sdteske    {FIGPAR_TYPE_STR,  "searchbox_color",             {NULL},        &setattr},
84293619Sdteske    {FIGPAR_TYPE_STR,  "searchbox_title_color",       {NULL},        &setattr},
85293619Sdteske    {FIGPAR_TYPE_STR,  "searchbox_border_color",      {NULL},        &setattr},
86293619Sdteske    {FIGPAR_TYPE_STR,  "position_indicator_color",    {NULL},        &setattr},
87293619Sdteske    {FIGPAR_TYPE_STR,  "menubox_color",               {NULL},        &setattr},
88293619Sdteske    {FIGPAR_TYPE_STR,  "menubox_border_color",        {NULL},        &setattr},
89293619Sdteske    {FIGPAR_TYPE_STR,  "item_color",                  {NULL},        &setattr},
90293619Sdteske    {FIGPAR_TYPE_STR,  "item_selected_color",         {NULL},        &setattr},
91293619Sdteske    {FIGPAR_TYPE_STR,  "tag_color",                   {NULL},        &setattr},
92293619Sdteske    {FIGPAR_TYPE_STR,  "tag_selected_color",          {NULL},        &setattr},
93293619Sdteske    {FIGPAR_TYPE_STR,  "tag_key_color",               {NULL},        &setattr},
94293619Sdteske    {FIGPAR_TYPE_STR,  "tag_key_selected_color",      {NULL},        &setattr},
95293619Sdteske    {FIGPAR_TYPE_STR,  "check_color",                 {NULL},        &setattr},
96293619Sdteske    {FIGPAR_TYPE_STR,  "check_selected_color",        {NULL},        &setattr},
97293619Sdteske    {FIGPAR_TYPE_STR,  "uarrow_color",                {NULL},        &setattr},
98293619Sdteske    {FIGPAR_TYPE_STR,  "darrow_color",                {NULL},        &setattr},
99293619Sdteske    {FIGPAR_TYPE_STR,  "itemhelp_color",              {NULL},        &setattr},
100293619Sdteske    {FIGPAR_TYPE_STR,  "form_active_text_color",      {NULL},        &setattr},
101293619Sdteske    {FIGPAR_TYPE_STR,  "form_text_color",             {NULL},        &setattr},
102293619Sdteske    {FIGPAR_TYPE_STR,  "form_item_readonly_color",    {NULL},        &setattr},
103293619Sdteske    {FIGPAR_TYPE_STR,  "gauge_color",                 {gauge_color}, &setattr},
104274116Sdteske    {0, NULL, {0}, NULL}
105274116Sdteske};
106274116Sdteske
107274116Sdteske/*
108274116Sdteske * figpar call-back for interpreting value as .dialogrc `Attribute'
109274116Sdteske */
110274116Sdteskestatic int
111293619Sdteskesetattr(struct figpar_config *option, uint32_t line __unused,
112274116Sdteske    char *directive __unused, char *value)
113274116Sdteske{
114274116Sdteske	char *cp = value;
115274116Sdteske	char *val;
116274116Sdteske	size_t len;
117274116Sdteske	char attrbuf[4];
118274116Sdteske
119274116Sdteske	if (option == NULL) {
120274116Sdteske		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
121274116Sdteske		    __LINE__, __func__);
122274116Sdteske		return (-1); /* Abort processing */
123274116Sdteske	}
124274116Sdteske
125274116Sdteske	/* Allocate memory for the data if not already done */
126274116Sdteske	if (option->value.str == NULL) {
127274116Sdteske		if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
128274116Sdteske			return (-1);
129274116Sdteske	}
130274116Sdteske
131274116Sdteske	/*
132274116Sdteske	 * If the first character is left-parenthesis, the format is
133274116Sdteske	 * `(background,foreground,highlight)' otherwise, we should take it
134274116Sdteske	 * as a reference to another color.
135274116Sdteske	 */
136274116Sdteske	if (*cp != '(') {
137274116Sdteske		/* Copy the [current] value from the referenced color */
138274116Sdteske		val = dialogrc_config_option(cp)->value.str;
139274116Sdteske		if (val != NULL)
140274116Sdteske			snprintf(option->value.str, STR_BUFSIZE, "%s", val);
141274116Sdteske
142274116Sdteske		return (0);
143274116Sdteske	} else
144274116Sdteske		cp++;
145274116Sdteske
146274116Sdteske	strtolower(cp);
147274116Sdteske
148274116Sdteske	/* Initialize the attrbuf (fg,bg,hi,NUL) */
149274116Sdteske	attrbuf[0] = '0';
150274116Sdteske	attrbuf[1] = '0';
151274116Sdteske	attrbuf[2] = 'B'; /* \ZB = disable; \Zb = enable (see dialog(1)) */
152274116Sdteske	attrbuf[3] = '\0';
153274116Sdteske
154274116Sdteske	/* Interpret the foreground color */
155274116Sdteske	if      (strncmp(cp, "red,",     4) == 0) attrbuf[0] = '1';
156274116Sdteske	else if (strncmp(cp, "green,",   6) == 0) attrbuf[0] = '2';
157274116Sdteske	else if (strncmp(cp, "yellow,",  7) == 0) attrbuf[0] = '3';
158274116Sdteske	else if (strncmp(cp, "blue,",    5) == 0) attrbuf[0] = '4';
159274116Sdteske	else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[0] = '5';
160274116Sdteske	else if (strncmp(cp, "cyan,",    5) == 0) attrbuf[0] = '6';
161274116Sdteske	else if (strncmp(cp, "white,",   6) == 0) attrbuf[0] = '7';
162274116Sdteske	else if (strncmp(cp, "black,",   6) == 0) attrbuf[0] = '8';
163274116Sdteske
164274116Sdteske	/* Advance to the background color */
165274116Sdteske	cp = strchr(cp, ',');
166274116Sdteske	if (cp == NULL)
167274116Sdteske		goto write_attrbuf;
168274116Sdteske	else
169274116Sdteske		cp++;
170274116Sdteske
171274116Sdteske	/* Interpret the background color */
172274116Sdteske	if      (strncmp(cp, "red,",     4) == 0) attrbuf[1] = '1';
173274116Sdteske	else if (strncmp(cp, "green,",   6) == 0) attrbuf[1] = '2';
174274116Sdteske	else if (strncmp(cp, "yellow,",  7) == 0) attrbuf[1] = '3';
175274116Sdteske	else if (strncmp(cp, "blue,",    5) == 0) attrbuf[1] = '4';
176274116Sdteske	else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[1] = '5';
177274116Sdteske	else if (strncmp(cp, "cyan,",    5) == 0) attrbuf[1] = '6';
178274116Sdteske	else if (strncmp(cp, "white,",   6) == 0) attrbuf[1] = '7';
179274116Sdteske	else if (strncmp(cp, "black,",   6) == 0) attrbuf[1] = '8';
180274116Sdteske
181274116Sdteske	/* Advance to the highlight */
182274116Sdteske	cp = strchr(cp, ',');
183274116Sdteske	if (cp == NULL)
184274116Sdteske		goto write_attrbuf;
185274116Sdteske	else
186274116Sdteske		cp++;
187274116Sdteske
188274116Sdteske	/* Trim trailing parenthesis */
189274116Sdteske	len = strlen(cp);
190274116Sdteske	if (cp[len - 1] == ')')
191274116Sdteske		cp[len - 1] = '\0';
192274116Sdteske
193274116Sdteske	/* Interpret the highlight (initialized to off above) */
194274116Sdteske	if (strcmp(cp, "on") == 0 || strncmp(cp, "on,", 3) == 0)
195274116Sdteske		attrbuf[2] = 'b'; /* \Zb = enable bold (see dialog(1)) */
196274116Sdteske
197274116Sdteskewrite_attrbuf:
198274116Sdteske	sprintf(option->value.str, "%s", attrbuf);
199274116Sdteske
200274116Sdteske	return (0);
201274116Sdteske}
202274116Sdteske
203274116Sdteske/*
204274116Sdteske * figpar call-back for interpreting value as .dialogrc `Boolean'
205274116Sdteske */
206274116Sdteskestatic int
207293619Sdteskesetbool(struct figpar_config *option, uint32_t line __unused,
208274116Sdteske    char *directive __unused, char *value)
209274116Sdteske{
210274116Sdteske
211274116Sdteske	if (option == NULL) {
212274116Sdteske		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
213274116Sdteske		    __LINE__, __func__);
214274116Sdteske		return (-1); /* Abort processing */
215274116Sdteske	}
216274116Sdteske
217274116Sdteske	/* Assume ON, check for OFF (case-insensitive) */
218274116Sdteske	option->value.boolean = 1;
219274116Sdteske	strtolower(value);
220274116Sdteske	if (strcmp(value, "off") == 0)
221274116Sdteske		option->value.boolean = 0;
222274116Sdteske
223274116Sdteske	return (0);
224274116Sdteske}
225274116Sdteske
226274116Sdteske/*
227274116Sdteske * figpar call-back for interpreting value as .dialogrc `Number'
228274116Sdteske */
229274116Sdteskestatic int
230293619Sdteskesetnum(struct figpar_config *option, uint32_t line __unused,
231275040Sdteske    char *directive __unused, char *value)
232274116Sdteske{
233274116Sdteske
234274116Sdteske	if (option == NULL) {
235274116Sdteske		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
236274116Sdteske		    __LINE__, __func__);
237274116Sdteske		return (-1); /* Abort processing */
238274116Sdteske	}
239274116Sdteske
240274116Sdteske	/* Convert the string to a 32-bit signed integer */
241274116Sdteske	option->value.num = (int32_t)strtol(value, (char **)NULL, 10);
242274116Sdteske
243274116Sdteske	return (0);
244274116Sdteske}
245274116Sdteske
246274116Sdteske/*
247274116Sdteske * figpar call-back for interpreting value as .dialogrc `String'
248274116Sdteske */
249274116Sdteskestatic int
250293619Sdteskesetstr(struct figpar_config *option, uint32_t line __unused,
251275040Sdteske    char *directive __unused, char *value)
252274116Sdteske{
253274116Sdteske	size_t len;
254274116Sdteske
255274116Sdteske	if (option == NULL) {
256274116Sdteske		warnx("%s:%d:%s: Missing callback parameter", __FILE__,
257274116Sdteske		    __LINE__, __func__);
258274116Sdteske		return (-1); /* Abort processing */
259274116Sdteske	}
260274116Sdteske
261274116Sdteske	/* Allocate memory for the data if not already done */
262274116Sdteske	if (option->value.str == NULL) {
263274116Sdteske		if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
264274116Sdteske			return (-1);
265274116Sdteske	}
266274116Sdteske
267274116Sdteske	/* Trim leading quote */
268274116Sdteske	if (*value == '"')
269274116Sdteske		value++;
270274116Sdteske
271274116Sdteske	/* Write the data into the buffer */
272274116Sdteske	snprintf(option->value.str, STR_BUFSIZE, "%s", value);
273274116Sdteske
274274116Sdteske	/* Trim trailing quote */
275274116Sdteske	len = strlen(option->value.str);
276274116Sdteske	if (option->value.str[len - 1] == '"')
277274116Sdteske		option->value.str[len - 1] = '\0';
278274116Sdteske
279274116Sdteske	return (0);
280274116Sdteske}
281274116Sdteske
282274116Sdteske/*
283274116Sdteske * Parse (in order of preference) $DIALOGRC or `$HOME/.dialogrc'. Returns zero
284274116Sdteske * on success, -1 on failure (and errno should be consulted).
285274116Sdteske */
286274116Sdteskeint
287274116Sdteskeparse_dialogrc(void)
288274116Sdteske{
289274116Sdteske	char *cp;
290274116Sdteske	int res;
291274116Sdteske	size_t len;
292274116Sdteske	char path[PATH_MAX];
293274116Sdteske
294274116Sdteske	/* Allow $DIALOGRC to override `$HOME/.dialogrc' default */
295274116Sdteske	if ((cp = getenv(ENV_DIALOGRC)) != NULL && *cp != '\0')
296274116Sdteske		snprintf(path, PATH_MAX, "%s", cp);
297274116Sdteske	else if ((cp = getenv(ENV_HOME)) != NULL) {
298274116Sdteske		/* Copy $HOME into buffer and append trailing `/' if missing */
299274116Sdteske		snprintf(path, PATH_MAX, "%s", cp);
300274116Sdteske		len = strlen(path);
301274116Sdteske		cp = path + len;
302274116Sdteske		if (len > 0 && len < (PATH_MAX - 1) && *(cp - 1) != '/') {
303274116Sdteske			*cp++ = '/';
304274116Sdteske			*cp = '\0';
305274116Sdteske			len++;
306274116Sdteske		}
307274116Sdteske
308274116Sdteske		/* If we still have room, shove in the name of rc file */
309274116Sdteske		if (len < (PATH_MAX - 1))
310274116Sdteske			snprintf(cp, PATH_MAX - len, "%s", DIALOGRC);
311274116Sdteske	} else {
312274116Sdteske		/* Like dialog(1), don't process a file if $HOME is unset */
313274116Sdteske		errno = ENOENT;
314274116Sdteske		return (-1);
315274116Sdteske	}
316274116Sdteske
317274116Sdteske	/* Process file (either $DIALOGRC if set, or `$HOME/.dialogrc') */
318293619Sdteske	res = parse_config(dialogrc_config,
319293619Sdteske		path, NULL, FIGPAR_BREAK_ON_EQUALS);
320274116Sdteske
321274116Sdteske	/* Set some globals based on what we parsed */
322274116Sdteske	use_shadow = dialogrc_config_option("use_shadow")->value.boolean;
323274116Sdteske	use_colors = dialogrc_config_option("use_colors")->value.boolean;
324274116Sdteske	snprintf(gauge_color, STR_BUFSIZE, "%s",
325274116Sdteske	    dialogrc_config_option("gauge_color")->value.str);
326274116Sdteske
327274116Sdteske	return (res);
328274116Sdteske}
329274116Sdteske
330274116Sdteske/*
331274116Sdteske * Return a pointer to the `.dialogrc' config option specific to `directive' or
332293619Sdteske * static figpar_dummy_config (full of NULLs) if none found (see
333275040Sdteske * get_config_option(3); part of figpar(3)).
334274116Sdteske */
335293619Sdteskestruct figpar_config *
336274116Sdteskedialogrc_config_option(const char *directive)
337274116Sdteske{
338274116Sdteske	return (get_config_option(dialogrc_config, directive));
339274116Sdteske}
340274116Sdteske
341274116Sdteske/*
342274116Sdteske * Free allocated items initialized by setattr() (via parse_config() callback
343274116Sdteske * matrix [dialogrc_config] used in parse_dialogrc() above).
344274116Sdteske */
345274116Sdteskevoid
346274116Sdteskedialogrc_free(void)
347274116Sdteske{
348274116Sdteske	char *value;
349274116Sdteske	uint32_t n;
350274116Sdteske
351274116Sdteske	for (n = 0; dialogrc_config[n].directive != NULL; n++) {
352274116Sdteske		if (dialogrc_config[n].action != &setattr)
353274116Sdteske			continue;
354274116Sdteske		value = dialogrc_config[n].value.str;
355274116Sdteske		if (value != NULL && value != gauge_color) {
356274116Sdteske			free(dialogrc_config[n].value.str);
357274116Sdteske			dialogrc_config[n].value.str = NULL;
358274116Sdteske		}
359274116Sdteske	}
360274116Sdteske}
361