1 /* $OpenBSD: utilities.c,v 1.12 2014/07/19 23:50:38 guenther Exp $ */ 2 /* $NetBSD: utilities.c,v 1.5 1996/02/28 21:04:21 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #define TELOPTS 34 #define TELCMDS 35 #define SLC_NAMES 36 37 #include "telnet_locl.h" 38 39 FILE *NetTrace = 0; /* Not in bss, since needs to stay */ 40 int prettydump; 41 42 /* 43 * upcase() 44 * 45 * Upcase (in place) the argument. 46 */ 47 48 void 49 upcase(argument) 50 char *argument; 51 { 52 int c; 53 54 while ((c = *argument) != 0) { 55 if (islower(c)) { 56 *argument = toupper(c); 57 } 58 argument++; 59 } 60 } 61 62 /* 63 * The following are routines used to print out debugging information. 64 */ 65 66 unsigned char NetTraceFile[MAXPATHLEN] = "(standard output)"; 67 68 void 69 SetNetTrace(file) 70 char *file; 71 { 72 if (NetTrace && NetTrace != stdout) 73 fclose(NetTrace); 74 if (file && (strcmp(file, "-") != 0)) { 75 NetTrace = fopen(file, "w"); 76 if (NetTrace) { 77 strlcpy((char *)NetTraceFile, file, sizeof(NetTraceFile)); 78 return; 79 } 80 fprintf(stderr, "Cannot open %s.\n", file); 81 } 82 NetTrace = stdout; 83 strlcpy((char *)NetTraceFile, "(standard output)", sizeof(NetTraceFile)); 84 } 85 86 void 87 Dump(direction, buffer, length) 88 char direction; 89 unsigned char *buffer; 90 int length; 91 { 92 # define BYTES_PER_LINE 32 93 # define min(x,y) ((x<y)? x:y) 94 unsigned char *pThis; 95 int offset; 96 97 offset = 0; 98 99 while (length) { 100 /* print one line */ 101 fprintf(NetTrace, "%c 0x%x\t", direction, offset); 102 pThis = buffer; 103 if (prettydump) { 104 buffer = buffer + min(length, BYTES_PER_LINE/2); 105 while (pThis < buffer) { 106 fprintf(NetTrace, "%c%.2x", 107 (((*pThis)&0xff) == 0xff) ? '*' : ' ', 108 (*pThis)&0xff); 109 pThis++; 110 } 111 length -= BYTES_PER_LINE/2; 112 offset += BYTES_PER_LINE/2; 113 } else { 114 buffer = buffer + min(length, BYTES_PER_LINE); 115 while (pThis < buffer) { 116 fprintf(NetTrace, "%.2x", (*pThis)&0xff); 117 pThis++; 118 } 119 length -= BYTES_PER_LINE; 120 offset += BYTES_PER_LINE; 121 } 122 if (NetTrace == stdout) { 123 fprintf(NetTrace, "\r\n"); 124 } else { 125 fprintf(NetTrace, "\n"); 126 } 127 if (length < 0) { 128 fflush(NetTrace); 129 return; 130 } 131 /* find next unique line */ 132 } 133 fflush(NetTrace); 134 } 135 136 137 void 138 printoption(direction, cmd, option) 139 char *direction; 140 int cmd, option; 141 { 142 if (!showoptions) 143 return; 144 if (cmd == IAC) { 145 if (TELCMD_OK(option)) 146 fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option)); 147 else 148 fprintf(NetTrace, "%s IAC %d", direction, option); 149 } else { 150 char *fmt; 151 fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : 152 (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; 153 if (fmt) { 154 fprintf(NetTrace, "%s %s ", direction, fmt); 155 if (TELOPT_OK(option)) 156 fprintf(NetTrace, "%s", TELOPT(option)); 157 else if (option == TELOPT_EXOPL) 158 fprintf(NetTrace, "EXOPL"); 159 else 160 fprintf(NetTrace, "%d", option); 161 } else 162 fprintf(NetTrace, "%s %d %d", direction, cmd, option); 163 } 164 if (NetTrace == stdout) { 165 fprintf(NetTrace, "\r\n"); 166 fflush(NetTrace); 167 } else { 168 fprintf(NetTrace, "\n"); 169 } 170 return; 171 } 172 173 void 174 optionstatus() 175 { 176 int i; 177 extern char will_wont_resp[], do_dont_resp[]; 178 179 for (i = 0; i < 256; i++) { 180 if (do_dont_resp[i]) { 181 if (TELOPT_OK(i)) 182 printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]); 183 else if (TELCMD_OK(i)) 184 printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]); 185 else 186 printf("resp DO_DONT %d: %d\n", i, 187 do_dont_resp[i]); 188 if (my_want_state_is_do(i)) { 189 if (TELOPT_OK(i)) 190 printf("want DO %s\n", TELOPT(i)); 191 else if (TELCMD_OK(i)) 192 printf("want DO %s\n", TELCMD(i)); 193 else 194 printf("want DO %d\n", i); 195 } else { 196 if (TELOPT_OK(i)) 197 printf("want DONT %s\n", TELOPT(i)); 198 else if (TELCMD_OK(i)) 199 printf("want DONT %s\n", TELCMD(i)); 200 else 201 printf("want DONT %d\n", i); 202 } 203 } else { 204 if (my_state_is_do(i)) { 205 if (TELOPT_OK(i)) 206 printf(" DO %s\n", TELOPT(i)); 207 else if (TELCMD_OK(i)) 208 printf(" DO %s\n", TELCMD(i)); 209 else 210 printf(" DO %d\n", i); 211 } 212 } 213 if (will_wont_resp[i]) { 214 if (TELOPT_OK(i)) 215 printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]); 216 else if (TELCMD_OK(i)) 217 printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]); 218 else 219 printf("resp WILL_WONT %d: %d\n", 220 i, will_wont_resp[i]); 221 if (my_want_state_is_will(i)) { 222 if (TELOPT_OK(i)) 223 printf("want WILL %s\n", TELOPT(i)); 224 else if (TELCMD_OK(i)) 225 printf("want WILL %s\n", TELCMD(i)); 226 else 227 printf("want WILL %d\n", i); 228 } else { 229 if (TELOPT_OK(i)) 230 printf("want WONT %s\n", TELOPT(i)); 231 else if (TELCMD_OK(i)) 232 printf("want WONT %s\n", TELCMD(i)); 233 else 234 printf("want WONT %d\n", i); 235 } 236 } else { 237 if (my_state_is_will(i)) { 238 if (TELOPT_OK(i)) 239 printf(" WILL %s\n", TELOPT(i)); 240 else if (TELCMD_OK(i)) 241 printf(" WILL %s\n", TELCMD(i)); 242 else 243 printf(" WILL %d\n", i); 244 } 245 } 246 } 247 248 } 249 250 void 251 printsub(direction, pointer, length) 252 char direction; /* '<' or '>' */ 253 unsigned char *pointer; /* where suboption data sits */ 254 int length; /* length of suboption data */ 255 { 256 int i; 257 extern int want_status_response; 258 259 if (showoptions || direction == 0 || 260 (want_status_response && (pointer[0] == TELOPT_STATUS))) { 261 if (direction) { 262 fprintf(NetTrace, "%s IAC SB ", 263 (direction == '<')? "RCVD":"SENT"); 264 if (length >= 3) { 265 int j; 266 267 i = pointer[length-2]; 268 j = pointer[length-1]; 269 270 if (i != IAC || j != SE) { 271 fprintf(NetTrace, "(terminated by "); 272 if (TELOPT_OK(i)) 273 fprintf(NetTrace, "%s ", TELOPT(i)); 274 else if (TELCMD_OK(i)) 275 fprintf(NetTrace, "%s ", TELCMD(i)); 276 else 277 fprintf(NetTrace, "%d ", i); 278 if (TELOPT_OK(j)) 279 fprintf(NetTrace, "%s", TELOPT(j)); 280 else if (TELCMD_OK(j)) 281 fprintf(NetTrace, "%s", TELCMD(j)); 282 else 283 fprintf(NetTrace, "%d", j); 284 fprintf(NetTrace, ", not IAC SE!) "); 285 } 286 } 287 length -= 2; 288 } 289 if (length < 1) { 290 fprintf(NetTrace, "(Empty suboption??\?)"); 291 if (NetTrace == stdout) 292 fflush(NetTrace); 293 return; 294 } 295 switch (pointer[0]) { 296 case TELOPT_TTYPE: 297 fprintf(NetTrace, "TERMINAL-TYPE "); 298 switch (pointer[1]) { 299 case TELQUAL_IS: 300 fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); 301 break; 302 case TELQUAL_SEND: 303 fprintf(NetTrace, "SEND"); 304 break; 305 default: 306 fprintf(NetTrace, 307 "- unknown qualifier %d (0x%x).", 308 pointer[1], pointer[1]); 309 } 310 break; 311 case TELOPT_TSPEED: 312 fprintf(NetTrace, "TERMINAL-SPEED"); 313 if (length < 2) { 314 fprintf(NetTrace, " (empty suboption??\?)"); 315 break; 316 } 317 switch (pointer[1]) { 318 case TELQUAL_IS: 319 fprintf(NetTrace, " IS "); 320 fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2); 321 break; 322 default: 323 if (pointer[1] == 1) 324 fprintf(NetTrace, " SEND"); 325 else 326 fprintf(NetTrace, " %d (unknown)", pointer[1]); 327 for (i = 2; i < length; i++) 328 fprintf(NetTrace, " ?%d?", pointer[i]); 329 break; 330 } 331 break; 332 333 case TELOPT_LFLOW: 334 fprintf(NetTrace, "TOGGLE-FLOW-CONTROL"); 335 if (length < 2) { 336 fprintf(NetTrace, " (empty suboption??\?)"); 337 break; 338 } 339 switch (pointer[1]) { 340 case LFLOW_OFF: 341 fprintf(NetTrace, " OFF"); break; 342 case LFLOW_ON: 343 fprintf(NetTrace, " ON"); break; 344 case LFLOW_RESTART_ANY: 345 fprintf(NetTrace, " RESTART-ANY"); break; 346 case LFLOW_RESTART_XON: 347 fprintf(NetTrace, " RESTART-XON"); break; 348 default: 349 fprintf(NetTrace, " %d (unknown)", pointer[1]); 350 } 351 for (i = 2; i < length; i++) 352 fprintf(NetTrace, " ?%d?", pointer[i]); 353 break; 354 355 case TELOPT_NAWS: 356 fprintf(NetTrace, "NAWS"); 357 if (length < 2) { 358 fprintf(NetTrace, " (empty suboption??\?)"); 359 break; 360 } 361 if (length == 2) { 362 fprintf(NetTrace, " ?%d?", pointer[1]); 363 break; 364 } 365 fprintf(NetTrace, " %d %d (%d)", 366 pointer[1], pointer[2], 367 (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); 368 if (length == 4) { 369 fprintf(NetTrace, " ?%d?", pointer[3]); 370 break; 371 } 372 fprintf(NetTrace, " %d %d (%d)", 373 pointer[3], pointer[4], 374 (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); 375 for (i = 5; i < length; i++) 376 fprintf(NetTrace, " ?%d?", pointer[i]); 377 break; 378 379 case TELOPT_LINEMODE: 380 fprintf(NetTrace, "LINEMODE "); 381 if (length < 2) { 382 fprintf(NetTrace, " (empty suboption??\?)"); 383 break; 384 } 385 switch (pointer[1]) { 386 case WILL: 387 fprintf(NetTrace, "WILL "); 388 goto common; 389 case WONT: 390 fprintf(NetTrace, "WONT "); 391 goto common; 392 case DO: 393 fprintf(NetTrace, "DO "); 394 goto common; 395 case DONT: 396 fprintf(NetTrace, "DONT "); 397 common: 398 if (length < 3) { 399 fprintf(NetTrace, "(no option??\?)"); 400 break; 401 } 402 switch (pointer[2]) { 403 case LM_FORWARDMASK: 404 fprintf(NetTrace, "Forward Mask"); 405 for (i = 3; i < length; i++) 406 fprintf(NetTrace, " %x", pointer[i]); 407 break; 408 default: 409 fprintf(NetTrace, "%d (unknown)", pointer[2]); 410 for (i = 3; i < length; i++) 411 fprintf(NetTrace, " %d", pointer[i]); 412 break; 413 } 414 break; 415 416 case LM_SLC: 417 fprintf(NetTrace, "SLC"); 418 for (i = 2; i < length - 2; i += 3) { 419 if (SLC_NAME_OK(pointer[i+SLC_FUNC])) 420 fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC])); 421 else 422 fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]); 423 switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { 424 case SLC_NOSUPPORT: 425 fprintf(NetTrace, " NOSUPPORT"); break; 426 case SLC_CANTCHANGE: 427 fprintf(NetTrace, " CANTCHANGE"); break; 428 case SLC_VARIABLE: 429 fprintf(NetTrace, " VARIABLE"); break; 430 case SLC_DEFAULT: 431 fprintf(NetTrace, " DEFAULT"); break; 432 } 433 fprintf(NetTrace, "%s%s%s", 434 pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", 435 pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", 436 pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); 437 if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| 438 SLC_FLUSHOUT| SLC_LEVELBITS)) 439 fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]); 440 fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]); 441 if ((pointer[i+SLC_VALUE] == IAC) && 442 (pointer[i+SLC_VALUE+1] == IAC)) 443 i++; 444 } 445 for (; i < length; i++) 446 fprintf(NetTrace, " ?%d?", pointer[i]); 447 break; 448 449 case LM_MODE: 450 fprintf(NetTrace, "MODE "); 451 if (length < 3) { 452 fprintf(NetTrace, "(no mode??\?)"); 453 break; 454 } 455 { 456 char tbuf[64]; 457 snprintf(tbuf, sizeof(tbuf), 458 "%s%s%s%s%s", 459 pointer[2]&MODE_EDIT ? "|EDIT" : "", 460 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", 461 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", 462 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", 463 pointer[2]&MODE_ACK ? "|ACK" : ""); 464 fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0"); 465 } 466 if (pointer[2]&~(MODE_MASK)) 467 fprintf(NetTrace, " (0x%x)", pointer[2]); 468 for (i = 3; i < length; i++) 469 fprintf(NetTrace, " ?0x%x?", pointer[i]); 470 break; 471 default: 472 fprintf(NetTrace, "%d (unknown)", pointer[1]); 473 for (i = 2; i < length; i++) 474 fprintf(NetTrace, " %d", pointer[i]); 475 } 476 break; 477 478 case TELOPT_STATUS: { 479 char *cp; 480 int j, k; 481 482 fprintf(NetTrace, "STATUS"); 483 484 switch (pointer[1]) { 485 default: 486 if (pointer[1] == TELQUAL_SEND) 487 fprintf(NetTrace, " SEND"); 488 else 489 fprintf(NetTrace, " %d (unknown)", pointer[1]); 490 for (i = 2; i < length; i++) 491 fprintf(NetTrace, " ?%d?", pointer[i]); 492 break; 493 case TELQUAL_IS: 494 if (--want_status_response < 0) 495 want_status_response = 0; 496 if (NetTrace == stdout) 497 fprintf(NetTrace, " IS\r\n"); 498 else 499 fprintf(NetTrace, " IS\n"); 500 501 for (i = 2; i < length; i++) { 502 switch(pointer[i]) { 503 case DO: cp = "DO"; goto common2; 504 case DONT: cp = "DONT"; goto common2; 505 case WILL: cp = "WILL"; goto common2; 506 case WONT: cp = "WONT"; goto common2; 507 common2: 508 i++; 509 if (TELOPT_OK((int)pointer[i])) 510 fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i])); 511 else 512 fprintf(NetTrace, " %s %d", cp, pointer[i]); 513 514 if (NetTrace == stdout) 515 fprintf(NetTrace, "\r\n"); 516 else 517 fprintf(NetTrace, "\n"); 518 break; 519 520 case SB: 521 fprintf(NetTrace, " SB "); 522 i++; 523 j = k = i; 524 while (j < length) { 525 if (pointer[j] == SE) { 526 if (j+1 == length) 527 break; 528 if (pointer[j+1] == SE) 529 j++; 530 else 531 break; 532 } 533 pointer[k++] = pointer[j++]; 534 } 535 printsub(0, &pointer[i], k - i); 536 if (i < length) { 537 fprintf(NetTrace, " SE"); 538 i = j; 539 } else 540 i = j - 1; 541 542 if (NetTrace == stdout) 543 fprintf(NetTrace, "\r\n"); 544 else 545 fprintf(NetTrace, "\n"); 546 547 break; 548 549 default: 550 fprintf(NetTrace, " %d", pointer[i]); 551 break; 552 } 553 } 554 break; 555 } 556 break; 557 } 558 559 case TELOPT_XDISPLOC: 560 fprintf(NetTrace, "X-DISPLAY-LOCATION "); 561 switch (pointer[1]) { 562 case TELQUAL_IS: 563 fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); 564 break; 565 case TELQUAL_SEND: 566 fprintf(NetTrace, "SEND"); 567 break; 568 default: 569 fprintf(NetTrace, "- unknown qualifier %d (0x%x).", 570 pointer[1], pointer[1]); 571 } 572 break; 573 574 case TELOPT_NEW_ENVIRON: 575 fprintf(NetTrace, "NEW-ENVIRON "); 576 switch (pointer[1]) { 577 case TELQUAL_IS: 578 fprintf(NetTrace, "IS "); 579 goto env_common; 580 case TELQUAL_SEND: 581 fprintf(NetTrace, "SEND "); 582 goto env_common; 583 case TELQUAL_INFO: 584 fprintf(NetTrace, "INFO "); 585 env_common: 586 { 587 int noquote = 2; 588 for (i = 2; i < length; i++ ) { 589 switch (pointer[i]) { 590 case NEW_ENV_VALUE: 591 fprintf(NetTrace, "\" VALUE " + noquote); 592 noquote = 2; 593 break; 594 595 case NEW_ENV_VAR: 596 fprintf(NetTrace, "\" VAR " + noquote); 597 noquote = 2; 598 break; 599 600 case ENV_ESC: 601 fprintf(NetTrace, "\" ESC " + noquote); 602 noquote = 2; 603 break; 604 605 case ENV_USERVAR: 606 fprintf(NetTrace, "\" USERVAR " + noquote); 607 noquote = 2; 608 break; 609 610 default: 611 if (isprint(pointer[i]) && pointer[i] != '"') { 612 if (noquote) { 613 putc('"', NetTrace); 614 noquote = 0; 615 } 616 putc(pointer[i], NetTrace); 617 } else { 618 fprintf(NetTrace, "\" %03o " + noquote, 619 pointer[i]); 620 noquote = 2; 621 } 622 break; 623 } 624 } 625 if (!noquote) 626 putc('"', NetTrace); 627 break; 628 } 629 } 630 break; 631 632 default: 633 if (TELOPT_OK(pointer[0])) 634 fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0])); 635 else 636 fprintf(NetTrace, "%d (unknown)", pointer[0]); 637 for (i = 1; i < length; i++) 638 fprintf(NetTrace, " %d", pointer[i]); 639 break; 640 } 641 if (direction) { 642 if (NetTrace == stdout) 643 fprintf(NetTrace, "\r\n"); 644 else 645 fprintf(NetTrace, "\n"); 646 } 647 if (NetTrace == stdout) 648 fflush(NetTrace); 649 } 650 } 651 652 /* EmptyTerminal - called to make sure that the terminal buffer is empty. 653 * Note that we consider the buffer to run all the 654 * way to the kernel (thus the poll). 655 */ 656 657 void 658 EmptyTerminal() 659 { 660 struct pollfd pfd[1]; 661 662 pfd[0].fd = tout; 663 pfd[0].events = POLLOUT; 664 665 if (TTYBYTES() == 0) { 666 (void) poll(pfd, 1, -1); /* wait for TTLOWAT */ 667 } else { 668 while (TTYBYTES()) { 669 (void) ttyflush(0); 670 (void) poll(pfd, 1, -1); /* wait for TTLOWAT */ 671 } 672 } 673 } 674 675 void 676 SetForExit() 677 { 678 setconnmode(0); 679 do { 680 (void)telrcv(); /* Process any incoming data */ 681 EmptyTerminal(); 682 } while (ring_full_count(&netiring)); /* While there is any */ 683 setcommandmode(); 684 fflush(stdout); 685 fflush(stderr); 686 setconnmode(0); 687 EmptyTerminal(); /* Flush the path to the tty */ 688 setcommandmode(); 689 } 690 691 void 692 Exit(returnCode) 693 int returnCode; 694 { 695 SetForExit(); 696 exit(returnCode); 697 } 698 699 void 700 ExitString(string, returnCode) 701 char *string; 702 int returnCode; 703 { 704 SetForExit(); 705 fwrite(string, 1, strlen(string), stderr); 706 exit(returnCode); 707 } 708