/* source: xio-readline.c */ /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for opening the readline address */ #include "xiosysincludes.h" #include "xioopen.h" #include "xio-termios.h" #include "xio-readline.h" #if WITH_READLINE /* options: history file prompt mode=vi? inputrc=? uses stdin!! */ /* length of buffer for dynamic prompt */ #define READLINE_MAXPROMPT 512 static int xioopen_readline(int argc, const char *argv[], struct opt *opts, int rw, xiofile_t *xfd, const struct addrdesc *addrdesc); const struct addrdesc xioaddr_readline = { "READLINE", 3, xioopen_readline, GROUP_FD|GROUP_TERMIOS|GROUP_READLINE, 0, 0, 0 HELP(NULL) }; const struct optdesc opt_history_file = { "history-file", "history", OPT_HISTORY_FILE, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.readline.history_file) }; const struct optdesc opt_prompt = { "prompt", NULL, OPT_PROMPT, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.readline.prompt) }; const struct optdesc opt_noprompt = { "noprompt", NULL, OPT_NOPROMPT, GROUP_READLINE, PH_LATE, TYPE_BOOL, OFUNC_SPEC, 0 }; const struct optdesc opt_noecho = { "noecho", NULL, OPT_NOECHO, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_SPEC, 0 }; static int xioopen_readline( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc) { struct single *sfd = &xfd->stream; int rw = (xioflags & XIO_ACCMODE); char msgbuf[256], *cp = msgbuf; bool noprompt = false; char *noecho = NULL; if (argc != 1) { xio_syntax(argv[0], 0, argc-1, addrdesc->syntax); return STAT_NORETRY; } if (!(xioflags & XIO_MAYCONVERT)) { Error("address with data processing not allowed here"); return STAT_NORETRY; } xfd->common.flags |= XIO_DOESCONVERT; strcpy(cp, "using "); cp = strchr(cp, '\0'); if ((rw+1)&1) { strcpy(cp, "readline on stdin for reading"); cp = strchr(cp, '\0'); if ((rw+1)&2) { strcpy(cp, " and "); cp = strchr(cp, '\0'); } } if ((rw+1)&2) { strcpy(cp, "stdio for writing"); cp = strchr(cp, '\0'); } Notice(msgbuf); xfd->stream.fd = 0; /* stdin */ xfd->stream.howtoend = END_NONE; xfd->stream.dtype = XIODATA_READLINE; #if WITH_TERMIOS if (Isatty(xfd->stream.fd)) { if (Tcgetattr(xfd->stream.fd, &xfd->stream.savetty) < 0) { Warn2("cannot query current terminal settings on fd %d. %s", xfd->stream.fd, strerror(errno)); } else { xfd->stream.ttyvalid = true; } } #endif /* WITH_TERMIOS */ if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; applyopts(sfd, -1, opts, PH_INIT); applyopts2(sfd, -1, opts, PH_INIT, PH_FD); Using_history(); applyopts_offset(&xfd->stream, opts); retropt_bool(opts, OPT_NOPROMPT, &noprompt); if (!noprompt && !xfd->stream.para.readline.prompt) { xfd->stream.para.readline.dynbytes = READLINE_MAXPROMPT; xfd->stream.para.readline.dynprompt = Malloc(xfd->stream.para.readline.dynbytes+1); xfd->stream.para.readline.dynend = xfd->stream.para.readline.dynprompt; } #if HAVE_REGEX_H retropt_string(opts, OPT_NOECHO, &noecho); if (noecho) { int errcode; char errbuf[128]; if ((errcode = regcomp(&xfd->stream.para.readline.noecho, noecho, REG_EXTENDED|REG_NOSUB)) != 0) { regerror(errcode, &xfd->stream.para.readline.noecho, errbuf, sizeof(errbuf)); Error3("regcomp(%p, \"%s\", REG_EXTENDED|REG_NOSUB): %s", &xfd->stream.para.readline.noecho, noecho, errbuf); return -1; } xfd->stream.para.readline.hasnoecho = true; } #endif /* HAVE_REGEX_H */ if (xfd->stream.para.readline.history_file) { Read_history(xfd->stream.para.readline.history_file); } #if _WITH_TERMIOS xiotermios_clrflag(xfd->stream.fd, 3, ICANON|ECHO); xiotermios_flush(xfd->stream.fd); #endif /* _WITH_TERMIOS */ return _xio_openlate(&xfd->stream, opts); } ssize_t xioread_readline(struct single *pipe, void *buff, size_t bufsiz) { /*! indent */ ssize_t bytes; char *line; int _errno; #if HAVE_REGEX_H if (pipe->para.readline.dynprompt && pipe->para.readline.hasnoecho && !regexec(&pipe->para.readline.noecho, pipe->para.readline.dynprompt, 0, NULL, 0)) { #if _WITH_TERMIOS /* under these conditions, we do not echo input, thus we circumvent readline */ struct termios saveterm, setterm; *pipe->para.readline.dynend = '\0'; Tcgetattr(pipe->fd, &saveterm); /*! error */ setterm = saveterm; setterm.c_lflag |= ICANON; Tcsetattr(pipe->fd, TCSANOW, &setterm); /*!*/ #endif /* _WITH_TERMIOS */ do { bytes = Read(pipe->fd, buff, bufsiz); } while (bytes < 0 && errno == EINTR); if (bytes < 0) { _errno = errno; Error4("read(%d, %p, "F_Zu"): %s", pipe->fd, buff, bufsiz, strerror(_errno)); errno = _errno; return -1; } #if _WITH_TERMIOS setterm.c_lflag &= ~ICANON; Tcgetattr(pipe->fd, &setterm); /*! error */ Tcsetattr(pipe->fd, TCSANOW, &saveterm); /*!*/ #endif /* _WITH_TERMIOS */ pipe->para.readline.dynend = pipe->para.readline.dynprompt; /*Write(pipe->fd, "\n", 1);*/ /*!*/ return bytes; } #endif /* HAVE_REGEX_H */ #if _WITH_TERMIOS xiotermios_setflag(pipe->fd, 3, ECHO); xiotermios_flush(pipe->fd); #endif /* _WITH_TERMIOS */ if (pipe->para.readline.prompt || pipe->para.readline.dynprompt) { /* we must carriage return, because readline will first print the prompt */ ssize_t writt; writt = writefull(pipe->fd, "\r", 1); if (writt < 0) { Warn2("write(%d, \"\\r\", 1): %s", pipe->fd, strerror(errno)); } else if (writt < 1) { Warn1("write() only wrote "F_Zu" of 1 byte", writt); } } if (pipe->para.readline.dynprompt) { *pipe->para.readline.dynend = '\0'; line = Readline(pipe->para.readline.dynprompt); pipe->para.readline.dynend = pipe->para.readline.dynprompt; } else { line = Readline(pipe->para.readline.prompt); } /* GNU readline defines no error return */ if (line == NULL) { return 0; /* EOF */ } #if _WITH_TERMIOS xiotermios_clrflag(pipe->fd, 3, ECHO); xiotermios_flush(pipe->fd); #endif /* _WITH_TERMIOS */ Add_history(line); bytes = strlen(line); ((char *)buff)[0] = '\0'; strncat(buff, line, bufsiz-1); free(line); if ((size_t)bytes < bufsiz) { strcat(buff, "\n"); ++bytes; } return bytes; } void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) { if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) { /* we save the last part of the output as possible prompt */ const void *ptr = buff; const void *pcr; const void *plf; size_t len; if (bytes > pipe->para.readline.dynbytes) { ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes; len = pipe->para.readline.dynbytes; } else { len = bytes; } pcr = memrchr(ptr, '\r', len); plf = memrchr(ptr, '\n', len); if (pcr != NULL || plf != NULL) { const void *peol = Max(pcr, plf); /* forget old prompt */ pipe->para.readline.dynend = pipe->para.readline.dynprompt; len -= (peol+1 - ptr); /* new prompt starts here */ ptr = (const char *)peol+1; } if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len > pipe->para.readline.dynbytes) { memmove(pipe->para.readline.dynprompt, pipe->para.readline.dynend - (pipe->para.readline.dynbytes - len), pipe->para.readline.dynbytes - len); pipe->para.readline.dynend = pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len; } memcpy(pipe->para.readline.dynend, ptr, len); pipe->para.readline.dynend = pipe->para.readline.dynend + len; } return; } #endif /* WITH_READLINE */