VirtualBox

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

Last change on this file since 1714 was 1674, checked in by bird, 17 years ago

Fixed the error message on _spawnvp failure (windows) and added an error message for execvp failure (the rest).

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