Coverage Report

Created: 2025-08-26 06:43

/src/clib/deps/commander/commander.c
Line
Count
Source (jump to first uncovered line)
1
2
//
3
// commander.c
4
//
5
// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
6
//
7
8
#include <stdio.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <assert.h>
12
#include "commander.h"
13
14
/*
15
 * Output error and exit.
16
 */
17
18
static void
19
0
error(char *msg) {
20
0
  fprintf(stderr, "%s\n", msg);
21
0
  exit(1);
22
0
}
23
24
/*
25
 * Output command version.
26
 */
27
28
static void
29
0
command_version(command_t *self) {
30
0
  printf("%s\n", self->version);
31
0
  command_free(self);
32
0
  exit(0);
33
0
}
34
35
/*
36
 * Output command help.
37
 */
38
39
void
40
0
command_help(command_t *self) {
41
0
  printf("\n");
42
0
  printf("  Usage: %s %s\n", self->name, self->usage);
43
0
  printf("\n");
44
0
  printf("  Options:\n");
45
0
  printf("\n");
46
47
0
  int i;
48
0
  for (i = 0; i < self->option_count; ++i) {
49
0
    command_option_t *option = &self->options[i];
50
0
    printf("    %s, %-25s %s\n"
51
0
      , option->small
52
0
      , option->large_with_arg
53
0
      , option->description);
54
0
  }
55
56
0
  printf("\n");
57
0
  command_free(self);
58
0
  exit(0);
59
0
}
60
61
/*
62
 * Initialize with program `name` and `version`.
63
 */
64
65
void
66
0
command_init(command_t *self, const char *name, const char *version) {
67
0
  self->arg = NULL;
68
0
  self->name = name;
69
0
  self->version = version;
70
0
  self->option_count = self->argc = 0;
71
0
  self->usage = "[options]";
72
0
  self->nargv = NULL;
73
0
  command_option(self, "-V", "--version", "output program version", command_version);
74
0
  command_option(self, "-h", "--help", "output help information", command_help);
75
0
}
76
77
/*
78
 * Free up commander after use.
79
 */
80
81
void
82
0
command_free(command_t *self) {
83
0
  int i;
84
85
0
  for (i = 0; i < self->option_count; ++i) {
86
0
    command_option_t *option = &self->options[i];
87
0
    free(option->argname);
88
0
    free(option->large);
89
0
  }
90
91
0
  if (self->nargv) {
92
0
    for (i = 0; self->nargv[i]; ++i) {
93
0
      free(self->nargv[i]);
94
0
    }
95
0
    free(self->nargv);
96
0
  }
97
0
}
98
99
/*
100
 * Parse argname from `str`. For example
101
 * Take "--required <arg>" and populate `flag`
102
 * with "--required" and `arg` with "<arg>".
103
 */
104
105
static void
106
0
parse_argname(const char *str, char *flag, char *arg) {
107
0
  int buffer = 0;
108
0
  size_t flagpos = 0;
109
0
  size_t argpos = 0;
110
0
  size_t len = strlen(str);
111
0
  size_t i;
112
113
0
  for (i = 0; i < len; ++i) {
114
0
    if (buffer || '[' == str[i] || '<' == str[i]) {
115
0
      buffer = 1;
116
0
      arg[argpos++] = str[i];
117
0
    } else {
118
0
      if (' ' == str[i]) continue;
119
0
      flag[flagpos++] = str[i];
120
0
    }
121
0
  }
122
123
0
  arg[argpos] = '\0';
124
0
  flag[flagpos] = '\0';
125
0
}
126
127
/*
128
 * Normalize the argument vector by exploding
129
 * multiple options (if any). For example
130
 * "foo -abc --scm git" -> "foo -a -b -c --scm git"
131
 */
