:: bzip3 / include / yarg.h 10.4 KB raw

1
/* Written by Kamila Szewczyk (k@iczelia.net) */
2
3
#ifndef _YARG_H
4
#define _YARG_H
5
6
#include <stdio.h>
7
#include <stdbool.h>
8
#include <stdarg.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <limits.h>
12
13
typedef enum {
14
  no_argument,
15
  required_argument,
16
  optional_argument
17
} yarg_arg_type;
18
19
typedef struct {
20
  int opt;
21
  yarg_arg_type type;
22
  const char * long_opt;
23
} yarg_options;
24
25
typedef enum {
26
  YARG_STYLE_WINDOWS,
27
  YARG_STYLE_UNIX,
28
  YARG_STYLE_UNIX_SHORT
29
} yarg_style;
30
31
typedef struct {
32
  bool dash_dash;
33
  yarg_style style;
34
} yarg_settings;
35
36
typedef struct {
37
  int opt;
38
  const char * long_opt;
39
  char * arg;
40
} yarg_option;
41
42
typedef struct {
43
  yarg_option * args;
44
  int argc;
45
  char ** pos_args;
46
  int pos_argc;
47
  char * error;
48
} yarg_result;
49
50
static const char yarg_oom[] = "Out of memory";
51
static int yarg_asprintf(char ** strp, const char * fmt, ...) {
52
  if (fmt == yarg_oom)
53
    goto use_yarg_oom;
54
  va_list ap;
55
  va_start(ap, fmt);
56
  int len = vsnprintf(NULL, 0, fmt, ap);
57
  va_end(ap);
58
  if (len < 0)
59
    goto use_yarg_oom;
60
  *strp = (char *) malloc(len + 1);
61
  if (!*strp)
62
    goto use_yarg_oom;
63
  va_start(ap, fmt);
64
  len = vsnprintf(*strp, len + 1, fmt, ap);
65
  va_end(ap);
66
  return len;
67
68
use_yarg_oom:
69
    *strp = (char *)yarg_oom;
70
    return sizeof(yarg_oom);
71
}
72
73
static char * yarg_strdup(const char * str) {
74
  char * new_str = (char *) calloc(strlen(str) + 1, 1);
75
  if (!new_str) return NULL;
76
  strcpy(new_str, str);
77
  return new_str;
78
}
79
80
static int yarg_parse_unix(int argc, char * argv[], yarg_options opt[],
81
                           yarg_result * res, bool dash_dash) {
82
  int no_args = 0, no_pos_args = 0;
83
  for (int i = 1; i < argc; i++) {
84
    if (argv[i][0] == '-') {
85
      if (argv[i][1] == '-') {
86
        if (dash_dash && argv[i][2] == '\0')
87
          { no_pos_args += argc - i - 1; break; }
88
        char * long_opt = argv[i] + 2; yarg_options * o = NULL;
89
        int len = 0; while (long_opt[len] && long_opt[len] != '=') len++;
90
        for (int j = 0; opt[j].opt; j++)
91
          if (opt[j].long_opt && !strncmp(opt[j].long_opt, long_opt, len))
92
            { o = &opt[j]; break; }
93
        if (!o) {
94
          yarg_asprintf(&res->error, "--%.*s -- unknown option\n", len, long_opt);
95
          return 0;
96
        }
97
        if (o->type == required_argument) {
98
          if (long_opt[len] == '=') {
99
            // Ignore.
100
          } else if (argv[i + 1] && argv[i + 1][0] != '-') {
101
            i++;
102
          } else {
103
            yarg_asprintf(&res->error, "--%s -- missing argument\n", o->long_opt);
104
            return 0;
105
          }
106
        } else if (o->type == optional_argument) {
107
          if (long_opt[len] == '=') {
108
          } else if (argv[i + 1] && argv[i + 1][0] != '-') {
109
            i++;
110
          }
111
        }
112
        no_args++;
113
      } else {
114
        for (int j = 1; argv[i][j]; j++) {
115
          char c = argv[i][j]; yarg_options * o = NULL;
116
          for (int k = 0; opt[k].opt; k++)
117
            if (opt[k].opt == c)
118
              { o = &opt[k]; break; }
119
          if (!o) {
120
            yarg_asprintf(&res->error, "-%c -- unknown option\n", c);
121
            return 0;
122
          }
123
          if (o->type == required_argument) {
124
            if (argv[i][j + 1]) {
125
              // Ignore.
126
            } else if (argv[i + 1] && argv[i + 1][0] != '-') {
127
              i++;
128
            } else {
129
              yarg_asprintf(&res->error, "-%c -- missing argument\n", c);
130
              return 0;
131
            }
132
            no_args++;
133
            break;
134
          } else if(o->type == optional_argument) {
135
            if (argv[i][j + 1]) {
136
              // Ignore.
137
              no_args++;
138
              break;
139
            } else if (argv[i + 1] && argv[i + 1][0] != '-') {
140
              i++;
141
              no_args++;
142
              break;
143
            }
144
          }
145
          no_args++;
146
        }
147
      }
148
    } else no_pos_args++;
149
  }
150
151
  res->args = (yarg_option *) calloc((no_args + 1) * sizeof(yarg_option), 1);
152
  res->pos_args = (char **) calloc((no_pos_args + 1) * sizeof(char *), 1);
153
  if(!res->args || !res->pos_args) {
154
    yarg_asprintf(&res->error, yarg_oom);
155
    return 0;
156
  }
157
158
  for (int i = 1; i < argc; i++) {
159
    if (argv[i][0] == '-') {
160
      if (argv[i][1] == '-') {
161
        if (dash_dash && argv[i][2] == '\0') {
162
          for (int j = i + 1; j < argc; j++)
163
            if(!(res->pos_args[res->pos_argc++] = yarg_strdup(argv[j]))) {
164
              yarg_asprintf(&res->error, yarg_oom);
165
              return 0;
166
            }
167
          break;
168
        }
169
        char * long_opt = argv[i] + 2; yarg_options * o = NULL;
170
        int len = 0; while (long_opt[len] && long_opt[len] != '=') len++;
171
        for (int j = 0; opt[j].opt; j++)
172
          if (opt[j].long_opt && !strncmp(opt[j].long_opt, long_opt, len))
173
            { o = &opt[j]; break; }
174
        res->args[res->argc].opt = o->opt;
175
        res->args[res->argc].long_opt = o->long_opt;
176
        if (o->type == required_argument || o->type == optional_argument) {
177
          if (long_opt[len] == '=') {
178
            if(!(res->args[res->argc].arg = yarg_strdup(long_opt + len + 1))) {
179
              yarg_asprintf(&res->error, yarg_oom);
180
              return 0;
181
            }
182
          } else if (argv[i + 1] && argv[i + 1][0] != '-') {
183
            if(!(res->args[res->argc].arg = yarg_strdup(argv[++i]))) {
184
              yarg_asprintf(&res->error, yarg_oom);
185
              return 0;
186
            }
187
          }
188
        }
189
        res->argc++;
190
      } else {
191
        for (int j = 1; argv[i][j]; j++) {
192
          char c = argv[i][j]; yarg_options * o = NULL;
193
          for (int k = 0; opt[k].opt; k++)
194
            if (opt[k].opt == c)
195
              { o = &opt[k]; break; }
196
          if (!o) {
197
            yarg_asprintf(&res->error, "-%c -- unknown option\n", c);
198
            return 0;
199
          }
200
          res->args[res->argc].opt = c;
201
          res->args[res->argc].long_opt = o->long_opt;
202
          if (o->type == required_argument || o->type == optional_argument) {
203
            if (argv[i][j + 1]) {
204
              if(!(res->args[res->argc++].arg = yarg_strdup(argv[i] + j + 1))) {
205
                yarg_asprintf(&res->error, yarg_oom);
206
                return 0;
207
              }
208
              break;
209
            } else if (argv[i + 1] && argv[i + 1][0] != '-') {
210
              if(!(res->args[res->argc++].arg = yarg_strdup(argv[++i]))) {
211
                yarg_asprintf(&res->error, yarg_oom);
212
                return 0;
213
              }
214
              break;
215
            }
216
          }
217
          res->argc++;
218
        }
219
      }
220
    } else if(!(res->pos_args[res->pos_argc++] = yarg_strdup(argv[i]))) {
221
      yarg_asprintf(&res->error, yarg_oom);
222
      return 0;
223
    }
224
  }
225
226
  return 1;
227
}
228
229
static int yarg_parse_unix_short(int argc, char * argv[], yarg_options opt[],
230
                                 yarg_result * res, bool dash_dash, char opt_char) {
231
  int no_args = 0, no_pos_args = 0;
232
  for (int i = 1; i < argc; i++) {
233
    if (argv[i][0] == opt_char) {
234
      if (dash_dash && argv[i][1] == '\0') {
235
        no_pos_args += argc - i - 1;
236
        break;
237
      }
238
      char * long_opt = argv[i] + 1; yarg_options * o = NULL;
239
      int len = 0; while (long_opt[len] && long_opt[len] != '=') len++;
240
      for (int j = 0; opt[j].opt; j++)
241
        if (opt[j].long_opt && !strncmp(opt[j].long_opt, long_opt, len))
242
          { o = &opt[j]; break; }
243
      if (!o) {
244
        yarg_asprintf(&res->error, "%c%.*s -- unknown option\n", opt_char, len, long_opt);
245
        return 0;
246
      }
247
      if (o->type == required_argument) {
248
        if (long_opt[len] == '=') {
249
          // Ignore.
250
        } else if (argv[i + 1] && argv[i + 1][0] != opt_char) {
251
          i++;
252
        } else {
253
          yarg_asprintf(&res->error, "%c%s -- missing argument\n", opt_char, o->long_opt);
254
          return 0;
255
        }
256
      } else if (o->type == optional_argument) {
257
        if (long_opt[len] == '=') {
258
          // Ignore.
259
        } else if (argv[i + 1] && argv[i + 1][0] != opt_char) {
260
          i++;
261
        }
262
      }
263
      no_args++;
264
    } else no_pos_args++;
265
  }
266
  
267
  res->args = (yarg_option *) calloc((no_args + 1) * sizeof(yarg_option), 1);
268
  res->pos_args = (char **) calloc((no_pos_args + 1) * sizeof(char *), 1);
269
  if (!res->args || !res->pos_args) {
270
    yarg_asprintf(&res->error, yarg_oom);
271
    return 0;
272
  }
273
274
  for (int i = 1; i < argc; i++) {
275
    if (argv[i][0] == opt_char) {
276
      if (dash_dash && argv[i][1] == '\0') {
277
        for (int j = i + 1; j < argc; j++)
278
          if(!(res->pos_args[res->pos_argc++] = yarg_strdup(argv[j]))) {
279
            yarg_asprintf(&res->error, yarg_oom);
280
            return 0;
281
          }
282
        break;
283
      }
284
      char * long_opt = argv[i] + 1; yarg_options * o = NULL;
285
      int len = 0; while (long_opt[len] && long_opt[len] != '=') len++;
286
      for (int j = 0; opt[j].opt; j++)
287
        if (opt[j].long_opt && !strncmp(opt[j].long_opt, long_opt, len))
288
          { o = &opt[j]; break; }
289
      res->args[res->argc].opt = o->opt;
290
      res->args[res->argc].long_opt = o->long_opt;
291
      if (o->type == required_argument || o->type == optional_argument) {
292
        if (long_opt[len] == '=') {
293
          if(!(res->args[res->argc].arg = yarg_strdup(long_opt + len + 1))) {
294
            yarg_asprintf(&res->error, yarg_oom);
295
            return 0;
296
          }
297
        } else if (argv[i + 1] && argv[i + 1][0] != opt_char) {
298
          if(!(res->args[res->argc].arg = yarg_strdup(argv[++i]))) {
299
            yarg_asprintf(&res->error, yarg_oom);
300
            return 0;
301
          }
302
        }
303
      }
304
      res->argc++;
305
    } else if(!(res->pos_args[res->pos_argc++] = yarg_strdup(argv[i]))) {
306
      yarg_asprintf(&res->error, yarg_oom);
307
      return 0;
308
    }
309
  }
310
311
  return 1;
312
}
313
314
void yarg_destroy(yarg_result * r) {
315
  if(r) {
316
    if(r->args) {
317
      for (int i = 0; i < r->argc; i++) {
318
        free(r->args[i].arg);
319
      }
320
    }
321
    free(r->args);
322
    if(r->pos_args) {
323
      for (int i = 0; i < r->pos_argc; i++) {
324
        free(r->pos_args[i]);
325
      }
326
    }
327
    free(r->pos_args);
328
    if (r->error != yarg_oom)
329
      free(r->error);
330
  }
331
  free(r);
332
}
333
334
yarg_result * yarg_parse(int argc, char * argv[], yarg_options opt[], yarg_settings settings) {
335
  yarg_result * res = (yarg_result *) calloc(sizeof(yarg_result), 1);
336
  if (!res) return NULL;
337
  switch (settings.style) {
338
    case YARG_STYLE_WINDOWS:
339
      yarg_parse_unix_short(argc, argv, opt, res, false, '/');
340
      break;
341
    case YARG_STYLE_UNIX:
342
      yarg_parse_unix(argc, argv, opt, res, settings.dash_dash);
343
      break;
344
    case YARG_STYLE_UNIX_SHORT:
345
      yarg_parse_unix_short(argc, argv, opt, res, settings.dash_dash, '-');
346
      break;
347
  }
348
  return res;
349
}
350
351
#endif
tab: 248 wrap: offon