/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2010 Nexenta Systems, Inc. All rights reserved. */ /*LINTLIBRARY*/ #include #include #include #include "valtools.h" #include #include #include #include "libadm.h" static int insert(struct _choice_ *, CKMENU *); static char *strtoki(char *, char *); static char **match(CKMENU *, char *, int); static int getstr(char *, char *, char *, char *, char *); static int getnum(char *, int, int *, int *); static struct _choice_ *next(struct _choice_ *); static char *deferr; static char *errmsg; static char *defhlp; #define PROMPT "Enter selection" #define MESG0 "Entry does not match available menu selection. " #define MESG1 "the number of the menu item you wish to select, or " #define MESG2 "the token which is associated with the menu item,\ or a partial string which uniquely identifies the \ token for the menu item. Enter ?? to reprint the menu." #define TOOMANY "Too many items selected from menu" #define NOTUNIQ "The entered text does not uniquely identify a menu choice." #define BADNUM "Bad numeric choice specification" static char * setmsg(CKMENU *menup, short flag) { int n; char *msg; n = (int)(6 + sizeof (MESG2)); if (flag) n += (int)(sizeof (MESG0)); if (menup->attr & CKUNNUM) { msg = calloc((size_t)n, sizeof (char)); if (flag) (void) strcpy(msg, MESG0); else msg[0] = '\0'; (void) strcat(msg, "Enter "); (void) strcat(msg, MESG2); } else { msg = calloc(n+sizeof (MESG1), sizeof (char)); if (flag) (void) strcpy(msg, MESG0); else msg[0] = '\0'; (void) strcat(msg, "Enter "); (void) strcat(msg, MESG1); (void) strcat(msg, MESG2); } return (msg); } CKMENU * allocmenu(char *label, int attr) { CKMENU *pt; if (pt = calloc(1, sizeof (CKMENU))) { pt->attr = attr; pt->label = label; } return (pt); } void ckitem_err(CKMENU *menup, char *error) { deferr = setmsg(menup, 1); puterror(stdout, deferr, error); free(deferr); } void ckitem_hlp(CKMENU *menup, char *help) { defhlp = setmsg(menup, 0); puthelp(stdout, defhlp, help); free(defhlp); } int ckitem(CKMENU *menup, char *item[], short max, char *defstr, char *error, char *help, char *prompt) { int n, i; char strval[MAX_INPUT]; char **list; if ((menup->nchoices <= 0) && !menup->invis) return (4); /* nothing to choose from */ if (menup->attr & CKONEFLAG) { if (((n = menup->nchoices) <= 1) && menup->invis) { for (i = 0; menup->invis[i]; ++i) n++; } if (n <= 1) { if (menup->choice) item[0] = menup->choice->token; else if (menup->invis) item[0] = menup->invis[0]; item[1] = NULL; return (0); } } if (max < 1) max = menup->nchoices; if (!prompt) prompt = PROMPT; defhlp = setmsg(menup, 0); deferr = setmsg(menup, 1); reprint: printmenu(menup); start: if (n = getstr(strval, defstr, error, help, prompt)) { free(defhlp); free(deferr); return (n); } if (strcmp(strval, "??") == 0) { goto reprint; } if ((defstr) && (strcmp(strval, defstr) == 0)) { item[0] = defstr; item[1] = NULL; } else { list = match(menup, strval, (int)max); if (!list) { puterror(stderr, deferr, (errmsg ? errmsg : error)); goto start; } for (i = 0; (i < max); i++) item[i] = list[i]; free(list); } free(defhlp); free(deferr); return (0); } static int getnum(char *strval, int max, int *begin, int *end) { int n; char *pt; *begin = *end = 0; pt = strval; for (;;) { if (*pt == '$') { n = max; pt++; } else { n = (int)strtol(pt, &pt, 10); if ((n <= 0) || (n > max)) return (1); } while (isspace((unsigned char)*pt)) pt++; if (!*begin && (*pt == '-')) { *begin = n; pt++; while (isspace((unsigned char)*pt)) pt++; continue; } else if (*pt) { return (1); /* wasn't a number, or an invalid one */ } else if (*begin) { *end = n; break; } else { *begin = n; break; } } if (!*end) *end = *begin; return ((*begin <= *end) ? 0 : 1); } static char ** match(CKMENU *menup, char *strval, int max) { struct _choice_ *chp; char **choice; int begin, end; char *pt, *found; int i, len, nchoice; nchoice = 0; choice = calloc((size_t)max, sizeof (char *)); do { if (pt = strpbrk(strval, " \t,")) { do { *pt++ = '\0'; } while (strchr(" \t,", *pt)); } if (nchoice >= max) { errmsg = TOOMANY; return (NULL); } if (!(menup->attr & CKUNNUM) && isdigit((unsigned char)*strval)) { if (getnum(strval, (int)menup->nchoices, &begin, &end)) { errmsg = BADNUM; return (NULL); } chp = menup->choice; for (i = 1; chp; i++) { if ((i >= begin) && (i <= end)) { if (nchoice >= max) { errmsg = TOOMANY; return (NULL); } choice[nchoice++] = chp->token; } chp = chp->next; } continue; } found = NULL; chp = menup->choice; for (i = 0; chp; i++) { len = (int)strlen(strval); if (strncmp(chp->token, strval, (size_t)len) == 0) { if (chp->token[len] == '\0') { found = chp->token; break; } else if (found) { errmsg = NOTUNIQ; return (NULL); /* not unique */ } found = chp->token; } chp = chp->next; } if (menup->invis) { for (i = 0; menup->invis[i]; ++i) { len = (int)strlen(strval); if (strncmp(menup->invis[i], strval, (size_t)len) == 0) { #if _3b2 if (chp->token[len] == '\0') { #else if (menup->invis[i][len] == '\0') { #endif found = menup->invis[i]; break; } else if (found) { errmsg = NOTUNIQ; return (NULL); } found = menup->invis[i]; } } } if (found) { choice[nchoice++] = found; continue; } errmsg = NULL; return (NULL); } while (((strval = pt) != NULL) && *pt); return (choice); } int setitem(CKMENU *menup, char *choice) { struct _choice_ *chp; int n; char *pt; if (choice == NULL) { /* request to clear memory usage */ chp = menup->choice; while (chp) { struct _choice_ *_chp = chp; chp = chp->next; menup->longest = menup->nchoices = 0; (void) free(_chp->token); /* free token and text */ (void) free(_chp); } return (1); } if ((chp = calloc(1, sizeof (struct _choice_))) == NULL) return (1); if ((pt = strdup(choice)) == NULL) { free(chp); return (1); } if (!*pt || isspace((unsigned char)*pt)) { free(chp); return (2); } chp->token = strtoki(pt, " \t\n"); chp->text = strtoki(NULL, ""); if (chp->text) { while (isspace((unsigned char)*chp->text)) chp->text++; } n = (int)strlen(chp->token); if (n > menup->longest) menup->longest = (short)n; if (insert(chp, menup)) menup->nchoices++; else free(chp); /* duplicate entry */ return (0); } int setinvis(CKMENU *menup, char *choice) { int index; index = 0; if (choice == NULL) { if (menup->invis == NULL) return (0); while (menup->invis[index]) free(menup->invis[index]); free(menup->invis); return (0); } if (menup->invis == NULL) menup->invis = calloc(2, sizeof (char *)); else { while (menup->invis[index]) index++; /* count invisible choices */ menup->invis = realloc(menup->invis, (index+2)* sizeof (char *)); menup->invis[index+1] = NULL; } if (!menup->invis) return (-1); menup->invis[index] = strdup(choice); return (0); } static int insert(struct _choice_ *chp, CKMENU *menup) { struct _choice_ *last, *base; int n; base = menup->choice; last = NULL; if (!(menup->attr & CKALPHA)) { while (base) { if (strcmp(base->token, chp->token) == 0) return (0); last = base; base = base->next; } if (last) last->next = chp; else menup->choice = chp; return (1); } while (base) { if ((n = strcmp(base->token, chp->token)) == 0) return (0); if (n > 0) { /* should come before this one */ break; } last = base; base = base->next; } if (last) { chp->next = last->next; last->next = chp; } else { chp->next = menup->choice; menup->choice = chp; } return (1); } void printmenu(CKMENU *menup) { int i; struct _choice_ *chp; char *pt; char format[16]; int c; (void) fputc('\n', stderr); if (menup->label) { (void) puttext(stderr, menup->label, 0, 0); (void) fputc('\n', stderr); } (void) sprintf(format, "%%-%ds", menup->longest+5); (void) next(NULL); chp = ((menup->attr & CKALPHA) ? next(menup->choice) : menup->choice); for (i = 1; chp; ++i) { if (!(menup->attr & CKUNNUM)) (void) fprintf(stderr, "%3d ", i); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) fprintf(stderr, format, chp->token); if (chp->text) { /* there is text associated with the token */ pt = chp->text; while (*pt) { (void) fputc(*pt, stderr); if (*pt++ == '\n') { if (!(menup->attr & CKUNNUM)) (void) fprintf(stderr, "%5s", ""); /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) fprintf(stderr, format, ""); while (isspace((unsigned char)*pt)) ++pt; } } } (void) fputc('\n', stderr); chp = ((menup->attr & CKALPHA) ? next(menup->choice) : chp->next); if (chp && ((i % 10) == 0)) { /* page the choices */ (void) fprintf(stderr, "\n... %d more menu choices to follow;", menup->nchoices - i); (void) fprintf(stderr, /* CSTYLED */ "\n for more choices, to stop \ display:"); /* ignore other chars */ while (((c = getc(stdin)) != EOF) && (c != '\n')) ; (void) fputc('\n', stderr); if (c == EOF) break; /* stop printing menu */ } } } static int getstr(char *strval, char *defstr, char *error, char *help, char *prompt) { char input[MAX_INPUT]; char end[MAX_INPUT]; *end = '\0'; if (defstr) { (void) snprintf(end, MAX_INPUT, "(default: %s) ", defstr); } if (ckquit) { (void) strlcat(end, "[?,??,q]", MAX_INPUT); } else { (void) strlcat(end, "[?,??]", MAX_INPUT); } start: (void) fputc('\n', stderr); (void) puttext(stderr, prompt, 0, 0); (void) fprintf(stderr, " %s: ", end); if (getinput(input)) return (1); if (strlen(input) == 0) { if (defstr) { (void) strcpy(strval, defstr); return (0); } puterror(stderr, deferr, (errmsg ? errmsg : error)); goto start; } else if (strcmp(input, "?") == 0) { puthelp(stderr, defhlp, help); goto start; } else if (ckquit && (strcmp(input, "q") == 0)) { /* (void) strcpy(strval, input); */ return (3); } (void) strcpy(strval, input); return (0); } static struct _choice_ * next(struct _choice_ *chp) { static char *last; static char *first; struct _choice_ *found; if (!chp) { last = NULL; return (NULL); } found = NULL; for (first = NULL; chp; chp = chp->next) { if (last && strcmp(last, chp->token) >= 0) continue; /* lower than the last one we found */ if (!first || strcmp(first, chp->token) > 0) { first = chp->token; found = chp; } } last = first; return (found); } static char * strtoki(char *string, char *sepset) { char *p, *q, *r; static char *savept; /* first or subsequent call */ p = (string == NULL)? savept: string; if (p == NULL) /* return if no tokens remaining */ return (NULL); q = p + strspn(p, sepset); /* skip leading separators */ if (*q == '\0') /* return if no tokens remaining */ return (NULL); if ((r = strpbrk(q, sepset)) == NULL) /* move past token */ savept = 0; /* indicate this is last token */ else { *r = '\0'; savept = ++r; } return (q); }