1: static char *sccsid = "@(#)sh.c 4.2 3/11/81"; 2: 3: #include "sh.h" 4: #include <sys/ioctl.h> 5: /* 6: * C Shell 7: * 8: * Bill Joy, UC Berkeley, California, USA 9: * October 1978, May 1980 10: * 11: * Jim Kulp, IIASA, Laxenburg, Austria 12: * April 1980 13: */ 14: 15: char *pathlist[] = { ".", "/usr/ucb", "/bin", "/usr/bin", 0 }; 16: char HIST = '!'; 17: char HISTSUB = '^'; 18: bool nofile; 19: bool reenter; 20: bool nverbose; 21: bool nexececho; 22: bool quitit; 23: bool fast; 24: bool prompt = 1; 25: 26: main(c, av) 27: int c; 28: char **av; 29: { 30: register char **v, *cp; 31: register int f; 32: struct ltchars ltc; 33: 34: settimes(); /* Immed. estab. timing base */ 35: erf = open("/tmp/errors",2); /* open error file */ 36: v = av; 37: if (eq(v[0], "a.out")) /* A.out's are quittable */ 38: quitit = 1; 39: uid = getuid(); 40: loginsh = **v == '-'; 41: if (loginsh) 42: time(&chktim); 43: 44: /* 45: * Move the descriptors to safe places. 46: * The variable didfds is 0 while we have only FSH* to work with. 47: * When didfds is true, we have 0,1,2 and prefer to use these. 48: */ 49: initdesc(); 50: 51: /* 52: * Initialize the shell variables. 53: * ARGV and PROMPT are initialized later. 54: * STATUS is also munged in several places. 55: * CHILD is munged when forking/waiting 56: */ 57: 58: set("status", "0"); 59: dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a 60: * login shell */ 61: if (cp == NOSTR) 62: fast++; /* No home -> can't read scripts */ 63: else 64: set("home", savestr(cp)); 65: /* 66: * Grab other useful things from the environment. 67: * Should we grab everything?? 68: */ 69: if ((cp = getenv("USER")) != NOSTR) 70: set("user", savestr(cp)); 71: if ((cp = getenv("TERM")) != NOSTR) 72: set("term", savestr(cp)); 73: /* 74: * Re-initialize path if set in environment 75: */ 76: if ((cp = getenv("PATH")) == NOSTR) 77: set1("path", saveblk(pathlist), &shvhed); 78: else { 79: register unsigned i = 0; 80: register char *dp; 81: register char **pv; 82: 83: for (dp = cp; *dp; dp++) 84: if (*dp == ':') 85: i++; 86: pv = (char **)calloc(i+2, sizeof (char **)); 87: for (dp = cp, i = 0; ;) 88: if (*dp == ':') { 89: *dp = 0; 90: pv[i++] = savestr(*cp ? cp : "."); 91: *dp++ = ':'; 92: cp = dp; 93: } else if (*dp++ == 0) { 94: pv[i++] = savestr(*cp ? cp : "."); 95: break; 96: } 97: pv[i] = 0; 98: set1("path", pv, &shvhed); 99: } 100: set("shell", SHELLPATH); 101: 102: doldol = putn(getpid()); /* For $$ */ 103: shtemp = strspl("/tmp/sh", doldol); /* For << */ 104: 105: /* 106: * Record the interrupt states from the parent process. 107: * If the parent is non-interruptible our hand must be forced 108: * or we (and our children) won't be either. 109: * Our children inherit termination from our parent. 110: * We catch it only if we are the login shell. 111: */ 112: parintr = signal(SIGINT, SIG_IGN); /* parents interruptibility */ 113: sigset(SIGINT, parintr); /* ... restore */ 114: parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */ 115: signal(SIGTERM, parterm); /* ... restore */ 116: 117: /* 118: * Process the arguments. 119: * 120: * Note that processing of -v/-x is actually delayed till after 121: * script processing. 122: * 123: * We set the first character of our name to be '-' if we are 124: * a shell running interruptible commands. Many programs which 125: * examine ps'es use this to filter such shells out. 126: */ 127: c--, v++; 128: while (c > 0 && (cp = v[0])[0] == '-') { 129: do switch (*cp++) { 130: 131: case 0: /* - Interruptible, no prompt */ 132: prompt = 0; 133: setintr++; 134: nofile++; 135: break; 136: 137: case 'c': /* -c Command input from arg */ 138: if (c == 1) 139: exit(0); 140: c--, v++; 141: arginp = v[0]; 142: prompt = 0; 143: nofile++; 144: break; 145: 146: case 'e': /* -e Exit on any error */ 147: exiterr++; 148: break; 149: 150: case 'f': /* -f Fast start */ 151: fast++; 152: break; 153: 154: case 'i': /* -i Interactive, even if !intty */ 155: intact++; 156: nofile++; 157: break; 158: 159: case 'n': /* -n Don't execute */ 160: noexec++; 161: break; 162: 163: case 'q': /* -q (Undoc'd) ... die on quit */ 164: quitit = 1; 165: break; 166: 167: case 's': /* -s Read from std input */ 168: nofile++; 169: break; 170: 171: case 't': /* -t Read one line from input */ 172: onelflg = 2; 173: prompt = 0; 174: nofile++; 175: break; 176: 177: case 'v': /* -v Echo hist expanded input */ 178: nverbose = 1; /* ... later */ 179: break; 180: 181: case 'x': /* -x Echo just before execution */ 182: nexececho = 1; /* ... later */ 183: break; 184: 185: case 'V': /* -V Echo hist expanded input */ 186: setNS("verbose"); /* NOW! */ 187: break; 188: 189: case 'X': /* -X Echo just before execution */ 190: setNS("echo"); /* NOW! */ 191: break; 192: 193: } while (*cp); 194: v++, c--; 195: } 196: 197: if (quitit) /* With all due haste, for debugging */ 198: signal(SIGQUIT, SIG_DFL); 199: 200: /* 201: * Unless prevented by -, -c, -i, -s, or -t, if there 202: * are remaining arguments the first of them is the name 203: * of a shell file from which to read commands. 204: */ 205: if (nofile == 0 && c > 0) { 206: nofile = open(v[0], 0); 207: if (nofile < 0) { 208: child++; /* So this ... */ 209: Perror(v[0]); /* ... doesn't return */ 210: } 211: file = v[0]; 212: SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 213: prompt = 0; 214: c--, v++; 215: } 216: /* 217: * Consider input a tty if it really is or we are interactive. 218: */ 219: intty = intact || isatty(SHIN); 220: /* 221: * Decide whether we should play with signals or not. 222: * If we are explicitly told (via -i, or -) or we are a login 223: * shell (arg0 starts with -) or the input and output are both 224: * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 225: * Note that in only the login shell is it likely that parent 226: * may have set signals to be ignored 227: */ 228: if (loginsh || intact || intty && isatty(SHOUT)) 229: setintr = 1; 230: #ifdef TELL 231: settell(); 232: #endif 233: /* 234: * Save the remaining arguments in argv. 235: */ 236: setq("argv", v, &shvhed); 237: 238: /* 239: * Set up the prompt. 240: */ 241: if (prompt) 242: set("prompt", uid == 0 ? "# " : "% "); 243: 244: /* 245: * If we are an interactive shell, then start fiddling 246: * with the signals; this is a tricky game. 247: */ 248: shpgrp = getpgrp(0); 249: opgrp = tpgrp = -1; 250: oldisc = -1; 251: if (setintr) { 252: er("intr\n"); 253: **av = '-'; 254: if (!quitit) 255: signal(SIGQUIT, SIG_IGN); 256: sigset(SIGINT, pintr); 257: sighold(SIGINT); 258: signal(SIGTERM, SIG_IGN); 259: if (quitit == 0 && arginp == 0) { 260: signal(SIGTSTP, SIG_IGN); 261: signal(SIGTTIN, SIG_IGN); 262: signal(SIGTTOU, SIG_IGN); 263: /* 264: * Wait till in foreground, in case someone 265: * stupidly runs 266: * csh & 267: * dont want to try to grab away the tty. 268: */ 269: if (isatty(FSHDIAG)) 270: f = FSHDIAG; 271: else if (isatty(FSHOUT)) 272: f = FSHOUT; 273: else if (isatty(OLDSTD)) 274: f = OLDSTD; 275: else 276: f = -1; 277: retry: 278: if (ioctl(f, TIOCGPGRP, &tpgrp) == 0 && tpgrp != -1) { 279: int ldisc; 280: if (tpgrp != shpgrp) { 281: int old = sigsys(SIGTTIN, SIG_DFL); 282: kill(0, SIGTTIN); 283: sigsys(SIGTTIN, old); 284: goto retry; 285: } 286: if (ioctl(f, TIOCGETD, &oldisc) != 0) 287: goto notty; 288: if (oldisc != NTTYDISC) { 289: if (!loginsh) 290: printf("Switching to new tty driver...\n"); 291: ldisc = NTTYDISC; 292: ioctl(f, TIOCSETD, &ldisc); 293: } else 294: oldisc = -1; 295: if ((ioctl(f, TIOCGLTC, <c) == 0) && 296: (ltc.t_suspc != '\377')) 297: setstop = 1; 298: opgrp = shpgrp; 299: shpgrp = getpid(); 300: tpgrp = shpgrp; 301: ioctl(f, TIOCSPGRP, &shpgrp); 302: setpgrp(0, shpgrp); 303: dcopy(f, FSHTTY); 304: ioctl(FSHTTY, FIOCLEX, 0); 305: } else { 306: notty: 307: printf("Warning: no access to tty; thus no job control in this shell...\n"); 308: tpgrp = -1; 309: } 310: } 311: } 312: sigset(SIGCHLD, pchild); /* while signals not ready */ 313: 314: /* 315: * Set an exit here in case of an interrupt or error reading 316: * the shell start-up scripts. 317: */ 318: setexit(); 319: haderr = 0; /* In case second time through */ 320: if (!fast && reenter == 0) { 321: reenter++; 322: /* Will have value("home") here because set fast if don't */ 323: srccat(value("home"), "/.cshrc"); 324: if (!fast && !arginp && !onelflg) 325: dohash(); 326: if (loginsh) { 327: int ldisc; 328: srccat(value("home"), "/.login"); 329: } 330: } 331: 332: /* 333: * Now are ready for the -v and -x flags 334: */ 335: if (nverbose) 336: setNS("verbose"); 337: if (nexececho) 338: setNS("echo"); 339: 340: /* 341: * All the rest of the world is inside this call. 342: * The argument to process indicates whether it should 343: * catch "error unwinds". Thus if we are a interactive shell 344: * our call here will never return by being blown past on an error. 345: */ 346: process(setintr); 347: 348: /* 349: * Mop-up. 350: */ 351: if (loginsh) { 352: printf("logout\n"); 353: close(SHIN); 354: child++; 355: goodbye(); 356: } 357: exitstat(); 358: } 359: 360: untty() 361: { 362: 363: if (tpgrp > 0) { 364: setpgrp(0, opgrp); 365: ioctl(FSHTTY, TIOCSPGRP, &opgrp); 366: if (oldisc != -1 && oldisc != NTTYDISC) { 367: printf("\nReverting to old tty driver...\n"); 368: ioctl(FSHTTY, TIOCSETD, &oldisc); 369: } 370: } 371: } 372: 373: importpath(cp) 374: char *cp; 375: { 376: register int i = 0; 377: register char *dp; 378: register char **pv; 379: int c; 380: static char dot[2] = {'.', 0}; 381: 382: for (dp = cp; *dp; dp++) 383: if (*dp == ':') 384: i++; 385: /* 386: * i+2 where i is the number of colons in the path. 387: * There are i+1 directories in the path plus we need 388: * room for a zero terminator. 389: */ 390: pv = (char **) calloc(i+2, sizeof (char **)); 391: dp = cp; 392: i = 0; 393: if (*dp) 394: for (;;) { 395: if ((c = *dp) == ':' || c == 0) { 396: *dp = 0; 397: pv[i++] = savestr(*cp ? cp : dot); 398: if (c) { 399: cp = dp + 1; 400: *dp = ':'; 401: } else 402: break; 403: } 404: dp++; 405: } 406: pv[i] = 0; 407: set1("path", pv, &shvhed); 408: } 409: 410: /* 411: * Source to the file which is the catenation of the argument names. 412: */ 413: srccat(cp, dp) 414: char *cp, *dp; 415: { 416: register char *ep = strspl(cp, dp); 417: register int unit = dmove(open(ep, 0), -1); 418: 419: /* ioctl(unit, FIOCLEX, NULL); */ 420: xfree(ep); 421: srcunit(unit, 0); 422: } 423: 424: /* 425: * Source to a unit. If onlyown it must be our file or our group or 426: * we don't chance it. This occurs on ".cshrc"s and the like. 427: */ 428: srcunit(unit, onlyown) 429: register int unit; 430: bool onlyown; 431: { 432: /* We have to push down a lot of state here */ 433: /* All this could go into a structure */ 434: int oSHIN = -1, oldintty = intty; 435: struct whyle *oldwhyl = whyles; 436: char *ogointr = gointr, *oarginp = arginp; 437: char *oevalp = evalp, **oevalvec = evalvec; 438: int oonelflg = onelflg; 439: #ifdef TELL 440: bool otell = cantell; 441: #endif 442: struct Bin saveB; 443: 444: /* The (few) real local variables */ 445: jmp_buf oldexit; 446: int reenter; 447: 448: if (unit < 0) 449: return; 450: if (didfds) 451: donefds(); 452: if (onlyown) { 453: struct stat stb; 454: 455: if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_gid != getgid())) { 456: close(unit); 457: return; 458: } 459: } 460: 461: /* 462: * There is a critical section here while we are pushing down the 463: * input stream since we have stuff in different structures. 464: * If we weren't careful an interrupt could corrupt SHIN's Bin 465: * structure and kill the shell. 466: * 467: * We could avoid the critical region by grouping all the stuff 468: * in a single structure and pointing at it to move it all at 469: * once. This is less efficient globally on many variable references 470: * however. 471: */ 472: getexit(oldexit); 473: reenter = 0; 474: if (setintr) 475: sighold(SIGINT); 476: setexit(); 477: reenter++; 478: if (reenter == 1) { 479: /* Setup the new values of the state stuff saved above */ 480: copy((char *)&saveB, (char *)&B, sizeof saveB); 481: fbuf = (char **) 0; 482: fseekp = feobp = fblocks = 0; 483: oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 484: intty = isatty(SHIN), whyles = 0, gointr = 0; 485: evalvec = 0; evalp = 0; 486: /* 487: * Now if we are allowing commands to be interrupted, 488: * we let ourselves be interrupted. 489: */ 490: if (setintr) 491: sigrelse(SIGINT); 492: #ifdef TELL 493: settell(); 494: #endif 495: process(0); /* 0 -> blow away on errors */ 496: } 497: if (setintr) 498: sigrelse(SIGINT); 499: if (oSHIN >= 0) { 500: register int i; 501: 502: /* We made it to the new state... free up its storage */ 503: /* This code could get run twice but xfree doesn't care */ 504: for (i = 0; i < fblocks; i++) 505: xfree(fbuf[i]); 506: xfree((char *)fbuf); 507: 508: /* Reset input arena */ 509: copy((char *)&B, (char *)&saveB, sizeof B); 510: 511: close(SHIN), SHIN = oSHIN; 512: arginp = oarginp, onelflg = oonelflg; 513: evalp = oevalp, evalvec = oevalvec; 514: intty = oldintty, whyles = oldwhyl, gointr = ogointr; 515: #ifdef TELL 516: cantell = otell; 517: #endif 518: } 519: 520: resexit(oldexit); 521: /* 522: * If process reset() (effectively an unwind) then 523: * we must also unwind. 524: */ 525: if (reenter >= 2) 526: error(NOSTR); 527: } 528: 529: goodbye() 530: { 531: 532: if (loginsh) { 533: signal(SIGQUIT, SIG_IGN); 534: sigset(SIGINT, SIG_IGN); 535: signal(SIGTERM, SIG_IGN); 536: setintr = 0; /* No interrupts after "logout" */ 537: if (adrof("home")) 538: srccat(value("home"), "/.logout"); 539: } 540: exitstat(); 541: } 542: 543: exitstat() 544: { 545: 546: /* 547: * Note that if STATUS is corrupted (i.e. getn bombs) 548: * then error will exit directly because we poke child here. 549: * Otherwise we might continue unwarrantedly (sic). 550: */ 551: child++; 552: exit(getn(value("status"))); 553: } 554: 555: char *jobargv[2] = { "jobs", 0 }; 556: /* 557: * Catch an interrupt, e.g. during lexical input. 558: * If we are an interactive shell, we reset the interrupt catch 559: * immediately. In any case we drain the shell output, 560: * and finally go through the normal error mechanism, which 561: * gets a chance to make the shell go away. 562: */ 563: pintr() 564: { 565: pintr1(1); 566: } 567: 568: pintr1(wantnl) 569: bool wantnl; 570: { 571: register char **v; 572: 573: if (setintr) { 574: sigrelse(SIGINT); 575: if (pjobs) { 576: pjobs = 0; 577: printf("\n"); 578: dojobs(jobargv); 579: bferr("Interrupted"); 580: } 581: } 582: if (setintr) 583: sighold(SIGINT); 584: sigrelse(SIGCHLD); 585: draino(); 586: 587: /* 588: * If we have an active "onintr" then we search for the label. 589: * Note that if one does "onintr -" then we shan't be interruptible 590: * so we needn't worry about that here. 591: */ 592: if (gointr) { 593: search(ZGOTO, 0, gointr); 594: timflg = 0; 595: if (v = pargv) 596: pargv = 0, blkfree(v); 597: if (v = gargv) 598: gargv = 0, blkfree(v); 599: reset(); 600: } else if (intty && wantnl) 601: printf("\n"); /* Some like this, others don't */ 602: error(NOSTR); 603: } 604: 605: /* 606: * Process is the main driving routine for the shell. 607: * It runs all command processing, except for those within { ... } 608: * in expressions (which is run by a routine evalav in sh.exp.c which 609: * is a stripped down process), and `...` evaluation which is run 610: * also by a subset of this code in sh.glob.c in the routine backeval. 611: * 612: * The code here is a little strange because part of it is interruptible 613: * and hence freeing of structures appears to occur when none is necessary 614: * if this is ignored. 615: * 616: * Note that if catch is not set then we will unwind on any error. 617: * If an end-of-file occurs, we return. 618: */ 619: process(catch) 620: bool catch; 621: { 622: register char *cp; 623: jmp_buf osetexit; 624: struct command *t; 625: 626: getexit(osetexit); 627: for (;;) { 628: pendjob(); 629: paraml.next = paraml.prev = ¶ml; 630: paraml.word = ""; 631: t = 0; 632: setexit(); 633: justpr = 0; /* A chance to execute */ 634: 635: /* 636: * Interruptible during interactive reads 637: */ 638: if (setintr) 639: sigrelse(SIGINT); 640: 641: /* 642: * For the sake of reset() 643: */ 644: freelex(¶ml), freesyn(t), t = 0; 645: 646: if (haderr) { 647: if (!catch) { 648: /* unwind */ 649: doneinp = 0; 650: resexit(osetexit); 651: reset(); 652: } 653: haderr = 0; 654: /* 655: * Every error is eventually caught here or 656: * the shell dies. It is at this 657: * point that we clean up any left-over open 658: * files, by closing all but a fixed number 659: * of pre-defined files. Thus routines don't 660: * have to worry about leaving files open due 661: * to deeper errors... they will get closed here. 662: */ 663: closem(); 664: continue; 665: } 666: if (doneinp) { 667: doneinp = 0; 668: break; 669: } 670: if (chkstop) 671: chkstop--; 672: if (neednote) 673: pnote(); 674: if (intty && evalvec == 0) { 675: mailchk(); 676: /* 677: * If we are at the end of the input buffer 678: * then we are going to read fresh stuff. 679: * Otherwise, we are rereading input and don't 680: * need or want to prompt. 681: */ 682: if ((fseekp == feobp) && prompt) 683: if (!whyles) 684: for (cp = value("prompt"); *cp; cp++) 685: if (*cp == HIST) 686: printf("%d", eventno + 1); 687: else { 688: if (*cp == '\\' && cp[1] == HIST) 689: cp++; 690: putchar(*cp | QUOTE); 691: } 692: else 693: /* 694: * Prompt for forward reading loop 695: * body content. 696: */ 697: printf("? "); 698: flush(); 699: } 700: err = 0; 701: 702: /* 703: * Echo not only on VERBOSE, but also with history expansion. 704: * If there is a lexical error then we forego history echo. 705: */ 706: if (lex(¶ml) && !err && intty || adrof("verbose")) { 707: haderr = 1; 708: prlex(¶ml); 709: haderr = 0; 710: } 711: 712: /* 713: * The parser may lose space if interrupted. 714: */ 715: if (setintr) 716: sighold(SIGINT); 717: 718: /* 719: * Save input text on the history list if it 720: * is from the terminal at the top level and not 721: * in a loop. 722: */ 723: if (catch && intty && !whyles) 724: savehist(¶ml); 725: 726: /* 727: * Print lexical error messages. 728: */ 729: if (err) 730: error(err); 731: 732: /* 733: * If had a history command :p modifier then 734: * this is as far as we should go 735: */ 736: if (justpr) 737: reset(); 738: 739: alias(¶ml); 740: 741: /* 742: * Parse the words of the input into a parse tree. 743: */ 744: t = syntax(paraml.next, ¶ml, 0); 745: if (err) 746: error(err); 747: 748: /* 749: * Execute the parse tree 750: */ 751: execute(t, tpgrp); 752: 753: /* 754: * Made it! 755: */ 756: freelex(¶ml), freesyn(t); 757: } 758: resexit(osetexit); 759: } 760: 761: dosource(t) 762: register char **t; 763: { 764: register char *f; 765: register int u; 766: 767: t++; 768: f = globone(*t); 769: u = dmove(open(f, 0), -1); 770: xfree(f); 771: if (u < 0) 772: Perror(f); 773: srcunit(u, 0); 774: } 775: 776: /* 777: * Check for mail. 778: * If we are a login shell, then we don't want to tell 779: * about any mail file unless its been modified 780: * after the time we started. 781: * This prevents us from telling the user things he already 782: * knows, since the login program insists on saying 783: * "You have mail." 784: */ 785: mailchk() 786: { 787: register struct varent *v; 788: register char **vp; 789: time_t t; 790: int intvl, cnt; 791: struct stat stb; 792: bool new; 793: 794: v = adrof("mail"); 795: if (v == 0) 796: return; 797: time(&t); 798: vp = v->vec; 799: cnt = blklen(vp); 800: intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 801: if (intvl < 1) 802: intvl = 1; 803: if (chktim + intvl > t) 804: return; 805: for (; *vp; vp++) { 806: if (stat(*vp, &stb) < 0) 807: continue; 808: new = stb.st_mtime > time0; 809: if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 810: (stb.st_atime < chktim && stb.st_mtime < chktim) || 811: loginsh && !new) 812: continue; 813: if (cnt == 1) 814: printf("You have %smail.\n", new ? "new " : ""); 815: else 816: printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); 817: } 818: chktim = t; 819: } 820: 821: #include <pwd.h> 822: /* 823: * Extract a home directory from the password file 824: * The argument points to a buffer where the name of the 825: * user whose home directory is sought is currently. 826: * We write the home directory of the user back there. 827: */ 828: gethdir(home) 829: char *home; 830: { 831: register struct passwd *pp = getpwnam(home); 832: 833: if (pp == 0) 834: return (1); 835: strcpy(home, pp->pw_dir); 836: return (0); 837: } 838: 839: /* 840: * Move the initial descriptors to their eventual 841: * resting places, closin all other units. 842: */ 843: initdesc() 844: { 845: 846: didcch = 0; /* Havent closed for child */ 847: didfds = 0; /* 0, 1, 2 aren't set up */ 848: SHIN = dcopy(0, FSHIN); 849: SHOUT = dcopy(1, FSHOUT); 850: SHDIAG = dcopy(2, FSHDIAG); 851: OLDSTD = dcopy(SHIN, FOLDSTD); 852: closem(); 853: } 854: 855: exit(i) 856: int i; 857: { 858: 859: untty(); 860: #ifdef PROF 861: IEH3exit(i); 862: #else 863: _exit(i); 864: #endif 865: }