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