Code

sysdb: Use the asynchronous readline interface; handle asynch server replies.
[sysdb.git] / src / tools / sysdb / input.c
1 /*
2  * SysDB - src/tools/sysdb/input.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "tools/sysdb/input.h"
33 #include "tools/sysdb/command.h"
35 #include "utils/strbuf.h"
37 #include <sys/select.h>
39 #include <stdio.h>
40 #include <stdlib.h>
42 #include <string.h>
44 #include <termios.h>
45 #include <unistd.h>
47 #if HAVE_EDITLINE_READLINE_H
48 #       include <editline/readline.h>
49 #       if HAVE_EDITLINE_HISTORY_H
50 #               include <editline/history.h>
51 #       endif
52 #elif HAVE_READLINE_READLINE_H
53 #       include <readline/readline.h>
54 #       if HAVE_READLINE_HISTORY_H
55 #               include <readline/history.h>
56 #       endif
57 #elif HAVE_READLINE_H
58 #       include <readline.h>
59 #       if HAVE_HISTORY_H
60 #               include <history.h>
61 #       endif
62 #endif /* READLINEs */
64 /*
65  * public variables
66  */
68 sdb_input_t *sysdb_input = NULL;
70 /*
71  * private variables
72  */
74 static struct termios orig_term_attrs;
76 /*
77  * private helper functions
78  */
80 static void
81 reset_term_attrs(void)
82 {
83         tcsetattr(STDIN_FILENO, TCSANOW, &orig_term_attrs);
84 } /* reset_term_attrs */
86 static void
87 term_rawmode(void)
88 {
89         struct termios attrs;
91         /* setup terminal to operate in non-canonical mode
92          * and single character input */
93         memset(&orig_term_attrs, 0, sizeof(orig_term_attrs));
94         tcgetattr(STDIN_FILENO, &orig_term_attrs);
95         atexit(reset_term_attrs);
97         memset(&attrs, 0, sizeof(attrs));
98         tcgetattr(STDIN_FILENO, &attrs);
99         attrs.c_lflag &= (tcflag_t)(~ICANON);
100         attrs.c_cc[VMIN] = 1;
101         tcsetattr(STDIN_FILENO, TCSANOW, &attrs);
102 } /* term_rawmode */
104 static void
105 handle_input(char *line)
107         if (! line) {
108                 sysdb_input->eof = 1;
109                 return;
110         }
112         sdb_strbuf_append(sysdb_input->input, line);
113         sdb_strbuf_append(sysdb_input->input, "\n");
114         free(line);
116         rl_callback_handler_remove();
117 } /* handle_input */
119 /* wait for a new line of data to be available */
120 static ssize_t
121 input_readline(void)
123         size_t len;
125         fd_set fds;
126         int client_fd;
128         const char *prompt = "sysdb=> ";
130         if (sysdb_input->query_len)
131                 prompt = "sysdb-> ";
133         rl_callback_handler_install(prompt, handle_input);
134         client_fd = sdb_client_sockfd(sysdb_input->client);
136         len = sdb_strbuf_len(sysdb_input->input);
137         while ((sdb_strbuf_len(sysdb_input->input) == len)
138                         && (! sysdb_input->eof)) {
139                 int n;
141                 /* XXX: some versions of libedit don't properly reset the terminal in
142                  * rl_callback_read_char(); detect those versions */
143                 term_rawmode();
145                 FD_ZERO(&fds);
146                 FD_SET(STDIN_FILENO, &fds);
147                 FD_SET(client_fd, &fds);
149                 n = select(client_fd + 1, &fds, NULL, NULL, /* timeout = */ NULL);
150                 if (n < 0)
151                         return (ssize_t)n;
152                 else if (! n)
153                         continue;
155                 /* handle user input with highest priority */
156                 if (FD_ISSET(STDIN_FILENO, &fds)) {
157                         rl_callback_read_char();
158                         continue;
159                 }
161                 if (! FD_ISSET(client_fd, &fds))
162                         continue;
164                 /* some response / error message from the server pending */
165                 /* XXX: clear current line */
166                 printf("\n");
167                 sdb_command_print_reply(sysdb_input);
168                 rl_forced_update_display();
169         }
171         /* new data available */
172         return (ssize_t)(sdb_strbuf_len(sysdb_input->input) - len);
173 } /* input_readline */
175 /*
176  * public API
177  */
179 int
180 sdb_input_init(sdb_input_t *input)
182         /* register input handler */
183         sysdb_input = input;
185         if (! isatty(STDIN_FILENO))
186                 return -1;
188         term_rawmode();
189         return 0;
190 } /* sdb_input_init */
192 ssize_t
193 sdb_input_readline(char *buf, int *n_chars, size_t max_chars)
195         const char *data;
196         size_t len;
198         len = sdb_strbuf_len(sysdb_input->input) - sysdb_input->tokenizer_pos;
200         if (! len) {
201                 ssize_t n = input_readline();
202                 if (n <= 0) {
203                         *n_chars = 0; /* YY_NULL */
204                         return n;
205                 }
206                 len += (size_t)n;
207         }
209         len = (len < max_chars) ? len : max_chars;
210         data = sdb_strbuf_string(sysdb_input->input);
211         data += sysdb_input->tokenizer_pos;
212         strncpy(buf, data, len);
214         sysdb_input->tokenizer_pos += len;
215         *n_chars = (int)len;
216         return (ssize_t)len;
217 } /* sdb_input_readline */
219 int
220 sdb_input_exec_query(void)
222         char *query = sdb_command_exec(sysdb_input);
224         HIST_ENTRY *current_hist;
225         const char *hist_line = NULL;
227         if (! query)
228                 return -1;
230         current_hist = current_history();
231         if (current_hist)
232                 hist_line = current_hist->line;
234         if (*query != ' ')
235                 if ((! hist_line) || strcmp(hist_line, query))
236                         add_history(query);
237         free(query);
238         return 0;
239 } /* sdb_input_exec_query */
241 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */