xref: /third_party/jerryscript/jerry-main/cli.c (revision 425bb815)
1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18
19#include "cli.h"
20
21/*
22 * Fixed layout settings
23 */
24
25/**
26 * Wrap lines at:
27 */
28#define CLI_LINE_LENGTH 80
29
30/**
31 * Indent various lines with:
32 */
33#define CLI_LINE_INDENT 2
34
35/**
36 * Tab stop (for multi-column display) at:
37 */
38#define CLI_LINE_TAB 24
39
40/**
41 * Declare a char VLA and concatenate a program name and a sub-command name
42 * (separated by a single space) into the new array. Useful for printing command
43 * line option usage summary for sub-commands.
44 *
45 * @param CMDNAME name of the new array variable.
46 * @param PROGNAME string containing the name of the program.
47 * @param CMD string continaing the name of the sub-command.
48 */
49#define CLI_CMD_NAME(CMDNAME, PROGNAME, CMD) \
50  char CMDNAME[strlen ((PROGNAME)) + strlen ((CMD)) + 2]; \
51  strncpy (CMDNAME, (PROGNAME), strlen ((PROGNAME))); \
52  CMDNAME[strlen ((PROGNAME))] = ' '; \
53  strncpy (CMDNAME + strlen ((PROGNAME)) + 1, (CMD), strlen ((CMD)) + 1)
54
55/*
56 * Command line option handling
57 */
58
59/**
60 * Initialize a command line option processor.
61 *
62 * @return the state that should be passed to other cli_ functions.
63 */
64cli_state_t
65cli_init (const cli_opt_t *options_p, /**< array of option definitions, terminated by CLI_OPT_DEFAULT */
66          int argc, /**< number of command line arguments */
67          char **argv) /**< array of command line arguments */
68{
69  return (cli_state_t)
70  {
71    .error = NULL,
72    .arg = NULL,
73    .argc = argc,
74    .argv = argv,
75    .opts = options_p
76  };
77} /* cli_init */
78
79/**
80 * Use another option list.
81 */
82void
83cli_change_opts (cli_state_t *state_p, /**< state of the command line option processor */
84                 const cli_opt_t *options_p) /**< array of option definitions, terminated by CLI_OPT_DEFAULT */
85{
86  state_p->opts = options_p;
87} /* cli_change_opts */
88
89/**
90 * Checks whether the current argument is an option.
91 *
92 * Note:
93 *   The state_p->error is not NULL on error and it contains the error message.
94 *
95 * @return the ID of the option that was found or a CLI_OPT_ constant otherwise.
96 */
97int
98cli_consume_option (cli_state_t *state_p) /**< state of the command line option processor */
99{
100  if (state_p->error != NULL)
101  {
102    return CLI_OPT_END;
103  }
104
105  if (state_p->argc <= 0)
106  {
107    state_p->arg = NULL;
108    return CLI_OPT_END;
109  }
110
111  const char *arg = state_p->argv[0];
112
113  state_p->arg = arg;
114
115  if (arg[0] != '-' || arg[1] == '\0')
116  {
117    return CLI_OPT_DEFAULT;
118  }
119
120  if (arg[1] == '-')
121  {
122    arg += 2;
123
124    for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++)
125    {
126      if (opt->longopt != NULL && strcmp (arg, opt->longopt) == 0)
127      {
128        state_p->argc--;
129        state_p->argv++;
130        return opt->id;
131      }
132    }
133
134    state_p->error = "Unknown long option";
135    return CLI_OPT_END;
136  }
137
138  arg++;
139
140  for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++)
141  {
142    if (opt->opt != NULL && strcmp (arg, opt->opt) == 0)
143    {
144      state_p->argc--;
145      state_p->argv++;
146      return opt->id;
147    }
148  }
149
150  state_p->error = "Unknown option";
151  return CLI_OPT_END;
152} /* cli_consume_option */
153
154/**
155 * Returns the next argument as string.
156 *
157 * Note:
158 *   The state_p->error is not NULL on error and it contains the error message.
159 *
160 * @return argument string
161 */
162const char *
163cli_consume_string (cli_state_t *state_p) /**< state of the command line option processor */
164{
165  if (state_p->error != NULL)
166  {
167    return NULL;
168  }
169
170  if (state_p->argc <= 0)
171  {
172    state_p->error = "Expected string argument";
173    state_p->arg = NULL;
174    return NULL;
175  }
176
177  state_p->arg = state_p->argv[0];
178
179  state_p->argc--;
180  state_p->argv++;
181  return state_p->arg;
182} /* cli_consume_string */
183
184/**
185 * Returns the next argument as integer.
186 *
187 * Note:
188 *   The state_p->error is not NULL on error and it contains the error message.
189 *
190 * @return argument integer
191 */
192int
193cli_consume_int (cli_state_t *state_p) /**< state of the command line option processor */
194{
195  if (state_p->error != NULL)
196  {
197    return 0;
198  }
199
200  state_p->error = "Expected integer argument";
201
202  if (state_p->argc <= 0)
203  {
204    state_p->arg = NULL;
205    return 0;
206  }
207
208  state_p->arg = state_p->argv[0];
209
210  char *endptr;
211  long int value = strtol (state_p->arg, &endptr, 10);
212
213  if (*endptr != '\0')
214  {
215    return 0;
216  }
217
218  state_p->error = NULL;
219  state_p->argc--;
220  state_p->argv++;
221  return (int) value;
222} /* cli_consume_int */
223
224/*
225 * Print helper functions
226 */
227
228/**
229 * Pad with spaces.
230 */
231static void
232cli_print_pad (int cnt) /**< number of spaces to print */
233{
234  for (int i = 0; i < cnt; i++)
235  {
236    printf (" ");
237  }
238} /* cli_print_pad */
239
240/**
241 * Print the prefix of a string.
242 */
243static void
244cli_print_prefix (const char *str, /**< string to print */
245                  int len) /**< length of the prefix to print */
246{
247  for (int i = 0; i < len; i++)
248  {
249    printf ("%c", *str++);
250  }
251} /* cli_print_prefix */
252
253/**
254 * Print usage summary of options.
255 */
256static void
257cli_opt_usage (const char *prog_name_p, /**< program name, typically argv[0] */
258               const char *command_name_p, /**< command name if available */
259               const cli_opt_t *opts_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
260{
261  int length = (int) strlen (prog_name_p);
262  const cli_opt_t *current_opt_p = opts_p;
263
264  printf ("%s", prog_name_p);
265
266  if (command_name_p != NULL)
267  {
268    int command_length = (int) strlen (command_name_p);
269
270    if (length + 1 + command_length > CLI_LINE_LENGTH)
271    {
272      length = CLI_LINE_INDENT - 1;
273      printf ("\n");
274      cli_print_pad (length);
275    }
276
277    printf (" %s", command_name_p);
278  }
279
280  while (current_opt_p->id != CLI_OPT_DEFAULT)
281  {
282    const char *opt_p = current_opt_p->opt;
283    int opt_length = 2 + 1;
284
285    if (opt_p == NULL)
286    {
287      opt_p = current_opt_p->longopt;
288      opt_length++;
289    }
290
291    opt_length += (int) strlen (opt_p);
292
293    if (length + 1 + opt_length >= CLI_LINE_LENGTH)
294    {
295      length = CLI_LINE_INDENT - 1;
296      printf ("\n");
297      cli_print_pad (length);
298    }
299    length += opt_length;
300
301    printf (" [");
302
303    if (current_opt_p->opt != NULL)
304    {
305      printf ("-%s", opt_p);
306    }
307    else
308    {
309      printf ("--%s", opt_p);
310    }
311
312    if (current_opt_p->meta != NULL)
313    {
314      printf (" %s", current_opt_p->meta);
315    }
316
317    printf ("]");
318
319    current_opt_p++;
320  }
321
322  if (current_opt_p->meta != NULL)
323  {
324    const char *opt_p = current_opt_p->meta;
325    int opt_length = (int) (2 + strlen (opt_p));
326
327    if (length + 1 + opt_length >= CLI_LINE_LENGTH)
328    {
329      length = CLI_LINE_INDENT - 1;
330      printf ("\n");
331      cli_print_pad (length);
332    }
333
334    printf (" [%s]", opt_p);
335  }
336
337  printf ("\n\n");
338} /* cli_opt_usage */
339
340/**
341 * Print a help message wrapped into the second column.
342 */
343static void
344cli_print_help (const char *help) /**< the help message to print */
345{
346  while (help != NULL && *help != 0)
347  {
348    int length = -1;
349    int i = 0;
350    for (; i < CLI_LINE_LENGTH - CLI_LINE_TAB && help[i] != 0; i++)
351    {
352      if (help[i] == ' ')
353      {
354        length = i;
355      }
356    }
357    if (length < 0 || i < CLI_LINE_LENGTH - CLI_LINE_TAB)
358    {
359      length = i;
360    }
361
362    cli_print_prefix (help, length);
363
364    help += length;
365    while (*help == ' ')
366    {
367      help++;
368    }
369
370    if (*help != 0)
371    {
372      printf ("\n");
373      cli_print_pad (CLI_LINE_TAB);
374    }
375  }
376} /* cli_print_help */
377
378/**
379 * Print detailed help for options.
380 */
381void
382cli_help (const char *prog_name_p, /**< program name, typically argv[0] */
383          const char *command_name_p, /**< command name if available */
384          const cli_opt_t *options_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
385{
386  cli_opt_usage (prog_name_p, command_name_p, options_p);
387
388  const cli_opt_t *opt_p = options_p;
389
390  while (opt_p->id != CLI_OPT_DEFAULT)
391  {
392    int length = CLI_LINE_INDENT;
393    cli_print_pad (CLI_LINE_INDENT);
394
395    if (opt_p->opt != NULL)
396    {
397      printf ("-%s", opt_p->opt);
398      length += (int) (strlen (opt_p->opt) + 1);
399    }
400
401    if (opt_p->opt != NULL && opt_p->longopt != NULL)
402    {
403      printf (", ");
404      length += 2;
405    }
406
407    if (opt_p->longopt != NULL)
408    {
409      printf ("--%s", opt_p->longopt);
410      length += (int) (strlen (opt_p->longopt) + 2);
411    }
412
413    if (opt_p->meta != NULL)
414    {
415      printf (" %s", opt_p->meta);
416      length += 1 + (int) strlen (opt_p->meta);
417    }
418
419    if (opt_p->help != NULL)
420    {
421      if (length >= CLI_LINE_TAB)
422      {
423        printf ("\n");
424        length = 0;
425      }
426      cli_print_pad (CLI_LINE_TAB - length);
427      length = CLI_LINE_TAB;
428
429      cli_print_help (opt_p->help);
430    }
431
432    printf ("\n");
433    opt_p++;
434  }
435
436  if (opt_p->help != NULL)
437  {
438    int length = 0;
439
440    if (opt_p->meta != NULL)
441    {
442      length = (int) (CLI_LINE_INDENT + strlen (opt_p->meta));
443
444      cli_print_pad (CLI_LINE_INDENT);
445      printf ("%s", opt_p->meta);
446    }
447
448    if (length >= CLI_LINE_TAB)
449    {
450      printf ("\n");
451      length = 0;
452    }
453
454    cli_print_pad (CLI_LINE_TAB - length);
455
456    cli_print_help (opt_p->help);
457    printf ("\n");
458  }
459} /* cli_help */
460