1
0
Files
irix-657m-src/eoe/cmd/fx/input.c
2022-09-29 17:59:04 +03:00

1536 lines
30 KiB
C

#include "fx.h"
#include <setjmp.h>
#include <stdarg.h>
#ifdef ARCS_SA
#include <libsc.h>
#else
#include <sys/signal.h>
#include <stdlib.h>
#endif
#ifdef _STANDALONE
# undef stdin
# define stdin 0 /* sa fgets takes a filedescr */
# undef stdout
# define stdout (void*)1L
# undef putchar /* use actual routine for standalone */
#endif
/* references to this generated by menucvt.awk; there used to be
* a seperate copy of the string for each menu. */
const char menu_title[] = "please choose one (? for help, .. to quit this menu)";
/*
* input routines. prompting, aborting, etc. since the basic input
* mechanism is via gets(), all input is cooked ala unix. there are
* 2 kinds of prompts: prompts for a menu choice, and prompts for
* informational parameters.
*/
/*
* structure for control of prompts and quits
*/
typedef struct callrec
{
char *name; /* name for prompting at this level */
jmp_buf lab; /* quit label at this level */
int argc; char **argv; /* argument list at this level */
struct callrec *chain; /* previous level */
} CR;
CR *chead; /* current level of control */
ITEM name_item; /* current item named by nameitem() */
MENU *cmenu; /* current menu used by nameitem() */
MENU *rmenu; /* root of menus used by nameitem() */
MENU *pmenu; /* last menu named by nameitem() */
MENU *prmenu; /* prompt menu */
ITEM *dot_item, *dotdot_item, *help_item;
/* used in standalone.c also (gross, knows jmp_buf's type). */
typedef int64_t *_jmp;
_jmp sig_jmp;
/*
* macros to get funcval fields from item
*/
FUNCVAL **funcvallist;
# define MENUFIELD(t) ((*(funcvallist + (t)->value))->menu)
# define FUNCFIELD(t) ((*(funcvallist + (t)->value))->func)
# define HELPFIELD(t) ((*(funcvallist + (t)->value))->helpstrings)
extern FUNCVAL *scsi_funcvallist[];
/* Pointers to the . .. and help functions for the current driver */
extern ITEM scsi_dot_item, scsi_dotdot_item, scsi_help_item;
extern MENU scsi_fx_menu;
extern MENU *rmenu, *cmenu;
extern int nomenus; /* see fx.c, -c option */
static void printc(CR *cp);
static void menu_mprint(MENU *m);
/*
* flush buffered output if any
*/
void
flushoutp(void)
{
fflush(stdout);
}
/*
* read a line, and split into args. the given buf is divided into
* a data part and an arg pointers part. pass back argc and argv.
*/
void
mgetargs(char *line, uint len, int *_argc, char ***_argv)
{
register char **av; register int n;
ggets(line, len);
/* put arg pointers at the end of data */
av = (char **)(line + strlen(line) + 1);
n = (__psint_t)av % sizeof *av;
if( n != 0 )
av = (char **)((char *)av - n) + 1;
*_argv = av;
av = (char **)(line + len);
av = (char **)((char *)av - n);
/* split the line using the arg pointer part */
*_argc = strbreak(line, *_argv, av - *_argv);
}
/*
* get an input string. strip leading and trailing blanks
* Exits on EOF. Note that it now goes through the 'graceful exit'
* path that check if anything should be flushed, etc.
*/
void
ggets(char *buf, int len)
{
register char *cp = buf;
if(fgets(buf, len, stdin) == NULL) {
newline();
exit_func();
}
while(*cp && *cp != '\n') {
if(cp != buf || !isspace(*cp)) /* ignore leading space */
len--;
cp++;
}
*cp = '\0';
while (cp > buf && isspace(cp[-1]))
*--cp = '\0';
}
/*
* split a line into args, with simple quoting. return the actual
* number of args.
*/
uint
strbreak(char *line, char **vec, int maxvec)
{
register char c, *tp, *sp;
int nvec;
char quotc;
nvec = 0;
sp = line;
for( ;; )
{
while( isspace(*sp) )
sp++;
if( *sp == '\0' )
break;
if( nvec >= maxvec )
return -1;
*vec++ = sp;
nvec++;
quotc = 0;
tp = sp;
while( (c = *sp++) != '\0' && (quotc || !isspace(c)) )
{
if( quotc )
{
if( c == quotc )
quotc = 0;
else
*tp++ = c;
continue;
}
if( c == '\\' )
*tp++ = (*sp == '\0' ? '\\' : *sp++);
else
if( c == '\'' )
quotc = c;
else
*tp++ = c;
}
*tp = '\0';
if( c == '\0' )
break;
}
return nvec;
}
/* ----- menu routines ----- */
/* init the stuff for this drive type. Was part of menuloop, but
* to avoid finding all cases where menu stuff is used, when -c is
* passed, I want to init the menus, but not enter the loop...
*/
void
menuloop_init(void)
{
switch (drivernum) {
case SCSI_NUMBER:
#ifdef SMFD_NUMBER
case SMFD_NUMBER:
#endif /* SMFD_NUMBER */
funcvallist = scsi_funcvallist;
dot_item = &scsi_dot_item;
dotdot_item = &scsi_dotdot_item;
help_item = &scsi_help_item;
init_menus(&scsi_fx_menu);
break;
default:
printf("Don't know how about driver type %d\n",
drivernum);
exit(1);
}
}
/*
* repeated selections from a menu. use the current menu to start with.
* subsequent function calls may change the current menu. this is the
* Only way to start up nameitem() correctly.
*/
void
menuloop(void)
{
for( ;; ) {
argcheck();
callmenu(cmenu);
}
}
/*
* traverse the menu tree to find the named item. return a pointer.
* global inputs:
* rmenu is a pointer to the root menu.
* cmenu is a pointer to the current menu.
* dot_item is just a handle on a dummy item and funcval.
* dotdot_item is a legit item which executes ".." from the menu root.
* help_item is a legit item which implements the help function.
* dot_item, dotdot_item, and help_item are created by the menu scripts.
*
* side-effects:
* name_item the returned item.
* pmenu is a pointer to the parent menu of the item.
*
* the caller has already set up the "current" menu and path.
*
* there are 2 special cases: "?" and "..". for "?", fake an item
* with the right menu pointer. for "..", back up one level of menu,
* or pop back one level of control if already at the root.
*
* We used to print the menu #, but it turns out that the
* numbers are confusing, because they change from release
* to release, and depending on whether or not expert mode is on
* in some cases, and worst of all, the same number can mean
* different things for different drive types. So now it is
* just the item, along with a visual indication of the least ambiguous
* prefix. Olson, 3/91.
*/
static ITEM *
nameitem(char *path)
{
register ITEM *t;
register MENU *m;
STRBUF comp;
register int i;
register char c;
m = cmenu;
c = *path++;
if( c == '/' )
{
m = rmenu;
while( c == '/' )
c = *path++;
}
for( ;; )
{
pmenu = m;
if( c == '\0' )
{
t = dot_item;
name_item.value = t->value;
name_item.name = m->name;
MENUFIELD(&name_item) = m;
return &name_item;
}
i = 0;
while( c != '\0' && c != '/' )
{
comp.c[i++] = c;
c = *path++;
}
comp.c[i] = '\0';
while( c == '/' )
c = *path++;
if( strcmp(comp.c, "..") == 0 )
{
m = m->dotdot;
if( m != 0 )
continue;
t = dotdot_item;
}
else
if( strcmp(comp.c, "?") == 0 )
{
t = help_item;
}
else
{
if(!(t = matchitem(m, comp.c, 0)))
return 0;
m = MENUFIELD(t);
}
if( c == '\0' )
{
name_item = *t;
return &name_item;
}
if( m == 0 )
{
printf("not a menu\n");
return 0;
}
}
}
/*
* prompt with a menu, and implement the response.
*/
void
callmenu(MENU *m)
{
char line[INBUFSIZE]; int argc; char **argv;
register ITEM *t;
if( m->nitems <= 0 )
menu_digest(m);
if(m->initfunc) /* call initial function, if set */
(*m->initfunc)();
newline();
menu_mprint(m);
prmenu = m;
prompt("> ");
mgetargs(line, sizeof line, &argc, &argv);
if( argc <= 0 )
return;
if( (t = nameitem(*argv)) != 0 )
{
*argv = t->name;
prmenu = pmenu;
if( MENUFIELD(t) == 0 )
newline();
callsub(FUNCFIELD(t), argc, argv);
return;
}
}
/*
* match a string against a menu, and return the corresponding item.
*/
ITEM *
matchitem(MENU *m, char *s, int numok)
{
register int n;
if( (n = match(m, s, numok)) < 0 )
return 0;
return m->items+n;
}
/*
* validate a numeric string. return true if it is syntactically ok,
* pass back the number in *ip
*/
iscnum(char *src, uint64_t *ip)
{
return *src != '\0' && *skipcnum(src, 0, ip) == '\0';
}
/*
* match a string against a menu, and return the index of the
* corresponding item.
* BUT disallow match if item is 'expert' & expert mode is not set.
* Need to do expert check here rather than matchitem, so we don't get
* bogus ambiguous matches on menu choices that aren't visible!
* Numeric input allowed for numok. This is currently
* used ONLY for selecting a (non-scsi) drive type from a known list.
*/
int
match(MENU *m, char *s, int numok)
{
register int nmatches, ix;
uint64_t l;
register ITEM *t;
if( m->nitems <= 0 )
menu_digest(m);
if(numok && iscnum(s, &l))
{
if( 0 < (int)l && l <= m->nitems)
return (int)l-1;
printf("%d is out of range\n", (int)l);
return -1;
}
nmatches = 0;
/*
* search for a menu item with a matching item.
* if there is an exact match, return its index.
* otherwise, if the number of items with matching
* prefix is exactly one, return its index. if
* more or less than one, say so. Use m->nitems
* so we don't check invisible expert mode stuff when
* not in in expert mode...
*/
for( t = m->items; t < &m->items[m->nitems]; t++ )
{
if( strcmp(t->name, s) == 0 )
{
ix = t - m->items;
return ix;
}
if( wmatch(t->name, s) )
{
if(++nmatches > 1) {
if(nmatches == 2) /* on 2nd match, print msg and 1st */
printf("%s is ambiguous: %s", s, m->items[ix].name);
printf(", %s", t->name);
}
else
ix = t - m->items;
}
}
if( nmatches == 1 )
return ix;
if( nmatches <= 0 )
{
printf("No match found for \"%s\"\n", s);
return -1;
}
newline(); /* ambiguous, print the newline */
return -1;
}
/*
* match an item name against a string, and return true if there is
* a matching prefix. if it is a compound item name, try matching
* against its subparts; _ is NOT considered punctuation.
*/
int
wmatch(char *iname, char *s)
{
if( prefmatch(iname, s) )
return 1;
while( *iname != '\0' )
{
if( isspace(*iname) || (*iname != '_' && ispunct(*iname)))
if( prefmatch(iname+1, s) )
return 1;
iname++;
}
return 0;
}
/*
* match a particular subpart of an item name against a string, and
* return true if they match. disregard punctuation in the item name,
* and spaces in the string. at least one char must match.
*/
int
prefmatch(char *ipart, char *str)
{
register char *s;
s = str;
for( ;; )
{
if( *ipart != *s )
{
while( isspace(*ipart) || ispunct(*ipart) )
ipart++;
while( isspace(*s) )
s++;
}
if( *s == '\0' )
return s > str;
if( *ipart != *s )
return 0;
ipart++ , s++;
}
}
/*
* set off a string with dashes
*/
void
setoff(const char *s, ...)
{
va_list args;
printf("----- ");
va_start(args, s);
vprintf(s, args);
va_end(args);
printf("-----\n");
}
void
newline(void)
{
printf("\n");
}
/*
* warning banner
*/
void
banner(char *s)
{
register int i;
for( i = 5; --i >= 0; )
printf(" *");
while( *s != 0 )
printf(" %c", *s++);
for( i = 5; --i >= 0; )
printf(" *");
newline();
}
/*
* initialize menu tree
*/
void
init_menus(MENU *m)
{
rmenu = cmenu = m;
menu_traverse(rmenu);
}
/*
* search the menu tree for submenus, and create dotdot links
*/
void
menu_traverse(MENU *m)
{
register ITEM *t;
menu_digest(m);
for( t = m->items; t->name != 0; t++ )
if( MENUFIELD(t) != 0 )
{
MENUFIELD(t)->dotdot = m;
menu_traverse(MENUFIELD(t));
}
}
/*
* set up internal menu parameters, how long and how wide.
*/
void
menu_digest(MENU *m)
{
ITEM *t, *t1;
int i, j, k;
m->nitems = 0;
m->maxwidth = 0;
for( t = m->items; t->name != 0; t++ )
{
if (t->expert && !expert) break; /* Note: all nonexpert items */
/* MUST come first in a menu. */
i = strlen(t->name);
if( i > m->maxwidth )
m->maxwidth = i;
m->nitems++;
}
/* set the number of chars needed for uniqueness in menu
* items, to clue the user. */
for(i=0, t=m->items; i < m->nitems; t++, i++) {
t->uniqchars = 1;
for(j=0, t1=m->items; j < m->nitems; t1++, j++) {
if(t == t1)
continue;
k = strlen(t->name);
while(strncmp(t->name, t1->name, t->uniqchars) == 0
/*
* Don't continue checking if name is subset of
* another name
*/
&& t->uniqchars < k)
t->uniqchars++;
}
}
}
/*ARGSUSED*/
static void
menusub(int i, ITEM *t, char *tgt)
{
sprintf(tgt, "[%.*s]%s", t->uniqchars, t->name,
t->name+t->uniqchars);
}
static void
menunumsub(int i, ITEM *t, char *tgt)
{
sprintf(tgt, "%d) %s", i, t->name);
}
/* print a menu with / denoting submenus */
/*ARGSUSED*/
static void
menumsub(int i, ITEM *t, char *tgt)
{
sprintf(tgt, "[%.*s]%s%s", t->uniqchars, t->name,
t->name+t->uniqchars, MENUFIELD(t) != 0 ? "/" : "");
}
static void
menu_print(MENU *m, int numok)
{
if(nomenus) return;
setoff(m->title);
colprint(m->items, m->nitems, sizeof *m->items,
m->maxwidth+2, numok ? menunumsub : menusub);
}
static void
menu_mprint(MENU *m)
{
if(nomenus) return;
setoff(m->title);
colprint(m->items, m->nitems, sizeof *m->items,
m->maxwidth+2, menumsub);
}
/*
* return a symbolic interpretation of flag bits
*/
char *
attribs(ITEM *items, int flags)
{
static STRBUF s;
register ITEM *t;
register char *cp, *f;
cp = s.c;
f = "";
for( t = items; t->name != 0; t++ )
if( (flags & t->value) != 0 )
{
sprintf(cp, "%s%s", f, t->name);
cp += strlen(cp);
flags &= ~t->value;
f = "|";
}
if( flags != 0 || cp == s.c )
sprintf(cp, "%s0x%x", f, flags);
return s.c;
}
/* ----- menu routines with prompting and quit control ----- */
/*
* setup to call the named subroutine with no arguments
*/
void
callfunc(void (*sub)(void), char *line)
{
int argc; char *argv[MAX_VEC];
argc = strbreak(line, argv, MAX_VEC);
callsub(sub, argc, argv);
}
/*
* set up control record (prompt, arguments, and quit label) for
* and call the given subroutine.
*/
void
callsub(void (*sub)(void), int argc, char **argv)
{
volatile int oldintr; /* ensure on stack for restoring */
CR c1;
argc--; c1.name = *argv++;
c1.argc = argc; c1.argv = argv;
c1.chain = chead;
chead = &c1;
sig_jmp = (_jmp)chead->lab;
if(setjmp(sig_jmp))
exercising = 0; /* see gread() */
else {
(void)setintr(oldintr = setintr(0));
(*sub)();
}
/* do setintr after chead changed, so we go to the prev chead->lab */
chead = c1.chain;
sig_jmp = (_jmp)chead->lab;
(void)setintr(oldintr);
}
/*
* print prompt by tracing back through control records.
*/
void
prompt(char *s)
{
if(!chead) return; /* early error message, with nomenus */
printc(chead);
printf("%s", s);
}
static void
printc(CR *cp)
{
if( cp->chain == 0 )
{
printf("%s", cp->name);
prmenupath(prmenu);
return;
}
printc(cp->chain);
printf("/%s", cp->name);
}
/*
* print a menu path, ala pwd
*/
void
prmenupath(MENU *m)
{
if( m == 0 || m->dotdot == 0 )
return;
prmenupath(m->dotdot);
printf("/%s", m->name);
}
/*
* change the current menu to the just-named menu from nameitem().
*/
void
dot_func(void)
{
argcheck();
cmenu = MENUFIELD(&name_item);
}
/*
* dotdot out of the root menu. optionally update the disk label.
* called as a menu function, 2 levels are popped to accomplish the
* desired purpose.
*/
void
dotdot_func(void)
{
optupdate();
chead = chead->chain;
sig_jmp = (_jmp)chead->lab;
mpop();
}
/*
* pop out of the current level of control.
*/
void
mpop(void)
{
/* this is done for the nomenus (-c) code, so that anybody causing
* an error causes us to exit. */
if(nomenus)
exit(2); /* semi-unique error code, so we can tell we pop'ed */
longjmp(sig_jmp, 1);
}
/* ----- */
/* ---- printing by cols ----- */
static uint linewidth = 79; /* max # characters on a line */
uint minspacing = 3; /* min width of a word column */
uint maxspacing = 8; /* max width of a word column */
/*
* print a table by columns. compute number and width of columns to
* make it look nice.
*/
void
colprint(void *p, uint n, uint size, uint w, void (*sub)(int,char *,char *))
{
STRBUF s;
register int wordwidth, rowsize;
int nrows, ncols, itemlag;
register int row, col, i;
register char *t;
if( n <= 0 )
return;
/*
* first see how many word columns will fit on one line.
* make sure word columns are at least minwidth in width.
* then check if the number of columns can be reduced
* without increasing the number of rows
*/
wordwidth = w + minspacing;
ncols = (linewidth + minspacing) / wordwidth;
nrows = (n + ncols - 1) / ncols;
ncols = (n + nrows - 1) / nrows;
/*
* now try to distribute the word columns evenly across the line,
* but don't let them get spaced too far apart
*/
wordwidth = linewidth / ncols;
if( wordwidth > w + maxspacing )
wordwidth = w + maxspacing;
rowsize = nrows * size;
itemlag = 0;
for( row = 0; row < nrows; row++ )
{
t = (char *)p + row * size;
i = row + 1;
for( col = 0; col < ncols; col++ )
{
if( i > n )
break;
(*sub)(i, t, s.c);
/* The following condition is true only for ARCS. Do not
print non usable items.
*/
if ((strncmp("[n1",s.c,3) == 0) || (strncmp("[n2",s.c,3) == 0))
while( --itemlag >= 0 );
else {
while( --itemlag >= 0 )
putchar(' ');
printf("%s", s.c);
}
itemlag = wordwidth-strlen(s.c);
t += rowsize;
i += nrows;
}
newline();
itemlag = 0;
}
}
/* ----- interactive hacks ----- */
/*
* interactively get a string. prompt using the given name. pass back
* the string in str. if the default string is not 0, use it if the
* response is null.
*/
void
getstring(char *str, char *dfl, char *name)
{
STRBUF s;
register int firsttime, ointr;
for( firsttime = 1;; firsttime = 0 )
{
if( !firsttime /* || dfl == 0 */ )
printf("please enter a string\n");
prompt(": "); printf("\"%s\" = ", name);
if( dfl != 0 ) printf("(%s) ", dfl);
ointr = mkintr();
ggets(s.c, sizeof s.c);
(void)setintr(ointr);
if( qcheck(s.c) )
continue;
if( *s.c == '\0' && dfl != 0 )
strcpy(str, dfl);
else
strcpy(str, s.c);
break;
}
}
/* get a fractional answer; return the value. Only used interactively.
very special purpose, returns numerator where divisor is given.
Used for the buffer full/empty stuff for CDC drives. Fraction
will be >= 0 and < 1.
*/
getfrac(uint curval, uint divisor, char *prmpt)
{
char line[INBUFSIZE]; int argc; char **argv;
int ointr;
uint64_t tmpn;
for(;;) {
prompt(": ");
printf("%s = (%u/%u) ", prmpt, curval,divisor);
ointr = mkintr();
mgetargs(line, sizeof line, &argc, &argv);
(void)setintr(ointr);
if(argc == 0)
return curval; /* accept default */
if(!qcheck(*argv) && argc == 1 && iscnum(*argv, &tmpn) &&
tmpn < divisor)
return tmpn;
printf("please enter the numerator so the ratio is between 0 and 1\n");
}
}
/* get cnt numbers with no prompts, but with error checking. Returns #
* of numbers retrieved; 0 if none, -1 if something entered, but not a
* number, or out of range.
*/
get_nums(uint64_t hi, uint64_t *n, uint cnt)
{
char line[INBUFSIZE];
int argc;
char **argv;
int i, ointr;
ointr = mkintr();
mgetargs(line, sizeof line, &argc, &argv);
(void)setintr(ointr);
if( argc > 0 ) {
if(qcheck(*argv))
return -1; /* help wanted */
if(argc > cnt)
return -1;
}
else
return argc;
for(i=0 ; i<argc; i++, argv++) {
if(!iscnum(*argv, n)) {
printf("%s isn't valid\n", *argv);
break;
}
if(hi && (*n >= hi)) {
printf("%llu out of range\n", *n);
break;
}
n++;
}
if(i != argc)
return -1;
return i;
}
/*
* interactively get a number quantity. prompt using the given name.
* pass back the number via _n.
* If there is an upper limit (hi non-zero), and an out of range
* number is given, give them the range and re-prompt.
* Pop back if the response is 'q'.
* Return 0 if the user accepts the default value, else 1.
*/
getnum(uint64_t *_n, uint64_t hi, char *name)
{
uint64_t n;
int ret;
for(;;) {
n = *_n;
prompt(": ");
printf("%s = ", name);
printf("(%llu) ", n);
ret = get_nums(hi, &n, 1);
if(ret == 1 || ret == 0) /* gave us 1, or used default */
break;
printf("please enter a number");
if(hi)
printf(" from 0..%llu", hi-1);
newline();
}
*_n = n;
return ret;
}
/*
* convert (initial) string to number,
* This is almost identical to the new stroul() ansi routine.
* it's so close, let's just use strtoull()...
*/
char *
skipcnum(char *sp, int defradix, uint64_t *ip)
{
char *nsp;
*ip = strtoull(sp, &nsp, defradix);
if(nsp == sp)
*ip = 0;
return nsp;
}
/*
* decode the given string as a block number or cyl/hd/sector. passes back
* the block number in _bn. returns 1 if the string is syntactically ok.
*/
isbn(char *str, daddr_t *_bn)
{
daddr_t b;
uint64_t l;
if( iscnum(str, &l) )
b = l;
else
return 0;
*_bn = b;
return 1;
}
/*
* interactively get a (daddr_t) block number. the number may be entered
* as cyl/hd/sec. prompt using the given name. pass back the block
* number via _n. if the initial value of *_n is >=0, use it as the default.
* pop back if the response is 'q'.
*/
void
getbn(daddr_t *_bn, daddr_t hi, char *name)
{
char line[INBUFSIZE]; int argc; char **argv;
daddr_t b;
register int firsttime, ointr;
int result;
for( firsttime = 1;; firsttime = 0 )
{
b = *_bn;
if( !firsttime || b < 0 )
{
printf("please enter a bn");
if( hi > 0 )
printf(" from 0 to %lld", hi-1);
newline();
}
prompt(": "); printf("%s = ", name);
if( b >= 0 )
printf("(%llu) ", b);
ointr = mkintr();
mgetargs(line, sizeof line, &argc, &argv);
(void)setintr(ointr);
if(argc > 0 && qcheck(*argv))
continue;
if(argc == 1)
result = isbn(*argv, &b);
else
result = !argc; /* error if argc not 0 */
if(!result || b < 0) {
printf("?\n");
continue;
}
if( hi > 0 )
if( !(0 <= b && b < hi) )
{
printf("%s out of range (%lld not between 0 and %lld)\n", name,
b, hi);
continue;
}
break;
}
*_bn = b;
}
/*
* interactively get a menu choice (index). prompt using the given name.
* pass back the choice via _n. if the initial value of *_n is >=0, use
* it as the default. pop back if the response is '..'.
*/
void
getchoice(int *_n, MENU *m, const char *name, int numok)
{
char line[INBUFSIZE]; int argc; char **argv;
register int n;
int firsttime;
if( m->nitems <= 0 )
menu_digest(m);
for( firsttime = 1;; firsttime = 0 )
{
n = *_n;
if( !firsttime || n < 0 )
menu_print(m, numok);
prompt(": "); printf("%s = ", name);
if (n >= 0 && n < m->nitems)
printf("(%s) ", m->items[n].name);
(void)mkintr();
mgetargs(line, sizeof line, &argc, &argv);
(void)setintr(0);
if( argc > 0 )
{
if( qcheck(*argv) )
continue;
if(argc > 1 || (n = match(m, *argv, numok)) < 0 ) {
if(argc > 1) printf("?\n");
/* else match printed a message */
continue;
}
}
if( !(0 <= n && n < m->nitems) )
{
printf("out of range\n");
continue;
}
break;
}
*_n = n;
}
/*
* check an argument number string for legality. pass back the number
* via _n. if *_n >= 0, use it as the default. pop back if illegal.
*/
static void
checknum(char *str, uint64_t *_n, uint64_t hi, char *name)
{
uint64_t n;
n = *_n;
if(!*str || !iscnum(str, &n))
argerr("non-numeric %s", name);
if(hi && n >= hi)
argerr("%s out of range 0..%d", name, hi-1);
*_n = n;
}
/*
* check an argument block number string for legality. pass back the
* number via _n. if *_n >= 0, use it as the default. pop back if illegal.
*/
void
checkbn(char *str, daddr_t *_bn, daddr_t hi, char *name)
{
daddr_t b;
b = *_bn;
if( !(b >= 0 && *str == '\0') )
if( !isbn(str, &b) )
argerr("unrecognizable %s", name);
if( !(0 <= b && b < hi) )
argerr("%s out of range 0..%d", name, hi-1);
*_bn = b;
}
/*
* get a number parameter from the arglist, or by prompting.
* this one really does take an int, not an int64.
*/
argnum(uint *(_n), uint dfl, uint hi, char *name)
{
uint64_t tmp;
int ret;
*_n = dfl;
tmp = (uint64_t)dfl;
if( noargs() ) {
ret = getnum(&tmp, (uint64_t)hi, name);
*_n = (uint)tmp;
return ret;
}
else {
checknum(getarg(), &tmp, (uint64_t)hi, name);
*_n = (uint)tmp;
return 1;
}
}
/*
* get a block number from the arglist, or by prompting.
*/
void
argbn(daddr_t *_bn, daddr_t dfl, daddr_t hi, char *name)
{
*_bn = dfl;
if( noargs() )
getbn(_bn, hi, name);
else
checkbn(getarg(), _bn, hi, name);
}
/*
* get a slice from the arglist, or by prompting.
* this also is really just int, not int64
*/
void
argslice(uint *_slice, uint dfl, uint hi, char *name)
{
uint64_t tmp;
*_slice = dfl;
tmp = (uint64_t)dfl;
if( noargs() )
getnum(&tmp, (uint64_t)hi, name);
else
checknum(getarg(), &tmp, (uint64_t)hi, name);
*_slice = (uint)tmp;
}
/*
* validate a choice. if invalid, report it and pop out of the current func
*/
static
checkchoice(char *s, int *_n, MENU *m)
{
int n;
n = *_n;
if( !(n >= 0 && *s == '\0') )
if( (n = match(m, s, 0)) < 0 )
mpop();
*_n = n;
return 0;
}
/*
* get a menu choice from the arglist, or by prompting.
*/
void
argchoice(int *_n, int dfl, MENU *m, const char *name)
{
*_n = dfl;
if( noargs() )
getchoice(_n, m, name, 0);
else
checkchoice(getarg(), _n, m);
}
/*
* get a yes-no answer, default given by yflag.
* return true if the answer is yes.
*/
yesno(int yflag, char *s)
{
char line[INBUFSIZE]; int argc; char **argv;
int y;
register int firsttime, ointr;
if(nomenus)
return 0; /* shouldn't be here normally; when we do get here,
* we don't want to take the default action */
for( firsttime = 1;; firsttime = 0 )
{
y = yflag;
if( !firsttime /* || yflag < 0 */ )
printf("please enter a yes or no\n");
printf("%s? ", s);
if( y >= 0 )
printf("(%s) ", y? "yes" : "no");
ointr = mkintr();
mgetargs(line, sizeof line, &argc, &argv);
(void)setintr(ointr);
if( argc > 0 )
{
if( qcheck(*argv) )
continue;
if( argc == 1 && wmatch("yes", *argv) )
{
y = 1;
break;
}
if( argc == 1 && wmatch("no", *argv) )
{
y = 0;
break;
}
printf("?\n");
continue;
}
if( y >= 0 )
break;
}
return y;
}
/*
* get a yes-no answer, default yes.
* return true if the answer is yes.
*/
yes(char *s)
{
return yesno(1, s);
}
/*
* get a yes-no answer, default no.
* return true if the answer is no.
*/
no(char *s)
{
return !yesno(0, s);
}
/*
* get a string parameter from the arglist, or by prompting.
*/
void
argstring(char *str, char *dfl, char *name)
{
if( !noargs() )
checkstring(str, dfl);
else
getstring(str, dfl, name);
}
/*
* validate a string argument.
*/
void
checkstring(char *str, char *dfl)
{
strcpy(str, getarg());
if( *str == '\0' && dfl != 0 )
strcpy(str, dfl);
}
/*
* return true if the arglist is exhausted
*/
noargs(void)
{
return chead->argc <= 0;
}
/*
* ensure that the arglist is exhausted.
* if not, pop out of the current func
*/
void
argcheck(void)
{
if(!nomenus && !noargs())
argerr("arg count");
}
/*
* get the current arg from the arglist
*/
char *
getarg(void)
{
if( --chead->argc >= 0 )
return *chead->argv++;
return 0;
}
/*
* get flags from the arglist. cp is a list of flag chars, fp is a list
* of flag variables.
*/
void
checkflags(char *cp, char *fp)
{
register CR *crp;
register char *ap;
register int i;
char argbotch;
argbotch = 0;
crp = chead;
for( i = 0; cp[i] != '\0'; i++ )
fp[i] = 0;
while( crp->argc > 0 && *(ap = *crp->argv) == '-' )
{
crp->argc--; crp->argv++;
while(*++ap)
{
for( i = 0; cp[i] != '\0'; i++ )
if( cp[i] == *ap )
break;
if( cp[i] == '\0' )
{
errwarn("unknown flag %c", *ap);
argbotch++;
continue;
}
fp[i]++;
}
}
if( argbotch )
argerr("legal flags are: %s", cp);
}
/* ----- help routines ----- */
/*
* check for "quit" response; goto nearest quit label if there is a match.
* check for "help" response; return 1 to caller if there is a match.
*/
int
qcheck(char *s)
{
if( strcmp("..", s) == 0 )
mpop();
return strcmp("?", s) == 0;
}
/*
* print a pair of help strings in our standard format
* if first string is empty, this is a continuation line,
* so don't print the -, it looks strange, particular
* for the -options help lines.
*/
static void
helpprint(char *a, char *b)
{
printf("%-20s %s %s\n", a, *a ? "-" : " ", b);
}
/*
* help for a (sub)menu. print help for each of its items.
*/
static void
subhelp(MENU *m, char *s)
{
register ITEM *t;
if( s != 0 )
setoff(s);
for( t = m->items; t->name != 0; t++ ){
if (t->expert && !expert) break;
helpitem(0, t);
}
}
/*
* the help function. print long help for each named function,
* or if there are no named items, short help for each item in
* the current menu.
*/
void
help_func(void)
{
register ITEM *t;
register char *ap;
if( noargs() ) {
printf("On all menus, only the shortest unique prefix of the choice\n"
" needs to be typed. \"..\" returns to the level above, or\n"
" ends the entry of a list of items. Items ending in / have\n"
" further menus. ? \"somechoice\" gives help on that choice.\n\n");
subhelp(cmenu, 0);
return;
}
while( (ap = getarg()) != 0 )
{
if( (t = nameitem(ap)) != 0 )
{
helpitem(1, t);
continue;
}
printf("no help for %s\n", ap);
}
}
/*
* print help for one item, which may or may not be a menu
*/
void
helpitem(int n, register ITEM *t)
{
if( MENUFIELD(t) != 0 )
menuhelp(n, t);
else
funchelp(n, t);
}
/*
* print help for one function item
*/
void
funchelp(int n, ITEM *t)
{
register char **i;
i = HELPFIELD(t);
helpprint(i[0], i[1]);
i += 2;
if( n )
while( *i != 0 )
helpprint("", *i++);
}
/*
* print help for one non-function item
*/
void
menuhelp(int n, ITEM *t)
{
funchelp(n, t);
if( n )
subhelp(MENUFIELD(t), t->name);
}