VirtualBox

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

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

GPLv2 -> GPLv3. See Ticket #44 for clarifications. Fixes #44.

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