/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "errf.h" #include "panic.h" #include "config.h" #include "nested.h" #include "escaped.h" #include "stdio-util.h" #include "interact.h" static int pp_initted = 0; static int pp_ifd; static int pp_ofd; static int tio_saved; static int flags_saved; #define SAVE_FLAGS (O_NONBLOCK) static struct termios tio_old; static struct termios tio_new; static void get_pp_fds(void) { if (! pp_initted) { pp_ifd = open("/dev/tty",O_RDWR,0); if (pp_ifd < 0) { pp_ifd = 0; pp_ofd = 1; } else { pp_ofd = pp_ifd; } pp_initted = 1; } } static void save_tio(int fd) { tio_saved = (tcgetattr(fd,&tio_old) >= 0); flags_saved = fcntl(fd,F_GETFL,0) & SAVE_FLAGS; } static void restore_tio(int fd) { tcsetattr(fd,TCSANOW,&tio_old); fcntl(fd,F_SETFL,(fcntl(fd,F_GETFL,0)&~SAVE_FLAGS)|flags_saved); } static void echo_off(int fd) { if (tio_saved) { tio_new = tio_old; tio_new.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOPRT|ECHOCTL); if (tio_old.c_lflag & ECHO) tio_new.c_lflag |= ECHONL; tcsetattr(fd,TCSANOW,&tio_new); } } static void nbio_off(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)&~O_NONBLOCK); } static void prompt(int to, const char *fmt, va_list ap) { FILE *f; NESTED int w(void *cookie __attribute__((__unused__)), const char *buf, int len) { return(write(to,buf,len)); } f = fwopen(0,w); vfprintf(f,fmt,ap); fclose(f); } void get_with_prompt(char **sp, int *lp, unsigned int flags, const char *fmt, ...) { va_list ap; FILE *f; int r; char c; int ifd; int ofd; int eofs; if (config_bool("no-interaction")) panic("not interactive"); if (flags & GWP_DEV_TTY) { get_pp_fds(); ifd = pp_ifd; ofd = pp_ofd; } else { ifd = 0; ofd = 1; } save_tio(ifd); if (flags & GWP_NO_ECHO) echo_off(ifd); nbio_off(ifd); if (flags & GWP_ESC_BLK) { int len; NESTED int w(void *cookie __attribute__((__unused__)), const char *buf, int len) { return(write(ofd,buf,len)); } f = fwopen(0,&w); va_start(ap,fmt); len = va_arg(ap,int); va_end(ap); print_escaped(f,fmt,len,len); fclose(f); } else { va_start(ap,fmt); prompt(ofd,fmt,ap); va_end(ap); } f = fopen_alloc(sp,lp); eofs = 0; while (1) { r = read(ifd,&c,1); switch (r) { case -1: switch (errno) { case EINTR: continue; break; default: fprintf(errf,"%s: read: %s\n",__progname,strerror(errno)); exit(1); break; } break; case 0: fprintf(errf,"%s: read EOF\n",__progname); if (eofs++ > 20) exit(1); break; case 1: if (eofs) eofs --; break; default: fprintf(errf,"%s: read returned %d?\n",__progname,r); exit(1); break; } if ((c == '\n') || (c == '\r')) { fclose(f); restore_tio(ifd); return; } putc(c,f); } } int prompted_choice(const char *prompt, int nresp, ...) { va_list ap; const char **resps; int *lens; int i; char *resp; int rlen; int s; if (config_bool("no-interaction")) panic("not interactive"); resps = malloc(nresp*sizeof(const char *)); lens = malloc(nresp*sizeof(int)); va_start(ap,nresp); for (i=0;i (1) { get_with_prompt(&resp,&rlen,GWP_DEV_TTY,"%s",prompt); while ((rlen > 0) && isspace((unsigned char)resp[rlen-1])) rlen --; if (rlen < 1) continue; for (s=0;(s