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)
106 {
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)
122 {
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)
181 {
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)
194 {
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)
221 {
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 : */