VirtualBox

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

Last change on this file since 2101 was 2074, checked in by bird, 16 years ago

kmk_redirect: OS/2 hack - close files, zap environment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: redirect.c 2074 2008-11-17 02:55:56Z bird $ */
2/** @file
3 * kmk_redirect - Do simple program <-> file redirection (++).
4 */
5
6/*
7 * Copyright (c) 2007-2008 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#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <errno.h>
33#include <fcntl.h>
34#if defined(_MSC_VER)
35# include <io.h>
36# include <direct.h>
37# include <process.h>
38#else
39# include <unistd.h>
40#endif
41
42#ifdef __OS2__
43# define INCL_BASE
44# include <os2.h>
45# ifndef LIBPATHSTRICT
46# define LIBPATHSTRICT 3
47# endif
48#endif
49
50
51static const char *name(const char *pszName)
52{
53 const char *psz = strrchr(pszName, '/');
54#if defined(_MSC_VER) || defined(__OS2__)
55 const char *psz2 = strrchr(pszName, '\\');
56 if (!psz2)
57 psz2 = strrchr(pszName, ':');
58 if (psz2 && (!psz || psz2 > psz))
59 psz = psz2;
60#endif
61 return psz ? psz + 1 : pszName;
62}
63
64
65static int usage(FILE *pOut, const char *argv0)
66{
67 fprintf(pOut,
68 "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] -- <program> [args]\n"
69 " or: %s --help\n"
70 " or: %s --version\n"
71 "\n"
72 "The rwa+tb is like for fopen, if not specified it defaults to w+.\n"
73 "The <fd> is either a number or an alias for the standard handles:\n"
74 " i = stdin\n"
75 " o = stdout\n"
76 " e = stderr\n"
77 "\n"
78 "The -c switch will close the specified file descriptor.\n"
79 "\n"
80 "The -Z switch zaps the environment.\n"
81 "\n"
82 "The -E switch is for making changes to the environment in a putenv\n"
83 "fashion.\n"
84 "\n"
85 "The -C switch is for changing the current directory. This takes immediate\n"
86 "effect, so be careful where you put it.\n"
87 "\n"
88 "This command is really just a quick hack to avoid invoking the shell\n"
89 "on Windows (cygwin) where forking is very expensive and has exhibited\n"
90 "stability issues on SMP machines. This tool may be retired when kBuild\n"
91 "starts using the kmk_kash shell.\n"
92 ,
93 argv0, argv0, argv0);
94 return 1;
95}
96
97
98int main(int argc, char **argv, char **envp)
99{
100 int i;
101#if defined(_MSC_VER)
102 intptr_t rc;
103#endif
104 FILE *pStdErr = stderr;
105 FILE *pStdOut = stdout;
106
107 /*
108 * Parse arguments.
109 */
110 if (argc <= 1)
111 return usage(pStdErr, name(argv[0]));
112 for (i = 1; i < argc; i++)
113 {
114 if (argv[i][0] == '-')
115 {
116 int fd;
117 int fdOpened;
118 int fOpen;
119 char *psz = &argv[i][1];
120 if (*psz == '-')
121 {
122 /* '--' ? */
123 if (!psz[1])
124 {
125 i++;
126 break;
127 }
128
129 /* convert to short. */
130 if (!strcmp(psz, "-help"))
131 psz = "h";
132 else if (!strcmp(psz, "-version"))
133 psz = "V";
134 else if (!strcmp(psz, "-env"))
135 psz = "E";
136 else if (!strcmp(psz, "-chdir"))
137 psz = "C";
138 else if (!strcmp(psz, "-zap-env"))
139 psz = "Z";
140 else if (!strcmp(psz, "-close"))
141 psz = "c";
142 }
143
144 /*
145 * Deal with the obligatory help and version switches first.
146 */
147 if (*psz == 'h')
148 {
149 usage(pStdOut, name(argv[0]));
150 return 0;
151 }
152 if (*psz == 'V')
153 {
154 printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n"
155 "Copyright (C) 2007-2008 Knut St. Osmundsen\n",
156 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH,
157 KBUILD_SVN_REV);
158 return 0;
159 }
160
161 /*
162 * Environment switch?
163 */
164 if (*psz == 'E')
165 {
166 psz++;
167 if (*psz == ':' || *psz == '=')
168 psz++;
169 else
170 {
171 if (i + 1 >= argc)
172 {
173 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
174 return 1;
175 }
176 psz = argv[++i];
177 }
178#ifdef __OS2__
179 if ( !strncmp(psz, "BEGINLIBPATH=", sizeof("BEGINLIBPATH=") - 1)
180 || !strncmp(psz, "ENDLIBPATH=", sizeof("ENDLIBPATH=") - 1)
181 || !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1))
182 {
183 ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH
184 : *psz == 'E' ? END_LIBPATH
185 : LIBPATHSTRICT;
186 const char *pszVal = strchr(psz, '=') + 1;
187 APIRET rc = DosSetExtLIBPATH(pszVal, ulVar);
188 if (rc)
189 {
190 fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n",
191 name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc);
192 return 1;
193 }
194 }
195 else
196#endif /* __OS2__ */
197 if (putenv(psz))
198 {
199 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
200 return 1;
201 }
202 continue;
203 }
204
205 /*
206 * Change directory switch?
207 */
208 if (*psz == 'C')
209 {
210 psz++;
211 if (*psz == ':' || *psz == '=')
212 psz++;
213 else
214 {
215 if (i + 1 >= argc)
216 {
217 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
218 return 1;
219 }
220 psz = argv[++i];
221 }
222 if (!chdir(psz))
223 continue;
224#ifdef _MSC_VER
225 {
226 /* drop trailing slash if any. */
227 size_t cch = strlen(psz);
228 if ( cch > 2
229 && (psz[cch - 1] == '/' || psz[cch - 1] == '\\')
230 && psz[cch - 1] != ':')
231 {
232 int rc2;
233 char *pszCopy = strdup(psz);
234 do pszCopy[--cch] = '\0';
235 while ( cch > 2
236 && (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\')
237 && pszCopy[cch - 1] != ':');
238 rc2 = chdir(pszCopy);
239 free(pszCopy);
240 if (!rc2)
241 continue;
242 }
243 }
244#endif
245 fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
246 return 1;
247 }
248
249 /*
250 * Zap environment switch?
251 * This is a bit of a hack.
252 */
253 if (*psz == 'Z')
254 {
255 unsigned i = 0;
256 while (envp[i] != NULL)
257 i++;
258 while (i-- > 0)
259 {
260 char *pszEqual = strchr(envp[i], '=');
261 char *pszCopy;
262
263 if (pszEqual)
264 *pszEqual = '\0';
265 pszCopy = strdup(envp[i]);
266 if (pszEqual)
267 *pszEqual = '=';
268
269#if defined(_MSC_VER) || defined(__OS2__)
270 putenv(pszCopy);
271#else
272 unsetenv(pszCopy);
273#endif
274 free(pszCopy);
275 }
276 continue;
277 }
278
279 /*
280 * Close the specified file descriptor (no stderr/out/in aliases).
281 */
282 if (*psz == 'c')
283 {
284 psz++;
285 if (!*psz)
286 {
287 i++;
288 if (i >= argc)
289 {
290 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
291 return 1;
292 }
293 psz = argv[i];
294 }
295
296 fd = (int)strtol(psz, &psz, 0);
297 if (!fd || *psz)
298 {
299 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
300 return 1;
301
302 }
303 if (fd < 0)
304 {
305 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
306 return 1;
307 }
308 /** @todo deal with stderr */
309 close(fd);
310 continue;
311 }
312
313 /*
314 * Parse a file descriptor argument.
315 */
316
317 /* mode */
318 switch (*psz)
319 {
320 case 'r':
321 psz++;
322 if (*psz == '+')
323 {
324 fOpen = O_RDWR;
325 psz++;
326 }
327 else
328 fOpen = O_RDONLY;
329 break;
330
331 case 'w':
332 psz++;
333 if (*psz == '+')
334 {
335 psz++;
336 fOpen = O_RDWR | O_CREAT | O_TRUNC;
337 }
338 else
339 fOpen = O_WRONLY | O_CREAT | O_TRUNC;
340 break;
341
342 case 'a':
343 psz++;
344 if (*psz == '+')
345 {
346 psz++;
347 fOpen = O_RDWR | O_CREAT | O_APPEND;
348 }
349 else
350 fOpen = O_WRONLY | O_CREAT | O_APPEND;
351 break;
352
353 case 'i': /* make sure stdin is read-only. */
354 fOpen = O_RDONLY;
355 break;
356
357 case '+':
358 fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]);
359 return 1;
360
361 default:
362 fOpen = O_RDWR | O_CREAT | O_TRUNC;
363 break;
364 }
365
366 /* binary / text modifiers */
367 switch (*psz)
368 {
369 case 'b':
370#ifdef O_BINARY
371 fOpen |= O_BINARY;
372#endif
373 psz++;
374 break;
375
376 case 't':
377#ifdef O_TEXT
378 fOpen |= O_TEXT;
379#endif
380 psz++;
381 break;
382
383 default:
384#ifdef O_BINARY
385 fOpen |= O_BINARY;
386#endif
387 break;
388
389 }
390
391 /* convert to file descriptor number */
392 switch (*psz)
393 {
394 case 'i':
395 fd = 0;
396 psz++;
397 break;
398
399 case 'o':
400 fd = 1;
401 psz++;
402 break;
403
404 case 'e':
405 fd = 2;
406 psz++;
407 break;
408
409 case '0':
410 if (!psz[1])
411 {
412 fd = 0;
413 psz++;
414 break;
415 }
416 case '1':
417 case '2':
418 case '3':
419 case '4':
420 case '5':
421 case '6':
422 case '7':
423 case '8':
424 case '9':
425 fd = (int)strtol(psz, &psz, 0);
426 if (!fd)
427 {
428 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
429 return 1;
430
431 }
432 if (fd < 0)
433 {
434 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
435 return 1;
436 }
437 break;
438
439 /*
440 * Invalid argument.
441 */
442 default:
443 fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]);
444 return 1;
445 }
446
447 /*
448 * Check for the filename.
449 */
450 if (*psz)
451 {
452 if (*psz != ':' && *psz != '=')
453 {
454 fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]);
455 return 1;
456 }
457 psz++;
458 }
459 else
460 {
461 i++;
462 if (i >= argc)
463 {
464 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
465 return 1;
466 }
467 psz = argv[i];
468 }
469
470 /*
471 * Setup the redirection.
472 */
473 if (fd == fileno(pStdErr))
474 {
475 /*
476 * Move stderr to a new location, making it close on exec.
477 * If pStdOut has already teamed up with pStdErr, update it too.
478 */
479 FILE *pNew;
480 fdOpened = dup(fileno(pStdErr));
481 if (fdOpened == -1)
482 {
483 fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno));
484 return 1;
485 }
486#ifdef _MSC_VER
487 /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now.
488 * SetHandleInformation + set FNOINHERIT in CRT.
489 */
490#else
491 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1)
492 {
493 fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno));
494 return 1;
495 }
496#endif
497
498 pNew = fdopen(fdOpened, "w");
499 if (!pNew)
500 {
501 fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno));
502 return 1;
503 }
504 if (pStdOut == pStdErr)
505 pStdOut = pNew;
506 pStdErr = pNew;
507 }
508 else if (fd == 1 && pStdOut != pStdErr)
509 pStdOut = pStdErr;
510
511 /*
512 * Close and open the new file descriptor.
513 */
514 close(fd);
515#if defined(_MSC_VER)
516 if (!strcmp(psz, "/dev/null"))
517 psz = (char *)"nul";
518#endif
519 fdOpened = open(psz, fOpen, 0666);
520 if (fdOpened == -1)
521 {
522 fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
523 return 1;
524 }
525 if (fdOpened != fd)
526 {
527 /* move it (dup2 returns 0 on MSC). */
528 if (dup2(fdOpened, fd) == -1)
529 {
530 fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
531 return 1;
532 }
533 close(fdOpened);
534 }
535 }
536 else
537 {
538 fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);
539 return usage(pStdErr, name(argv[0]));
540 }
541 }
542
543 /*
544 * Make sure there's something to execute.
545 */
546 if (i >= argc)
547 {
548 fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0]));
549 return usage(pStdErr, name(argv[0]));
550 }
551
552#if defined(_MSC_VER)
553 if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */
554 {
555 fclose(pStdErr);
556 pStdErr = NULL;
557 }
558
559 /** @todo
560 * We'll have to find the '--' in the commandline and pass that
561 * on to CreateProcess or spawn. Otherwise, the argument qouting
562 * is gonna be messed up.
563 */
564 rc = _spawnvp(_P_WAIT, argv[i], &argv[i]);
565 if (rc == -1 && pStdErr)
566 {
567 fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
568 rc = 1;
569 }
570 return rc;
571#else
572 execvp(argv[i], &argv[i]);
573 fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
574 return 1;
575#endif
576}
577
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