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