e344fe7cb45098f4c7130546da02a23b98c201ee
[vsc-common.git] / cli / command.c
1 /*
2  * command.c: Library for interactive commandline interfaces
3  *
4  * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #include <libvscmisc/assert.h>
24 #include <libvscmisc/error.h>
25 #include <libvscmisc/list.h>
26 #include <libvscmisc/memory.h>
27 #include <libvscmisc/string.h>
28
29 #include "../include/libvsccli/command.h"
30 #include "../include/libvsccli/option.h"
31 #include "command.h"
32 #include "token.h"
33 #include "option.h"
34
35 extern const struct VscCliCommandInfo **_vsc_cli_command_infos;
36
37 const struct VscCliCommandInfo *
38 _vsc_cli_command_info_lookup (const char *command_name)
39 {
40         int i;
41
42         VSC__ASSERT (command_name != NULL);
43
44         for (i = 0; _vsc_cli_command_infos[i] != NULL; i++) {
45                 if (strcmp (command_name, _vsc_cli_command_infos[i]->name) == 0) {
46                         return _vsc_cli_command_infos[i];
47                 }
48         }
49
50         return NULL;
51 }
52
53 static void
54 _command_free (struct VscCliCommand **command)
55 {
56         VSC__ASSERT (command != NULL);
57
58         if (*command != NULL) {
59                 vsc_cli_option_list_free (&(*command)->option_list);
60         }
61
62         vsc_free (command);
63 }
64
65 static struct VscCliCommand *
66 _command_duplicate (struct VscError *error,
67                     const struct VscCliCommand *command)
68 {
69         struct VscCliCommand *command_duplicate = NULL;
70
71         VSC__ASSERT (error != NULL);
72         VSC__ASSERT (! error->occured);
73         VSC__ASSERT (command != NULL);
74
75         command_duplicate = vsc_memdup (error, command,
76                                         sizeof (struct VscCliCommand));
77
78         if (error->occured) {
79                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
80                 goto failure;
81         }
82
83         command_duplicate->option_list =
84            _vsc_cli_option_list_duplicate (error, command->option_list);
85
86         if (error->occured) {
87                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
88                 goto failure;
89         }
90
91         command_duplicate->next = NULL;
92
93         return command_duplicate;
94
95 failure:
96         _command_free (&command_duplicate);
97
98         return NULL;
99 }
100
101 void
102 _vsc_cli_command_list_append (struct VscError *error,
103                               struct VscCliCommand **command_list,
104                               const struct VscCliCommand *command)
105 {
106         VSC__ASSERT (error != NULL);
107         VSC__ASSERT (! error->occured);
108         VSC__ASSERT (command_list != NULL);
109         VSC__ASSERT (command != NULL);
110
111         vsc_list_append
112            (error, (struct VscList **) command_list,
113             (struct VscList *) command,
114             (VscList_DuplicateFunction) _command_duplicate);
115
116         if (error->occured) {
117                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
118                 return;
119         }
120 }
121
122 void
123 vsc_cli_command_list_free (struct VscCliCommand **command_list)
124 {
125         VSC__ASSERT (command_list != NULL);
126
127         vsc_list_free ((struct VscList **) command_list,
128                        (VscList_FreeFunction) _command_free);
129 }
130
131 int /* command_parsed */
132 vsc_cli_command_parse (struct VscError *error,
133                        const char *input, const char **input_tail,
134                        struct VscCliCommand *command)
135 {
136         int i;
137         int command_parsed = TRUE;
138         char *command_name = NULL;
139         const struct VscCliCommandInfo *command_info;
140         const struct VscCliOptionInfo *option_info;
141         struct VscCliOption option;
142         int option_parsed;
143         int data_option_index = 0;
144
145         VSC__ASSERT (error != NULL);
146         VSC__ASSERT (! error->occured);
147         VSC__ASSERT (input != NULL);
148         VSC__ASSERT (input_tail != NULL);
149         VSC__ASSERT (command != NULL);
150
151         *input_tail = input;
152
153         memset (command, 0, sizeof (struct VscCliCommand));
154
155         /*
156          * Parse command name.
157          */
158         do {
159                 _vsc_cli_token_parse (error, *input_tail, &command_name, input_tail);
160
161                 if (error->occured) {
162                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
163                         goto failure;
164                 }
165         } while (command_name == NULL && *input_tail != NULL);
166
167         if (command_name == NULL) {
168                 if (*input_tail != NULL) {
169                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
170                                      "Expecting command in '%s'", input);
171                 }
172
173                 goto failure;
174         }
175
176         command_info = _vsc_cli_command_info_lookup (command_name);
177
178         if (command_info == NULL) {
179                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
180                              "Command '%s' is unknown", command_name);
181
182                 goto failure;
183         }
184
185         command->info = command_info;
186
187         /*
188          * Parse command options.
189          */
190         while (TRUE) {
191                 option_parsed =
192                    _vsc_cli_option_parse (error, command_info->option_infos,
193                                           data_option_index, command_name, *input_tail,
194                                           input_tail, &option);
195
196                 if (error->occured) {
197                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
198                         goto failure;
199                 }
200
201                 if (option_parsed) {
202                         if (option.info->type == VSC_CLI__OPTION_TYPE__DATA) {
203                                 ++data_option_index;
204                         }
205
206                         _vsc_cli_option_list_append (error, &command->option_list,
207                                                      &option);
208                         vsc_free (&option.string);
209
210                         if (error->occured) {
211                                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
212                                 goto failure;
213                         }
214                 } else {
215                         break;
216                 }
217         }
218
219         /*
220          * Check required options.
221          */
222         for (i = 0; command_info->option_infos[i].long_name != NULL; ++i) {
223                 option_info = &command_info->option_infos[i];
224
225                 if (! option_info->required) {
226                         continue;
227                 }
228
229                 if (_vsc_cli_option_list_lookup (command->option_list,
230                                                  option_info->long_name) == NULL) {
231                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
232                                      "Command '%s' requires %s option",
233                                      command_info->name,
234                                      _vsc_cli_option_syntax (option_info, FALSE));
235
236                         goto failure;
237                 }
238         }
239
240 cleanup:
241         vsc_free (&command_name);
242
243         return command_parsed;
244
245 failure:
246         vsc_cli_option_list_free (&command->option_list);
247
248         command_parsed = FALSE;
249
250         goto cleanup;
251 }