client: Add command completion
[platform/upstream/connman.git] / client / input.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <glib.h>
31 #include <readline/readline.h>
32 #include <readline/history.h>
33
34 #include <gdbus.h>
35 #include "input.h"
36 #include "commands.h"
37
38 static DBusConnection *connection;
39 static GMainLoop *main_loop;
40 static bool interactive = false;
41
42 static bool save_input;
43 static char *saved_line;
44 static int saved_point;
45
46 void __connmanctl_quit(void)
47 {
48         if (main_loop != NULL)
49                 g_main_loop_quit(main_loop);
50 }
51
52 bool __connmanctl_is_interactive(void)
53 {
54         return interactive;
55 }
56
57 void __connmanctl_save_rl(void)
58 {
59         if (interactive == false)
60                 return;
61
62         save_input = !RL_ISSTATE(RL_STATE_DONE);
63
64         if (save_input) {
65                 saved_point = rl_point;
66                 saved_line = rl_copy_text(0, rl_end);
67                 rl_save_prompt();
68                 rl_replace_line("", 0);
69                 rl_redisplay();
70         }
71 }
72
73 void __connmanctl_redraw_rl(void)
74 {
75         if (interactive == false)
76                 return;
77
78         if (save_input) {
79                 rl_restore_prompt();
80                 rl_replace_line(saved_line, 0);
81                 rl_point = saved_point;
82                 rl_redisplay();
83                 free(saved_line);
84         }
85
86         save_input = 0;
87 }
88
89 static void rl_handler(char *input)
90 {
91         char **args;
92         int num, err;
93
94         if (input == NULL) {
95                 rl_newline(1, '\n');
96                 g_main_loop_quit(main_loop);
97                 return;
98         }
99         if (*input != '\0')
100                 add_history(input);
101
102         args = g_strsplit(input, " ", 0);
103         num = g_strv_length(args);
104
105         err = __connmanctl_commands(connection, args, num);
106
107         g_strfreev(args);
108
109         if (err > 0)
110                 g_main_loop_quit(main_loop);
111 }
112
113 static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
114                 gpointer user_data)
115 {
116         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
117                 g_main_loop_quit(main_loop);
118                 return FALSE;
119         }
120
121         rl_callback_read_char();
122         return TRUE;
123 }
124
125 static char **complete_command(const char *text, int start, int end)
126 {
127         char **command = NULL;
128
129         rl_attempted_completion_over = 1;
130
131         if (start == 0)
132                 command = rl_completion_matches(text,
133                                 __connmanctl_lookup_command);
134
135         return command;
136 }
137
138 int __connmanctl_input_init(int argc, char *argv[])
139 {
140         char *help[] = {
141                 "help",
142                 NULL
143         };
144         guint source = 0;
145         int err;
146         DBusError dbus_err;
147
148         dbus_error_init(&dbus_err);
149         connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &dbus_err);
150
151         if (dbus_error_is_set(&dbus_err)) {
152                 fprintf(stderr, "Error: %s\n", dbus_err.message);
153                 dbus_error_free(&dbus_err);
154                 return 1;
155         }
156
157         if (argc < 2) {
158                 GIOChannel *channel;
159
160                 interactive = true;
161
162                 channel = g_io_channel_unix_new(fileno(stdin));
163                 source = g_io_add_watch(channel,
164                                 G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
165                                 input_handler, NULL);
166                 g_io_channel_unref(channel);
167
168                 rl_callback_handler_install("connmanctl> ", rl_handler);
169                 rl_attempted_completion_function = complete_command;
170                 err = -EINPROGRESS;
171
172         } else {
173                 interactive = false;
174
175                 if (strcmp(argv[1], "--help") == 0 ||
176                                 strcmp(argv[1], "-h") == 0)
177                         err = __connmanctl_commands(connection, help, 1);
178                 else
179                         err = __connmanctl_commands(connection, argv + 1,
180                                         argc -1);
181         }
182
183         if (err == -EINPROGRESS) {
184                 main_loop = g_main_loop_new(NULL, FALSE);
185                 g_main_loop_run(main_loop);
186
187                 if (source > 0)
188                         g_source_remove(source);
189
190                 err = 0;
191         }
192
193         if (interactive == true) {
194                 rl_callback_handler_remove();
195                 rl_message("");
196         }
197
198         dbus_connection_unref(connection);
199         if (main_loop != NULL)
200                 g_main_loop_unref(main_loop);
201
202         if (err < 0)
203                 err = -err;
204         else
205                 err = 0;
206
207         return err;
208 }