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 * 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 * 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 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 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 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 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 ** 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