7 typedef struct { char *data
; size_t len
, cap
; } String
;
9 void resize(String
*s
, size_t len
) {
11 if (s
->cap
< s
->len
) {
13 s
->data
= (char *)realloc(s
->data
, s
->cap
);
18 void append(String
*s
, const char *data
, size_t len
) {
19 resize(s
, s
->len
+ len
);
20 memcpy(s
->data
+ s
->len
- len
, data
, len
);
23 typedef enum { space
= 0, other
= 1, backslash
= 2, apostrophe
= 3, quotation_mark
= 4 } CharClass
;
24 typedef enum { outside
, unq
, unq_esc
, sq
, sq_esc
, dq
, dq_esc
} State
;
26 // current State -> CharClass -> next State
27 const State transitions
[][5] = {
28 [outside
] = {outside
, unq
, unq_esc
, sq
, dq
},
29 [unq
] = {outside
, unq
, unq_esc
, sq
, dq
},
30 [unq_esc
] = {unq
, unq
, unq
, unq
, unq
},
31 [sq
] = {sq
, sq
, sq_esc
, unq
, sq
},
32 [sq_esc
] = {sq
, sq
, sq
, sq
, sq
},
33 [dq
] = {dq
, dq
, dq_esc
, dq
, unq
},
34 [dq_esc
] = {dq
, dq
, dq
, dq
, dq
},
37 CharClass
charClass(int c
) {
38 return c
== '\\' ? backslash
: c
== '\'' ? apostrophe
: c
== '"' ? quotation_mark
:
39 isspace(c
) ? space
: other
;
42 // expandArg writes NULL-terminated expansions of `arg', a NULL-terminated
43 // string, to stdout. If arg does not begin with `@' or does not refer to a
44 // file, it is written as is. Otherwise the contents of the file are
45 // recursively expanded. On unexpected EOF in malformed response files an
46 // incomplete final argument is written, even if it is empty, to parse like GCC.
47 void expandArg(String
*arg
) {
49 if (arg
->data
[0] != '@' || !(f
= fopen(&arg
->data
[1], "r"))) {
50 fwrite(arg
->data
, 1, arg
->len
, stdout
);
59 State next
= transitions
[cur
][charClass(c
)];
60 if ((cur
== unq
&& next
== outside
) || (cur
!= outside
&& c
== EOF
)) {
64 } else if (cur
== unq_esc
|| cur
== sq_esc
|| cur
== dq_esc
||
65 (cur
== outside
? next
== unq
: cur
== next
)) {
75 int main(int argc
, char **argv
) {
79 append(&arg
, *argv
, strlen(*argv
) + 1);