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