[misc] Add missing initializer in _error_default struct instance.
[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 /*!
22  * @file
23  */
24
25 #include <string.h>
26
27 #include <libvscmisc/assert.h>
28 #include <libvscmisc/error.h>
29 #include <libvscmisc/list.h>
30 #include <libvscmisc/memory.h>
31 #include <libvscmisc/string.h>
32
33 #include "../include/libvsccli/command.h"
34 #include "../include/libvsccli/option.h"
35 #include "command.h"
36 #include "token.h"
37 #include "option.h"
38
39 extern const struct VscCliCommandInfo **_vsc_cli_command_infos;
40
41 const struct VscCliCommandInfo *
42 _vsc_cli_command_info_lookup (const char *command_name)
43 {
44         int i;
45
46         VSC__ASSERT (command_name != NULL);
47
48         for (i = 0; _vsc_cli_command_infos[i] != NULL; i++) {
49                 if (strcmp (command_name, _vsc_cli_command_infos[i]->name) == 0) {
50                         return _vsc_cli_command_infos[i];
51                 }
52         }
53
54         return NULL;
55 }
56
57 static void
58 _command_free (struct VscCliCommand **command)
59 {
60         VSC__ASSERT (command != NULL);
61
62         if (*command != NULL) {
63                 vsc_cli_option_list_free (&(*command)->option_list);
64         }
65
66         vsc_free (command);
67 }
68
69 static struct VscCliCommand *
70 _command_duplicate (struct VscError *error,
71                     const struct VscCliCommand *command)
72 {
73         struct VscCliCommand *command_duplicate = NULL;
74
75         VSC__ASSERT (error != NULL);
76         VSC__ASSERT (! error->occured);
77         VSC__ASSERT (command != NULL);
78
79         command_duplicate = vsc_memdup (error, command,
80                                         sizeof (struct VscCliCommand));
81
82         if (error->occured) {
83                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
84                 goto failure;
85         }
86
87         command_duplicate->option_list =
88            _vsc_cli_option_list_duplicate (error, command->option_list);
89
90         if (error->occured) {
91                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
92                 goto failure;
93         }
94
95         command_duplicate->next = NULL;
96
97         return command_duplicate;
98
99 failure:
100         _command_free (&command_duplicate);
101
102         return NULL;
103 }
104
105 void
106 _vsc_cli_command_list_append (struct VscError *error,
107                               struct VscCliCommand **command_list,
108                               const struct VscCliCommand *command)
109 {
110         VSC__ASSERT (error != NULL);
111         VSC__ASSERT (! error->occured);
112         VSC__ASSERT (command_list != NULL);
113         VSC__ASSERT (command != NULL);
114
115         vsc_list_append
116            (error, (struct VscList **) command_list,
117             (struct VscList *) command,
118             (VscList_DuplicateFunction) _command_duplicate);
119
120         if (error->occured) {
121                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
122                 return;
123         }
124 }
125
126 void
127 vsc_cli_command_list_free (struct VscCliCommand **command_list)
128 {
129         VSC__ASSERT (command_list != NULL);
130
131         vsc_list_free ((struct VscList **) command_list,
132                        (VscList_FreeFunction) _command_free);
133 }
134
135 int /* command_parsed */
136 vsc_cli_command_parse (struct VscError *error,
137                        const char *input, const char **input_tail,
138                        struct VscCliCommand *command)
139 {
140         int i;
141         int command_parsed = TRUE;
142         char *command_name = NULL;
143         const struct VscCliCommandInfo *command_info;
144         const struct VscCliOptionInfo *option_info;
145         struct VscCliOption option;
146         int option_parsed;
147         int untagged_option_index = 0;
148
149         VSC__ASSERT (error != NULL);
150         VSC__ASSERT (! error->occured);
151         VSC__ASSERT (input != NULL);
152         VSC__ASSERT (input_tail != NULL);
153         VSC__ASSERT (command != NULL);
154
155         *input_tail = input;
156
157         memset (command, 0, sizeof (struct VscCliCommand));
158
159         /*
160          * Parse command name.
161          */
162         do {
163                 _vsc_cli_token_parse (error, *input_tail, &command_name, input_tail);
164
165                 if (error->occured) {
166                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
167                         goto failure;
168                 }
169         } while (command_name == NULL && *input_tail != NULL);
170
171         if (command_name == NULL) {
172                 if (*input_tail != NULL) {
173                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
174                                      "Expecting command in '%s'", input);
175                 }
176
177                 goto failure;
178         }
179
180         command_info = _vsc_cli_command_info_lookup (command_name);
181
182         if (command_info == NULL) {
183                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
184                              "Command '%s' is unknown", command_name);
185
186                 goto failure;
187         }
188
189         command->info = command_info;
190
191         /*
192          * Parse command options.
193          */
194         while (TRUE) {
195                 option_parsed =
196                    _vsc_cli_option_parse (error, command_info->option_infos,
197                                           untagged_option_index, command_name,
198                                           *input_tail, input_tail, &option);
199
200                 if (error->occured) {
201                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
202                         goto failure;
203                 }
204
205                 if (option_parsed) {
206                         if (! (option.info->flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
207                                 ++untagged_option_index;
208                         }
209
210                         _vsc_cli_option_list_append (error, &command->option_list,
211                                                      &option);
212                         vsc_free (&option.string);
213
214                         if (error->occured) {
215                                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
216                                 goto failure;
217                         }
218                 } else {
219                         break;
220                 }
221         }
222
223         /*
224          * Check required options.
225          */
226         for (i = 0; command_info->option_infos[i].long_name != NULL; ++i) {
227                 option_info = &command_info->option_infos[i];
228
229                 if (! (option_info->flags & VSC_CLI__OPTION_FLAG__REQUIRED)) {
230                         continue;
231                 }
232
233                 if (_vsc_cli_option_list_lookup (command->option_list,
234                                                  option_info->long_name) == NULL) {
235                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
236                                      "Command '%s' requires %s option",
237                                      command_info->name,
238                                      _vsc_cli_option_syntax (option_info, FALSE));
239
240                         goto failure;
241                 }
242         }
243
244 cleanup:
245         vsc_free (&command_name);
246
247         return command_parsed;
248
249 failure:
250         vsc_cli_option_list_free (&command->option_list);
251
252         command_parsed = FALSE;
253
254         goto cleanup;
255 }