Changeset 2840 in kBuild for trunk/src/kmk/kmkbuiltin/submit.c
- Timestamp:
- Aug 25, 2016 9:47:04 PM (9 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/submit.c
r2837 r2840 1 1 /* $Id$ */ 2 2 /** @file 3 * k mk_redirect - Do simple program <-> file redirection (++).3 * kMk Builtin command - submit job to a kWorker. 4 4 */ 5 5 … … 30 30 # define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */ 31 31 #endif 32 #include "config.h" 32 #include "make.h" 33 #include "job.h" 34 #include "variable.h" 35 #include "pathstuff.h" 33 36 #include <stdio.h> 34 37 #include <stdlib.h> 35 38 #include <string.h> 36 39 #include <errno.h> 37 #include <fcntl.h> 40 #ifdef HAVE_ALLOCA_H 41 # include <alloca.h> 42 #endif 38 43 #if defined(_MSC_VER) 39 44 # include <ctype.h> … … 45 50 #endif 46 51 52 #include "kmkbuiltin.h" 53 #include "err.h" 54 47 55 #ifdef __OS2__ 48 56 # define INCL_BASE … … 53 61 #endif 54 62 63 /********************************************************************************************************************************* 64 * Structures and Typedefs * 65 *********************************************************************************************************************************/ 66 typedef struct WORKERINSTANCE *PWORKERINSTANCE; 67 typedef struct WORKERINSTANCE 68 { 69 /** Pointer to the next worker instance. */ 70 PWORKERINSTANCE pNext; 71 /** Pointer to the previous worker instance. */ 72 PWORKERINSTANCE pPrev; 73 /** 32 or 64. */ 74 unsigned cBits; 75 /** The process handle. */ 76 HANDLE hProcess; 77 78 } WORKERINSTANCE; 79 55 80 56 81 /********************************************************************************************************************************* 57 82 * Global Variables * 58 83 *********************************************************************************************************************************/ 59 /** Number of times the '-v' switch was seen. */ 60 static unsigned g_cVerbosity = 0; 61 62 63 #if defined(_MSC_VER) 64 65 /** 66 * Checks if this is an Watcom option where we must just pass thru the string 67 * as-is. 68 * 69 * This is currnetly only used for -d (defining macros). 70 * 71 * @returns 1 if pass-thru, 0 if not. 72 * @param pszArg The argument to consider. 73 */ 74 static int isWatcomPassThruOption(const char *pszArg) 75 { 76 char ch = *pszArg++; 77 if (ch != '-' && ch != '/') 78 return 0; 79 ch = *pszArg++; 80 switch (ch) 81 { 82 /* Example: -d+VAR="string-value" */ 83 case 'd': 84 if (ch == '+') 85 ch = *pszArg++; 86 if (!isalpha(ch) && ch != '_') 87 return 0; 88 return 1; 89 90 default: 91 return 0; 92 } 93 } 94 95 96 /** 97 * Replaces arguments in need of quoting. 98 * 99 * This will "leak" the original and/or the replacement string, depending on 100 * how you look at it. 101 * 102 * For details on how MSC parses the command line, see "Parsing C Command-Line 103 * Arguments": http://msdn.microsoft.com/en-us/library/a1y7w461.aspx 104 * 105 * @param argc The argument count. 106 * @param argv The argument vector. 107 * @param fWatcomBrainDamage Set if we're catering for wcc, wcc386 or similar 108 * OpenWatcom tools. They seem to follow some 109 * ancient or home made quoting convention. 110 * @param pStdErr For verbose debug info. 111 */ 112 static void quoteArguments(int argc, char **argv, int fWatcomBrainDamage, FILE *pStdErr) 113 { 114 int i; 115 for (i = 0; i < argc; i++) 116 { 117 const char *pszOrgOrg = argv[i]; 118 const char *pszOrg = pszOrgOrg; 119 size_t cchOrg = strlen(pszOrg); 120 const char *pszQuotes = (const char *)memchr(pszOrg, '"', cchOrg); 121 const char *pszProblem = NULL; 122 if ( pszQuotes 123 || cchOrg == 0 124 || (pszProblem = (const char *)memchr(pszOrg, ' ', cchOrg)) != NULL 125 || (pszProblem = (const char *)memchr(pszOrg, '\t', cchOrg)) != NULL 126 || (pszProblem = (const char *)memchr(pszOrg, '\n', cchOrg)) != NULL 127 || (pszProblem = (const char *)memchr(pszOrg, '\r', cchOrg)) != NULL 128 || (pszProblem = (const char *)memchr(pszOrg, '&', cchOrg)) != NULL 129 || (pszProblem = (const char *)memchr(pszOrg, '>', cchOrg)) != NULL 130 || (pszProblem = (const char *)memchr(pszOrg, '<', cchOrg)) != NULL 131 || (pszProblem = (const char *)memchr(pszOrg, '|', cchOrg)) != NULL 132 || (pszProblem = (const char *)memchr(pszOrg, '%', cchOrg)) != NULL 133 || (pszProblem = (const char *)memchr(pszOrg, '\'', cchOrg)) != NULL 134 || ( !fWatcomBrainDamage 135 && (pszProblem = (const char *)memchr(pszOrg, '=', cchOrg)) != NULL) 136 ) 137 { 138 char ch; 139 int fComplicated = pszQuotes || (cchOrg > 0 && pszOrg[cchOrg - 1] == '\\'); 140 size_t cchNew = fComplicated ? cchOrg * 2 + 2 : cchOrg + 2; 141 char *pszNew = (char *)malloc(cchNew + 1 /*term*/ + 3 /*passthru hack*/); 142 143 argv[i] = pszNew; 144 145 /* Watcom does not grok stuff like "-i=c:\program files\watcom\h", 146 it think it's a source specification. In that case the quote 147 must follow the equal sign. */ 148 if (fWatcomBrainDamage) 149 { 150 size_t cchUnquoted = 0; 151 if (pszOrg[0] == '@') /* Response file quoting: @"file name.rsp" */ 152 cchUnquoted = 1; 153 else if (pszOrg[0] == '-' || pszOrg[0] == '/') /* Switch quoting. */ 154 { 155 if (isWatcomPassThruOption(pszOrg)) 156 cchUnquoted = strlen(pszOrg) + 1; 157 else 158 { 159 const char *pszNeedQuoting = (const char *)memchr(pszOrg, '=', cchOrg); /* For -i=dir and similar. */ 160 if ( pszNeedQuoting == NULL 161 || (uintptr_t)pszNeedQuoting > (uintptr_t)(pszProblem ? pszProblem : pszQuotes)) 162 pszNeedQuoting = pszProblem ? pszProblem : pszQuotes; 163 else 164 pszNeedQuoting++; 165 cchUnquoted = pszNeedQuoting - pszOrg; 166 } 167 } 168 if (cchUnquoted) 169 { 170 memcpy(pszNew, pszOrg, cchUnquoted); 171 pszNew += cchUnquoted; 172 pszOrg += cchUnquoted; 173 cchOrg -= cchUnquoted; 174 } 175 } 176 177 *pszNew++ = '"'; 178 if (fComplicated) 179 { 180 while ((ch = *pszOrg++) != '\0') 181 { 182 if (ch == '"') 183 { 184 *pszNew++ = '\\'; 185 *pszNew++ = '"'; 186 } 187 else if (ch == '\\') 188 { 189 /* Backslashes are a bit complicated, they depends on 190 whether a quotation mark follows them or not. They 191 only require escaping if one does. */ 192 unsigned cSlashes = 1; 193 while ((ch = *pszOrg) == '\\') 194 { 195 pszOrg++; 196 cSlashes++; 197 } 198 if (ch == '"' || ch == '\0') /* We put a " at the EOS. */ 199 { 200 while (cSlashes-- > 0) 201 { 202 *pszNew++ = '\\'; 203 *pszNew++ = '\\'; 204 } 205 } 206 else 207 while (cSlashes-- > 0) 208 *pszNew++ = '\\'; 209 } 210 else 211 *pszNew++ = ch; 212 } 213 } 214 else 215 { 216 memcpy(pszNew, pszOrg, cchOrg); 217 pszNew += cchOrg; 218 } 219 *pszNew++ = '"'; 220 *pszNew = '\0'; 221 } 222 223 if (g_cVerbosity > 0) 224 { 225 if (argv[i] == pszOrgOrg) 226 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", i, pszOrgOrg); 227 else 228 { 229 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", i, argv[i]); 230 fprintf(pStdErr, "kmk_redirect: debug:(orig[%i]=%s<eos>)\n", i, pszOrgOrg); 231 } 232 } 233 } 234 235 /*for (i = 0; i < argc; i++) fprintf(stderr, "argv[%u]=%s;;\n", i, argv[i]);*/ 236 } 237 238 239 /** Used by safeCloseFd. */ 240 static void __cdecl ignore_invalid_parameter(const wchar_t *a, const wchar_t *b, const wchar_t *c, unsigned d, uintptr_t e) 241 { 242 } 243 244 #endif /* _MSC_VER */ 245 246 247 /** 248 * Safely works around MS CRT's pedantic close() function. 249 * 250 * @param fd The file handle. 251 */ 252 static void safeCloseFd(int fd) 253 { 254 #ifdef _MSC_VER 255 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler(); 256 _set_invalid_parameter_handler(ignore_invalid_parameter); 257 close(fd); 258 _set_invalid_parameter_handler(pfnOld); 259 #else 260 close(fd); 261 #endif 262 } 263 264 265 static const char *name(const char *pszName) 266 { 267 const char *psz = strrchr(pszName, '/'); 268 #if defined(_MSC_VER) || defined(__OS2__) 269 const char *psz2 = strrchr(pszName, '\\'); 270 if (!psz2) 271 psz2 = strrchr(pszName, ':'); 272 if (psz2 && (!psz || psz2 > psz)) 273 psz = psz2; 274 #endif 275 return psz ? psz + 1 : pszName; 276 } 84 static PWORKERINSTANCE g_pIdleHead; 85 static PWORKERINSTANCE g_pIdleTail; 86 277 87 278 88 … … 280 90 { 281 91 fprintf(pOut, 282 "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage] [-v] -- <program> [args]\n" 92 "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n" 93 " [-C|--chdir <dir>] [--wcc-brain-damage]\n" 94 " [-3|--32-bit] [-6|--64-bit] [-v] -- <program> [args]\n" 283 95 " or: %s --help\n" 284 96 " or: %s --version\n" 285 97 "\n" 286 "The rwa+tb is like for fopen, if not specified it defaults to w+.\n" 287 "The <fd> is either a number or an alias for the standard handles:\n" 288 " i = stdin\n" 289 " o = stdout\n" 290 " e = stderr\n" 98 "Options:\n" 99 " -Z, --zap-env, -i, --ignore-environment\n" 100 " Zaps the environment. Position dependent.\n" 101 " -E, --set <var>=[value]\n" 102 " Sets an enviornment variable putenv fashion. Position dependent.\n" 103 " -U, --unset <var>\n" 104 " Removes an environment variable. Position dependent.\n" 105 " -C, --chdir <dir>\n" 106 " Specifies the current directory for the program. Relative paths\n" 107 " are relative to the previous -C option. Default is getcwd value.\n" 108 " -3, --32-bit\n" 109 " Selects a 32-bit kWorker process. Default: kmk bit count\n" 110 " -6, --64-bit\n" 111 " Selects a 64-bit kWorker process. Default: kmk bit count\n" 112 " --wcc-brain-damage\n" 113 " Works around wcc and wcc386 (Open Watcom) not following normal\n" 114 " quoting conventions on Windows, OS/2, and DOS.\n" 115 " -v,--verbose\n" 116 " More verbose execution.\n" 117 " -V,--version\n" 118 " Show the version number.\n" 119 " -h,--help\n" 120 " Show this usage information.\n" 291 121 "\n" 292 "The -c switch will close the specified file descriptor.\n"293 "\n"294 "The -Z switch zaps the environment.\n"295 "\n"296 "The -E switch is for making changes to the environment in a putenv\n"297 "fashion.\n"298 "\n"299 "The -C switch is for changing the current directory. This takes immediate\n"300 "effect, so be careful where you put it.\n"301 "\n"302 "The --wcc-brain-damage switch is to work around wcc and wcc386 (Open Watcom)\n"303 "not following normal quoting conventions on Windows, OS/2, and DOS.\n"304 "\n"305 "The -v switch is for making the thing more verbose.\n"306 "\n"307 "This command was originally just a quick hack to avoid invoking the shell\n"308 "on Windows (cygwin) where forking is very expensive and has exhibited\n"309 "stability issues on SMP machines. It has since grown into something like\n"310 "/usr/bin/env on steroids.\n"311 122 , 312 123 argv0, argv0, argv0); … … 315 126 316 127 317 int main(int argc, char **argv, char **envp)128 int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild) 318 129 { 319 int i; 320 #if defined(_MSC_VER) 321 intptr_t rc; 322 #else 323 int j; 324 #endif 325 FILE *pStdErr = stderr; 326 FILE *pStdOut = stdout; 327 int fWatcomBrainDamage = 0; 130 int rcExit = 0; 131 int iArg; 132 unsigned cAllocatedEnvVars; 133 unsigned iEnvVar; 134 unsigned cEnvVars; 135 char **papszEnv = NULL; 136 const char *pszCwd = NULL; 137 unsigned cBitsWorker = 0; 138 int fWatcomBrainDamage = 0; 139 int cVerbosity = 0; 140 size_t const cbCwdBuf = GET_PATH_MAX; 141 PATH_VAR(szCwd); 142 143 g_progname = argv[0]; 328 144 329 145 /* 330 * Parse arguments.146 * Create default program environment. 331 147 */ 332 if (argc <= 1) 333 return usage(pStdErr, name(argv[0])); 334 for (i = 1; i < argc; i++) 148 if (getcwd_fs(szCwd, cbCwdBuf) != NULL) 149 { /* likely */ } 150 else 151 return err(1, "getcwd_fs failed\n"); 152 153 papszEnv = pChild->environment; 154 if (papszEnv) 155 pChild->environment = papszEnv = target_environment(pChild->file); 156 cEnvVars = 0; 157 while (papszEnv[cEnvVars] != NULL) 158 cEnvVars++; 159 cAllocatedEnvVars = cEnvVars; 160 161 /* 162 * Parse the command line. 163 */ 164 for (iArg = 1; iArg < argc; iArg++) 335 165 { 336 if (argv[i][0] == '-') 166 const char *pszArg = argv[iArg]; 167 if (*pszArg == '-') 337 168 { 338 int fd; 339 int fdOpened; 340 int fOpen; 341 char *psz = &argv[i][1]; 342 if (*psz == '-') 169 char chOpt = *++pszArg; 170 if (chOpt != '-') 343 171 { 344 /* '--' ? */ 345 if (!psz[1]) 346 { 347 i++; 348 break; 349 } 350 351 /* convert to short. */ 352 if (!strcmp(psz, "-help")) 353 psz = "h"; 354 else if (!strcmp(psz, "-version")) 355 psz = "V"; 356 else if (!strcmp(psz, "-env")) 357 psz = "E"; 358 else if (!strcmp(psz, "-chdir")) 359 psz = "C"; 360 else if (!strcmp(psz, "-zap-env")) 361 psz = "Z"; 362 else if (!strcmp(psz, "-close")) 363 psz = "c"; 364 else if (!strcmp(psz, "-wcc-brain-damage")) 365 { 366 fWatcomBrainDamage = 1; 367 continue; 368 } 369 } 370 371 /* 372 * Deal with the obligatory help and version switches first. 373 */ 374 if (*psz == 'h') 375 { 376 usage(pStdOut, name(argv[0])); 377 return 0; 378 } 379 if (*psz == 'V') 380 { 381 printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n" 382 "Copyright (C) 2007-2012 knut st. osmundsen\n", 383 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH, 384 KBUILD_SVN_REV); 385 return 0; 386 } 387 388 /* 389 * Environment switch? 390 */ 391 if (*psz == 'E') 392 { 393 psz++; 394 if (*psz == ':' || *psz == '=') 395 psz++; 172 if (chOpt != '\0') 173 { /* likely */ } 396 174 else 397 175 { 398 if (i + 1 >= argc) 399 { 400 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]); 401 return 1; 402 } 403 psz = argv[++i]; 404 } 405 #ifdef __OS2__ 406 if ( !strncmp(psz, "BEGINLIBPATH=", sizeof("BEGINLIBPATH=") - 1) 407 || !strncmp(psz, "ENDLIBPATH=", sizeof("ENDLIBPATH=") - 1) 408 || !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1)) 409 { 410 ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH 411 : *psz == 'E' ? END_LIBPATH 412 : LIBPATHSTRICT; 413 const char *pszVal = strchr(psz, '=') + 1; 414 APIRET rc = DosSetExtLIBPATH(pszVal, ulVar); 415 if (rc) 416 { 417 fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n", 418 name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc); 419 return 1; 420 } 421 } 422 else 423 #endif /* __OS2__ */ 424 { 425 const char *pchEqual = strchr(psz, '='); 426 if (pchEqual && pchEqual[1] != '\0') 427 { 428 if (putenv(psz)) 429 { 430 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno)); 431 return 1; 432 } 433 } 434 else 435 { 436 size_t cchVar = pchEqual ? (size_t)(pchEqual - psz) : strlen(psz); 437 char *pszCopy = (char *)malloc(cchVar + 2); 438 memcpy(pszCopy, psz, cchVar); 439 440 #if defined(_MSC_VER) || defined(__OS2__) 441 pszCopy[cchVar] = '='; 442 pszCopy[cchVar + 1] = '\0'; 443 if (putenv(pszCopy)) 444 { 445 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno)); 446 return 1; 447 } 448 #else 449 pszCopy[cchVar] = '\0'; 450 if (unsetenv(pszCopy)) 451 { 452 fprintf(pStdErr, "%s: error: unsetenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno)); 453 return 1; 454 } 455 #endif 456 free(pszCopy); 457 } 458 } 459 continue; 460 } 461 462 /* 463 * Change directory switch? 464 */ 465 if (*psz == 'C') 466 { 467 psz++; 468 if (*psz == ':' || *psz == '=') 469 psz++; 470 else 471 { 472 if (i + 1 >= argc) 473 { 474 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]); 475 return 1; 476 } 477 psz = argv[++i]; 478 } 479 if (!chdir(psz)) 480 continue; 481 #ifdef _MSC_VER 482 { 483 /* drop trailing slash if any. */ 484 size_t cch = strlen(psz); 485 if ( cch > 2 486 && (psz[cch - 1] == '/' || psz[cch - 1] == '\\') 487 && psz[cch - 1] != ':') 488 { 489 int rc2; 490 char *pszCopy = strdup(psz); 491 do pszCopy[--cch] = '\0'; 492 while ( cch > 2 493 && (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\') 494 && pszCopy[cch - 1] != ':'); 495 rc2 = chdir(pszCopy); 496 free(pszCopy); 497 if (!rc2) 498 continue; 499 } 500 } 501 #endif 502 fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno)); 503 return 1; 504 } 505 506 /* 507 * Zap environment switch? 508 * This is a bit of a hack. 509 */ 510 if (*psz == 'Z') 511 { 512 unsigned j = 0; 513 while (envp[j] != NULL) 514 j++; 515 while (j-- > 0) 516 { 517 char *pszEqual = strchr(envp[j], '='); 518 char *pszCopy; 519 520 if (pszEqual) 521 *pszEqual = '\0'; 522 pszCopy = strdup(envp[j]); 523 if (pszEqual) 524 *pszEqual = '='; 525 526 #if defined(_MSC_VER) || defined(__OS2__) 527 putenv(pszCopy); 528 #else 529 unsetenv(pszCopy); 530 #endif 531 free(pszCopy); 532 } 533 continue; 534 } 535 536 /* 537 * Verbose operation switch? 538 */ 539 if (*psz == 'v') 540 { 541 g_cVerbosity++; 542 continue; 543 } 544 545 /* 546 * Close the specified file descriptor (no stderr/out/in aliases). 547 */ 548 if (*psz == 'c') 549 { 550 psz++; 551 if (!*psz) 552 { 553 i++; 554 if (i >= argc) 555 { 556 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0])); 557 return 1; 558 } 559 psz = argv[i]; 560 } 561 562 fd = (int)strtol(psz, &psz, 0); 563 if (!fd || *psz) 564 { 565 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]); 566 return 1; 567 568 } 569 if (fd < 0) 570 { 571 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]); 572 return 1; 573 } 574 /** @todo deal with stderr */ 575 safeCloseFd(fd); 576 continue; 577 } 578 579 /* 580 * Parse a file descriptor argument. 581 */ 582 583 /* mode */ 584 switch (*psz) 585 { 586 case 'r': 587 psz++; 588 if (*psz == '+') 589 { 590 fOpen = O_RDWR; 591 psz++; 592 } 593 else 594 fOpen = O_RDONLY; 595 break; 596 597 case 'w': 598 psz++; 599 if (*psz == '+') 600 { 601 psz++; 602 fOpen = O_RDWR | O_CREAT | O_TRUNC; 603 } 604 else 605 fOpen = O_WRONLY | O_CREAT | O_TRUNC; 606 break; 607 608 case 'a': 609 psz++; 610 if (*psz == '+') 611 { 612 psz++; 613 fOpen = O_RDWR | O_CREAT | O_APPEND; 614 } 615 else 616 fOpen = O_WRONLY | O_CREAT | O_APPEND; 617 break; 618 619 case 'i': /* make sure stdin is read-only. */ 620 fOpen = O_RDONLY; 621 break; 622 623 case '+': 624 fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]); 625 return 1; 626 627 default: 628 fOpen = O_RDWR | O_CREAT | O_TRUNC; 629 break; 630 } 631 632 /* binary / text modifiers */ 633 switch (*psz) 634 { 635 case 'b': 636 #ifdef O_BINARY 637 fOpen |= O_BINARY; 638 #endif 639 psz++; 640 break; 641 642 case 't': 643 #ifdef O_TEXT 644 fOpen |= O_TEXT; 645 #endif 646 psz++; 647 break; 648 649 default: 650 #ifdef O_BINARY 651 fOpen |= O_BINARY; 652 #endif 653 break; 654 655 } 656 657 /* convert to file descriptor number */ 658 switch (*psz) 659 { 660 case 'i': 661 fd = 0; 662 psz++; 663 break; 664 665 case 'o': 666 fd = 1; 667 psz++; 668 break; 669 670 case 'e': 671 fd = 2; 672 psz++; 673 break; 674 675 case '0': 676 if (!psz[1]) 677 { 678 fd = 0; 679 psz++; 680 break; 681 } 682 case '1': 683 case '2': 684 case '3': 685 case '4': 686 case '5': 687 case '6': 688 case '7': 689 case '8': 690 case '9': 691 fd = (int)strtol(psz, &psz, 0); 692 if (!fd) 693 { 694 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]); 695 return 1; 696 697 } 698 if (fd < 0) 699 { 700 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]); 701 return 1; 702 } 703 break; 704 705 /* 706 * Invalid argument. 707 */ 708 default: 709 fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]); 710 return 1; 711 } 712 713 /* 714 * Check for the filename. 715 */ 716 if (*psz) 717 { 718 if (*psz != ':' && *psz != '=') 719 { 720 fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]); 721 return 1; 722 } 723 psz++; 176 errx(1, "Incomplete option: '-'"); 177 return usage(stderr, argv[0]); 178 } 724 179 } 725 180 else 726 181 { 727 i++; 728 if (i >= argc) 729 { 730 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0])); 731 return 1; 732 } 733 psz = argv[i]; 182 pszArg++; 183 184 /* '--' indicates where the bits to execute start. */ 185 if (*pszArg == '\0') 186 { 187 iArg++; 188 break; 189 } 190 191 if (strcmp(pszArg, "watcom-brain-damage") == 0) 192 { 193 fWatcomBrainDamage = 1; 194 continue; 195 } 196 197 /* convert to short. */ 198 if (strcmp(pszArg, "help") == 0) 199 chOpt = 'h'; 200 else if (strcmp(pszArg, "version") == 0) 201 chOpt = 'V'; 202 else if (strcmp(pszArg, "set") == 0) 203 chOpt = 'E'; 204 else if (strcmp(pszArg, "unset") == 0) 205 chOpt = 'U'; 206 else if ( strcmp(pszArg, "zap-env") == 0 207 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ ) 208 chOpt = 'Z'; 209 else if (strcmp(pszArg, "chdir") == 0) 210 chOpt = 'C'; 211 else if (strcmp(pszArg, "32-bit") == 0) 212 chOpt = '3'; 213 else if (strcmp(pszArg, "64-bit") == 0) 214 chOpt = '6'; 215 else if (strcmp(pszArg, "verbose") == 0) 216 chOpt = 'v'; 217 else 218 { 219 errx(1, "Unknown option: '%s'", pszArg - 2); 220 return usage(stderr, argv[0]); 221 } 222 pszArg = ""; 734 223 } 735 224 736 /* 737 * Setup the redirection. 738 */ 739 if (fd == fileno(pStdErr)) 225 do 740 226 { 741 /* 742 * Move stderr to a new location, making it close on exec. 743 * If pStdOut has already teamed up with pStdErr, update it too. 744 */ 745 FILE *pNew; 746 fdOpened = dup(fileno(pStdErr)); 747 if (fdOpened == -1) 748 { 749 fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno)); 750 return 1; 751 } 752 #ifdef _MSC_VER 753 /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now. 754 * SetHandleInformation + set FNOINHERIT in CRT. 755 */ 227 /* Get option value first, if the option takes one. */ 228 const char *pszValue = NULL; 229 switch (chOpt) 230 { 231 case 'E': 232 case 'U': 233 case 'C': 234 if (*pszArg != '\0') 235 pszValue = pszArg + (*pszArg == ':' || *pszArg == '='); 236 else if (++iArg < argc) 237 pszValue = argv[iArg]; 238 else 239 { 240 errx(1, "Option -%c requires an value!", chOpt); 241 return usage(stderr, argv[0]); 242 } 243 break; 244 } 245 246 switch (chOpt) 247 { 248 case 'Z': 249 case 'i': /* GNU env compatibility. */ 250 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 251 free(papszEnv[iEnvVar]); 252 papszEnv[0] = NULL; 253 cEnvVars = 0; 254 break; 255 256 case 'E': 257 { 258 const char *pszEqual = strchr(pszValue, '='); 259 if (pszEqual) 260 { 261 size_t const cchVar = pszValue - pszEqual; 262 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 263 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 264 && papszEnv[iEnvVar][cchVar] == '=') 265 { 266 if (cVerbosity > 0) 267 fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue); 268 free(papszEnv[iEnvVar]); 269 papszEnv[iEnvVar] = xstrdup(pszValue); 270 break; 271 } 272 if (iEnvVar == cEnvVars) 273 { 274 /* Append new variable. We probably need to resize the vector. */ 275 if ((cEnvVars + 2) > cAllocatedEnvVars) 276 { 277 cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf; 278 pChild->environment = papszEnv = (char **)xrealloc(papszEnv, 279 cAllocatedEnvVars * sizeof(papszEnv[0])); 280 } 281 papszEnv[cEnvVars++] = xstrdup(pszValue); 282 papszEnv[cEnvVars] = NULL; 283 if (cVerbosity > 0) 284 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]); 285 } 286 else 287 { 288 /* Check for duplicates. */ 289 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++) 290 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 291 && papszEnv[iEnvVar][cchVar] == '=') 292 { 293 if (cVerbosity > 0) 294 fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 295 free(papszEnv[iEnvVar]); 296 cEnvVars--; 297 if (iEnvVar != cEnvVars) 298 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 299 papszEnv[cEnvVars] = NULL; 300 iEnvVar--; 301 } 302 } 303 } 304 else 305 return errx(1, "Missing '=': -E %s", pszValue); 306 break; 307 } 308 309 case 'U': 310 { 311 if (strchr(pszValue, '=') == NULL) 312 { 313 unsigned cRemoved = 0; 314 size_t const cchVar = strlen(pszValue); 315 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 316 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 317 && papszEnv[iEnvVar][cchVar] == '=') 318 { 319 if (cVerbosity > 0) 320 fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n" 321 : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 322 free(papszEnv[iEnvVar]); 323 cEnvVars--; 324 if (iEnvVar != cEnvVars) 325 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 326 papszEnv[cEnvVars] = NULL; 327 cRemoved++; 328 iEnvVar--; 329 } 330 if (cVerbosity > 0 && !cRemoved) 331 fprintf(stderr, "kSubmit: not found '%s'\n", pszValue); 332 } 333 else 334 return errx(1, "Found invalid variable name character '=' in: -U %s", pszValue); 335 break; 336 } 337 338 case 'C': 339 { 340 size_t cchNewCwd = strlen(pszValue); 341 size_t offDst; 342 if (cchNewCwd) 343 { 344 #ifdef HAVE_DOS_PATHS 345 if (*pszValue == '/' || *pszValue == '\\') 346 { 347 if (pszValue[1] == '/' || pszValue[1] == '\\') 348 offDst = 0; /* UNC */ 349 else if (szCwd[1] == ':' && isalpha(szCwd[0])) 350 offDst = 2; /* Take drive letter from CWD. */ 351 else 352 return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", szCwd, pszValue); 353 } 354 else if ( pszValue[1] == ':' 355 && isalpha(pszValue[0])) 356 { 357 if (pszValue[2] == '/'|| pszValue[2] == '\\') 358 offDst = 0; /* DOS style absolute path. */ 359 else if ( szCwd[1] == ':' 360 && tolower(szCwd[0]) == tolower(pszValue[0]) ) 361 { 362 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */ 363 cchNewCwd -= 2; 364 offDst = strlen(szCwd); 365 } 366 else 367 { 368 /* Get current CWD on the specified drive and append value. */ 369 int iDrive = tolower(pszValue[0]) - 'a' + 1; 370 if (!_getdcwd(iDrive, szCwd, cbCwdBuf)) 371 return err(1, "_getdcwd(%d,,) failed", iDrive); 372 pszValue += 2; 373 cchNewCwd -= 2; 374 } 375 } 756 376 #else 757 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1) 758 { 759 fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno)); 760 return 1; 761 } 762 #endif 763 764 pNew = fdopen(fdOpened, "w"); 765 if (!pNew) 766 { 767 fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno)); 768 return 1; 769 } 770 if (pStdOut == pStdErr) 771 pStdOut = pNew; 772 pStdErr = pNew; 773 } 774 else if (fd == 1 && pStdOut != pStdErr) 775 pStdOut = pStdErr; 776 777 /* 778 * Close and open the new file descriptor. 779 */ 780 safeCloseFd(fd); 781 #if defined(_MSC_VER) 782 if (!strcmp(psz, "/dev/null")) 783 psz = (char *)"nul"; 784 #endif 785 fdOpened = open(psz, fOpen, 0666); 786 if (fdOpened == -1) 787 { 788 fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno)); 789 return 1; 790 } 791 if (fdOpened != fd) 792 { 793 /* move it (dup2 returns 0 on MSC). */ 794 if (dup2(fdOpened, fd) == -1) 795 { 796 fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno)); 797 return 1; 798 } 799 close(fdOpened); 800 } 377 if (*pszValue == '/') 378 offDst = 0; 379 #endif 380 else 381 offDst = strlen(szCwd); /* Relative path, append to the existing CWD value. */ 382 383 /* Do the copying. */ 384 #ifdef HAVE_DOS_PATHS 385 if (offDst > 0 && szCwd[offDst - 1] != '/' && szCwd[offDst - 1] != '\\') 386 #else 387 if (offDst > 0 && szCwd[offDst - 1] != '/') 388 #endif 389 szCwd[offDst++] = '/'; 390 if (offDst + cchNewCwd >= cbCwdBuf) 391 return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, szCwd, pszValue); 392 memcpy(&szCwd[offDst], pszValue, cchNewCwd + 1); 393 } 394 /* else: relative, no change - quitely ignore. */ 395 break; 396 } 397 398 case '3': 399 cBitsWorker = 32; 400 break; 401 402 case '6': 403 cBitsWorker = 64; 404 break; 405 406 case 'v': 407 cVerbosity++; 408 break; 409 410 case 'h': 411 usage(stdout, argv[0]); 412 return 0; 413 414 case 'V': 415 printf("kmk_submit - kBuild version %d.%d.%d (r%u)\n" 416 "Copyright (C) 2007-2016 knut st. osmundsen\n", 417 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH, 418 KBUILD_SVN_REV); 419 return 0; 420 } 421 } while ((chOpt = *pszArg++) != '\0'); 801 422 } 802 423 else 803 424 { 804 fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);805 return usage( pStdErr, name(argv[0]));425 errx(1, "Unknown argument: '%s'", pszArg); 426 return usage(stderr, argv[0]); 806 427 } 807 428 } 808 429 809 430 /* 810 * Make sure there'ssomething to execute.431 * Check that we've got something to execute. 811 432 */ 812 if (i >=argc)433 if (iArg < argc) 813 434 { 814 fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0])); 815 return usage(pStdErr, name(argv[0])); 435 816 436 } 817 818 #if defined(_MSC_VER) 819 if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */ 437 else 820 438 { 821 fclose(pStdErr);822 pStdErr = NULL;439 errx(1, "Nothing to executed!"); 440 rcExit = usage(stderr, argv[0]); 823 441 } 824 442 825 /* MSC is a PITA since it refuses to quote the arguments... */ 826 quoteArguments(argc - i, &argv[i], fWatcomBrainDamage, pStdErr); 827 rc = _spawnvp(_P_WAIT, argv[i], &argv[i]); 828 if (rc == -1 && pStdErr) 829 { 830 fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); 831 rc = 1; 832 } 833 return rc; 834 #else 835 if (g_cVerbosity > 0) 836 for (j = i; j < argc; j++) 837 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]); 838 execvp(argv[i], &argv[i]); 839 fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); 840 return 1; 841 #endif 443 return rcExit; 842 444 } 843 445 446 447
Note:
See TracChangeset
for help on using the changeset viewer.