VirtualBox

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

Last change on this file since 2134 was 2114, checked in by bird, 16 years ago

redirect.c: warning

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

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