1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30
31 /*
32 * Copyright (c) 2018, Joyent, Inc.
33 */
34
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <valtools.h>
40 #include <stdlib.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <limits.h>
44 #include <wchar.h>
45 #include "usage.h"
46 #include "libadm.h"
47
48 #define BADPID (-2)
49 #define INVISMAXSIZE 36
50
51 static char *prog;
52 static char *deflt = NULL, *prompt = NULL, *error = NULL, *help = NULL;
53 static int kpid = BADPID;
54 static int signo;
55
56 static char *label, **invis;
57 static int ninvis = 0;
58 static int max = 1;
59 static int attr = CKALPHA;
60
61 #define MAXSIZE 128
62 #define LSIZE 1024
63 #define INTERR \
64 "%s: ERROR: internal error occurred while attempting menu setup\n"
65 #define MYOPTS \
66 "\t-f file #file containing choices\n" \
67 "\t-l label #menu label\n" \
68 "\t-i invis [, ...] #invisible menu choices\n" \
69 "\t-m max #maximum choices user may select\n" \
70 "\t-n #do not sort choices alphabetically\n" \
71 "\t-o #don't prompt if only one choice\n" \
72 "\t-u #unnumbered choices\n"
73
74 static const char husage[] = "Wh";
75 static const char eusage[] = "We";
76
77 static void
usage(void)78 usage(void)
79 {
80 switch (*prog) {
81 default:
82 (void) fprintf(stderr,
83 gettext("usage: %s [options] [choice [...]]\n"), prog);
84 (void) fprintf(stderr, gettext(OPTMESG));
85 (void) fprintf(stderr, gettext(MYOPTS));
86 (void) fprintf(stderr, gettext(STDOPTS));
87 break;
88
89 case 'h':
90 (void) fprintf(stderr,
91 gettext("usage: %s [options] [choice [...]]\n"), prog);
92 (void) fprintf(stderr, gettext(OPTMESG));
93 (void) fprintf(stderr,
94 gettext("\t-W width\n\t-h help\n"));
95 break;
96
97 case 'e':
98 (void) fprintf(stderr,
99 gettext("usage: %s [options] [choice [...]]\n"), prog);
100 (void) fprintf(stderr, gettext(OPTMESG));
101 (void) fprintf(stderr,
102 gettext("\t-W width\n\t-e error\n"));
103 break;
104 }
105 exit(1);
106 }
107
108 /*
109 * Given argv[0], return a pointer to the basename of the program.
110 */
111 static char *
prog_name(char * arg0)112 prog_name(char *arg0)
113 {
114 char *str;
115
116 /* first strip trailing '/' characters (exec() allows these!) */
117 str = arg0 + strlen(arg0);
118 while (str > arg0 && *--str == '/')
119 *str = '\0';
120 if ((str = strrchr(arg0, '/')) != NULL)
121 return (str + 1);
122 return (arg0);
123 }
124
125 int
main(int argc,char ** argv)126 main(int argc, char **argv)
127 {
128 CKMENU *mp;
129 FILE *fp = NULL;
130 int c, i;
131 char **item;
132 char temp[LSIZE * MB_LEN_MAX];
133 size_t mmax;
134 size_t invismaxsize = INVISMAXSIZE;
135 size_t n, r;
136 wchar_t wline[LSIZE], wtemp[LSIZE];
137
138 (void) setlocale(LC_ALL, "");
139
140 #if !defined(TEXT_DOMAIN)
141 #define TEXT_DOMAIN "SYS_TEST"
142 #endif
143 (void) textdomain(TEXT_DOMAIN);
144
145 prog = prog_name(argv[0]);
146
147 invis = (char **)calloc(invismaxsize, sizeof (char *));
148 if (!invis) {
149 (void) fprintf(stderr,
150 gettext("Not enough memory\n"));
151 exit(1);
152 }
153 while ((c = getopt(argc, argv, "m:oni:l:f:ud:p:e:h:k:s:QW:?")) != EOF) {
154 /* check for invalid option */
155 if ((*prog == 'e') && !strchr(eusage, c))
156 usage(); /* no valid options */
157 if ((*prog == 'h') && !strchr(husage, c))
158 usage();
159
160 switch (c) {
161 case 'Q':
162 ckquit = 0;
163 break;
164
165 case 'W':
166 ckwidth = atol(optarg);
167 if (ckwidth < 0) {
168 (void) fprintf(stderr,
169 gettext("%s: ERROR: negative display width specified\n"),
170 prog);
171 exit(1);
172 }
173 break;
174
175 case 'm':
176 max = atoi(optarg);
177 if (max > SHRT_MAX || max < SHRT_MIN) {
178 (void) fprintf(stderr,
179 gettext("%s: ERROR: too large or too small max value specified\n"),
180 prog);
181 exit(1);
182 }
183 break;
184
185 case 'o':
186 attr |= CKONEFLAG;
187 break;
188
189 case 'n':
190 attr &= ~CKALPHA;
191 break;
192
193 case 'i':
194 invis[ninvis++] = optarg;
195 if (ninvis == invismaxsize) {
196 invismaxsize += INVISMAXSIZE;
197 invis = (char **)realloc(invis,
198 invismaxsize * sizeof (char *));
199 if (!invis) {
200 (void) fprintf(stderr,
201 gettext("Not enough memory\n"));
202 exit(1);
203 }
204 (void) memset(invis + ninvis, 0,
205 (invismaxsize - ninvis) *
206 sizeof (char *));
207 }
208 break;
209
210 case 'l':
211 label = optarg;
212 break;
213
214 case 'f':
215 if ((fp = fopen(optarg, "r")) == NULL) {
216 (void) fprintf(stderr,
217 gettext("%s: ERROR: can't open %s\n"),
218 prog, optarg);
219 exit(1);
220 }
221 break;
222
223 case 'u':
224 attr |= CKUNNUM;
225 break;
226
227 case 'd':
228 deflt = optarg;
229 break;
230
231 case 'p':
232 prompt = optarg;
233 break;
234
235 case 'e':
236 error = optarg;
237 break;
238
239 case 'h':
240 help = optarg;
241 break;
242
243 case 'k':
244 kpid = atoi(optarg);
245 break;
246
247 case 's':
248 signo = atoi(optarg);
249 break;
250
251 default:
252 usage();
253 }
254 }
255
256 if (signo) {
257 if (kpid == BADPID)
258 usage();
259 } else
260 signo = SIGTERM;
261
262 mp = allocmenu(label, attr);
263 if (fp) {
264 *wtemp = L'\0';
265 while (fgetws(wline, LSIZE, fp)) {
266 /*
267 * Skip comment lines, those beginning with '#'.
268 * Note: AT&T forgot this & needs the next 2 lines.
269 */
270 if (*wline == L'#')
271 continue;
272 n = wcslen(wline);
273 if ((n != 0) && (wline[n - 1] == L'\n'))
274 wline[n - 1] = L'\0';
275 /*
276 * if the line begins with a space character,
277 * this is a continuous line to the previous line.
278 */
279 if (iswspace(*wline)) {
280 (void) wcscat(wtemp, L"\n");
281 (void) wcscat(wtemp, wline);
282 } else {
283 if (*wtemp) {
284 n = wcslen(wtemp);
285 r = wcstombs(temp, wtemp,
286 n * MB_LEN_MAX);
287 if (r == (size_t)-1) {
288 (void) fprintf(stderr,
289 gettext("Invalid character in the menu definition.\n"));
290 exit(1);
291 }
292 if (setitem(mp, temp)) {
293 (void) fprintf(stderr,
294 gettext(INTERR), prog);
295 exit(1);
296 }
297 }
298 (void) wcscpy(wtemp, wline);
299 }
300 }
301 if (*wtemp) {
302 n = wcslen(wtemp);
303 r = wcstombs(temp, wtemp, n * MB_LEN_MAX);
304 if (r == (size_t)-1) {
305 (void) fprintf(stderr,
306 gettext("Invalid character in the menu definition.\n"));
307 exit(1);
308 }
309 if (setitem(mp, temp)) {
310 (void) fprintf(stderr, gettext(INTERR), prog);
311 exit(1);
312 }
313 }
314 }
315
316 while (optind < argc) {
317 if (setitem(mp, argv[optind++])) {
318 (void) fprintf(stderr, gettext(INTERR), prog);
319 exit(1);
320 }
321 }
322
323 for (n = 0; n < ninvis; ) {
324 if (setinvis(mp, invis[n++])) {
325 (void) fprintf(stderr, gettext(INTERR), prog);
326 exit(1);
327 }
328 }
329
330 if (*prog == 'e') {
331 ckindent = 0;
332 ckitem_err(mp, error);
333 exit(0);
334 } else if (*prog == 'h') {
335 ckindent = 0;
336 ckitem_hlp(mp, help);
337 exit(0);
338 }
339
340 if (max < 1) {
341 mmax = mp->nchoices;
342 } else {
343 mmax = max;
344 }
345
346 /*
347 * if -o option is specified, mp->nchoices is 1, and if no invisible
348 * item is specified, ckitem() will consume two entries of item,
349 * even though 'max' is set to 1. So to take care of that problem, we
350 * allocate one extra element for item
351 */
352 item = (char **)calloc(mmax+1, sizeof (char *));
353 if (!item) {
354 (void) fprintf(stderr,
355 gettext("Not enough memory\n"));
356 exit(1);
357 }
358 n = ckitem(mp, item, max, deflt, error, help, prompt);
359 if (n == 3) {
360 if (kpid > -2)
361 (void) kill(kpid, signo);
362 (void) puts("q");
363 } else if (n == 0) {
364 i = 0;
365 while (item[i])
366 (void) puts(item[i++]);
367 }
368 return (n);
369 }
370