1#include "../../cmd/dev/config.h"
2#include "table.h"
3#include "tcutil.h"
4#include "version.h"
5
6#include <assert.h>
7#include <ctype.h>
8#include <limits.h> /* CHAR_BIT */
9#include <stdarg.h>
10#include <stdlib.h> /* malloc */
11#include <string.h> /* strcmp */
12#include <sys/stat.h>
13
14/* riscv toolchain lacks getline, provides __getline instead */
15#if defined(__riscv) && !defined(__TC)
16static inline ssize_t
17getline(char **lineptr, size_t *n, FILE *stream)
18{
19 return __getline(lineptr, n, stream);
20}
21#endif
22
23/* character classification */
24
25int
26isalnum_(int c)
27{
28 return isalnum(c) || c == '_';
29}
30
31int
32isutf8first(int c_)
33{
34 unsigned int c = c_;
35 if (c < 0xc0)
36 return 0;
37 if (c >= 0xfc)
38 return 6;
39 if (c >= 0xf8)
40 return 5;
41 if (c >= 0xf0)
42 return 4;
43 if (c >= 0xe0)
44 return 3;
45 return 2;
46}
47
48int
49isutf8follow(int c)
50{
51 return c >= 0x80 && c < 0xc0;
52}
53
54int
55isoctal(int c)
56{
57 return '0' <= c && c <= '7';
58}
59
60int
61xvalue(char c)
62{
63 return ('0' <= c && c <= '9') ? c - '0'
64 : ('A' <= c && c <= 'F') ? c - ('A' - 10)
65 : ('a' <= c && c <= 'f') ? c - ('a' - 10)
66 : -1;
67}
68
69int
70starts_with(const char *str, const char *prefix)
71{
72 size_t len = strlen(prefix);
73 return strncmp(str, prefix, len) == 0;
74}
75
76int
77most_significant_bit(size_t x)
78{
79 int bit;
80
81 assert(x > 0);
82#if defined(__GNUC__) || defined(__TC)
83 if (sizeof(size_t) == sizeof(unsigned int)) {
84 return (int)(sizeof(x) * CHAR_BIT - 1) - __builtin_clz(x);
85 } else if (sizeof(size_t) == sizeof(unsigned long)) {
86 return (int)(sizeof(x) * CHAR_BIT - 1) - __builtin_clzl(x);
87 }
88#endif
89 for (bit = 0;; ++bit) {
90 x >>= 1;
91 if (x <= 0)
92 return bit;
93 }
94}
95
96/* string and path helpers */
97
98void *
99read_or_die(FILE *fp, void *buf, long offset, size_t size, const char *msg)
100{
101 if (buf == NULL)
102 buf = malloc_or_die(size);
103 if ((offset >= 0 && fseek(fp, offset, SEEK_SET) != 0) || fread(buf, 1, size, fp) != size)
104 error(msg);
105 return buf;
106}
107
108void *
109malloc_or_die(size_t size)
110{
111 void *p = malloc(size);
112 if (p == NULL) {
113 fprintf(stderr, "memory overflow\n");
114 exit(1);
115 }
116 return p;
117}
118
119void *
120calloc_or_die(size_t size)
121{
122 void *p = calloc(1, size);
123 if (p == NULL) {
124 fprintf(stderr, "memory overflow\n");
125 exit(1);
126 }
127 return p;
128}
129
130void *
131realloc_or_die(void *ptr, size_t size)
132{
133 void *p = realloc(ptr, size);
134 if (p == NULL) {
135 fprintf(stderr, "memory overflow\n");
136 exit(1);
137 }
138 return p;
139}
140
141const struct Name *
142alloc_label(void)
143{
144 static int label_no;
145 char buf[2 + sizeof(int) * 3 + 1];
146
147 ++label_no;
148 snprintf(buf, sizeof(buf), "l.%04d", label_no);
149 return alloc_name(buf, NULL, 1);
150}
151
152ssize_t
153getline_chomp(char **lineptr, size_t *n, FILE *stream)
154{
155 ssize_t len;
156 char *line;
157
158 len = getline(lineptr, n, stream);
159 if (len > 0) {
160 line = *lineptr;
161 /* chomp CR, LF, CR+LF */
162 if (line[len - 1] == '\n')
163 line[--len] = '\0';
164 if (len > 0 && line[len - 1] == '\r')
165 line[--len] = '\0';
166 }
167 return len;
168}
169
170static ssize_t
171getline_cat(char **lineptr, size_t *n, FILE *stream, size_t curlen)
172{
173 char *nextline = NULL;
174 size_t capa = 0;
175 ssize_t len;
176 char *oldline;
177 char *reallocated;
178
179 len = getline_chomp(&nextline, &capa, stream);
180 if (len == -1)
181 return -1;
182 if (len > 0) {
183 oldline = *lineptr;
184 reallocated = realloc(oldline, curlen + len + 1);
185 if (reallocated == NULL)
186 return -1;
187
188 memcpy(reallocated + curlen, nextline, len + 1);
189 *lineptr = reallocated;
190 *n = curlen + len; /* nul not included */
191 free(nextline);
192 }
193 return curlen + len;
194}
195
196ssize_t
197getline_cont(char **lineptr, size_t *capa, FILE *stream, int *plineno)
198{
199 int lineno = *plineno;
200 ssize_t len;
201 ssize_t nextlen;
202
203 len = getline_chomp(lineptr, capa, stream);
204 if (len != -1) {
205 /* handle backslash continuation */
206 while (++lineno, len > 0 && (*lineptr)[len - 1] == '\\') {
207 (*lineptr)[--len] = '\0';
208 nextlen = getline_cat(lineptr, capa, stream, len);
209 if (nextlen == -1)
210 break;
211 len = nextlen;
212 }
213 }
214 *plineno = lineno;
215 return len;
216}
217
218int
219is_fullpath(const char *filename)
220{
221 const char *p;
222
223 if (*filename != '/')
224 return 0;
225 for (p = filename;;) {
226 p = strstr(p, "/..");
227 if (p == NULL)
228 return 1;
229 if (p[3] == '/' || p[3] == '\0')
230 return 0;
231 p += 3;
232 }
233}
234
235char *
236join_paths(const char *paths[])
237{
238 struct StringBuffer sb;
239 const char **pp;
240 const char *p;
241 const char *last_path;
242 int parent_count;
243 enum Top {
244 OTHER,
245 ROOTDIR, /* / */
246 CURDIR, /* */
247 };
248 enum Top top;
249
250 sb_init(&sb);
251 parent_count = 0;
252 top = OTHER;
253 last_path = NULL;
254
255 for (pp = paths; (p = *pp++) != NULL;) {
256 const char *q;
257 int end;
258 ptrdiff_t len;
259
260 last_path = p;
261 if (*p == '/') { /* absolute, reset */
262 sb_init(&sb);
263 parent_count = 0;
264 top = ROOTDIR;
265 }
266
267 for (end = 0; !end;) {
268 while (*p == '/')
269 ++p;
270 if (*p == '\0')
271 break;
272
273 q = strchr(p, '/');
274 if (q == NULL) { /* last component */
275 q = p + strlen(p);
276 end = 1;
277 }
278 len = q - p;
279 if (len == 1 && *p == '.') {
280 if (sb.elems->len == 0 && top == OTHER)
281 top = CURDIR;
282 } else if (len == 2 && strncmp(p, "..", 2) == 0) {
283 if (sb.elems->len > 0) {
284 void *elem = vec_pop(sb.elems);
285 free(elem);
286 } else {
287 if (top == ROOTDIR)
288 return NULL; /* illegal */
289 ++parent_count;
290 top = OTHER;
291 }
292 } else {
293 sb_append(&sb, p, q);
294 }
295 p = q;
296 }
297 }
298
299 for (; parent_count > 0; --parent_count)
300 sb_prepend(&sb, "..", NULL);
301 switch (top) {
302 case OTHER:
303 case CURDIR:
304 if (sb.elems->len == 0)
305 sb_prepend(&sb, ".", NULL);
306 break;
307 case ROOTDIR:
308 sb_prepend(&sb, sb.elems->len > 0 ? "" : "/", NULL);
309 break;
310 }
311 if (last_path != NULL) {
312 size_t len = strlen(last_path);
313 if (len > 0 && last_path[len - 1] == '/' && !(top == ROOTDIR && sb.elems->len == 1))
314 sb_append(&sb, "", NULL);
315 }
316 return sb_join(&sb, "/");
317}
318
319char *
320get_ext(const char *filename)
321{
322 const char *last_slash;
323 char *dot;
324
325 last_slash = strrchr(filename, '/');
326 if (last_slash == NULL)
327 last_slash = filename;
328 dot = strrchr(last_slash, '.');
329 return dot != NULL ? (char *)&dot[1] : (char *)&last_slash[strlen(last_slash)];
330}
331
332char *
333change_ext(const char *path, const char *ext)
334{
335 const char *p;
336 const char *q;
337 size_t len;
338 size_t ext_len;
339 char *s;
340
341 p = strrchr(path, '/');
342 if (p == NULL)
343 p = path;
344
345 q = strrchr(p, '.');
346 len = q != NULL ? (size_t)(q - path) : strlen(path);
347 ext_len = strlen(ext);
348 s = malloc(len + 1 + ext_len + 1);
349 if (s != NULL) {
350 memcpy(s, path, len);
351 s[len] = '.';
352 strcpy(s + (len + 1), ext);
353 }
354 return s;
355}
356
357void
358put_padding(FILE *fp, long start)
359{
360 long cur;
361 long size;
362 long i;
363
364 cur = ftell(fp);
365 if (cur < 0)
366 return; /* todo: error */
367 if (start > cur) {
368 size = start - cur;
369 for (i = 0; i < size; ++i)
370 fputc(0x00, fp);
371 }
372}
373
374int
375is_file(const char *path)
376{
377 struct stat st;
378 return stat(path, &st) == 0 && S_ISREG(st.st_mode); /* follows symlinks */
379}
380
381void
382show_version(const char *exe, int arch)
383{
384 /* order must match TC_ARCH_* constants in config.h */
385 static const char *const archs[] = {
386 NULL,
387 "x86_64",
388 "arm64",
389 "riscv64",
390 "wasm",
391 };
392
393 if (exe == NULL) {
394 printf("%s\n", VERSION);
395 } else if (arch <= 0) {
396 printf("%s version %s\n", exe, VERSION);
397 } else {
398 printf(
399 "%s version %s\n"
400 "Target: %s\n",
401 exe,
402 VERSION,
403 archs[arch]
404 );
405 }
406}
407
408_Noreturn void
409error(const char *fmt, ...)
410{
411 va_list ap;
412 va_start(ap, fmt);
413 vfprintf(stderr, fmt, ap);
414 va_end(ap);
415 fprintf(stderr, "\n");
416 exit(1);
417}
418
419void
420show_error_line(const char *line, const char *p, int len)
421{
422 size_t pos;
423 size_t i;
424
425 fprintf(stderr, "%s\n", line);
426 pos = p - line;
427 if (pos <= strlen(line)) {
428 for (i = 0; i < pos; ++i)
429 fputc(line[i] == '\t' ? '\t' : ' ', stderr);
430 fprintf(stderr, "^");
431 for (i = 1; i < (size_t)len; ++i)
432 fprintf(stderr, "~");
433 fprintf(stderr, "\n");
434 }
435}
436
437/* value range checks for immediate operands */
438
439int
440is_im8(int64_t x)
441{
442 return x <= (((int64_t)1 << 7) - 1) && x >= -((int64_t)1 << 7);
443}
444
445int
446is_im16(int64_t x)
447{
448 return x <= (((int64_t)1 << 15) - 1) && x >= -((int64_t)1 << 15);
449}
450
451int
452is_im32(int64_t x)
453{
454 return x <= (((int64_t)1 << 31) - 1) && x >= -((int64_t)1 << 31);
455}
456
457const char *
458skip_whitespaces(const char *s)
459{
460 for (;;) {
461 while (isspace(*s))
462 ++s;
463 /* gas allows block comments and line comments wherever whitespace is valid */
464 if (s[0] == '/' && s[1] == '*') {
465 s += 2;
466 while (*s != '\0' && !(s[0] == '*' && s[1] == '/'))
467 ++s;
468 if (*s != '\0')
469 s += 2;
470 continue;
471 }
472 if (s[0] == '/' && s[1] == '/') {
473 while (*s != '\0')
474 ++s;
475 continue;
476 }
477 break;
478 }
479 return s;
480}
481
482/* detect block comment start without consuming it via skip_whitespaces */
483const char *
484block_comment_start(const char *p)
485{
486 while (isspace(*p))
487 ++p;
488 return (*p == '/' && p[1] == '*') ? p : NULL;
489}
490
491const char *
492block_comment_end(const char *p)
493{
494 for (;;) {
495 p = strchr(p, '*');
496 if (p == NULL)
497 return NULL;
498 if (*(++p) == '/')
499 return p + 1;
500 }
501}
502
503int64_t
504wrap_value(int64_t value, int size, int is_unsigned)
505{
506 if (is_unsigned) {
507 switch (size) {
508 case 1:
509 value = (uint8_t)value;
510 break;
511 case 2:
512 value = (uint16_t)value;
513 break;
514 case 4:
515 value = (uint32_t)value;
516 break;
517 default:
518 break;
519 }
520 } else {
521 switch (size) {
522 case 1:
523 value = (int8_t)value;
524 break;
525 case 2:
526 value = (int16_t)value;
527 break;
528 case 4:
529 value = (int32_t)value;
530 break;
531 default:
532 break;
533 }
534 }
535 return value;
536}
537
538/* container: growable pointer vector */
539
540struct Vector *
541new_vector(void)
542{
543 struct Vector *vec = malloc_or_die(sizeof(struct Vector));
544 vec_init(vec);
545 return vec;
546}
547
548void
549free_vector(struct Vector *vec)
550{
551 free(vec->data);
552 free(vec);
553}
554
555void
556vec_init(struct Vector *vec)
557{
558 vec->data = NULL;
559 vec->capacity = 0;
560 vec->len = 0;
561}
562
563void
564vec_clear(struct Vector *vec)
565{
566 vec->len = 0;
567}
568
569void
570vec_push(struct Vector *vec, const void *elem)
571{
572 if (vec->capacity <= vec->len) {
573 if (vec->capacity <= 0)
574 vec->capacity = 16;
575 else
576 vec->capacity <<= 1;
577 vec->data = realloc_or_die(vec->data, sizeof(*vec->data) * vec->capacity);
578 }
579 vec->data[vec->len++] = (void *)elem;
580}
581
582void *
583vec_pop(struct Vector *vec)
584{
585 return vec->len > 0 ? vec->data[--vec->len] : NULL;
586}
587
588void
589vec_insert(struct Vector *vec, int pos, const void *elem)
590{
591 int len = vec->len;
592
593 if (pos < 0 || pos > len)
594 return;
595
596 if (pos < len) {
597 vec_push(vec, NULL);
598 memmove(&vec->data[pos + 1], &vec->data[pos], sizeof(void *) * (len - pos));
599 vec->data[pos] = (void *)elem;
600 } else {
601 vec_push(vec, elem);
602 }
603}
604
605void
606vec_remove_at(struct Vector *vec, int index)
607{
608 int d;
609
610 if (index < 0 || index >= vec->len)
611 return;
612 d = vec->len - index - 1;
613 if (d > 0)
614 memmove(&vec->data[index], &vec->data[index + 1], d * sizeof(*vec->data));
615 --vec->len;
616}
617
618int
619vec_contains(struct Vector *vec, void *elem)
620{
621 int i, len;
622
623 len = vec->len;
624 for (i = 0; i < len; ++i) {
625 if (vec->data[i] == elem)
626 return 1;
627 }
628 return 0;
629}
630
631void
632vec_concat(struct Vector *dst, const struct Vector *src)
633{
634 int i;
635
636 for (i = 0; i < src->len; ++i)
637 vec_push(dst, src->data[i]);
638}
639
640/* container: growable byte buffer */
641
642void
643data_release(struct DataStorage *data)
644{
645 if (data->chunk_stack != NULL) {
646 free_vector(data->chunk_stack);
647 data->chunk_stack = NULL;
648 }
649 if (data->buf != NULL) {
650 free(data->buf);
651 data_init(data);
652 }
653}
654
655void
656data_init(struct DataStorage *data)
657{
658 data->chunk_stack = NULL;
659 data->buf = NULL;
660 data->capacity = 0;
661 data->len = 0;
662}
663
664void
665data_reserve(struct DataStorage *data, size_t capacity)
666{
667 const size_t min_cap = 16;
668 size_t c;
669
670 if (data->capacity < capacity) {
671 c = data->capacity << 1;
672 if (c > capacity)
673 capacity = c;
674 if (min_cap > capacity)
675 capacity = min_cap;
676 data->buf = realloc_or_die(data->buf, sizeof(*data->buf) * capacity);
677 data->capacity = capacity;
678 }
679}
680
681void
682data_insert(struct DataStorage *data, ssize_t pos_, const void *buf, size_t size)
683{
684 size_t pos;
685 size_t newlen;
686
687 pos = pos_ == -1 ? data->len : (size_t)pos_;
688 assert(pos <= data->len);
689 newlen = data->len + size;
690 data_reserve(data, newlen);
691 if (pos < data->len)
692 memmove(data->buf + pos + size, data->buf + pos, data->len - pos);
693 memcpy(data->buf + pos, buf, size);
694 data->len = newlen;
695}
696
697void
698data_append(struct DataStorage *data, const void *buf, size_t size)
699{
700 data_insert(data, -1, buf, size);
701}
702
703void
704data_push(struct DataStorage *data, unsigned char c)
705{
706 unsigned char buf[1] = {c};
707 data_insert(data, -1, buf, 1);
708}
709
710void
711data_align(struct DataStorage *data, int align)
712{
713 size_t len;
714 size_t aligned_len;
715 size_t add;
716 void *zero;
717
718 len = data->len;
719 aligned_len = ALIGN(len, align);
720 add = aligned_len - len;
721 if (add <= 0)
722 return;
723
724 zero = calloc_or_die(add);
725 data_append(data, zero, add);
726 free(zero);
727
728 assert(data->len == aligned_len);
729}
730
731void
732data_concat(struct DataStorage *dst, struct DataStorage *src)
733{
734 data_insert(dst, -1, src->buf, src->len);
735}
736
737void
738data_leb128(struct DataStorage *data, ssize_t pos, int64_t val)
739{
740 unsigned char buf[12], *p = buf;
741 const int64_t max = (int64_t)1 << 6;
742
743 for (;;) {
744 if (val < max && val >= -max) {
745 *p++ = val & 0x7f;
746 data_insert(data, pos, buf, p - buf);
747 return;
748 }
749 *p++ = (val & 0x7f) | 0x80;
750 val >>= 7;
751 }
752}
753
754void
755data_uleb128(struct DataStorage *data, ssize_t pos, uint64_t val)
756{
757 unsigned char buf[12], *p = buf;
758 const uint64_t max = (uint64_t)1 << 7;
759
760 for (;;) {
761 if (val < max) {
762 *p++ = val & 0x7f;
763 data_insert(data, pos, buf, p - buf);
764 return;
765 }
766 *p++ = (val & 0x7f) | 0x80;
767 val >>= 7;
768 }
769}
770
771void
772data_string(struct DataStorage *data, const void *str, size_t len)
773{
774 data_uleb128(data, -1, len);
775 data_append(data, (const unsigned char *)str, len);
776}
777
778void
779data_varint32(struct DataStorage *data, ssize_t pos, int64_t val)
780{
781 unsigned char buf[5], *p = buf;
782 int i;
783
784 for (i = 0; i < 4; ++i) {
785 *p++ = (val & 0x7f) | 0x80;
786 val >>= 7;
787 }
788 *p++ = val & 0x7f;
789 data_insert(data, pos, buf, p - buf);
790}
791
792void
793data_varuint32(struct DataStorage *data, ssize_t pos, uint64_t val)
794{
795 unsigned char buf[5], *p = buf;
796 int i;
797
798 for (i = 0; i < 4; ++i) {
799 *p++ = (val & 0x7f) | 0x80;
800 val >>= 7;
801 }
802 *p++ = val & 0x7f;
803 data_insert(data, pos, buf, p - buf);
804}
805
806void
807data_open_chunk(struct DataStorage *data)
808{
809 struct Vector *stack = data->chunk_stack;
810 if (stack == NULL)
811 data->chunk_stack = stack = new_vector();
812 vec_push(stack, INT2VOIDP(data->len));
813}
814
815void
816data_close_chunk(struct DataStorage *data, ssize_t num)
817{
818 struct Vector *stack = data->chunk_stack;
819 size_t pos;
820
821 assert(stack != NULL && stack->len > 0);
822 pos = VOIDP2INT(vec_pop(stack));
823 if (num == (ssize_t)-1)
824 num = data->len - pos;
825 data_uleb128(data, pos, num);
826}
827
828/* container: rope-style string builder */
829
830struct StringElement {
831 const char *start;
832 size_t len;
833};
834
835void
836sb_init(struct StringBuffer *sb)
837{
838 sb->elems = new_vector();
839}
840
841void
842sb_clear(struct StringBuffer *sb)
843{
844 vec_clear(sb->elems);
845}
846
847int
848sb_empty(struct StringBuffer *sb)
849{
850 return sb->elems->len == 0;
851}
852
853void
854sb_insert(struct StringBuffer *sb, int pos, const char *start, const char *end)
855{
856 struct StringElement *elem = malloc(sizeof(*elem));
857
858 if (elem != NULL) {
859 elem->start = start;
860 elem->len = end != NULL ? (size_t)(end - start) : strlen(start);
861 assert(0 <= pos && pos <= sb->elems->len);
862 vec_insert(sb->elems, pos, elem);
863 }
864}
865
866char *
867sb_join(struct StringBuffer *sb, const char *separator)
868{
869 size_t total_len;
870 int count;
871 int i;
872 size_t sepalen;
873 char *str;
874 char *p;
875
876 total_len = 0;
877 count = sb->elems->len;
878 for (i = 0; i < count; ++i) {
879 struct StringElement *elem = sb->elems->data[i];
880 total_len += elem->len;
881 }
882 sepalen = separator != NULL ? strlen(separator) : 0;
883 if (count > 0 && sepalen > 0)
884 total_len += sepalen * (count - 1);
885
886 str = malloc(total_len + 1);
887 if (str != NULL) {
888 p = str;
889 for (i = 0; i < count; ++i) {
890 struct StringElement *elem;
891 if (i > 0 && sepalen > 0) {
892 memcpy(p, separator, sepalen);
893 p += sepalen;
894 }
895 elem = sb->elems->data[i];
896 memcpy(p, elem->start, elem->len);
897 p += elem->len;
898 }
899 *p = '\0';
900 }
901 return str;
902}
903
904/* string escaping for assembler output */
905
906static const char *
907escape_hex(int c)
908{
909 char *s = malloc_or_die(5);
910 /* hex escapes are not length-limited so following hex chars would
911 * be consumed, must use octal which is always 3 digits */
912 snprintf(s, 5, "\\%03o", c & 0xff);
913 return s;
914}
915
916static const char *
917escape(int c)
918{
919 switch (c) {
920 case '\0':
921 return "\\000"; /* octal, at most 3 digits in assembler */
922 case '\n':
923 return "\\n";
924 case '\r':
925 return "\\r";
926 case '\t':
927 return "\\t";
928 case '"':
929 return "\\\"";
930 case '\\':
931 return "\\\\";
932 default:
933 if (c < 0x20 || c >= 0x7f)
934 return escape_hex(c);
935 return NULL;
936 }
937}
938
939void
940escape_string(const char *str, size_t size, struct StringBuffer *sb)
941{
942 const char *s, *p;
943 const char *end;
944 const char *e;
945 int hex;
946
947 end = str + size;
948 hex = 0;
949 for (s = p = str; p < end; ++p) {
950 if (hex && isxdigit(*p)) {
951 e = escape_hex(*p);
952 } else {
953 e = escape(*p);
954 if (e == NULL) {
955 hex = 0;
956 continue;
957 }
958 }
959
960 if (p > s)
961 sb_append(sb, s, p);
962 sb_append(sb, e, NULL);
963 s = p + 1;
964 hex = e[1] == 'x';
965 }
966 if (p > s) {
967 assert(!hex);
968 sb_append(sb, s, p);
969 }
970}
971
972/* option parser: long-option style for toolchain commands */
973
974int optind;
975int optopt;
976int opterr = 1;
977char *optarg;
978
979int
980optparse(int argc, char *const argv[], const struct option *opts)
981{
982#define ERROR(...) \
983 do { \
984 if (opterr) \
985 fprintf(stderr, __VA_ARGS__); \
986 } while (0)
987
988 char *arg;
989 char *p;
990 char *q;
991 char c;
992 int opt;
993 size_t len;
994
995 if (optind == 0)
996 optind = 1;
997
998 if (optind >= argc)
999 return -1;
1000
1001 optarg = NULL;
1002 optopt = 0;
1003
1004 arg = argv[optind];
1005 p = arg;
1006 if (*p != '-' || p[1] == '\0')
1007 return -1;
1008
1009 p += 1;
1010 ++optind;
1011 for (; opts->name != NULL; ++opts) {
1012 len = strlen(opts->name);
1013 if (strncmp(p, opts->name, len) == 0) {
1014 opt = opts->val;
1015 if (opt == 0)
1016 opt = arg[1];
1017 q = p + len;
1018 c = *q;
1019 if (opts->has_arg) {
1020 if (c != '\0') {
1021 optarg = q + (c == '=' ? 1 : 0);
1022 } else if (opts->has_arg == required_argument) {
1023 if (optind < argc) {
1024 optarg = argv[optind++];
1025 } else {
1026 ERROR(
1027 "%s: option '--%s' "
1028 "requires an argument\n",
1029 argv[0],
1030 opts->name
1031 );
1032 break;
1033 }
1034 }
1035 } else {
1036 if (c != '\0') {
1037 if (c != '=')
1038 continue;
1039 ERROR(
1040 "%s: option '--%s' doesn't allow "
1041 "an argument\n",
1042 argv[0],
1043 opts->name
1044 );
1045 break;
1046 }
1047 }
1048 return opt;
1049 }
1050 }
1051
1052 optopt = arg[1];
1053 return '?';
1054#undef ERROR
1055}