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