getopt: fix null pointer arithmetic ub
[musl] / src / misc / getopt.c
1 #define _BSD_SOURCE
2 #include <unistd.h>
3 #include <wchar.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stdlib.h>
7 #include "locale_impl.h"
8 #include "stdio_impl.h"
9
10 char *optarg;
11 int optind=1, opterr=1, optopt, __optpos, __optreset=0;
12
13 #define optpos __optpos
14 weak_alias(__optreset, optreset);
15
16 void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
17 {
18         FILE *f = stderr;
19         b = __lctrans_cur(b);
20         FLOCK(f);
21         fputs(a, f)>=0
22         && fwrite(b, strlen(b), 1, f)
23         && fwrite(c, 1, l, f)==l
24         && putc('\n', f);
25         FUNLOCK(f);
26 }
27
28 int getopt(int argc, char * const argv[], const char *optstring)
29 {
30         int i;
31         wchar_t c, d;
32         int k, l;
33         char *optchar;
34
35         if (!optind || __optreset) {
36                 __optreset = 0;
37                 __optpos = 0;
38                 optind = 1;
39         }
40
41         if (optind >= argc || !argv[optind])
42                 return -1;
43
44         if (argv[optind][0] != '-') {
45                 if (optstring[0] == '-') {
46                         optarg = argv[optind++];
47                         return 1;
48                 }
49                 return -1;
50         }
51
52         if (!argv[optind][1])
53                 return -1;
54
55         if (argv[optind][1] == '-' && !argv[optind][2])
56                 return optind++, -1;
57
58         if (!optpos) optpos++;
59         if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) {
60                 k = 1;
61                 c = 0xfffd; /* replacement char */
62         }
63         optchar = argv[optind]+optpos;
64         optpos += k;
65
66         if (!argv[optind][optpos]) {
67                 optind++;
68                 optpos = 0;
69         }
70
71         if (optstring[0] == '-' || optstring[0] == '+')
72                 optstring++;
73
74         i = 0;
75         d = 0;
76         do {
77                 l = mbtowc(&d, optstring+i, MB_LEN_MAX);
78                 if (l>0) i+=l; else i++;
79         } while (l && d != c);
80
81         if (d != c || c == ':') {
82                 optopt = c;
83                 if (optstring[0] != ':' && opterr)
84                         __getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
85                 return '?';
86         }
87         if (optstring[i] == ':') {
88                 optarg = 0;
89                 if (optstring[i+1] != ':' || optpos) {
90                         optarg = argv[optind++];
91                         if (optpos) optarg += optpos;
92                         optpos = 0;
93                 }
94                 if (optind > argc) {
95                         optopt = c;
96                         if (optstring[0] == ':') return ':';
97                         if (opterr) __getopt_msg(argv[0],
98                                 ": option requires an argument: ",
99                                 optchar, k);
100                         return '?';
101                 }
102         }
103         return c;
104 }
105
106 weak_alias(getopt, __posix_getopt);