version 1.7.3.0
[socat.git] / xio-readline.c
blobcacb9a719138cbc8cc688ccc36bf4213f3b0570c
1 /* source: xio-readline.c */
2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for opening the readline address */
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
10 #include "xio-termios.h"
11 #include "xio-readline.h"
14 #if WITH_READLINE
17 options: history file
18 prompt
19 mode=vi?
20 inputrc=?
22 uses stdin!!
25 /* length of buffer for dynamic prompt */
26 #define READLINE_MAXPROMPT 512
28 static int xioopen_readline(int argc, const char *argv[], struct opt *opts,
29 int rw, xiofile_t *xfd, unsigned groups,
30 int dummy1, int dummy2, int dummy3);
33 const struct addrdesc addr_readline = {
34 "readline", 3, xioopen_readline, GROUP_FD|GROUP_TERMIOS|GROUP_READLINE, 0, 0, 0 HELP(NULL) };
36 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) };
37 const struct optdesc opt_prompt = { "prompt", NULL, OPT_PROMPT, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.readline.prompt) };
38 const struct optdesc opt_noprompt = { "noprompt", NULL, OPT_NOPROMPT, GROUP_READLINE, PH_LATE, TYPE_BOOL, OFUNC_SPEC, 0 };
39 const struct optdesc opt_noecho = { "noecho", NULL, OPT_NOECHO, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_SPEC, 0 };
41 static int xioopen_readline(int argc, const char *argv[], struct opt *opts,
42 int xioflags, xiofile_t *xfd, unsigned groups,
43 int dummy1, int dummy2, int dummy3) {
44 int rw = (xioflags & XIO_ACCMODE);
45 char msgbuf[256], *cp = msgbuf;
46 bool noprompt = false;
47 char *noecho = NULL;
49 if (argc != 1) {
50 Error1("%s: 0 parameters required", argv[0]);
51 return STAT_NORETRY;
54 if (!(xioflags & XIO_MAYCONVERT)) {
55 Error("address with data processing not allowed here");
56 return STAT_NORETRY;
58 xfd->common.flags |= XIO_DOESCONVERT;
60 strcpy(cp, "using "); cp = strchr(cp, '\0');
61 if ((rw+1)&1) {
62 strcpy(cp, "readline on stdin for reading"); cp = strchr(cp, '\0');
64 if ((rw+1)&2)
65 strcpy(cp, " and "); cp = strchr(cp, '\0');
67 if ((rw+1)&2) {
68 strcpy(cp, "stdio for writing"); cp = strchr(cp, '\0');
70 Notice(msgbuf);
72 xfd->stream.fd = 0; /* stdin */
73 xfd->stream.howtoend = END_NONE;
74 xfd->stream.dtype = XIODATA_READLINE;
76 #if WITH_TERMIOS
77 if (Isatty(xfd->stream.fd)) {
78 if (Tcgetattr(xfd->stream.fd, &xfd->stream.savetty) < 0) {
79 Warn2("cannot query current terminal settings on fd %d. %s",
80 xfd->stream.fd, strerror(errno));
81 } else {
82 xfd->stream.ttyvalid = true;
85 #endif /* WITH_TERMIOS */
87 if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1;
88 applyopts(-1, opts, PH_INIT);
90 applyopts2(xfd->stream.fd, opts, PH_INIT, PH_FD);
92 Using_history();
93 applyopts_offset(&xfd->stream, opts);
94 retropt_bool(opts, OPT_NOPROMPT, &noprompt);
95 if (!noprompt && !xfd->stream.para.readline.prompt) {
96 xfd->stream.para.readline.dynbytes = READLINE_MAXPROMPT;
97 xfd->stream.para.readline.dynprompt =
98 Malloc(xfd->stream.para.readline.dynbytes+1);
99 xfd->stream.para.readline.dynend =
100 xfd->stream.para.readline.dynprompt;
103 #if HAVE_REGEX_H
104 retropt_string(opts, OPT_NOECHO, &noecho);
105 if (noecho) {
106 int errcode;
107 char errbuf[128];
108 if ((errcode = regcomp(&xfd->stream.para.readline.noecho, noecho,
109 REG_EXTENDED|REG_NOSUB))
110 != 0) {
111 regerror(errcode, &xfd->stream.para.readline.noecho,
112 errbuf, sizeof(errbuf));
113 Error3("regcomp(%p, \"%s\", REG_EXTENDED|REG_NOSUB): %s",
114 &xfd->stream.para.readline.noecho, noecho, errbuf);
115 return -1;
117 xfd->stream.para.readline.hasnoecho = true;
119 #endif /* HAVE_REGEX_H */
120 if (xfd->stream.para.readline.history_file) {
121 Read_history(xfd->stream.para.readline.history_file);
123 #if _WITH_TERMIOS
124 xiotermios_clrflag(xfd->stream.fd, 3, ICANON);
125 xiotermios_clrflag(xfd->stream.fd, 3, ECHO);
126 #endif /* _WITH_TERMIOS */
127 return _xio_openlate(&xfd->stream, opts);
131 ssize_t xioread_readline(struct single *pipe, void *buff, size_t bufsiz) {
132 /*! indent */
133 ssize_t bytes;
134 char *line;
135 int _errno;
137 #if HAVE_REGEX_H
138 if (pipe->para.readline.dynprompt &&
139 pipe->para.readline.hasnoecho &&
140 !regexec(&pipe->para.readline.noecho,
141 pipe->para.readline.dynprompt, 0, NULL, 0)) {
142 #if _WITH_TERMIOS
143 /* under these conditions, we do not echo input, thus we circumvent
144 readline */
145 struct termios saveterm, setterm;
146 *pipe->para.readline.dynend = '\0';
147 Tcgetattr(pipe->fd, &saveterm); /*! error */
148 setterm = saveterm;
149 setterm.c_lflag |= ICANON;
150 Tcsetattr(pipe->fd, TCSANOW, &setterm); /*!*/
151 #endif /* _WITH_TERMIOS */
152 do {
153 bytes = Read(pipe->fd, buff, bufsiz);
154 } while (bytes < 0 && errno == EINTR);
155 if (bytes < 0) {
156 _errno = errno;
157 Error4("read(%d, %p, "F_Zu"): %s",
158 pipe->fd, buff, bufsiz, strerror(_errno));
159 errno = _errno;
160 return -1;
162 #if _WITH_TERMIOS
163 setterm.c_lflag &= ~ICANON;
164 Tcgetattr(pipe->fd, &setterm); /*! error */
165 Tcsetattr(pipe->fd, TCSANOW, &saveterm); /*!*/
166 #endif /* _WITH_TERMIOS */
167 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
168 /*Write(pipe->fd, "\n", 1);*/ /*!*/
169 return bytes;
171 #endif /* HAVE_REGEX_H */
173 #if _WITH_TERMIOS
174 xiotermios_setflag(pipe->fd, 3, ECHO);
175 #endif /* _WITH_TERMIOS */
176 if (pipe->para.readline.prompt || pipe->para.readline.dynprompt) {
177 /* we must carriage return, because readline will first print the
178 prompt */
179 ssize_t writt;
180 writt = writefull(pipe->fd, "\r", 1);
181 if (writt < 0) {
182 Warn2("write(%d, \"\\r\", 1): %s",
183 pipe->fd, strerror(errno));
184 } else if (writt < 1) {
185 Warn1("write() only wrote "F_Zu" of 1 byte", writt);
189 if (pipe->para.readline.dynprompt) {
190 *pipe->para.readline.dynend = '\0';
191 line = Readline(pipe->para.readline.dynprompt);
192 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
193 } else {
194 line = Readline(pipe->para.readline.prompt);
196 /* GNU readline defines no error return */
197 if (line == NULL) {
198 return 0; /* EOF */
200 #if _WITH_TERMIOS
201 xiotermios_clrflag(pipe->fd, 3, ECHO);
202 #endif /* _WITH_TERMIOS */
203 Add_history(line);
204 bytes = strlen(line);
205 ((char *)buff)[0] = '\0'; strncat(buff, line, bufsiz-1);
206 free(line);
207 if ((size_t)bytes < bufsiz) {
208 strcat(buff, "\n"); ++bytes;
210 return bytes;
213 void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
214 if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) {
215 /* we save the last part of the output as possible prompt */
216 const void *ptr = buff;
217 const void *pcr;
218 const void *plf;
219 size_t len;
221 if (bytes > pipe->para.readline.dynbytes) {
222 ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes;
223 len = pipe->para.readline.dynbytes;
224 } else {
225 len = bytes;
227 pcr = memrchr(ptr, '\r', len);
228 plf = memrchr(ptr, '\n', len);
229 if (pcr != NULL || plf != NULL) {
230 const void *peol = Max(pcr, plf);
231 /* forget old prompt */
232 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
233 len -= (peol+1 - ptr);
234 /* new prompt starts here */
235 ptr = (const char *)peol+1;
237 if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len >
238 pipe->para.readline.dynbytes) {
239 memmove(pipe->para.readline.dynprompt,
240 pipe->para.readline.dynend -
241 (pipe->para.readline.dynbytes - len),
242 pipe->para.readline.dynbytes - len);
243 pipe->para.readline.dynend =
244 pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len;
246 memcpy(pipe->para.readline.dynend, ptr, len);
247 pipe->para.readline.dynend = pipe->para.readline.dynend + len;
249 return;
252 #endif /* WITH_READLINE */