-
Notifications
You must be signed in to change notification settings - Fork 0
/
str2argv.c
235 lines (210 loc) · 6.24 KB
/
str2argv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
* from utils 2.0
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <stdlib.h>
#include "shebang.h"
#define MAXSTRSIZE 5120
int str2argv(const char* str, int* argc, char* argv[], char** cmd, int* gnabehs)
{
/*
* str2argv:
* converts the string str in a comand name cmd, an argument count
* argc and the array argv containing the tokens (words) found in str
* delimiteds by ' ' (space), '\t' (tab) or '\n' (new line).
* str2argv expects at input the value in argc being the maximum space allocated by
* the array pointed to by argv.
* str2argv also looks for GNABEHS string and if found, stores its position in
* gnabehs.
* If some argument in argv contains and environment variable it will be replaced by
* its value.
*
*
* WARNING: argv is an in/out parameter and should be allocated
* before the call to str2argv.
*
* params:
* str the command string
* argc input/output -> the size of input argv
* argv output -> the splitted array
* cmd output -> the command name
* gnabehs input -> can be NULL and no value will be stored
* output -> the position of GNABEHS in argv
*/
const char* delim = " \t\n";
static char _str[MAXSTRSIZE];
int i = 1;
char* p;
// default to 0 (false), GNABEHS not found
if (gnabehs != NULL) {
*gnabehs = 0;
}
#ifndef NDEBUG
fprintf(stderr, "str2argv: str='%s' *argc=%d argv[]=%p cmd=%p\n", str, *argc, argv, cmd);
#endif
if ( strlen(str) > MAXSTRSIZE )
return -2;
if ( argc == NULL || argv == NULL || cmd == NULL ) {
return -2;
}
/* find the quotes and preserve whitespace inside them */
char* s = (char*)str;
while ( (p = strpbrk(s, "'\"")) ) {
char d = *p;
char* q;
char* r;
#ifndef NDEBUG
fprintf(stderr, "while: p='%s'\n", p);
#endif
if ( (q = strchr(p+1, d)) == NULL ) {
/* unbalanced quotes */
return -3;
}
for ( r = p+1; r < q; r++ ) {
switch ( *r ) {
case ' ':
*r = '\x1d';
break;
case '\t':
*r = '\x1e';
break;
case '\n':
*r = '\x1f';
break;
}
}
s = q + 1;
}
if ( strpbrk(str, delim) ) {
#ifndef NDEBUG
fprintf(stderr, "str2argv: str='%s' contains delimiters in '%s'\n", str, delim);
#endif
strcpy(_str, str);
*cmd = strtok(_str, delim);
#ifndef NDEBUG
fprintf(stderr, "str2argv: *cmd='%s'\n", *cmd);
#endif
argv[0] = basename(*cmd);
#ifndef NDEBUG
fprintf(stderr, "str2argv: argv[0]='%s'\n", argv[0]);
#endif
i = 1;
while (i < *argc) {
const char* _tok = strtok(NULL, delim);
if ( _tok == NULL) {
break;
}
if (strncmp(GNABEHS, _tok, strlen(GNABEHS)) == 0) {
if (gnabehs != NULL) {
*gnabehs = i;
}
break;
}
char* var = NULL;
char buf[MAXBUFSIZE] = { '\0' };
/* the '$' can be in any position inside the string */
/* we should look for repetitions of '$', we are now only looking for first one */
if ((var = strchr(_tok, '$')) != NULL) {
#ifndef NDEBUG
fprintf(stderr, "str2argv: _tok='%s' contains '$' at var='%s'\n", _tok, var);
#endif
char* duptok = strdup(_tok);
if (var != _tok) {
int offset = (var-_tok);
#ifndef NDEBUG
fprintf(stderr, "str2argv: duptok='%s' var-_tok-1=%ld\n", duptok, offset);
#endif
duptok[offset] = '\0';
#ifndef NDEBUG
fprintf(stderr, "str2argv: duptok='%s'\n", duptok);
#endif
strcat(buf, duptok);
}
char varname[MAXBUFSIZE];
var++;
char* varend;
int v;
for (varend=var, v=0; *varend; varend++, v++) {
char c = *varend;
/* we are supporting letters (uppercase and lowercase), numbers and '_' as variable names */
if (c != '_' && (c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
break;
}
varname[v] = *varend;
}
varname[v] = 0;
#ifndef NDEBUG
fprintf(stderr, "str2argv: variable='%s'\n", varname);
fprintf(stderr, "str2argv: varend='%s'\n", varend);
#endif
char* val = getenv(varname);
#ifndef NDEBUG
fprintf(stderr, "str2argv: getenv val='%s'\n", val);
#endif
if (val != NULL) {
strcat(buf, val);
}
else {
fprintf(stderr, "%s: WARNING: '%s' not found in env\n", SHEBANGNAME, varname);
}
strcat(buf, varend);
argv[i] = strdup(buf);
}
else {
argv[i] = (char*)_tok;
}
#ifndef NDEBUG
fprintf(stderr, "str2argv: %d: strtok = '%s'\n", i, argv[i]);
#endif
i++;
}
char* _tok = strtok(NULL, delim);
if ( i == *argc && _tok ) {
#ifndef NDEBUG
fprintf(stderr, "str2argv: returning -1: i=%d _tok='%s'\n", i, _tok);
#endif
return -1;
}
argv[i] = NULL;
*argc = i;
}
else {
*cmd = (char*) str;
argv[0] = basename((char*)str);
argv[1] = NULL;
*argc = 1;
#ifndef NDEBUG
fprintf(stderr, "str2argv: cmd = %s\n", *cmd);
#endif
}
#ifndef NDEBUG
fprintf(stderr, "str2argv: setting *argc = %d\n", *argc);
#endif
/* revert the whitespaces preserved inside quotes */
for ( i=0; i < *argc; i++ ) {
char* r;
#ifndef NDEBUG
fprintf(stderr, "str2argv: reverting whitespace in argv[%d]\n", i);
#endif
for ( r=argv[i]; *r != '\0'; r++ ) {
switch ( *r ) {
case '\x1d':
*r = ' ';
break;
case '\x1e':
*r = '\t';
break;
case '\x1f':
*r = '\n';
break;
}
}
}
#ifndef NDEBUG
fprintf(stderr, "str2argv: returning argc=%d\n", *argc);
#endif
return *argc;
}