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