source: trunk/essentials/sys-apps/findutils/lib/buildcmd.c

Last change on this file was 3182, checked in by bird, 18 years ago

Don't use _SC_ARG_MAX, it's too low.

File size: 12.9 KB
Line 
1/* buildcmd.c -- build command lines from a list of arguments.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2003, 2005 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17 USA.
18*/
19
20#include <config.h>
21
22# ifndef PARAMS
23# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
24# define PARAMS(Args) Args
25# else
26# define PARAMS(Args) ()
27# endif
28# endif
29
30#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
31#include <string.h>
32#endif
33
34
35#if DO_MULTIBYTE
36# if HAVE_MBRLEN
37# include <wchar.h>
38# else
39 /* Simulate mbrlen with mblen as best we can. */
40# define mbstate_t int
41# define mbrlen(s, n, ps) mblen (s, n)
42# endif
43#endif
44
45#ifdef HAVE_LOCALE_H
46#include <locale.h>
47#endif
48#if ENABLE_NLS
49# include <libintl.h>
50# define _(Text) gettext (Text)
51#else
52# define _(Text) Text
53#define textdomain(Domain)
54#define bindtextdomain(Package, Directory)
55#endif
56#ifdef gettext_noop
57# define N_(String) gettext_noop (String)
58#else
59/* See locate.c for explanation as to why not use (String) */
60# define N_(String) String
61#endif
62
63#ifndef _POSIX_SOURCE
64#include <sys/param.h>
65#endif
66
67#ifdef HAVE_LIMITS_H
68#include <limits.h>
69#endif
70
71/* The presence of unistd.h is assumed by gnulib these days, so we
72 * might as well assume it too.
73 */
74/* for sysconf() */
75#include <unistd.h>
76
77#include <assert.h>
78
79/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
80 We try to make it as large as possible. */
81#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
82#define ARG_MAX sysconf (_SC_ARG_MAX)
83#endif
84#ifndef ARG_MAX
85#define ARG_MAX NCARGS
86#endif
87
88
89
90#include <xalloc.h>
91#include <error.h>
92
93#include "buildcmd.h"
94
95
96extern char **environ;
97
98
99static char *mbstrstr PARAMS ((const char *haystack, const char *needle));
100
101/* Replace all instances of `replace_pat' in ARG with `linebuf',
102 and add the resulting string to the list of arguments for the command
103 to execute.
104 ARGLEN is the length of ARG, not including the null.
105 LBLEN is the length of LINEBUF, not including the null.
106 PFXLEN is the length of PREFIX. Substitution is not performed on
107 the prefix. The prefix is used if the argument contains replace_pat.
108
109 COMPAT: insertions on the SYSV version are limited to 255 chars per line,
110 and a max of 5 occurrences of replace_pat in the initial-arguments.
111 Those restrictions do not exist here. */
112
113void
114bc_do_insert (const struct buildcmd_control *ctl,
115 struct buildcmd_state *state,
116 char *arg, size_t arglen,
117 const char *prefix, size_t pfxlen,
118 const char *linebuf, size_t lblen,
119 int initial_args)
120{
121 /* Temporary copy of each arg with the replace pattern replaced by the
122 real arg. */
123 static char *insertbuf;
124 char *p;
125 size_t bytes_left = ctl->arg_max - 1; /* Bytes left on the command line. */
126 int need_prefix;
127
128 /* XXX: on systems lacking an upper limit for exec args, ctl->arg_max
129 * may have been set to LONG_MAX (see bc_get_arg_max()). Hence
130 * this xmalloc call may be a bad idea, especially since we are
131 * adding 1 to it...
132 */
133 if (!insertbuf)
134 insertbuf = (char *) xmalloc (ctl->arg_max + 1);
135 p = insertbuf;
136
137 do
138 {
139 size_t len; /* Length in ARG before `replace_pat'. */
140 char *s = mbstrstr (arg, ctl->replace_pat);
141 if (s)
142 {
143 len = s - arg;
144 }
145 else
146 {
147 len = arglen;
148 }
149
150 if (bytes_left <= len)
151 break;
152 else
153 bytes_left -= len;
154
155 strncpy (p, arg, len);
156 p += len;
157 arg += len;
158 arglen -= len;
159
160 if (s)
161 {
162 if (bytes_left <= (lblen + pfxlen))
163 break;
164 else
165 bytes_left -= (lblen + pfxlen);
166
167 if (prefix)
168 {
169 strcpy (p, prefix);
170 p += pfxlen;
171 }
172 strcpy (p, linebuf);
173 p += lblen;
174
175 arg += ctl->rplen;
176 arglen -= ctl->rplen;
177 }
178 }
179 while (*arg);
180 if (*arg)
181 error (1, 0, _("command too long"));
182 *p++ = '\0';
183
184 bc_push_arg (ctl, state,
185 insertbuf, p - insertbuf,
186 NULL, 0,
187 initial_args);
188}
189
190static
191void do_exec(const struct buildcmd_control *ctl,
192 struct buildcmd_state *state)
193{
194 (ctl->exec_callback)(ctl, state);
195}
196
197
198/* Return nonzero if there would not be enough room for an additional
199 * argument. We check the total number of arguments only, not the space
200 * occupied by those arguments.
201 *
202 * If we return zero, there still may not be enough room for the next
203 * argument, depending on its length.
204 */
205static int
206bc_argc_limit_reached(int initial_args,
207 const struct buildcmd_control *ctl,
208 struct buildcmd_state *state)
209{
210 /* Check to see if we about to exceed a limit set by xargs' -n option */
211 if (!initial_args && ctl->args_per_exec &&
212 ( (state->cmd_argc - ctl->initial_argc) == ctl->args_per_exec))
213 return 1;
214
215 /* We deliberately use an equality test here rather than >= in order
216 * to force a software failure if the code is modified in such a way
217 * that it fails to call this function for every new argument.
218 */
219 return state->cmd_argc == ctl->max_arg_count;
220}
221
222
223/* Add ARG to the end of the list of arguments `cmd_argv' to pass
224 to the command.
225 LEN is the length of ARG, including the terminating null.
226 If this brings the list up to its maximum size, execute the command.
227*/
228
229void
230bc_push_arg (const struct buildcmd_control *ctl,
231 struct buildcmd_state *state,
232 const char *arg, size_t len,
233 const char *prefix, size_t pfxlen,
234 int initial_args)
235{
236 if (!initial_args)
237 state->todo = 1;
238
239 if (arg)
240 {
241 if (state->cmd_argv_chars + len > ctl->arg_max)
242 {
243 if (initial_args || state->cmd_argc == ctl->initial_argc)
244 error (1, 0, _("can not fit single argument within argument list size limit"));
245 /* xargs option -i (replace_pat) implies -x (exit_if_size_exceeded) */
246 if (ctl->replace_pat
247 || (ctl->exit_if_size_exceeded &&
248 (ctl->lines_per_exec || ctl->args_per_exec)))
249 error (1, 0, _("argument list too long"));
250 do_exec (ctl, state);
251 }
252
253 if (bc_argc_limit_reached(initial_args, ctl, state))
254 do_exec (ctl, state);
255 }
256
257 if (state->cmd_argc >= state->cmd_argv_alloc)
258 {
259 if (!state->cmd_argv)
260 {
261 state->cmd_argv_alloc = 64;
262 state->cmd_argv = (char **) xmalloc (sizeof (char *) * state->cmd_argv_alloc);
263 }
264 else
265 {
266 state->cmd_argv_alloc *= 2;
267 state->cmd_argv = (char **) xrealloc (state->cmd_argv,
268 sizeof (char *) * state->cmd_argv_alloc);
269 }
270 }
271
272 if (!arg)
273 state->cmd_argv[state->cmd_argc++] = NULL;
274 else
275 {
276 state->cmd_argv[state->cmd_argc++] = state->argbuf + state->cmd_argv_chars;
277 if (prefix)
278 {
279 strcpy (state->argbuf + state->cmd_argv_chars, prefix);
280 state->cmd_argv_chars += pfxlen;
281 }
282
283 strcpy (state->argbuf + state->cmd_argv_chars, arg);
284 state->cmd_argv_chars += len;
285
286 /* If we have now collected enough arguments,
287 * do the exec immediately. This must be
288 * conditional on arg!=NULL, since do_exec()
289 * actually calls bc_push_arg(ctl, state, NULL, 0, false).
290 */
291 if (bc_argc_limit_reached(initial_args, ctl, state))
292 do_exec (ctl, state);
293 }
294
295 /* If this is an initial argument, set the high-water mark. */
296 if (initial_args)
297 {
298 state->cmd_initial_argv_chars = state->cmd_argv_chars;
299 }
300}
301
302
303/* Finds the first occurrence of the substring NEEDLE in the string
304 HAYSTACK. Both strings can be multibyte strings. */
305
306static char *
307mbstrstr (const char *haystack, const char *needle)
308{
309#if DO_MULTIBYTE
310 if (MB_CUR_MAX > 1)
311 {
312 size_t hlen = strlen (haystack);
313 size_t nlen = strlen (needle);
314 mbstate_t mbstate;
315 size_t step;
316
317 memset (&mbstate, 0, sizeof (mbstate_t));
318 while (hlen >= nlen)
319 {
320 if (memcmp (haystack, needle, nlen) == 0)
321 return (char *) haystack;
322 step = mbrlen (haystack, hlen, &mbstate);
323 if (step <= 0)
324 break;
325 haystack += step;
326 hlen -= step;
327 }
328 return NULL;
329 }
330#endif
331 return strstr (haystack, needle);
332}
333
334static size_t
335get_line_max(void)
336{
337 long val;
338#ifdef _SC_LINE_MAX
339 val = sysconf(_SC_LINE_MAX);
340#else
341 val = -1;
342#endif
343
344 if (val > 0)
345 return val;
346
347 /* either _SC_LINE_MAX was not available or
348 * there is no particular limit.
349 */
350#ifdef LINE_MAX
351 val = LINE_MAX;
352#endif
353
354 if (val > 0)
355 return val;
356
357 return 2048L; /* a reasonable guess. */
358}
359
360
361size_t
362bc_get_arg_max(void)
363{
364 long val;
365
366 /* We may resort to using LONG_MAX, so check it fits. */
367 /* XXX: better to do a compile-time check */
368 assert( (~(size_t)0) >= LONG_MAX);
369
370#ifndef __EMX__ /* our _SC_ARG_MAX value is too low for the env logic to work. */
371#ifdef _SC_ARG_MAX
372 val = sysconf(_SC_ARG_MAX);
373#else
374 val = -1;
375#endif
376
377 if (val > 0)
378 return val;
379#endif /* !__EMX__ */
380
381 /* either _SC_ARG_MAX was not available or
382 * there is no particular limit.
383 */
384#ifdef ARG_MAX
385 val = ARG_MAX;
386#endif
387
388 if (val > 0)
389 return val;
390
391 /* The value returned by this function bounds the
392 * value applied as the ceiling for the -s option.
393 * Hence it the system won't tell us what its limit
394 * is, we allow the user to specify more or less
395 * whatever value they like.
396 */
397 return LONG_MAX;
398}
399
400
401static int cb_exec_noop(const struct buildcmd_control *ctl,
402 struct buildcmd_state *state)
403{
404 /* does nothing. */
405 (void) ctl;
406 (void) state;
407
408 return 0;
409}
410
411
412/* Return how much of ARG_MAX is used by the environment. */
413size_t
414bc_size_of_environment (void)
415{
416 size_t len = 0u;
417 char **envp = environ;
418
419 while (*envp)
420 len += strlen (*envp++) + 1;
421
422 return len;
423}
424
425
426enum BC_INIT_STATUS
427bc_init_controlinfo(struct buildcmd_control *ctl)
428{
429 size_t size_of_environment = bc_size_of_environment();
430 size_t arg_max;
431
432 ctl->posix_arg_size_min = get_line_max();
433 arg_max = bc_get_arg_max();
434
435 /* POSIX.2 requires subtracting 2048. */
436 assert(arg_max > 2048u); /* XXX: this is an external condition, should not check it with assert. */
437 ctl->posix_arg_size_max = (arg_max - 2048);
438
439 ctl->exit_if_size_exceeded = 0;
440
441 /* Take the size of the environment into account. */
442 if (size_of_environment > ctl->posix_arg_size_max)
443 {
444 return BC_INIT_ENV_TOO_BIG;
445 }
446 else
447 {
448 ctl->posix_arg_size_max - size_of_environment;
449 }
450
451 /* need to subtract 2 on the following line - for Linux/PPC */
452 ctl->max_arg_count = (ctl->posix_arg_size_max / sizeof(char*)) - 2u;
453 assert(ctl->max_arg_count > 0);
454 ctl->rplen = 0u;
455 ctl->replace_pat = NULL;
456 ctl->initial_argc = 0;
457 ctl->exec_callback = cb_exec_noop;
458 ctl->lines_per_exec = 0;
459 ctl->args_per_exec = 0;
460
461 /* Set the initial value of arg_max to the largest value we can
462 * tolerate.
463 */
464 ctl->arg_max = ctl->posix_arg_size_max;
465
466 return BC_INIT_OK;
467}
468
469void
470bc_use_sensible_arg_max(struct buildcmd_control *ctl)
471{
472 size_t env_size = bc_size_of_environment();
473 const size_t arg_size = (128u * 1024u) + env_size;
474
475 /* Check against the upper and lower limits. */
476 if (arg_size > ctl->posix_arg_size_max)
477 ctl->arg_max = ctl->posix_arg_size_max - env_size;
478 else if (arg_size < ctl->posix_arg_size_min)
479 ctl->arg_max = ctl->posix_arg_size_min;
480 else
481 ctl->arg_max = arg_size;
482}
483
484
485
486
487void
488bc_init_state(const struct buildcmd_control *ctl,
489 struct buildcmd_state *state,
490 void *context)
491{
492 state->cmd_argc = 0;
493 state->cmd_argv_chars = 0;
494 state->cmd_argv = NULL;
495 state->cmd_argv_alloc = 0;
496
497 /* XXX: the following memory allocation is inadvisable on systems
498 * with no ARG_MAX, because ctl->arg_max may actually be close to
499 * LONG_MAX. Adding one to it is safe though because earlier we
500 * subtracted 2048.
501 */
502 assert(ctl->arg_max <= (LONG_MAX - 2048L));
503 state->argbuf = (char *) xmalloc (ctl->arg_max + 1u);
504
505 state->cmd_argv_chars = state->cmd_initial_argv_chars = 0;
506 state->todo = 0;
507 state->usercontext = context;
508}
509
510void
511bc_clear_args(const struct buildcmd_control *ctl,
512 struct buildcmd_state *state)
513{
514 state->cmd_argc = ctl->initial_argc;
515 state->cmd_argv_chars = state->cmd_initial_argv_chars;
516 state->todo = 0;
517}
518
Note: See TracBrowser for help on using the repository browser.