/* * Utility routines which interact with the user. */ #include #include #include #include #include "pline.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.) * * (Note that CHOK_TERM support is untested.) * * 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); addbytes(buf,len); clrtoeol(); push_sigint_throw(&j); c = getch(); 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. * * 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; int maxl; int l; JMP j; va_start(ap,prompt); n = 0; maxl = 0; while (1) { s = va_arg(ap,const char *); val = va_arg(ap,int); if (! s) break; l = strlen(s); if (l > maxl) maxl = l; n ++; } abortval = val; va_end(ap); if (n < 1) return(abortval); if (n >= LINES) panic("choose_one: too many choices"); if (n >= 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(abortval); } { const char *strings[n]; int vals[n]; char chars[n]; va_start(ap,prompt); for (i=0;i= n)) { beep(); continue; } clear(); showdisp(); val = vals[i]; break; } } return(val); } /* * Ask for yes/no confirmation of some action. ESC and SIGINT are * equivalent to N; keep this in mind when writing the prompt. * * One space is appended to the prompt, but nothing more; the prompt * string should be worded so as to make it clear what is being asked * and what meanings `yes' and `no' have. */ int confirm(const char *prompt) { int c; JMP j; if (setjmp(j.b)) { pop_sigint_throw(); move(LINES-1,0); clrtoeol(); return(0); } while (1) { mvprintw(LINES-1,0,"%s ",prompt); clrtoeol(); push_sigint_throw(&j); c = getch(); pop_sigint_throw(); move(LINES-1,0); clrtoeol(); switch (c) { case 'y': case 'Y': return(1); break; case 'n': case 'N': case '\33': return(0); break; } beep(); } }