#include "fx.h" #include #include #ifdef ARCS_SA #include #else #include #include #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= 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); }