[misc] Add error code 'GENERIC_BADNESS'
[vsc-common.git] / cli / help.c
1 /*
2  * help.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 <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include <libvscmisc/assert.h>
30 #include <libvscmisc/error.h>
31
32 #include "../include/libvsccli/help.h"
33 #include "../include/libvsccli/option.h"
34 #include "command.h"
35 #include "option.h"
36
37 #define _LENGTH_LIMIT 79
38
39 extern char *_vsc_cli_name;
40 extern char *_vsc_cli_help;
41 extern const struct VscCliOptionInfo *_vsc_cli_option_infos;
42 extern const struct VscCliCommandInfo **_vsc_cli_command_infos;
43 extern int _vsc_cli_output_coloring;
44 extern int _vsc_cli_interactive;
45
46 static int /* total */
47 _limited_print_words (const char* indent, int total, const char *string)
48 {
49         const char* ptr;
50         const char* start;
51
52         VSC__ASSERT (string != NULL);
53
54         ptr = string;
55         start = string;
56
57         while (*ptr != '\0') {
58                 if (*ptr == ' ') {
59                         ++ptr;
60
61                         if (total + (ptr - start) > _LENGTH_LIMIT) {
62                                 printf ("\n");
63                                 printf ("%s", indent);
64
65                                 total = strlen (indent);
66                         }
67
68                         while (start < ptr) {
69                                 putc (*start++, stdout);
70                                 ++total;
71                         }
72                 } else {
73                         ++ptr;
74                 }
75         }
76
77         if (total + (ptr - start) > _LENGTH_LIMIT) {
78                 printf ("\n");
79                 printf ("%s", indent);
80
81                 total = strlen (indent);
82         }
83
84         while (start < ptr) {
85                 putc (*start++, stdout);
86                 ++total;
87         }
88
89         return total;
90 }
91
92 static int /* total */
93 _limited_printf (const char* indent, int total, const char *format, ...)
94 {
95         va_list args;
96         int single;
97
98         VSC__ASSERT (indent != NULL);
99         VSC__ASSERT (format != NULL);
100
101         va_start (args, format);
102
103         single = vsnprintf (NULL, 0, format, args);
104
105         va_end (args);
106
107         if (single < 0) {
108                 goto cleanup;
109         }
110
111         if (total + single > _LENGTH_LIMIT) {
112                 printf ("\n");
113                 printf ("%s", indent);
114
115                 total = strlen (indent) + single;
116         } else {
117                 total += single;
118         }
119
120         va_start (args, format);
121
122         vprintf (format, args);
123
124 cleanup:
125         va_end (args);
126
127         return total;
128 }
129
130 static void
131 _help (struct VscError *error, const struct VscCliCommandInfo *command_info,
132        const struct VscCliOption *option_list, void *user_data);
133
134 static const struct VscCliOptionInfo _help_option_infos[] = {
135         { -1, "command", "Name of command", VSC_CLI__OPTION_TYPE__STRING,
136           VSC_CLI__OPTION_FLAG__NONE },
137         VSC_CLI__NULL__OPTION_INFO,
138 };
139
140 const struct VscCliCommandInfo vsc_cli_help_command_info = {
141         "help",
142         "Print help",
143         "Prints global or command specific help",
144         _help,
145         _help_option_infos,
146 };
147
148 static void
149 _help (struct VscError *error,
150        const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
151        const struct VscCliOption *option_list,
152        void *user_data VSC__ATTR__UNUSED)
153 {
154         int i, total, length;
155         const char *command_name;
156         const char *syntax;
157         char indent[256];
158         const struct VscCliCommandInfo *command_info_;
159         const struct VscCliOptionInfo *option_info;
160
161         VSC__ASSERT (error != NULL);
162         VSC__ASSERT (! error->occured);
163
164         command_name = vsc_cli_option_list_lookup_string (option_list, "command");
165
166         if (command_name != NULL) {
167                 command_info_ = _vsc_cli_command_info_lookup (command_name);
168
169                 if (command_info_ == NULL) {
170                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
171                                      "help: Command '%s' is unknown", command_name);
172                         return;
173                 }
174
175                 /*
176                  * NAME
177                  */
178                 printf (_vsc_cli_output_coloring ? "\033[1mNAME\033[0m\n"
179                                                  : "NAME\n");
180
181                 length = strlen (command_info_->name) + 10;
182
183                 if (length > 255) {
184                         length = 255;
185                 }
186
187                 for (int i = 0; i < length; i++) {
188                         indent[i] = ' ';
189                 }
190
191                 indent[length] = '\0';
192                 total = _limited_printf ("", 0, "       %s - ", command_info_->name);
193
194                 _limited_print_words (indent, total, command_info_->help);
195                 printf ("\n");
196                 printf ("\n");
197
198                 /*
199                  * SYNOPSIS
200                  */
201                 printf (_vsc_cli_output_coloring ? "\033[1mSYNOPSIS\033[0m\n"
202                                                  : "SYNOPSIS\n");
203
204                 total = _limited_printf ("", 0, "       %s", command_info_->name);
205                 length = strlen (command_info_->name) + 7;
206
207                 if (length > 255) {
208                         length = 255;
209                 }
210
211                 for (int i = 0; i < length; i++) {
212                         indent[i] = ' ';
213                 }
214
215                 indent[length] = '\0';
216
217                 for (i = 0; command_info_->option_infos[i].long_name != NULL; ++i) {
218                         option_info = &command_info_->option_infos[i];
219                         syntax = _vsc_cli_option_syntax (option_info, TRUE);
220
221                         if (option_info->flags & VSC_CLI__OPTION_FLAG__REQUIRED) {
222                                 total = _limited_printf (indent, total, " %s", syntax);
223                         } else {
224                                 total = _limited_printf (indent, total, " [%s]", syntax);
225                         }
226                 }
227
228                 printf ("\n");
229
230                 /*
231                  * DESCRIPTION
232                  */
233                 if (command_info_->description != NULL &&
234                     *command_info_->description != '\0') {
235                         printf ("\n");
236                         printf (_vsc_cli_output_coloring ? "\033[1mDESCRIPTION\033[0m\n"
237                                                          : "DESCRIPTION\n");
238
239                         total = _limited_print_words ("", 0, "       ");
240                         total = _limited_print_words ("       ", total,
241                                                       command_info_->description);
242
243                         printf ("\n");
244                 }
245
246                 /*
247                  * OPTIONS
248                  */
249                 if (command_info_->option_infos[0].long_name != NULL) {
250                         printf ("\n");
251                         printf (_vsc_cli_output_coloring ? "\033[1mOPTIONS\033[0m\n"
252                                                          : "OPTIONS\n");
253
254                         for (i = 0; command_info_->option_infos[i].long_name != NULL; ++i) {
255                                 option_info = &command_info_->option_infos[i];
256
257                                 printf ("       %s\n",
258                                         _vsc_cli_option_syntax (option_info, FALSE));
259
260                                 total = _limited_print_words ("", 0, "              ");
261                                 total = _limited_print_words ("              ", total,
262                                                               option_info->help);
263
264                                 printf ("\n");
265
266                                 if (command_info_->option_infos[i + 1].long_name != NULL) {
267                                         printf ("\n");
268                                 }
269                         }
270                 }
271         } else {
272                 if (! _vsc_cli_interactive) {
273                         /*
274                          * NAME
275                          */
276                         printf (_vsc_cli_output_coloring ? "\033[1mNAME\033[0m\n" : "NAME\n");
277
278                         length = strlen (_vsc_cli_name) + 10;
279
280                         if (length > 255) {
281                                 length = 255;
282                         }
283
284                         for (int i = 0; i < length; i++) {
285                                 indent[i] = ' ';
286                         }
287
288                         indent[length] = '\0';
289                         total = _limited_printf ("", 0, "       %s - ", _vsc_cli_name);
290
291                         _limited_print_words (indent, total, _vsc_cli_help);
292                         printf ("\n");
293                         printf ("\n");
294
295                         /*
296                          * SYNOPSIS
297                          */
298                         printf (_vsc_cli_output_coloring ? "\033[1mSYNOPSIS\033[0m\n"
299                                                          : "SYNOPSIS\n");
300
301                         total = _limited_printf ("", 0, "       %s", _vsc_cli_name);
302
303                         for (i = 0; _vsc_cli_option_infos[i].long_name != NULL; ++i) {
304                                 option_info = &_vsc_cli_option_infos[i];
305                                 syntax = _vsc_cli_option_syntax (option_info, TRUE);
306
307                                 if (option_info->flags & VSC_CLI__OPTION_FLAG__REQUIRED) {
308                                         total = _limited_printf ("      ", total, " %s", syntax);
309                                 } else {
310                                         total = _limited_printf ("      ", total, " [%s]", syntax);
311                                 }
312                         }
313
314                         length = strlen (_vsc_cli_name) + 7;
315
316                         if (length > 255) {
317                                 length = 255;
318                         }
319
320                         for (int i = 0; i < length; i++) {
321                                 indent[i] = ' ';
322                         }
323
324                         indent[length] = '\0';
325
326                         _limited_printf (indent, total, " [<command>[, <command>]...]");
327
328                         printf ("\n");
329
330                         /*
331                          * OPTIONS
332                          */
333                         if (_vsc_cli_option_infos[0].long_name != NULL) {
334                                 printf ("\n");
335                                 printf (_vsc_cli_output_coloring ? "\033[1mOPTIONS\033[0m\n"
336                                                                  : "OPTIONS\n");
337
338                                 for (i = 0; _vsc_cli_option_infos[i].long_name != NULL; ++i) {
339                                         option_info = &_vsc_cli_option_infos[i];
340
341                                         printf ("       %s\n"
342                                                 "              %s\n",
343                                                 _vsc_cli_option_syntax (option_info, FALSE),
344                                                 option_info->help);
345
346                                         if (_vsc_cli_option_infos[i + 1].long_name != NULL) {
347                                                 printf ("\n");
348                                         }
349                                 }
350                         }
351                 }
352
353                 /*
354                  * COMMANDS
355                  */
356                 if (_vsc_cli_command_infos[0]->name != NULL) {
357                         if (! _vsc_cli_interactive) {
358                                 printf ("\n");
359                         }
360
361                         printf (_vsc_cli_output_coloring ? "\033[1mCOMMANDS\033[0m\n"
362                                                          : "COMMANDS\n");
363
364                         for (i = 0; _vsc_cli_command_infos[i] != NULL; ++i) {
365                                 total = _limited_printf ("", 0, "       %-15s  ",
366                                                          _vsc_cli_command_infos[i]->name);
367
368                                 _limited_print_words ("                        ", total,
369                                                       _vsc_cli_command_infos[i]->help);
370                                 printf ("\n");
371                         }
372                 }
373         }
374 }