/* * Utility routines which interact with the user. */ #include #include #include #include #include "pline.h" #include "debug.h" #include "structs.h" #include "signals.h" #include "display.h" #include "disputil.h" /* * Display a prompt and accept an input string. prompt is, of course, * the prompt. buf and buflen describe the buffer available for the * input string. flags is a bitmask, containing zero or more of * * PRF_NOSTD * Don't handle the standard characters; pass _all_ typed * input to the approval function. * * approval is a function which decides what to do in response to each * typed character. Its return value is one of * * CHOK_OK * Accept this character and add it to the string; * continue reading. * CHOK_BAD * Don't accept this character, but do continue reading. * CHOK_TERM * Accept this character, add it to the string, but also * terminate the read operation. * CHOK_TERMNS * Don't add this character to the string, but do * terminate the read with the string so far. * CHOK_ABORT * Abort the read operation. * CHOK_DEL * Delete the previous character, if any. (Like termios's * ERASE character.) * CHOK_CLEAR * Delete the entire string-so-far. (Like termios's KILL * character.) * * PRF_NOSTD is implemented by using an internal approval function * which handles the standard characters and, for other characters, * calls the provided approval function. * * A SIGINT received during this produces an abort condition, as if it * had been a CHOK_ABORT character. * * There is no line editing beyond CHOK_DEL and CHOK_CLEAR. * * The standard characters are * * ESC CHOK_ABORT * CR (^M) CHOK_TERMNS * LF/NL (^J) CHOK_TERMNS * DEL CHOK_DEL * BS (^H) CHOK_DEL * CAN (^X) CHOK_CLEAR * NAK (^U) CHOK_CLEAR * * Note that a terminating NUL is always provided on successful * returns, so the maximum string length accepted is buflen-1. * * The return value is one of * * PR_OK * Everything went fine and a string was entered and * terminated normally. * PR_ABORTED * Something happened to abort the read: a SIGINT or a * CHOK_ABORT character. */ PRSTAT prompt_and_read(const char *prompt, char *buf, int buflen, unsigned int flags, CHOK (*approval)(char)) { CHOK (*app)(char); int len; int c; CHOK ok; JMP j; CHOK defapp(char ch) { switch (ch) { case '\33': return(CHOK_ABORT); break; case '\r': case '\n': return(CHOK_TERMNS); break; case '\177': case '\b': return(CHOK_DEL); break; case '\030': case '\025': return(CHOK_CLEAR); break; } return((*approval)(ch)); } if (setjmp(j.b)) { pop_sigint_throw(); move(LINES-1,0); clrtoeol(); return(PR_ABORTED); } app = (flags & PRF_NOSTD) ? approval : &defapp; len = 0; while (1) { mvaddstr(LINES-1,0,prompt); addnstr(buf,len); clrtoeol(); push_sigint_throw(&j); c = tget(); pop_sigint_throw(); ok = (*app)(c); switch (ok) { case CHOK_OK: if (len >= buflen-1) { beep(); break; } buf[len++] = c; break; case CHOK_BAD: beep(); break; case CHOK_TERM: if (len >= buflen-1) { beep(); break; } buf[len++] = c; /* fall through */ case CHOK_TERMNS: buf[len] = '\0'; move(LINES-1,0); clrtoeol(); return(PR_OK); break; case CHOK_ABORT: move(LINES-1,0); clrtoeol(); return(PR_ABORTED); break; case CHOK_DEL: if (len > 0) len --; break; case CHOK_CLEAR: len = 0; break; default: panic("bad return %d from approval function in prompt_and_read",(int)ok); break; } } } /* * Ask the user to choose one of a small number of choices, internal * version. * * This version takes a prompt and a list of string choices. The * return value is the index into the choice list, or -1 if aborted. * * It is an error if there are more choices than will fit on the * display or more choices than 26 (letters available), or if any * single choice string is longer than 70 characters. * * SIGINT also aborts the choice, like ESC. * * If the argument list does not contain at least one choice - ie, if * str1 is nil - then the operation returns the abort value * immediately, without any user interaction. */ static int choose_one_internal(const char *prompt, const char * const *svec, int vecn) { int i; int maxl; int l; JMP j; maxl = 0; for (i=0;i maxl) maxl = l; } if (vecn < 1) return(-1); if (vecn >= LINES) panic("choose_one: too many choices"); if (vecn >= 26) panic("choose_one: too many choices"); if (maxl > 70) panic("choose_one: choice too long"); if (setjmp(j.b)) { pop_sigint_throw(); move(LINES-1,0); clrtoeol(); return(-1); } if (vecn == 1) { mvprintw(0,0,"a) %s ",svec[0]); } else { for (i=0;i= 0) && (i < vecn)) { clear(); showdisp(); return(i); } beep(); } } /* * Ask the user to choose one of a small number of choices. * * The calling pattern is * * choose_one(prompt,str1,ret1,str2,ret2,...,strN,retN,(char *)0,retE) * * If str1 is chosen, ret1 is returned; if str2 is chosen, ret2 is * returned, etc. If ESC is entered, cancelling the choice, retE is * returned. * * It is an error if there are more choices than will fit on the * display or more choices than 26 (letters available), or if any * single choice string is longer than 70 characters. * * SIGINT also aborts the choice, like ESC. * * If the argument list does not contain at least one choice - ie, if * str1 is nil - then the operation returns the abort value * immediately, without any user interaction. */ int choose_one(const char *prompt, ...) { int n; va_list ap; const char *s; int val; int i; int abortval; va_start(ap,prompt); n = 0; while (1) { s = va_arg(ap,const char *); val = va_arg(ap,int); if (! s) break; n ++; } abortval = val; va_end(ap); if (n < 1) return(abortval); { const char *strings[n]; int vals[n]; va_start(ap,prompt); for (i=0;i