Version 1.8.0.0
[socat.git] / xio-readline.c
blob376cd26ea1c4ce095e467f69043602426ae29cc4
1 /* source: xio-readline.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
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, int rw, xiofile_t *xfd, const struct addrdesc *addrdesc);
31 const struct addrdesc xioaddr_readline = { "READLINE", 3, xioopen_readline, GROUP_FD|GROUP_TERMIOS|GROUP_READLINE, 0, 0, 0 HELP(NULL) };
33 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) };
34 const struct optdesc opt_prompt = { "prompt", NULL, OPT_PROMPT, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.readline.prompt) };
35 const struct optdesc opt_noprompt = { "noprompt", NULL, OPT_NOPROMPT, GROUP_READLINE, PH_LATE, TYPE_BOOL, OFUNC_SPEC, 0 };
36 const struct optdesc opt_noecho = { "noecho", NULL, OPT_NOECHO, GROUP_READLINE, PH_LATE, TYPE_STRING, OFUNC_SPEC, 0 };
38 static int xioopen_readline(
39 int argc,
40 const char *argv[],
41 struct opt *opts,
42 int xioflags,
43 xiofile_t *xfd,
44 const struct addrdesc *addrdesc)
46 struct single *sfd = &xfd->stream;
47 int rw = (xioflags & XIO_ACCMODE);
48 char msgbuf[256], *cp = msgbuf;
49 bool noprompt = false;
50 char *noecho = NULL;
52 if (argc != 1) {
53 xio_syntax(argv[0], 0, argc-1, addrdesc->syntax);
54 return STAT_NORETRY;
57 if (!(xioflags & XIO_MAYCONVERT)) {
58 Error("address with data processing not allowed here");
59 return STAT_NORETRY;
61 xfd->common.flags |= XIO_DOESCONVERT;
63 strcpy(cp, "using "); cp = strchr(cp, '\0');
64 if ((rw+1)&1) {
65 strcpy(cp, "readline on stdin for reading"); cp = strchr(cp, '\0');
67 if ((rw+1)&2) {
68 strcpy(cp, " and ");
69 cp = strchr(cp, '\0');
72 if ((rw+1)&2) {
73 strcpy(cp, "stdio for writing"); cp = strchr(cp, '\0');
75 Notice(msgbuf);
77 xfd->stream.fd = 0; /* stdin */
78 if (sfd->howtoend == END_UNSPEC)
79 xfd->stream.howtoend = END_NONE;
80 xfd->stream.dtype = XIODATA_READLINE;
82 #if WITH_TERMIOS
83 if (Isatty(xfd->stream.fd)) {
84 if (Tcgetattr(xfd->stream.fd, &xfd->stream.savetty) < 0) {
85 Warn2("cannot query current terminal settings on fd %d. %s",
86 xfd->stream.fd, strerror(errno));
87 } else {
88 xfd->stream.ttyvalid = true;
91 #endif /* WITH_TERMIOS */
93 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
94 applyopts(sfd, -1, opts, PH_INIT);
96 applyopts2(sfd, -1, opts, PH_INIT, PH_FD);
98 Using_history();
99 applyopts_offset(&xfd->stream, opts);
100 retropt_bool(opts, OPT_NOPROMPT, &noprompt);
101 if (!noprompt && !xfd->stream.para.readline.prompt) {
102 xfd->stream.para.readline.dynbytes = READLINE_MAXPROMPT;
103 xfd->stream.para.readline.dynprompt =
104 Malloc(xfd->stream.para.readline.dynbytes+1);
105 xfd->stream.para.readline.dynend =
106 xfd->stream.para.readline.dynprompt;
109 #if HAVE_REGEX_H
110 retropt_string(opts, OPT_NOECHO, &noecho);
111 if (noecho) {
112 int errcode;
113 char errbuf[128];
114 if ((errcode = regcomp(&xfd->stream.para.readline.noecho, noecho,
115 REG_EXTENDED|REG_NOSUB))
116 != 0) {
117 regerror(errcode, &xfd->stream.para.readline.noecho,
118 errbuf, sizeof(errbuf));
119 Error3("regcomp(%p, \"%s\", REG_EXTENDED|REG_NOSUB): %s",
120 &xfd->stream.para.readline.noecho, noecho, errbuf);
121 return -1;
123 xfd->stream.para.readline.hasnoecho = true;
125 #endif /* HAVE_REGEX_H */
126 if (xfd->stream.para.readline.history_file) {
127 Read_history(xfd->stream.para.readline.history_file);
129 #if _WITH_TERMIOS
130 xiotermios_clrflag(xfd->stream.fd, 3, ICANON|ECHO);
131 xiotermios_flush(xfd->stream.fd);
132 #endif /* _WITH_TERMIOS */
133 return _xio_openlate(&xfd->stream, opts);
137 ssize_t xioread_readline(struct single *pipe, void *buff, size_t bufsiz) {
138 /*! indent */
139 ssize_t bytes;
140 char *line;
141 int _errno;
143 #if HAVE_REGEX_H
144 if (pipe->para.readline.dynprompt &&
145 pipe->para.readline.hasnoecho &&
146 !regexec(&pipe->para.readline.noecho,
147 pipe->para.readline.dynprompt, 0, NULL, 0)) {
148 #if _WITH_TERMIOS
149 /* under these conditions, we do not echo input, thus we circumvent
150 readline */
151 struct termios saveterm, setterm;
152 *pipe->para.readline.dynend = '\0';
153 Tcgetattr(pipe->fd, &saveterm); /*! error */
154 setterm = saveterm;
155 setterm.c_lflag |= ICANON;
156 Tcsetattr(pipe->fd, TCSANOW, &setterm); /*!*/
157 #endif /* _WITH_TERMIOS */
158 do {
159 bytes = Read(pipe->fd, buff, bufsiz);
160 } while (bytes < 0 && errno == EINTR);
161 if (bytes < 0) {
162 _errno = errno;
163 Error4("read(%d, %p, "F_Zu"): %s",
164 pipe->fd, buff, bufsiz, strerror(_errno));
165 errno = _errno;
166 return -1;
168 #if _WITH_TERMIOS
169 setterm.c_lflag &= ~ICANON;
170 Tcgetattr(pipe->fd, &setterm); /*! error */
171 Tcsetattr(pipe->fd, TCSANOW, &saveterm); /*!*/
172 #endif /* _WITH_TERMIOS */
173 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
174 /*Write(pipe->fd, "\n", 1);*/ /*!*/
175 return bytes;
177 #endif /* HAVE_REGEX_H */
179 #if _WITH_TERMIOS
180 xiotermios_setflag(pipe->fd, 3, ECHO);
181 xiotermios_flush(pipe->fd);
182 #endif /* _WITH_TERMIOS */
183 if (pipe->para.readline.prompt || pipe->para.readline.dynprompt) {
184 /* we must carriage return, because readline will first print the
185 prompt */
186 ssize_t writt;
187 writt = writefull(pipe->fd, "\r", 1);
188 if (writt < 0) {
189 Warn2("write(%d, \"\\r\", 1): %s",
190 pipe->fd, strerror(errno));
191 } else if (writt < 1) {
192 Warn1("write() only wrote "F_Zu" of 1 byte", writt);
196 if (pipe->para.readline.dynprompt) {
197 *pipe->para.readline.dynend = '\0';
198 line = Readline(pipe->para.readline.dynprompt);
199 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
200 } else {
201 line = Readline(pipe->para.readline.prompt);
203 /* GNU readline defines no error return */
204 if (line == NULL) {
205 return 0; /* EOF */
207 #if _WITH_TERMIOS
208 xiotermios_clrflag(pipe->fd, 3, ECHO);
209 xiotermios_flush(pipe->fd);
210 #endif /* _WITH_TERMIOS */
211 Add_history(line);
212 bytes = strlen(line);
213 ((char *)buff)[0] = '\0'; strncat(buff, line, bufsiz-1);
214 free(line);
215 if ((size_t)bytes < bufsiz) {
216 strcat(buff, "\n"); ++bytes;
218 return bytes;
221 void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
222 if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) {
223 /* we save the last part of the output as possible prompt */
224 const void *ptr = buff;
225 const void *pcr;
226 const void *plf;
227 size_t len;
229 if (bytes > pipe->para.readline.dynbytes) {
230 ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes;
231 len = pipe->para.readline.dynbytes;
232 } else {
233 len = bytes;
235 pcr = memrchr(ptr, '\r', len);
236 plf = memrchr(ptr, '\n', len);
237 if (pcr != NULL || plf != NULL) {
238 const void *peol = Max(pcr, plf);
239 /* forget old prompt */
240 pipe->para.readline.dynend = pipe->para.readline.dynprompt;
241 len -= (peol+1 - ptr);
242 /* new prompt starts here */
243 ptr = (const char *)peol+1;
245 if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len >
246 pipe->para.readline.dynbytes) {
247 memmove(pipe->para.readline.dynprompt,
248 pipe->para.readline.dynend -
249 (pipe->para.readline.dynbytes - len),
250 pipe->para.readline.dynbytes - len);
251 pipe->para.readline.dynend =
252 pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len;
254 memcpy(pipe->para.readline.dynend, ptr, len);
255 pipe->para.readline.dynend = pipe->para.readline.dynend + len;
257 return;
260 #endif /* WITH_READLINE */