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