1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * dosys.cc 29 * 30 * Execute one commandline 31 */ 32 33 /* 34 * Included files 35 */ 36 #include <sys/wait.h> /* WIFEXITED(status) */ 37 #include <alloca.h> /* alloca() */ 38 39 #if defined(TEAMWARE_MAKE_CMN) || defined(MAKETOOL) /* tolik */ 40 #if defined(DISTRIBUTED) 41 # include <dm/Avo_CmdOutput.h> 42 # include <rw/xdrstrea.h> 43 #endif 44 #endif 45 46 #include <stdio.h> /* errno */ 47 #include <errno.h> /* errno */ 48 #include <fcntl.h> /* open() */ 49 #include <mksh/dosys.h> 50 #include <mksh/macro.h> /* getvar() */ 51 #include <mksh/misc.h> /* getmem(), fatal_mksh(), errmsg() */ 52 #include <mksdmsi18n/mksdmsi18n.h> /* libmksdmsi18n_init() */ 53 #include <sys/signal.h> /* SIG_DFL */ 54 #include <sys/stat.h> /* open() */ 55 #include <sys/wait.h> /* wait() */ 56 #include <ulimit.h> /* ulimit() */ 57 #include <unistd.h> /* close(), dup2() */ 58 59 60 61 /* 62 * Defined macros 63 */ 64 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ 65 #define SEND_MTOOL_MSG(cmds) \ 66 if (send_mtool_msgs) { \ 67 cmds \ 68 } 69 #else 70 #define SEND_MTOOL_MSG(cmds) 71 #endif 72 73 /* 74 * typedefs & structs 75 */ 76 77 /* 78 * Static variables 79 */ 80 81 /* 82 * File table of contents 83 */ 84 static Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path); 85 86 /* 87 * Workaround for NFS bug. Sometimes, when running 'open' on a remote 88 * dmake server, it fails with "Stale NFS file handle" error. 89 * The second attempt seems to work. 90 */ 91 int 92 my_open(const char *path, int oflag, mode_t mode) { 93 int res = open(path, oflag, mode); 94 if (res < 0 && (errno == ESTALE || errno == EAGAIN)) { 95 /* Stale NFS file handle. Try again */ 96 res = open(path, oflag, mode); 97 } 98 return res; 99 } 100 101 /* 102 * void 103 * redirect_io(char *stdout_file, char *stderr_file) 104 * 105 * Redirects stdout and stderr for a child mksh process. 106 */ 107 void 108 redirect_io(char *stdout_file, char *stderr_file) 109 { 110 long descriptor_limit; 111 int i; 112 113 if ((descriptor_limit = ulimit(UL_GDESLIM)) < 0) { 114 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 89, "ulimit() failed: %s"), errmsg(errno)); 115 } 116 for (i = 3; i < descriptor_limit; i++) { 117 (void) close(i); 118 } 119 if ((i = my_open(stdout_file, 120 O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, 121 S_IREAD | S_IWRITE)) < 0) { 122 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 90, "Couldn't open standard out temp file `%s': %s"), 123 stdout_file, 124 errmsg(errno)); 125 } else { 126 if (dup2(i, 1) == -1) { 127 fatal_mksh(NOCATGETS("*** Error: dup2(3, 1) failed: %s"), 128 errmsg(errno)); 129 } 130 close(i); 131 } 132 if (stderr_file == NULL) { 133 if (dup2(1, 2) == -1) { 134 fatal_mksh(NOCATGETS("*** Error: dup2(1, 2) failed: %s"), 135 errmsg(errno)); 136 } 137 } else if ((i = my_open(stderr_file, 138 O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, 139 S_IREAD | S_IWRITE)) < 0) { 140 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 91, "Couldn't open standard error temp file `%s': %s"), 141 stderr_file, 142 errmsg(errno)); 143 } else { 144 if (dup2(i, 2) == -1) { 145 fatal_mksh(NOCATGETS("*** Error: dup2(3, 2) failed: %s"), 146 errmsg(errno)); 147 } 148 close(i); 149 } 150 } 151 152 /* 153 * dosys_mksh(command, ignore_error, call_make, silent_error, target) 154 * 155 * Check if command string contains meta chars and dispatch to 156 * the proper routine for executing one command line. 157 * 158 * Return value: 159 * Indicates if the command execution failed 160 * 161 * Parameters: 162 * command The command to run 163 * ignore_error Should we abort when an error is seen? 164 * call_make Did command reference $(MAKE) ? 165 * silent_error Should error messages be suppressed for dmake? 166 * target Target we are building 167 * 168 * Global variables used: 169 * do_not_exec_rule Is -n on? 170 * working_on_targets We started processing real targets 171 */ 172 Doname 173 dosys_mksh(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target, Boolean redirect_out_err, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio) 174 { 175 register int length = command->hash.length; 176 register wchar_t *p; 177 register wchar_t *q; 178 register wchar_t *cmd_string; 179 struct stat before; 180 Doname result; 181 Boolean working_on_targets_mksh = true; 182 Wstring wcb(command); 183 p = wcb.get_string(); 184 cmd_string = p; 185 186 /* Strip spaces from head of command string */ 187 while (iswspace(*p)) { 188 p++, length--; 189 } 190 if (*p == (int) nul_char) { 191 return build_failed; 192 } 193 /* If we are faking it we just return */ 194 if (do_not_exec_rule && 195 working_on_targets_mksh && 196 !call_make && 197 !always_exec) { 198 return build_ok; 199 } 200 201 /* Copy string to make it OK to write it. */ 202 q = ALLOC_WC(length + 1); 203 (void) wscpy(q, p); 204 /* Write the state file iff this command uses make. */ 205 /* XXX - currently does not support recursive make's, $(MAKE)'s 206 if (call_make && command_changed) { 207 write_state_file(0, false); 208 } 209 (void) stat(make_state->string_mb, &before); 210 */ 211 /* 212 * Run command directly if it contains no shell meta chars, 213 * else run it using the shell. 214 */ 215 /* XXX - command->meta *may* not be set correctly */ 216 if (await(ignore_error, 217 silent_error, 218 target, 219 cmd_string, 220 command->meta ? 221 doshell(q, ignore_error, redirect_out_err, stdout_file, stderr_file, nice_prio) : 222 doexec(q, ignore_error, redirect_out_err, stdout_file, stderr_file, vroot_path, nice_prio), 223 false, 224 NULL, 225 -1)) { 226 227 #ifdef PRINT_EXIT_STATUS 228 warning_mksh(NOCATGETS("I'm in dosys_mksh(), and await() returned result of build_ok.")); 229 #endif 230 231 result = build_ok; 232 } else { 233 234 #ifdef PRINT_EXIT_STATUS 235 warning_mksh(NOCATGETS("I'm in dosys_mksh(), and await() returned result of build_failed.")); 236 #endif 237 238 result = build_failed; 239 } 240 retmem(q); 241 242 /* XXX - currently does not support recursive make's, $(MAKE)'s 243 if ((report_dependencies_level == 0) && 244 call_make) { 245 make_state->stat.time = (time_t)file_no_time; 246 (void)exists(make_state); 247 if (before.st_mtime == make_state->stat.time) { 248 return result; 249 } 250 makefile_type = reading_statefile; 251 if (read_trace_level > 1) { 252 trace_reader = true; 253 } 254 (void) read_simple_file(make_state, 255 false, 256 false, 257 false, 258 false, 259 false, 260 true); 261 trace_reader = false; 262 } 263 */ 264 return result; 265 } 266 267 /* 268 * doshell(command, ignore_error) 269 * 270 * Used to run command lines that include shell meta-characters. 271 * The make macro SHELL is supposed to contain a path to the shell. 272 * 273 * Return value: 274 * The pid of the process we started 275 * 276 * Parameters: 277 * command The command to run 278 * ignore_error Should we abort on error? 279 * 280 * Global variables used: 281 * filter_stderr If -X is on we redirect stderr 282 * shell_name The Name "SHELL", used to get the path to shell 283 */ 284 int 285 doshell(wchar_t *command, register Boolean ignore_error, Boolean redirect_out_err, char *stdout_file, char *stderr_file, int nice_prio) 286 { 287 char *argv[6]; 288 int argv_index = 0; 289 int cmd_argv_index; 290 int length; 291 char nice_prio_buf[MAXPATHLEN]; 292 register Name shell = getvar(shell_name); 293 register char *shellname; 294 char *tmp_mbs_buffer; 295 296 297 if (IS_EQUAL(shell->string_mb, "")) { 298 shell = shell_name; 299 } 300 if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) { 301 shellname = shell->string_mb; 302 } else { 303 shellname++; 304 } 305 306 /* 307 * Only prepend the /usr/bin/nice command to the original command 308 * if the nice priority, nice_prio, is NOT zero (0). 309 * Nice priorities can be a positive or a negative number. 310 */ 311 if (nice_prio != 0) { 312 argv[argv_index++] = (char *)NOCATGETS("nice"); 313 (void) sprintf(nice_prio_buf, NOCATGETS("-%d"), nice_prio); 314 argv[argv_index++] = strdup(nice_prio_buf); 315 } 316 argv[argv_index++] = shellname; 317 argv[argv_index++] = (char*)(ignore_error ? NOCATGETS("-c") : NOCATGETS("-ce")); 318 if ((length = wslen(command)) >= MAXPATHLEN) { 319 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); 320 (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); 321 cmd_argv_index = argv_index; 322 argv[argv_index++] = strdup(tmp_mbs_buffer); 323 retmem_mb(tmp_mbs_buffer); 324 } else { 325 WCSTOMBS(mbs_buffer, command); 326 cmd_argv_index = argv_index; 327 argv[argv_index++] = strdup(mbs_buffer); 328 } 329 argv[argv_index] = NULL; 330 (void) fflush(stdout); 331 if ((childPid = fork()) == 0) { 332 enable_interrupt((void (*) (int)) SIG_DFL); 333 if (redirect_out_err) { 334 redirect_io(stdout_file, stderr_file); 335 } 336 #if 0 337 if (filter_stderr) { 338 redirect_stderr(); 339 } 340 #endif 341 if (nice_prio != 0) { 342 (void) execve(NOCATGETS("/usr/bin/nice"), argv, environ); 343 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 92, "Could not load `/usr/bin/nice': %s"), 344 errmsg(errno)); 345 } else { 346 (void) execve(shell->string_mb, argv, environ); 347 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 93, "Could not load Shell from `%s': %s"), 348 shell->string_mb, 349 errmsg(errno)); 350 } 351 } 352 if (childPid == -1) { 353 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 94, "fork failed: %s"), 354 errmsg(errno)); 355 } 356 retmem_mb(argv[cmd_argv_index]); 357 return childPid; 358 } 359 360 /* 361 * exec_vp(name, argv, envp, ignore_error) 362 * 363 * Like execve, but does path search. 364 * This starts command when make invokes it directly (without a shell). 365 * 366 * Return value: 367 * Returns false if the exec failed 368 * 369 * Parameters: 370 * name The name of the command to run 371 * argv Arguments for the command 372 * envp The environment for it 373 * ignore_error Should we abort on error? 374 * 375 * Global variables used: 376 * shell_name The Name "SHELL", used to get the path to shell 377 * vroot_path The path used by the vroot package 378 */ 379 static Boolean 380 exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path) 381 { 382 register Name shell = getvar(shell_name); 383 register char *shellname; 384 char *shargv[4]; 385 Name tmp_shell; 386 387 if (IS_EQUAL(shell->string_mb, "")) { 388 shell = shell_name; 389 } 390 391 for (int i = 0; i < 5; i++) { 392 (void) execve_vroot(name, 393 argv + 1, 394 envp, 395 vroot_path, 396 VROOT_DEFAULT); 397 switch (errno) { 398 case ENOEXEC: 399 case ENOENT: 400 /* That failed. Let the shell handle it */ 401 shellname = strrchr(shell->string_mb, (int) slash_char); 402 if (shellname == NULL) { 403 shellname = shell->string_mb; 404 } else { 405 shellname++; 406 } 407 shargv[0] = shellname; 408 shargv[1] = (char*)(ignore_error ? NOCATGETS("-c") : NOCATGETS("-ce")); 409 shargv[2] = argv[0]; 410 shargv[3] = NULL; 411 tmp_shell = getvar(shell_name); 412 if (IS_EQUAL(tmp_shell->string_mb, "")) { 413 tmp_shell = shell_name; 414 } 415 (void) execve_vroot(tmp_shell->string_mb, 416 shargv, 417 envp, 418 vroot_path, 419 VROOT_DEFAULT); 420 return failed; 421 case ETXTBSY: 422 /* 423 * The program is busy (debugged?). 424 * Wait and then try again. 425 */ 426 (void) sleep((unsigned) i); 427 case EAGAIN: 428 break; 429 default: 430 return failed; 431 } 432 } 433 return failed; 434 } 435 436 /* 437 * doexec(command, ignore_error) 438 * 439 * Will scan an argument string and split it into words 440 * thus building an argument list that can be passed to exec_ve() 441 * 442 * Return value: 443 * The pid of the process started here 444 * 445 * Parameters: 446 * command The command to run 447 * ignore_error Should we abort on error? 448 * 449 * Global variables used: 450 * filter_stderr If -X is on we redirect stderr 451 */ 452 int 453 doexec(register wchar_t *command, register Boolean ignore_error, Boolean redirect_out_err, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio) 454 { 455 int arg_count = 5; 456 char **argv; 457 int length; 458 char nice_prio_buf[MAXPATHLEN]; 459 register char **p; 460 wchar_t *q; 461 register wchar_t *t; 462 char *tmp_mbs_buffer; 463 464 /* 465 * Only prepend the /usr/bin/nice command to the original command 466 * if the nice priority, nice_prio, is NOT zero (0). 467 * Nice priorities can be a positive or a negative number. 468 */ 469 if (nice_prio != 0) { 470 arg_count += 2; 471 } 472 for (t = command; *t != (int) nul_char; t++) { 473 if (iswspace(*t)) { 474 arg_count++; 475 } 476 } 477 argv = (char **)alloca(arg_count * (sizeof(char *))); 478 /* 479 * Reserve argv[0] for sh in case of exec_vp failure. 480 * Don't worry about prepending /usr/bin/nice command to argv[0]. 481 * In fact, doing it may cause the sh command to fail! 482 */ 483 p = &argv[1]; 484 if ((length = wslen(command)) >= MAXPATHLEN) { 485 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); 486 (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); 487 argv[0] = strdup(tmp_mbs_buffer); 488 retmem_mb(tmp_mbs_buffer); 489 } else { 490 WCSTOMBS(mbs_buffer, command); 491 argv[0] = strdup(mbs_buffer); 492 } 493 494 if (nice_prio != 0) { 495 *p++ = strdup(NOCATGETS("/usr/bin/nice")); 496 (void) sprintf(nice_prio_buf, NOCATGETS("-%d"), nice_prio); 497 *p++ = strdup(nice_prio_buf); 498 } 499 /* Build list of argument words. */ 500 for (t = command; *t;) { 501 if (p >= &argv[arg_count]) { 502 /* This should never happen, right? */ 503 WCSTOMBS(mbs_buffer, command); 504 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 95, "Command `%s' has more than %d arguments"), 505 mbs_buffer, 506 arg_count); 507 } 508 q = t; 509 while (!iswspace(*t) && (*t != (int) nul_char)) { 510 t++; 511 } 512 if (*t) { 513 for (*t++ = (int) nul_char; iswspace(*t); t++); 514 } 515 if ((length = wslen(q)) >= MAXPATHLEN) { 516 tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); 517 (void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1); 518 *p++ = strdup(tmp_mbs_buffer); 519 retmem_mb(tmp_mbs_buffer); 520 } else { 521 WCSTOMBS(mbs_buffer, q); 522 *p++ = strdup(mbs_buffer); 523 } 524 } 525 *p = NULL; 526 527 /* Then exec the command with that argument list. */ 528 (void) fflush(stdout); 529 if ((childPid = fork()) == 0) { 530 enable_interrupt((void (*) (int)) SIG_DFL); 531 if (redirect_out_err) { 532 redirect_io(stdout_file, stderr_file); 533 } 534 #if 0 535 if (filter_stderr) { 536 redirect_stderr(); 537 } 538 #endif 539 (void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path); 540 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 96, "Cannot load command `%s': %s"), argv[1], errmsg(errno)); 541 } 542 if (childPid == -1) { 543 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 97, "fork failed: %s"), 544 errmsg(errno)); 545 } 546 for (int i = 0; argv[i] != NULL; i++) { 547 retmem_mb(argv[i]); 548 } 549 return childPid; 550 } 551 552 /* 553 * await(ignore_error, silent_error, target, command, running_pid) 554 * 555 * Wait for one child process and analyzes 556 * the returned status when the child process terminates. 557 * 558 * Return value: 559 * Returns true if commands ran OK 560 * 561 * Parameters: 562 * ignore_error Should we abort on error? 563 * silent_error Should error messages be suppressed for dmake? 564 * target The target we are building, for error msgs 565 * command The command we ran, for error msgs 566 * running_pid The pid of the process we are waiting for 567 * 568 * Static variables used: 569 * filter_file The fd for the filter file 570 * filter_file_name The name of the filter file 571 * 572 * Global variables used: 573 * filter_stderr Set if -X is on 574 */ 575 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ 576 Boolean 577 await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, Boolean send_mtool_msgs, XDR *xdrs_p, int job_msg_id) 578 #else 579 Boolean 580 await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, Boolean send_mtool_msgs, void *xdrs_p, int job_msg_id) 581 #endif 582 { 583 int status; 584 char *buffer; 585 int core_dumped; 586 int exit_status; 587 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ 588 Avo_CmdOutput *make_output_msg; 589 #endif 590 FILE *outfp; 591 register pid_t pid; 592 struct stat stat_buff; 593 int termination_signal; 594 char tmp_buf[MAXPATHLEN]; 595 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ 596 RWCollectable *xdr_msg; 597 #endif 598 599 while ((pid = wait(&status)) != running_pid) { 600 if (pid == -1) { 601 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 98, "wait() failed: %s"), errmsg(errno)); 602 } 603 } 604 (void) fflush(stdout); 605 (void) fflush(stderr); 606 607 if (status == 0) { 608 609 #ifdef PRINT_EXIT_STATUS 610 warning_mksh(NOCATGETS("I'm in await(), and status is 0.")); 611 #endif 612 613 return succeeded; 614 } 615 616 #ifdef PRINT_EXIT_STATUS 617 warning_mksh(NOCATGETS("I'm in await(), and status is *NOT* 0.")); 618 #endif 619 620 621 exit_status = WEXITSTATUS(status); 622 623 #ifdef PRINT_EXIT_STATUS 624 warning_mksh(NOCATGETS("I'm in await(), and exit_status is %d."), exit_status); 625 #endif 626 627 termination_signal = WTERMSIG(status); 628 core_dumped = WCOREDUMP(status); 629 630 /* 631 * If the child returned an error, we now try to print a 632 * nice message about it. 633 */ 634 SEND_MTOOL_MSG( 635 make_output_msg = new Avo_CmdOutput(); 636 (void) sprintf(tmp_buf, "%d", job_msg_id); 637 make_output_msg->appendOutput(strdup(tmp_buf)); 638 ); 639 640 tmp_buf[0] = (int) nul_char; 641 if (!silent_error) { 642 if (exit_status != 0) { 643 (void) fprintf(stdout, 644 catgets(libmksdmsi18n_catd, 1, 103, "*** Error code %d"), 645 exit_status); 646 SEND_MTOOL_MSG( 647 (void) sprintf(&tmp_buf[strlen(tmp_buf)], 648 catgets(libmksdmsi18n_catd, 1, 104, "*** Error code %d"), 649 exit_status); 650 ); 651 } else { 652 (void) fprintf(stdout, 653 catgets(libmksdmsi18n_catd, 1, 105, "*** Signal %d"), 654 termination_signal); 655 SEND_MTOOL_MSG( 656 (void) sprintf(&tmp_buf[strlen(tmp_buf)], 657 catgets(libmksdmsi18n_catd, 1, 106, "*** Signal %d"), 658 termination_signal); 659 ); 660 if (core_dumped) { 661 (void) fprintf(stdout, 662 catgets(libmksdmsi18n_catd, 1, 107, " - core dumped")); 663 SEND_MTOOL_MSG( 664 (void) sprintf(&tmp_buf[strlen(tmp_buf)], 665 catgets(libmksdmsi18n_catd, 1, 108, " - core dumped")); 666 ); 667 } 668 } 669 if (ignore_error) { 670 (void) fprintf(stdout, 671 catgets(libmksdmsi18n_catd, 1, 109, " (ignored)")); 672 SEND_MTOOL_MSG( 673 (void) sprintf(&tmp_buf[strlen(tmp_buf)], 674 catgets(libmksdmsi18n_catd, 1, 110, " (ignored)")); 675 ); 676 } 677 (void) fprintf(stdout, "\n"); 678 (void) fflush(stdout); 679 SEND_MTOOL_MSG( 680 make_output_msg->appendOutput(strdup(tmp_buf)); 681 ); 682 } 683 SEND_MTOOL_MSG( 684 xdr_msg = (RWCollectable*) make_output_msg; 685 xdr(xdrs_p, xdr_msg); 686 delete make_output_msg; 687 ); 688 689 #ifdef PRINT_EXIT_STATUS 690 warning_mksh(NOCATGETS("I'm in await(), returning failed.")); 691 #endif 692 693 return failed; 694 } 695 696 /* 697 * sh_command2string(command, destination) 698 * 699 * Run one sh command and capture the output from it. 700 * 701 * Return value: 702 * 703 * Parameters: 704 * command The command to run 705 * destination Where to deposit the output from the command 706 * 707 * Static variables used: 708 * 709 * Global variables used: 710 */ 711 void 712 sh_command2string(register String command, register String destination) 713 { 714 register FILE *fd; 715 register int chr; 716 int status; 717 Boolean command_generated_output = false; 718 719 command->text.p = (int) nul_char; 720 WCSTOMBS(mbs_buffer, command->buffer.start); 721 if ((fd = popen(mbs_buffer, "r")) == NULL) { 722 WCSTOMBS(mbs_buffer, command->buffer.start); 723 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 111, "Could not run command `%s' for :sh transformation"), 724 mbs_buffer); 725 } 726 while ((chr = getc(fd)) != EOF) { 727 if (chr == (int) newline_char) { 728 chr = (int) space_char; 729 } 730 command_generated_output = true; 731 append_char(chr, destination); 732 } 733 734 /* 735 * We don't want to keep the last LINE_FEED since usually 736 * the output of the 'sh:' command is used to evaluate 737 * some MACRO. ( /bin/sh and other shell add a line feed 738 * to the output so that the prompt appear in the right place. 739 * We don't need that 740 */ 741 if (command_generated_output){ 742 if ( *(destination->text.p-1) == (int) space_char) { 743 * (-- destination->text.p) = '\0'; 744 } 745 } else { 746 /* 747 * If the command didn't generate any output, 748 * set the buffer to a null string. 749 */ 750 *(destination->text.p) = '\0'; 751 } 752 753 status = pclose(fd); 754 if (status != 0) { 755 WCSTOMBS(mbs_buffer, command->buffer.start); 756 fatal_mksh(catgets(libmksdmsi18n_catd, 1, 112, "The command `%s' returned status `%d'"), 757 mbs_buffer, 758 WEXITSTATUS(status)); 759 } 760 } 761 762