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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
24
25
26 /*
27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30 /*
31 * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
32 */
33
34 /*LINTLIBRARY*/
35
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include "valtools.h"
40 #include <sys/types.h>
41 #include <stdlib.h>
42 #include <strings.h>
43 #include "libadm.h"
44
45 static int insert(struct _choice_ *, CKMENU *);
46 static char *strtoki(char *, char *);
47 static char **match(CKMENU *, char *, int);
48 static int getstr(char *, char *, char *, char *, char *);
49 static int getnum(char *, int, int *, int *);
50 static struct _choice_ *next(struct _choice_ *);
51
52 static char *deferr;
53 static char *errmsg;
54 static char *defhlp;
55
56 #define PROMPT "Enter selection"
57 #define MESG0 "Entry does not match available menu selection. "
58 #define MESG1 "the number of the menu item you wish to select, or "
59 #define MESG2 "the token which is associated with the menu item,\
60 or a partial string which uniquely identifies the \
61 token for the menu item. Enter ?? to reprint the menu."
62
63 #define TOOMANY "Too many items selected from menu"
64 #define NOTUNIQ "The entered text does not uniquely identify a menu choice."
65 #define BADNUM "Bad numeric choice specification"
66
67 static char *
setmsg(CKMENU * menup,short flag)68 setmsg(CKMENU *menup, short flag)
69 {
70 int n;
71 char *msg;
72
73 n = (int)(6 + sizeof (MESG2));
74 if (flag)
75 n += (int)(sizeof (MESG0));
76
77 if (menup->attr & CKUNNUM) {
78 msg = calloc((size_t)n, sizeof (char));
79 if (flag)
80 (void) strcpy(msg, MESG0);
81 else
82 msg[0] = '\0';
83 (void) strcat(msg, "Enter ");
84 (void) strcat(msg, MESG2);
85 } else {
86 msg = calloc(n+sizeof (MESG1), sizeof (char));
87 if (flag)
88 (void) strcpy(msg, MESG0);
89 else
90 msg[0] = '\0';
91 (void) strcat(msg, "Enter ");
92 (void) strcat(msg, MESG1);
93 (void) strcat(msg, MESG2);
94 }
95 return (msg);
96 }
97
98 CKMENU *
allocmenu(char * label,int attr)99 allocmenu(char *label, int attr)
100 {
101 CKMENU *pt;
102
103 if (pt = calloc(1, sizeof (CKMENU))) {
104 pt->attr = attr;
105 pt->label = label;
106 }
107 return (pt);
108 }
109
110 void
ckitem_err(CKMENU * menup,char * error)111 ckitem_err(CKMENU *menup, char *error)
112 {
113 deferr = setmsg(menup, 1);
114 puterror(stdout, deferr, error);
115 free(deferr);
116 }
117
118 void
ckitem_hlp(CKMENU * menup,char * help)119 ckitem_hlp(CKMENU *menup, char *help)
120 {
121 defhlp = setmsg(menup, 0);
122 puthelp(stdout, defhlp, help);
123 free(defhlp);
124 }
125
126 int
ckitem(CKMENU * menup,char * item[],short max,char * defstr,char * error,char * help,char * prompt)127 ckitem(CKMENU *menup, char *item[], short max, char *defstr, char *error,
128 char *help, char *prompt)
129 {
130 int n, i;
131 char strval[MAX_INPUT];
132 char **list;
133
134 if ((menup->nchoices <= 0) && !menup->invis)
135 return (4); /* nothing to choose from */
136
137 if (menup->attr & CKONEFLAG) {
138 if (((n = menup->nchoices) <= 1) && menup->invis) {
139 for (i = 0; menup->invis[i]; ++i)
140 n++;
141 }
142 if (n <= 1) {
143 if (menup->choice)
144 item[0] = menup->choice->token;
145 else if (menup->invis)
146 item[0] = menup->invis[0];
147 item[1] = NULL;
148 return (0);
149 }
150 }
151
152 if (max < 1)
153 max = menup->nchoices;
154
155 if (!prompt)
156 prompt = PROMPT;
157 defhlp = setmsg(menup, 0);
158 deferr = setmsg(menup, 1);
159
160 reprint:
161 printmenu(menup);
162
163 start:
164 if (n = getstr(strval, defstr, error, help, prompt)) {
165 free(defhlp);
166 free(deferr);
167 return (n);
168 }
169 if (strcmp(strval, "??") == 0) {
170 goto reprint;
171 }
172 if ((defstr) && (strcmp(strval, defstr) == 0)) {
173 item[0] = defstr;
174 item[1] = NULL;
175 } else {
176 list = match(menup, strval, (int)max);
177 if (!list) {
178 puterror(stderr, deferr, (errmsg ? errmsg : error));
179 goto start;
180 }
181 for (i = 0; (i < max); i++)
182 item[i] = list[i];
183 free(list);
184 }
185 free(defhlp);
186 free(deferr);
187 return (0);
188 }
189
190 static int
getnum(char * strval,int max,int * begin,int * end)191 getnum(char *strval, int max, int *begin, int *end)
192 {
193 int n;
194 char *pt;
195
196 *begin = *end = 0;
197 pt = strval;
198 for (;;) {
199 if (*pt == '$') {
200 n = max;
201 pt++;
202 } else {
203 n = (int)strtol(pt, &pt, 10);
204 if ((n <= 0) || (n > max))
205 return (1);
206 }
207 while (isspace((unsigned char)*pt))
208 pt++;
209
210 if (!*begin && (*pt == '-')) {
211 *begin = n;
212 pt++;
213 while (isspace((unsigned char)*pt))
214 pt++;
215 continue;
216 } else if (*pt) {
217 return (1); /* wasn't a number, or an invalid one */
218 } else if (*begin) {
219 *end = n;
220 break;
221 } else {
222 *begin = n;
223 break;
224 }
225 }
226 if (!*end)
227 *end = *begin;
228 return ((*begin <= *end) ? 0 : 1);
229 }
230
231 static char **
match(CKMENU * menup,char * strval,int max)232 match(CKMENU *menup, char *strval, int max)
233 {
234 struct _choice_ *chp;
235 char **choice;
236 int begin, end;
237 char *pt, *found;
238 int i, len, nchoice;
239
240 nchoice = 0;
241 choice = calloc((size_t)max, sizeof (char *));
242
243 do {
244 if (pt = strpbrk(strval, " \t,")) {
245 do {
246 *pt++ = '\0';
247 } while (strchr(" \t,", *pt));
248 }
249
250 if (nchoice >= max) {
251 errmsg = TOOMANY;
252 return (NULL);
253 }
254 if (!(menup->attr & CKUNNUM) &&
255 isdigit((unsigned char)*strval)) {
256 if (getnum(strval, (int)menup->nchoices, &begin,
257 &end)) {
258 errmsg = BADNUM;
259 return (NULL);
260 }
261 chp = menup->choice;
262 for (i = 1; chp; i++) {
263 if ((i >= begin) && (i <= end)) {
264 if (nchoice >= max) {
265 errmsg = TOOMANY;
266 return (NULL);
267 }
268 choice[nchoice++] = chp->token;
269 }
270 chp = chp->next;
271 }
272 continue;
273 }
274
275 found = NULL;
276 chp = menup->choice;
277 for (i = 0; chp; i++) {
278 len = (int)strlen(strval);
279 if (strncmp(chp->token, strval, (size_t)len) == 0) {
280 if (chp->token[len] == '\0') {
281 found = chp->token;
282 break;
283 } else if (found) {
284 errmsg = NOTUNIQ;
285 return (NULL); /* not unique */
286 }
287 found = chp->token;
288 }
289 chp = chp->next;
290 }
291
292 if (menup->invis) {
293 for (i = 0; menup->invis[i]; ++i) {
294 len = (int)strlen(strval);
295 if (strncmp(menup->invis[i], strval,
296 (size_t)len) == 0) {
297 #if _3b2
298 if (chp->token[len] == '\0') {
299 #else
300 if (menup->invis[i][len] == '\0') {
301 #endif
302 found = menup->invis[i];
303 break;
304 } else if (found) {
305 errmsg = NOTUNIQ;
306 return (NULL);
307 }
308 found = menup->invis[i];
309 }
310 }
311 }
312 if (found) {
313 choice[nchoice++] = found;
314 continue;
315 }
316 errmsg = NULL;
317 return (NULL);
318 } while (((strval = pt) != NULL) && *pt);
319 return (choice);
320 }
321
322 int
323 setitem(CKMENU *menup, char *choice)
324 {
325 struct _choice_ *chp;
326 int n;
327 char *pt;
328
329 if (choice == NULL) {
330 /* request to clear memory usage */
331 chp = menup->choice;
332 while (chp) {
333 struct _choice_ *_chp = chp;
334
335 chp = chp->next;
336 menup->longest = menup->nchoices = 0;
337
338 (void) free(_chp->token); /* free token and text */
339 (void) free(_chp);
340 }
341 return (1);
342 }
343
344 if ((chp = calloc(1, sizeof (struct _choice_))) == NULL)
345 return (1);
346
347 if ((pt = strdup(choice)) == NULL) {
348 free(chp);
349 return (1);
350 }
351 if (!*pt || isspace((unsigned char)*pt)) {
352 free(chp);
353 return (2);
354 }
355
356 chp->token = strtoki(pt, " \t\n");
357 chp->text = strtoki(NULL, "");
358
359 if (chp->text) {
360 while (isspace((unsigned char)*chp->text))
361 chp->text++;
362 }
363 n = (int)strlen(chp->token);
364 if (n > menup->longest)
365 menup->longest = (short)n;
366
367 if (insert(chp, menup))
368 menup->nchoices++;
369 else
370 free(chp); /* duplicate entry */
371 return (0);
372 }
373
374 int
375 setinvis(CKMENU *menup, char *choice)
376 {
377 int index;
378
379 index = 0;
380 if (choice == NULL) {
381 if (menup->invis == NULL)
382 return (0);
383 while (menup->invis[index])
384 free(menup->invis[index]);
385 free(menup->invis);
386 return (0);
387 }
388
389 if (menup->invis == NULL)
390 menup->invis = calloc(2, sizeof (char *));
391 else {
392 while (menup->invis[index])
393 index++; /* count invisible choices */
394 menup->invis = realloc(menup->invis,
395 (index+2)* sizeof (char *));
396 menup->invis[index+1] = NULL;
397 }
398 if (!menup->invis)
399 return (-1);
400 menup->invis[index] = strdup(choice);
401 return (0);
402 }
403
404 static int
405 insert(struct _choice_ *chp, CKMENU *menup)
406 {
407 struct _choice_ *last, *base;
408 int n;
409
410 base = menup->choice;
411 last = NULL;
412
413 if (!(menup->attr & CKALPHA)) {
414 while (base) {
415 if (strcmp(base->token, chp->token) == 0)
416 return (0);
417 last = base;
418 base = base->next;
419 }
420 if (last)
421 last->next = chp;
422 else
423 menup->choice = chp;
424 return (1);
425 }
426
427 while (base) {
428 if ((n = strcmp(base->token, chp->token)) == 0)
429 return (0);
430 if (n > 0) {
431 /* should come before this one */
432 break;
433 }
434 last = base;
435 base = base->next;
436 }
437 if (last) {
438 chp->next = last->next;
439 last->next = chp;
440 } else {
441 chp->next = menup->choice;
442 menup->choice = chp;
443 }
444 return (1);
445 }
446
447 void
448 printmenu(CKMENU *menup)
449 {
450 int i;
451 struct _choice_ *chp;
452 char *pt;
453 char format[16];
454 int c;
455
456 (void) fputc('\n', stderr);
457 if (menup->label) {
458 (void) puttext(stderr, menup->label, 0, 0);
459 (void) fputc('\n', stderr);
460 }
461 (void) sprintf(format, "%%-%ds", menup->longest+5);
462
463 (void) next(NULL);
464 chp = ((menup->attr & CKALPHA) ? next(menup->choice) : menup->choice);
465 for (i = 1; chp; ++i) {
466 if (!(menup->attr & CKUNNUM))
467 (void) fprintf(stderr, "%3d ", i);
468 /* LINTED E_SEC_PRINTF_VAR_FMT */
469 (void) fprintf(stderr, format, chp->token);
470 if (chp->text) {
471 /* there is text associated with the token */
472 pt = chp->text;
473 while (*pt) {
474 (void) fputc(*pt, stderr);
475 if (*pt++ == '\n') {
476 if (!(menup->attr & CKUNNUM))
477 (void) fprintf(stderr,
478 "%5s", "");
479 /* LINTED E_SEC_PRINTF_VAR_FMT */
480 (void) fprintf(stderr, format, "");
481 while (isspace((unsigned char)*pt))
482 ++pt;
483 }
484 }
485 }
486 (void) fputc('\n', stderr);
487 chp = ((menup->attr & CKALPHA) ?
488 next(menup->choice) : chp->next);
489 if (chp && ((i % 10) == 0)) {
490 /* page the choices */
491 (void) fprintf(stderr,
492 "\n... %d more menu choices to follow;",
493 menup->nchoices - i);
494 (void) fprintf(stderr,
495 /* CSTYLED */
496 "\n<RETURN> for more choices, <CTRL-D> to stop \
497 display:");
498 /* ignore other chars */
499 while (((c = getc(stdin)) != EOF) && (c != '\n'))
500 ;
501 (void) fputc('\n', stderr);
502 if (c == EOF)
503 break; /* stop printing menu */
504 }
505 }
506 }
507
508 static int
509 getstr(char *strval, char *defstr, char *error, char *help, char *prompt)
510 {
511 char input[MAX_INPUT];
512 char end[MAX_INPUT];
513
514 *end = '\0';
515 if (defstr) {
516 (void) snprintf(end, MAX_INPUT, "(default: %s) ", defstr);
517 }
518 if (ckquit) {
519 (void) strlcat(end, "[?,??,q]", MAX_INPUT);
520 } else {
521 (void) strlcat(end, "[?,??]", MAX_INPUT);
522 }
523
524 start:
525 (void) fputc('\n', stderr);
526 (void) puttext(stderr, prompt, 0, 0);
527 (void) fprintf(stderr, " %s: ", end);
528
529 if (getinput(input))
530 return (1);
531
532 if (strlen(input) == 0) {
533 if (defstr) {
534 (void) strcpy(strval, defstr);
535 return (0);
536 }
537 puterror(stderr, deferr, (errmsg ? errmsg : error));
538 goto start;
539 } else if (strcmp(input, "?") == 0) {
540 puthelp(stderr, defhlp, help);
541 goto start;
542 } else if (ckquit && (strcmp(input, "q") == 0)) {
543 /* (void) strcpy(strval, input); */
544 return (3);
545 }
546 (void) strcpy(strval, input);
547 return (0);
548 }
549
550 static struct _choice_ *
551 next(struct _choice_ *chp)
552 {
553 static char *last;
554 static char *first;
555 struct _choice_ *found;
556
557 if (!chp) {
558 last = NULL;
559 return (NULL);
560 }
561
562 found = NULL;
563 for (first = NULL; chp; chp = chp->next) {
564 if (last && strcmp(last, chp->token) >= 0)
565 continue; /* lower than the last one we found */
566
567 if (!first || strcmp(first, chp->token) > 0) {
568 first = chp->token;
569 found = chp;
570 }
571 }
572 last = first;
573 return (found);
574 }
575
576 static char *
577 strtoki(char *string, char *sepset)
578 {
579 char *p, *q, *r;
580 static char *savept;
581
582 /* first or subsequent call */
583 p = (string == NULL)? savept: string;
584
585 if (p == NULL) /* return if no tokens remaining */
586 return (NULL);
587
588 q = p + strspn(p, sepset); /* skip leading separators */
589
590 if (*q == '\0') /* return if no tokens remaining */
591 return (NULL);
592
593 if ((r = strpbrk(q, sepset)) == NULL) /* move past token */
594 savept = 0; /* indicate this is last token */
595 else {
596 *r = '\0';
597 savept = ++r;
598 }
599 return (q);
600 }
601