1 /*
2  * Copyright (c) 2013,2015-2016 Ilya A. Arkhipov
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  * $FreeBSD$
27  */
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <dialog.h>
32 #include <dlg_keys.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stringlist.h>
37 #include <locale.h>
38 #include <unistd.h>
39 
40 #include "mixedlist.h"
41 
42 #define	D4PVERSION	"0.1.6"
43 
44 static int list_no = 0;
45 static int group = 0;
46 
47 /* The initial items size */
48 static int items_sz = 5;
49 /* Enables items */
50 static StringList *enable_items = NULL;
51 /* New items */
52 static StringList *new_items = NULL;
53 
54 /* add item to items */
55 static void
add_item(dialog_mixedlist ** items,char const * name,char const * text,bool state,bool new,int type,int grp)56 add_item(dialog_mixedlist **items, char const *name, char const *text,
57 		bool state, bool new, int type, int grp)
58 {
59 
60 	if ((list_no + 1 > items_sz) || *items == NULL) {
61 		items_sz *= 2;
62 		*items = realloc(*items, items_sz * sizeof(dialog_mixedlist));
63 		if (*items == NULL)
64 			err(EXIT_FAILURE, "Need more memory!");
65 	}
66 
67 	(*items)[list_no].name = strdup(name);
68 	(*items)[list_no].text = strdup(text);
69 	(*items)[list_no].state = state;
70 	(*items)[list_no].type = type;
71 	(*items)[list_no].group = grp;
72 	(*items)[list_no].new = new;
73 	list_no++;
74 }
75 
76 /* get description in env: %s_DESC */
77 static char const *
get_desc(char const * res,char const * fallback)78 get_desc(char const *res, char const *fallback)
79 {
80 	char buf[256];
81 	char *desc;
82 
83 	if (snprintf(buf, sizeof(buf), "%s_DESC", res) >= (int)sizeof(buf))
84 		warnx("Description for %s has been truncated", res);
85 
86 	desc = getenv(buf);
87 	if (desc == NULL || desc[0] == '\0')
88 		return (fallback);
89 
90 	return (desc);
91 }
92 
93 static bool
is_enable(char * name)94 is_enable(char *name)
95 {
96 	return (sl_find(enable_items, name) != NULL);
97 }
98 
99 static bool
is_new(char * name)100 is_new(char *name)
101 {
102 	return (sl_find(new_items, name) != NULL);
103 }
104 
105 static StringList *
parse_env_sl(char const * env_name)106 parse_env_sl(char const *env_name)
107 {
108 	StringList *sl;
109 	char *env;
110 	char *temp;
111 	char *token;
112 
113 	sl = sl_init();
114 	env = getenv(env_name);
115 	if (env != NULL) {
116 		temp = strdup(env);
117 		while ((token = strsep(&temp, " \t")) != NULL) {
118 			if (token[0] == '\0')
119 				continue;
120 			sl_add(sl, strdup(token));
121 		}
122 		free(temp);
123 	}
124 	return (sl);
125 }
126 
127 /* parsing part */
128 static int
parsing_env(dialog_mixedlist ** items,char const * env_name,int type)129 parsing_env(dialog_mixedlist **items, char const *env_name, int type)
130 {
131 	char *env, buf[256];
132 	char const *delimiter = " \t";
133 	char *token, *token2;
134 	char *temp, *tofree;
135 	char *temp2, *tofree2;
136 
137 	env = getenv(env_name);
138 	if (env == NULL)
139 		return (0);
140 
141 	if (strcmp(env_name, "ALL_OPTIONS") == 0) {
142 		tofree = temp = strdup(env);
143 
144 		while ((token = strsep(&temp, delimiter)) != NULL) {
145 			if (token[0] == '\0')
146 				continue;
147 			add_item(items, token, get_desc(token, ""), is_enable(token),
148 					is_new(token), type, group);
149 		}
150 		free(tofree);
151 	} else {
152 		tofree = temp = strdup(env);
153 		while ((token = strsep(&temp, delimiter)) != NULL) {
154 			if (token[0] == '\0')
155 				continue;
156 			add_item(items, get_desc(token, token), "", false, false,
157 					ITEM_SEPARATOR, group);
158 
159 			snprintf(buf, sizeof(buf), "%s_%s", env_name, token);
160 			env = getenv(buf);
161 			if (env == NULL)
162 				errx(EXIT_FAILURE, "%s does not exists", buf);
163 			tofree2 = temp2 = strdup(env);
164 			while ((token2 = strsep(&temp2, delimiter)) != NULL) {
165 				if (token2[0] == '\0')
166 					continue;
167 				add_item(items, token2, get_desc(token2, ""),
168 						is_enable(token2), is_new(token2), type, group);
169 			}
170 			free(tofree2);
171 			group++;
172 		}
173 
174 		free(tofree);
175 	}
176 	if (group == 0)
177 		group++;
178 
179 	return (0);
180 }
181 
182 
183 /* prepare items for next drawing*/
184 static dialog_mixedlist *
prepare_items(void)185 prepare_items(void)
186 {
187 	dialog_mixedlist *items = NULL;
188 
189 	enable_items = parse_env_sl("PORT_OPTIONS");
190 	new_items = parse_env_sl("NEW_OPTIONS");
191 
192 	parsing_env(&items, "ALL_OPTIONS", ITEM_CHECK);
193 	parsing_env(&items, "OPTIONS_GROUP", ITEM_CHECK);
194 	parsing_env(&items, "OPTIONS_MULTI", ITEM_CHECK);
195 	parsing_env(&items, "OPTIONS_SINGLE", ITEM_RADIO);
196 	parsing_env(&items, "OPTIONS_RADIO", ITEM_RADIO);
197 
198 	return (items);
199 }
200 
201 static int
mixedlist_show(const char * title,const char * cprompt,int height,int min_height,int width,dialog_mixedlist * items,bool align_center,bool fullscreen)202 mixedlist_show(const char *title, const char *cprompt, int height,
203 		int min_height, int width, dialog_mixedlist *items, bool align_center,
204 		bool fullscreen)
205 {
206 	int res;
207 
208 	if (list_no == 0) {
209 		end_dialog();
210 		err(EXIT_FAILURE, "List of items should not be empty");
211 	} else
212 		res = dlg_mixedlist(title, cprompt, height, min_height, width,
213 				list_no, items, align_center, fullscreen);
214 
215 	return (res);
216 }
217 
218 static void
usage(void)219 usage(void)
220 {
221 
222 	fprintf(stderr,"Usage: dialog4ports [-hv]\n");
223 	fprintf(stderr,"For more information please read man dialog4ports.\n");
224 	exit(1);
225 }
226 
227 int
main(int argc,char * argv[])228 main(int argc, char *argv[])
229 {
230 	char *portname, *temp;
231 	char buf[256];
232 	int i, res, ch;
233 	int height = 0, width = 80; // default values
234 	int min_height = 0;
235 	bool align_center = 0;
236 	bool fullscreen = 0;
237 	char *helpfile;
238 	dialog_mixedlist *items;
239 
240 	setlocale(LC_ALL, "");
241 	errno = 0;
242 
243 	while ((ch = getopt(argc, argv, "hv?")) != -1)
244 		switch (ch) {
245 			case 'v':
246 				fprintf(stderr,"dialog4ports version: %s\n", D4PVERSION);
247 				exit(0);
248 				break;
249 			case '?':
250 			case 'h':
251 				usage();
252 				break;
253 			default:
254 				exit(1);
255 		}
256 	argc -= optind;
257 	argv += optind;
258 
259 	init_dialog(stdin, stdout);
260 
261 	temp = getenv("D4PHEIGHT");
262 	if (temp != NULL) {
263 		errno = 0;
264 		height = strtol(temp, NULL, 0);
265 		if (errno != 0)
266 			height = 0;
267 	}
268 
269 	temp = getenv("D4PMINHEIGHT");
270 	if (temp != NULL && height == 0) {
271 		errno = 0;
272 		min_height = strtol(temp, NULL, 0) + MAGIC_BORDER;
273 		if (errno != 0)
274 			min_height = 0;
275 	}
276 
277 	temp = getenv("D4PWIDTH");
278 	if (temp != NULL) {
279 		errno = 0;
280 		width = strtol(temp, NULL, 0);
281 		if (errno != 0)
282 			width = 80;
283 	}
284 
285 	temp = getenv("D4PFULLSCREEN");
286 	if (temp != NULL &&
287 		(strcasecmp(temp, "Y") == 0 || strcasecmp(temp, "YES") == 0)) {
288 			fullscreen = 1;
289 	}
290 
291 	temp = getenv("D4PALIGNCENTER");
292 	if (temp != NULL &&
293 		(strcasecmp(temp, "Y") == 0 || strcasecmp(temp, "YES") == 0))
294 			align_center = 1;
295 
296 	temp = getenv("D4PASCIILINES");
297 	if (temp != NULL &&
298 			(strcasecmp(temp, "Y") == 0 || strcasecmp(temp, "YES") == 0))
299 		dialog_vars.ascii_lines = TRUE;
300 
301 	portname = getenv("PKGNAME");
302 	if (portname == NULL) {
303 		end_dialog();
304 		fprintf(stderr, "env PKGNAME is NULL\n");
305 		usage();
306 	}
307 
308 	if ((helpfile = getenv("PKGHELP")) != NULL) {
309 		if (eaccess(helpfile, R_OK) != 0) {
310 			end_dialog();
311 			err(EXIT_FAILURE, "%s", helpfile);
312 		}
313 		dialog_vars.help_file = helpfile;
314 	}
315 
316 	items = prepare_items();
317 
318 	snprintf(buf, sizeof(buf), " %s ", portname);
319 
320 	dlg_clear();
321 	res = mixedlist_show(buf, "", height, min_height, width, items,
322 			align_center, fullscreen);
323 
324 	if (dialog_state.screen_initialized) {
325 		end_dialog();
326 	}
327 
328 	if (res == 0) {
329 		/* return all active items */
330 		for (i = 0; i < list_no; i++) {
331 			if (items[i].state == 1) {
332 				fprintf(stderr, "\"%s\"", items[i].name);
333 				fprintf(stderr, " ");
334 			}
335 		}
336 	} else {
337 		return (1);
338 	}
339 
340 	return (0);
341 }
342