[misc] Add error code 'GENERIC_BADNESS'
[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_clone (struct VscError *error, const struct VscCliCommand *command)
71 {
72         struct VscCliCommand *command_clone = NULL;
73
74         VSC__ASSERT (error != NULL);
75         VSC__ASSERT (! error->occured);
76         VSC__ASSERT (command != NULL);
77
78         command_clone = vsc_memdup (error, command, sizeof (struct VscCliCommand));
79
80         if (error->occured) {
81                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
82                 goto failure;
83         }
84
85         command_clone->next = NULL;
86
87         command_clone->option_list =
88            _vsc_cli_option_list_clone (error, command->option_list);
89
90         if (error->occured) {
91                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
92                 goto failure;
93         }
94
95         return command_clone;
96
97 failure:
98         _command_free (&command_clone);
99
100         return NULL;
101 }
102
103 void
104 _vsc_cli_command_list_append_clone (struct VscError *error,
105                                     struct VscCliCommand **command_list,
106                                     const struct VscCliCommand *command)
107 {
108         VSC__ASSERT (error != NULL);
109         VSC__ASSERT (! error->occured);
110         VSC__ASSERT (command_list != NULL);
111         VSC__ASSERT (command != NULL);
112
113         vsc_list_append_clone
114            (error, (struct VscList **) command_list,
115             (struct VscList *) command, (VscList_CloneFunction) _command_clone);
116
117         if (error->occured) {
118                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
119                 return;
120         }
121 }
122
123 void
124 vsc_cli_command_list_free (struct VscCliCommand **command_list)
125 {
126         VSC__ASSERT (command_list != NULL);
127
128         vsc_list_free ((struct VscList **) command_list,
129                        (VscList_FreeFunction) _command_free);
130 }
131
132 int /* command_parsed */
133 vsc_cli_command_parse (struct VscError *error,
134                        const char *input, const char **input_tail,
135                        struct VscCliCommand *command)
136 {
137         int i;
138         int command_parsed = TRUE;
139         char *command_name = NULL;
140         const struct VscCliCommandInfo *command_info;
141         const struct VscCliOptionInfo *option_info;
142         struct VscCliOption option;
143         int option_parsed;
144         int untagged_option_index = 0;
145
146         VSC__ASSERT (error != NULL);
147         VSC__ASSERT (! error->occured);
148         VSC__ASSERT (input != NULL);
149         VSC__ASSERT (input_tail != NULL);
150         VSC__ASSERT (command != NULL);
151
152         *input_tail = input;
153
154         memset (command, 0, sizeof (struct VscCliCommand));
155
156         /*
157          * Parse command name.
158          */
159         do {
160                 _vsc_cli_token_parse (error, *input_tail, &command_name, input_tail);
161
162                 if (error->occured) {
163                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
164                         goto failure;
165                 }
166         } while (command_name == NULL && *input_tail != NULL);
167
168         if (command_name == NULL) {
169                 if (*input_tail != NULL) {
170                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
171                                      "Expecting command in '%s'", input);
172                 }
173
174                 goto failure;
175         }
176
177         command_info = _vsc_cli_command_info_lookup (command_name);
178
179         if (command_info == NULL) {
180                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
181                              "Command '%s' is unknown", command_name);
182
183                 goto failure;
184         }
185
186         command->info = command_info;
187
188         /*
189          * Parse command options.
190          */
191         while (TRUE) {
192                 option_parsed =
193                    _vsc_cli_option_parse (error, command_info->option_infos,
194                                           untagged_option_index, command_name,
195                                           *input_tail, input_tail, &option);
196
197                 if (error->occured) {
198                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
199                         goto failure;
200                 }
201
202                 if (option_parsed) {
203                         if (! (option.info->flags & VSC_CLI__OPTION_FLAG__TAGGED)) {
204                                 ++untagged_option_index;
205                         }
206
207                         _vsc_cli_option_list_append_clone (error, &command->option_list,
208                                                            &option);
209                         vsc_free (&option.string);
210
211                         if (error->occured) {
212                                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
213                                 goto failure;
214                         }
215                 } else {
216                         break;
217                 }
218         }
219
220         /*
221          * Check required options.
222          */
223         for (i = 0; command_info->option_infos[i].long_name != NULL; ++i) {
224                 option_info = &command_info->option_infos[i];
225
226                 if (! (option_info->flags & VSC_CLI__OPTION_FLAG__REQUIRED)) {
227                         continue;
228                 }
229
230                 if (_vsc_cli_option_list_lookup (command->option_list,
231                                                  option_info->long_name) == NULL) {
232                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
233                                      "Command '%s' requires %s option",
234                                      command_info->name,
235                                      _vsc_cli_option_syntax (option_info, FALSE));
236
237                         goto failure;
238                 }
239         }
240
241 cleanup:
242         vsc_free (&command_name);
243
244         return command_parsed;
245
246 failure:
247         vsc_cli_option_list_free (&command->option_list);
248
249         command_parsed = FALSE;
250
251         goto cleanup;
252 }