VirtualBox

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

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

Added two new options: -C <dir> and -E var=val.

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