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