VirtualBox

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

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

It's lu not ul you feed printf to print a ULONG.

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