132
133
static char **
134
0
normalize_args(int *argc, char **argv) {
135
0
  int size = 0;
136
0
  int alloc = *argc + 1;
137
0
  char **nargv = malloc(alloc * sizeof(char *));
138
0
  int i;
139
140
0
  for (i = 0; argv[i]; ++i) {
141
0
    const char *arg = argv[i];
142
0
    size_t len = strlen(arg);
143
144
    // short flag
145
0
    if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) {
146
0
      alloc += len - 2;
147
0
      nargv = realloc(nargv, alloc * sizeof(char *));
148
0
      for (size_t j = 1; j < len; ++j) {
149
0
        nargv[size] = malloc(3);
150
0
        sprintf(nargv[size], "-%c", arg[j]);
151
0
        size++;
152
0
      }
153
0
      continue;
154
0
    }
155
156
    // regular arg
157
0
    nargv[size] = malloc(len + 1);
158
0
    strcpy(nargv[size], arg);
159
0
    size++;
160
0
  }
161
162
0
  nargv[size] = NULL;
163
0
  *argc = size;
164
0
  return nargv;
165
0
}
166
167
/*
168
 * Define an option.
169
 */
170
171
void
172
0
command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {
173
0
  if (self->option_count == COMMANDER_MAX_OPTIONS) {
174
0
    command_free(self);
175
0
    error("Maximum option definitions exceeded");
176
0
  }
177
0
  int n = self->option_count++;
178
0
  command_option_t *option = &self->options[n];
179
0
  option->cb = cb;
180
0
  option->small = small;
181
0
  option->description = desc;
182
0
  option->required_arg = option->optional_arg = 0;
183
0
  option->large_with_arg = large;
184
0
  option->argname = malloc(strlen(large) + 1);
185
0
  assert(option->argname);
186
0
  option->large = malloc(strlen(large) + 1);
187
0
  assert(option->large);
188
0
  parse_argname(large, option->large, option->argname);
189
0
  if ('[' == option->argname[0]) option->optional_arg = 1;
190
0
  if ('<' == option->argname[0]) option->required_arg = 1;
191
0
}
192
193
/*
194
 * Parse `argv` (internal).
195
 * Input arguments should be normalized first
196
 * see `normalize_args`.
197
 */
198
199
static void
200
0
command_parse_args(command_t *self, int argc, char **argv) {
201
0
  int literal = 0;
202
0
  int i, j;
203
204
0
  for (i = 1; i < argc; ++i) {
205
0
    const char *arg = argv[i];
206
0
    for (j = 0; j < self->option_count; ++j) {
207
0
      command_option_t *option = &self->options[j];
208
209
      // match flag
210
0
      if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) {
211
0
        self->arg = NULL;
212
213
        // required
214
0
        if (option->required_arg) {
215
0
          arg = argv[++i];
216
0
          if (!arg || '-' == arg[0]) {
217
0
            fprintf(stderr, "%s %s argument required\n", option->large, option->argname);
218
0
            command_free(self);
219
0
            exit(1);
220
0
          }
221
0
          self->arg = arg;
222
0
        }
223
224
        // optional
225
0
        if (option->optional_arg) {
226
0
          if (argv[i + 1] && '-' != argv[i + 1][0]) {
227
0
            self->arg = argv[++i];
228
0
          }
229
0
        }
230
231
        // invoke callback
232
0
        option->cb(self);
233
0
        goto match;
234
0
      }
235
0
    }
236
237
    // --
238
0
    if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) {
239
0
      literal = 1;
240
0
      goto match;
241
0
    }
242
243
    // unrecognized
244
0
    if ('-' == arg[0] && !literal) {
245
0
      fprintf(stderr, "unrecognized flag %s\n", arg);
246
0
      command_free(self);
247
0
      exit(1);
248
0
    }
249
250
0
    int n = self->argc++;
251
0
    if (n == COMMANDER_MAX_ARGS) {
252
0
      command_free(self);
253
0
      error("Maximum number of arguments exceeded");
254
0
    }
255
0
    self->argv[n] = (char *) arg;
256
0
    match:;
257
0
  }
258
0
}
259
260
/*
261
 * Parse `argv` (public).
262
 */
263
264
void
265
0
command_parse(command_t *self, int argc, char **argv) {
266
0
  self->nargv = normalize_args(&argc, argv);
267
0
  command_parse_args(self, argc, self->nargv);
268
0
  self->argv[self->argc] = NULL;
269
0
}