Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libast/misc/cmdarg.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1985-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* Phong Vo <[email protected]> *
20
* *
21
***********************************************************************/
22
#pragma prototyped
23
/*
24
* Glenn Fowler
25
* AT&T Research
26
*
27
* xargs/tw command arg list support
28
*/
29
30
#define _AST_API_H 1
31
32
#include <ast.h>
33
#include <cmdlib.h>
34
#include <proc.h>
35
36
static const char lib[] = "libast:cmdarg";
37
38
static int
39
cmdrun(int argc, char** argv, Cmddisc_t* disc)
40
{
41
return procrun(argv[0], argv, PROC_ARGMOD|PROC_IGNOREPATH);
42
}
43
44
Cmdarg_t*
45
cmdopen(char** argv, int argmax, int size, const char* argpat, int flags)
46
{
47
Cmddisc_t disc;
48
49
memset(&disc, 0, sizeof(disc));
50
disc.version = CMD_VERSION;
51
if (!(flags & CMD_SILENT))
52
{
53
flags |= CMD_EXIT;
54
disc.errorf = errorf;
55
}
56
disc.flags = flags;
57
return cmdopen_20120411(argv, argmax, size, argpat, &disc);
58
}
59
60
#undef _AST_API_H
61
62
#include <ast_api.h>
63
64
#include <ctype.h>
65
#include <proc.h>
66
67
#ifndef ARG_MAX
68
#define ARG_MAX (64*1024)
69
#endif
70
#ifndef EXIT_QUIT
71
#define EXIT_QUIT 255
72
#endif
73
74
static const char* echo[] = { "echo", 0 };
75
76
Cmdarg_t*
77
cmdopen_20110505(char** argv, int argmax, int size, const char* argpat, int flags, Error_f errorf)
78
{
79
Cmddisc_t disc;
80
81
memset(&disc, 0, sizeof(disc));
82
disc.version = CMD_VERSION;
83
disc.flags = flags;
84
disc.errorf = errorf;
85
return cmdopen_20120411(argv, argmax, size, argpat, &disc);
86
}
87
88
/*
89
* open a cmdarg stream
90
* initialize the command for execution
91
* argv[-1] is reserved for procrun(PROC_ARGMOD)
92
*/
93
94
Cmdarg_t*
95
cmdopen_20120411(char** argv, int argmax, int size, const char* argpat, Cmddisc_t* disc)
96
{
97
register Cmdarg_t* cmd;
98
register int n;
99
register char** p;
100
register char* s;
101
char* sh;
102
char* exe;
103
int c;
104
int m;
105
int argc;
106
long x;
107
108
char** post = 0;
109
110
n = sizeof(char**);
111
if (*argv)
112
{
113
for (p = argv + 1; *p; p++)
114
{
115
if ((disc->flags & CMD_POST) && argpat && streq(*p, argpat))
116
{
117
*p = 0;
118
post = p + 1;
119
argpat = 0;
120
}
121
else
122
n += strlen(*p) + 1;
123
}
124
argc = p - argv;
125
}
126
else
127
argc = 0;
128
for (p = environ; *p; p++)
129
n += sizeof(char**) + strlen(*p) + 1;
130
if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0)
131
x = ARG_MAX;
132
133
#ifdef __linux__
134
// adjust for linux, perhaps page alignment is going on
135
// causes problems to tw when selecting many files
136
if (x > getpagesize () * 50)
137
x -= getpagesize ();
138
#endif
139
140
if (size <= 0 || size > x)
141
size = x;
142
sh = pathshell();
143
m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
144
m = roundof(m, sizeof(char**));
145
if (size < m)
146
{
147
if (disc->errorf)
148
(*disc->errorf)(NiL, sh, 2, "size must be at least %d", m);
149
return 0;
150
}
151
if ((m = x / 10) > 2048)
152
m = 2048;
153
if (size > (x - m))
154
size = x - m;
155
n = size - n;
156
m = ((disc->flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
157
if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
158
{
159
if (disc->errorf)
160
(*disc->errorf)(NiL, sh, ERROR_SYSTEM|2, "out of space");
161
return 0;
162
}
163
cmd->id = lib;
164
cmd->disc = disc;
165
cmd->errorf = disc->errorf;
166
if (!(cmd->runf = disc->runf))
167
cmd->runf = cmdrun;
168
c = n / sizeof(char**);
169
if (argmax <= 0 || argmax > c)
170
argmax = c;
171
s = cmd->buf;
172
if (!(exe = argv[0]))
173
{
174
exe = *(argv = (char**)echo);
175
cmd->echo = 1;
176
}
177
else if (streq(exe, echo[0]))
178
{
179
cmd->echo = 1;
180
disc->flags &= ~CMD_NEWLINE;
181
}
182
else if (!(disc->flags & CMD_CHECKED))
183
{
184
if (!pathpath(exe, NiL, PATH_REGULAR|PATH_EXECUTE, s, n + m))
185
{
186
n = EXIT_NOTFOUND;
187
if (cmd->errorf)
188
(*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command not found", exe);
189
if (disc->flags & CMD_EXIT)
190
(*error_info.exit)(n);
191
free(cmd);
192
return 0;
193
}
194
exe = s;
195
}
196
s += strlen(s) + 1;
197
if (m)
198
{
199
cmd->insert = strcpy(s, argpat);
200
cmd->insertlen = m - 1;
201
s += m;
202
}
203
s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
204
p = (char**)s;
205
n -= strlen(*p++ = sh) + 1;
206
cmd->argv = p;
207
*p++ = exe;
208
while (*p = *++argv)
209
p++;
210
if (m)
211
{
212
argmax = 1;
213
*p++ = 0;
214
cmd->insertarg = p;
215
argv = cmd->argv;
216
c = *cmd->insert;
217
while (s = *argv)
218
{
219
while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
220
s++;
221
*p++ = s ? *argv : (char*)0;
222
argv++;
223
}
224
*p++ = 0;
225
}
226
cmd->firstarg = cmd->nextarg = p;
227
cmd->laststr = cmd->nextstr = cmd->buf + n;
228
cmd->argmax = argmax;
229
cmd->flags = disc->flags;
230
cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
231
return cmd;
232
}
233
234
/*
235
* flush outstanding command file args
236
*/
237
238
int
239
cmdflush(register Cmdarg_t* cmd)
240
{
241
register char* s;
242
register char** p;
243
register int n;
244
245
if (cmd->flags & CMD_EMPTY)
246
cmd->flags &= ~CMD_EMPTY;
247
else if (cmd->nextarg <= cmd->firstarg)
248
return 0;
249
if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
250
{
251
if (cmd->errorf)
252
(*cmd->errorf)(NiL, cmd, 2, "%d arg command would be too long", cmd->argcount);
253
return -1;
254
}
255
cmd->total.args += cmd->argcount;
256
cmd->total.commands++;
257
cmd->argcount = 0;
258
if (p = cmd->postarg)
259
while (*cmd->nextarg++ = *p++);
260
else
261
*cmd->nextarg = 0;
262
if (s = cmd->insert)
263
{
264
char* a;
265
char* b;
266
char* e;
267
char* t;
268
char* u;
269
int c;
270
int m;
271
272
a = cmd->firstarg[0];
273
b = (char*)&cmd->nextarg[1];
274
e = cmd->nextstr;
275
c = *s;
276
m = cmd->insertlen;
277
for (n = 1; cmd->argv[n]; n++)
278
if (t = cmd->insertarg[n])
279
{
280
cmd->argv[n] = b;
281
for (;;)
282
{
283
if (!(u = strchr(t, c)))
284
{
285
b += sfsprintf(b, e - b, "%s", t);
286
break;
287
}
288
if (!strncmp(s, u, m))
289
{
290
b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
291
t = u + m;
292
}
293
else if (b >= e)
294
break;
295
else
296
{
297
*b++ = *u++;
298
t = u;
299
}
300
}
301
if (b < e)
302
*b++ = 0;
303
}
304
if (b >= e)
305
{
306
if (cmd->errorf)
307
(*cmd->errorf)(NiL, cmd, 2, "%s: command too large after insert", a);
308
return -1;
309
}
310
}
311
n = (int)(cmd->nextarg - cmd->argv);
312
cmd->nextarg = cmd->firstarg;
313
cmd->nextstr = cmd->laststr;
314
if (cmd->flags & (CMD_QUERY|CMD_TRACE))
315
{
316
p = cmd->argv;
317
sfprintf(sfstderr, "+ %s", *p);
318
while (s = *++p)
319
sfprintf(sfstderr, " %s", s);
320
if (!(cmd->flags & CMD_QUERY))
321
sfprintf(sfstderr, "\n");
322
else if (astquery(1, "? "))
323
{
324
return 0;
325
}
326
}
327
if (cmd->echo)
328
{
329
n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
330
for (p = cmd->argv + 1; s = *p++;)
331
sfputr(sfstdout, s, *p ? n : '\n');
332
n = 0;
333
}
334
else if ((n = (*cmd->runf)(n, cmd->argv, cmd->disc)) == -1)
335
{
336
n = EXIT_NOTFOUND - 1;
337
if (cmd->errorf)
338
(*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
339
if (cmd->flags & CMD_EXIT)
340
(*error_info.exit)(n);
341
}
342
else if (n >= EXIT_NOTFOUND - 1)
343
{
344
if (cmd->flags & CMD_EXIT)
345
(*error_info.exit)(n);
346
}
347
else if (!(cmd->flags & CMD_IGNORE))
348
{
349
if (n == EXIT_QUIT && (cmd->flags & CMD_EXIT))
350
(*error_info.exit)(2);
351
if (n)
352
error_info.errors++;
353
}
354
return n;
355
}
356
357
/*
358
* add file to the command arg list
359
*/
360
361
int
362
cmdarg(register Cmdarg_t* cmd, const char* file, register int len)
363
{
364
int i;
365
int r;
366
367
r = 0;
368
if (len > 0)
369
{
370
while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
371
{
372
if (cmd->nextarg == cmd->firstarg)
373
{
374
if (cmd->errorf)
375
(*cmd->errorf)(NiL, cmd, 2, "%s: path too long for exec args", file);
376
return -1;
377
}
378
if (i = cmdflush(cmd))
379
{
380
if (r < i)
381
r = i;
382
if (!(cmd->flags & CMD_IGNORE))
383
return r;
384
}
385
}
386
*cmd->nextarg++ = cmd->nextstr;
387
memcpy(cmd->nextstr, file, len);
388
cmd->nextstr[len] = 0;
389
cmd->argcount++;
390
if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
391
r = i;
392
}
393
else
394
cmd->argcount += len;
395
return r;
396
}
397
398
/*
399
* close a cmdarg stream
400
*/
401
402
int
403
cmdclose(Cmdarg_t* cmd)
404
{
405
int n;
406
407
if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
408
{
409
if (cmd->errorf)
410
(*cmd->errorf)(NiL, cmd, 2, "only %d arguments for last command", cmd->argcount);
411
n = -1;
412
}
413
else
414
{
415
cmd->flags &= ~CMD_MINIMUM;
416
n = cmdflush(cmd);
417
}
418
free(cmd);
419
return n;
420
}
421
422