VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/redirect.c@ 2856

Last change on this file since 2856 was 2839, checked in by bird, 9 years ago

kWorker: A little more hacking.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.1 KB
Line 
1/* $Id: redirect.c 2839 2016-08-25 21:46:44Z bird $ */
2/** @file
3 * kmk_redirect - Do simple program <-> file redirection (++).
4 */
5
6/*
7 * Copyright (c) 2007-2016 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#ifdef __APPLE__
30# define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
31#endif
32#include "config.h"
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37#include <fcntl.h>
38#if defined(_MSC_VER)
39# include <ctype.h>
40# include <io.h>
41# include <direct.h>
42# include <process.h>
43# include "quote_argv.h"
44#else
45# include <unistd.h>
46#endif
47
48#ifdef __OS2__
49# define INCL_BASE
50# include <os2.h>
51# ifndef LIBPATHSTRICT
52# define LIBPATHSTRICT 3
53# endif
54#endif
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** Number of times the '-v' switch was seen. */
61static unsigned g_cVerbosity = 0;
62
63
64#if defined(_MSC_VER)
65
66
67/** Used by safeCloseFd. */
68static void __cdecl ignore_invalid_parameter(const wchar_t *a, const wchar_t *b, const wchar_t *c, unsigned d, uintptr_t e)
69{
70}
71
72#endif /* _MSC_VER */
73
74
75/**
76 * Safely works around MS CRT's pedantic close() function.
77 *
78 * @param fd The file handle.
79 */
80static void safeCloseFd(int fd)
81{
82#ifdef _MSC_VER
83 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
84 _set_invalid_parameter_handler(ignore_invalid_parameter);
85 close(fd);
86 _set_invalid_parameter_handler(pfnOld);
87#else
88 close(fd);
89#endif
90}
91
92
93static const char *name(const char *pszName)
94{
95 const char *psz = strrchr(pszName, '/');
96#if defined(_MSC_VER) || defined(__OS2__)
97 const char *psz2 = strrchr(pszName, '\\');
98 if (!psz2)
99 psz2 = strrchr(pszName, ':');
100 if (psz2 && (!psz || psz2 > psz))
101 psz = psz2;
102#endif
103 return psz ? psz + 1 : pszName;
104}
105
106
107static int usage(FILE *pOut, const char *argv0)
108{
109 fprintf(pOut,
110 "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage] [-v] -- <program> [args]\n"
111 " or: %s --help\n"
112 " or: %s --version\n"
113 "\n"
114 "The rwa+tb is like for fopen, if not specified it defaults to w+.\n"
115 "The <fd> is either a number or an alias for the standard handles:\n"
116 " i = stdin\n"
117 " o = stdout\n"
118 " e = stderr\n"
119 "\n"
120 "The -c switch will close the specified file descriptor.\n"
121 "\n"
122 "The -Z switch zaps the environment.\n"
123 "\n"
124 "The -E switch is for making changes to the environment in a putenv\n"
125 "fashion.\n"
126 "\n"
127 "The -C switch is for changing the current directory. This takes immediate\n"
128 "effect, so be careful where you put it.\n"
129 "\n"
130 "The --wcc-brain-damage switch is to work around wcc and wcc386 (Open Watcom)\n"
131 "not following normal quoting conventions on Windows, OS/2, and DOS.\n"
132 "\n"
133 "The -v switch is for making the thing more verbose.\n"
134 "\n"
135 "This command was originally just a quick hack to avoid invoking the shell\n"
136 "on Windows (cygwin) where forking is very expensive and has exhibited\n"
137 "stability issues on SMP machines. It has since grown into something like\n"
138 "/usr/bin/env on steroids.\n"
139 ,
140 argv0, argv0, argv0);
141 return 1;
142}
143
144
145int main(int argc, char **argv, char **envp)
146{
147 int i;
148 int j;
149#if defined(_MSC_VER)
150 intptr_t rc;
151#endif
152 FILE *pStdErr = stderr;
153 FILE *pStdOut = stdout;
154 int fWatcomBrainDamage = 0;
155
156 /*
157 * Parse arguments.
158 */
159 if (argc <= 1)
160 return usage(pStdErr, name(argv[0]));
161 for (i = 1; i < argc; i++)
162 {
163 if (argv[i][0] == '-')
164 {
165 int fd;
166 int fdOpened;
167 int fOpen;
168 char *psz = &argv[i][1];
169 if (*psz == '-')
170 {
171 /* '--' ? */
172 if (!psz[1])
173 {
174 i++;
175 break;
176 }
177
178 /* convert to short. */
179 if (!strcmp(psz, "-help"))
180 psz = "h";
181 else if (!strcmp(psz, "-version"))
182 psz = "V";
183 else if (!strcmp(psz, "-env"))
184 psz = "E";
185 else if (!strcmp(psz, "-chdir"))
186 psz = "C";
187 else if (!strcmp(psz, "-zap-env"))
188 psz = "Z";
189 else if (!strcmp(psz, "-close"))
190 psz = "c";
191 else if (!strcmp(psz, "-wcc-brain-damage"))
192 {
193 fWatcomBrainDamage = 1;
194 continue;
195 }
196 }
197
198 /*
199 * Deal with the obligatory help and version switches first.
200 */
201 if (*psz == 'h')
202 {
203 usage(pStdOut, name(argv[0]));
204 return 0;
205 }
206 if (*psz == 'V')
207 {
208 printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n"
209 "Copyright (C) 2007-2012 knut st. osmundsen\n",
210 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH,
211 KBUILD_SVN_REV);
212 return 0;
213 }
214
215 /*
216 * Environment switch?
217 */
218 if (*psz == 'E')
219 {
220 psz++;
221 if (*psz == ':' || *psz == '=')
222 psz++;
223 else
224 {
225 if (i + 1 >= argc)
226 {
227 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
228 return 1;
229 }
230 psz = argv[++i];
231 }
232#ifdef __OS2__
233 if ( !strncmp(psz, "BEGINLIBPATH=", sizeof("BEGINLIBPATH=") - 1)
234 || !strncmp(psz, "ENDLIBPATH=", sizeof("ENDLIBPATH=") - 1)
235 || !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1))
236 {
237 ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH
238 : *psz == 'E' ? END_LIBPATH
239 : LIBPATHSTRICT;
240 const char *pszVal = strchr(psz, '=') + 1;
241 APIRET rc = DosSetExtLIBPATH(pszVal, ulVar);
242 if (rc)
243 {
244 fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n",
245 name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc);
246 return 1;
247 }
248 }
249 else
250#endif /* __OS2__ */
251 {
252 const char *pchEqual = strchr(psz, '=');
253 if (pchEqual && pchEqual[1] != '\0')
254 {
255 if (putenv(psz))
256 {
257 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
258 return 1;
259 }
260 }
261 else
262 {
263 size_t cchVar = pchEqual ? (size_t)(pchEqual - psz) : strlen(psz);
264 char *pszCopy = (char *)malloc(cchVar + 2);
265 memcpy(pszCopy, psz, cchVar);
266
267#if defined(_MSC_VER) || defined(__OS2__)
268 pszCopy[cchVar] = '=';
269 pszCopy[cchVar + 1] = '\0';
270 if (putenv(pszCopy))
271 {
272 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
273 return 1;
274 }
275#else
276 pszCopy[cchVar] = '\0';
277 if (unsetenv(pszCopy))
278 {
279 fprintf(pStdErr, "%s: error: unsetenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
280 return 1;
281 }
282#endif
283 free(pszCopy);
284 }
285 }
286 continue;
287 }
288
289 /*
290 * Change directory switch?
291 */
292 if (*psz == 'C')
293 {
294 psz++;
295 if (*psz == ':' || *psz == '=')
296 psz++;
297 else
298 {
299 if (i + 1 >= argc)
300 {
301 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
302 return 1;
303 }
304 psz = argv[++i];
305 }
306 if (!chdir(psz))
307 continue;
308#ifdef _MSC_VER
309 {
310 /* drop trailing slash if any. */
311 size_t cch = strlen(psz);
312 if ( cch > 2
313 && (psz[cch - 1] == '/' || psz[cch - 1] == '\\')
314 && psz[cch - 1] != ':')
315 {
316 int rc2;
317 char *pszCopy = strdup(psz);
318 do pszCopy[--cch] = '\0';
319 while ( cch > 2
320 && (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\')
321 && pszCopy[cch - 1] != ':');
322 rc2 = chdir(pszCopy);
323 free(pszCopy);
324 if (!rc2)
325 continue;
326 }
327 }
328#endif
329 fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
330 return 1;
331 }
332
333 /*
334 * Zap environment switch?
335 * This is a bit of a hack.
336 */
337 if (*psz == 'Z')
338 {
339 unsigned j = 0;
340 while (envp[j] != NULL)
341 j++;
342 while (j-- > 0)
343 {
344 char *pszEqual = strchr(envp[j], '=');
345 char *pszCopy;
346
347 if (pszEqual)
348 *pszEqual = '\0';
349 pszCopy = strdup(envp[j]);
350 if (pszEqual)
351 *pszEqual = '=';
352
353#if defined(_MSC_VER) || defined(__OS2__)
354 putenv(pszCopy);
355#else
356 unsetenv(pszCopy);
357#endif
358 free(pszCopy);
359 }
360 continue;
361 }
362
363 /*
364 * Verbose operation switch?
365 */
366 if (*psz == 'v')
367 {
368 g_cVerbosity++;
369 continue;
370 }
371
372 /*
373 * Close the specified file descriptor (no stderr/out/in aliases).
374 */
375 if (*psz == 'c')
376 {
377 psz++;
378 if (!*psz)
379 {
380 i++;
381 if (i >= argc)
382 {
383 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
384 return 1;
385 }
386 psz = argv[i];
387 }
388
389 fd = (int)strtol(psz, &psz, 0);
390 if (!fd || *psz)
391 {
392 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
393 return 1;
394
395 }
396 if (fd < 0)
397 {
398 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
399 return 1;
400 }
401 /** @todo deal with stderr */
402 safeCloseFd(fd);
403 continue;
404 }
405
406 /*
407 * Parse a file descriptor argument.
408 */
409
410 /* mode */
411 switch (*psz)
412 {
413 case 'r':
414 psz++;
415 if (*psz == '+')
416 {
417 fOpen = O_RDWR;
418 psz++;
419 }
420 else
421 fOpen = O_RDONLY;
422 break;
423
424 case 'w':
425 psz++;
426 if (*psz == '+')
427 {
428 psz++;
429 fOpen = O_RDWR | O_CREAT | O_TRUNC;
430 }
431 else
432 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
433 break;
434
435 case 'a':
436 psz++;
437 if (*psz == '+')
438 {
439 psz++;
440 fOpen = O_RDWR | O_CREAT | O_APPEND;
441 }
442 else
443 fOpen = O_WRONLY | O_CREAT | O_APPEND;
444 break;
445
446 case 'i': /* make sure stdin is read-only. */
447 fOpen = O_RDONLY;
448 break;
449
450 case '+':
451 fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]);
452 return 1;
453
454 default:
455 fOpen = O_RDWR | O_CREAT | O_TRUNC;
456 break;
457 }
458
459 /* binary / text modifiers */
460 switch (*psz)
461 {
462 case 'b':
463#ifdef O_BINARY
464 fOpen |= O_BINARY;
465#endif
466 psz++;
467 break;
468
469 case 't':
470#ifdef O_TEXT
471 fOpen |= O_TEXT;
472#endif
473 psz++;
474 break;
475
476 default:
477#ifdef O_BINARY
478 fOpen |= O_BINARY;
479#endif
480 break;
481
482 }
483
484 /* convert to file descriptor number */
485 switch (*psz)
486 {
487 case 'i':
488 fd = 0;
489 psz++;
490 break;
491
492 case 'o':
493 fd = 1;
494 psz++;
495 break;
496
497 case 'e':
498 fd = 2;
499 psz++;
500 break;
501
502 case '0':
503 if (!psz[1])
504 {
505 fd = 0;
506 psz++;
507 break;
508 }
509 case '1':
510 case '2':
511 case '3':
512 case '4':
513 case '5':
514 case '6':
515 case '7':
516 case '8':
517 case '9':
518 fd = (int)strtol(psz, &psz, 0);
519 if (!fd)
520 {
521 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
522 return 1;
523
524 }
525 if (fd < 0)
526 {
527 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
528 return 1;
529 }
530 break;
531
532 /*
533 * Invalid argument.
534 */
535 default:
536 fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]);
537 return 1;
538 }
539
540 /*
541 * Check for the filename.
542 */
543 if (*psz)
544 {
545 if (*psz != ':' && *psz != '=')
546 {
547 fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]);
548 return 1;
549 }
550 psz++;
551 }
552 else
553 {
554 i++;
555 if (i >= argc)
556 {
557 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
558 return 1;
559 }
560 psz = argv[i];
561 }
562
563 /*
564 * Setup the redirection.
565 */
566 if (fd == fileno(pStdErr))
567 {
568 /*
569 * Move stderr to a new location, making it close on exec.
570 * If pStdOut has already teamed up with pStdErr, update it too.
571 */
572 FILE *pNew;
573 fdOpened = dup(fileno(pStdErr));
574 if (fdOpened == -1)
575 {
576 fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno));
577 return 1;
578 }
579#ifdef _MSC_VER
580 /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now.
581 * SetHandleInformation + set FNOINHERIT in CRT.
582 */
583#else
584 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1)
585 {
586 fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno));
587 return 1;
588 }
589#endif
590
591 pNew = fdopen(fdOpened, "w");
592 if (!pNew)
593 {
594 fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno));
595 return 1;
596 }
597 if (pStdOut == pStdErr)
598 pStdOut = pNew;
599 pStdErr = pNew;
600 }
601 else if (fd == 1 && pStdOut != pStdErr)
602 pStdOut = pStdErr;
603
604 /*
605 * Close and open the new file descriptor.
606 */
607 safeCloseFd(fd);
608#if defined(_MSC_VER)
609 if (!strcmp(psz, "/dev/null"))
610 psz = (char *)"nul";
611#endif
612 fdOpened = open(psz, fOpen, 0666);
613 if (fdOpened == -1)
614 {
615 fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
616 return 1;
617 }
618 if (fdOpened != fd)
619 {
620 /* move it (dup2 returns 0 on MSC). */
621 if (dup2(fdOpened, fd) == -1)
622 {
623 fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
624 return 1;
625 }
626 close(fdOpened);
627 }
628 }
629 else
630 {
631 fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);
632 return usage(pStdErr, name(argv[0]));
633 }
634 }
635
636 /*
637 * Make sure there's something to execute.
638 */
639 if (i >= argc)
640 {
641 fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0]));
642 return usage(pStdErr, name(argv[0]));
643 }
644
645#if defined(_MSC_VER)
646 if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */
647 {
648 fclose(pStdErr);
649 pStdErr = NULL;
650 }
651
652 /* MSC is a PITA since it refuses to quote the arguments... */
653 quote_argv(argc - i, &argv[i], fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
654 if (g_cVerbosity > 0)
655 for (j = i; j < argc; j++)
656 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
657 rc = _spawnvp(_P_WAIT, argv[i], &argv[i]);
658 if (rc == -1 && pStdErr)
659 {
660 fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
661 rc = 1;
662 }
663 return rc;
664#else
665 if (g_cVerbosity > 0)
666 for (j = i; j < argc; j++)
667 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
668 execvp(argv[i], &argv[i]);
669 fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
670 return 1;
671#endif
672}
673
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette