1: /* Copyright (c) 1979 Regents of the University of California */ 2: #include "ex.h" 3: #include "ex_argv.h" 4: #include "ex_temp.h" 5: #include "ex_tty.h" 6: 7: #ifdef TRACE 8: char tttrace[] = { '/','d','e','v','/','t','t','y','x','x',0 }; 9: #endif 10: 11: /* 12: * The code for ex is divided as follows: 13: * 14: * ex.c Entry point and routines handling interrupt, hangup 15: * signals; initialization code. 16: * 17: * ex_addr.c Address parsing routines for command mode decoding. 18: * Routines to set and check address ranges on commands. 19: * 20: * ex_cmds.c Command mode command decoding. 21: * 22: * ex_cmds2.c Subroutines for command decoding and processing of 23: * file names in the argument list. Routines to print 24: * messages and reset state when errors occur. 25: * 26: * ex_cmdsub.c Subroutines which implement command mode functions 27: * such as append, delete, join. 28: * 29: * ex_data.c Initialization of options. 30: * 31: * ex_get.c Command mode input routines. 32: * 33: * ex_io.c General input/output processing: file i/o, unix 34: * escapes, filtering, source commands, preserving 35: * and recovering. 36: * 37: * ex_put.c Terminal driving and optimizing routines for low-level 38: * output (cursor-positioning); output line formatting 39: * routines. 40: * 41: * ex_re.c Global commands, substitute, regular expression 42: * compilation and execution. 43: * 44: * ex_set.c The set command. 45: * 46: * ex_subr.c Loads of miscellaneous subroutines. 47: * 48: * ex_temp.c Editor buffer routines for main buffer and also 49: * for named buffers (Q registers if you will.) 50: * 51: * ex_tty.c Terminal dependent initializations from termcap 52: * data base, grabbing of tty modes (at beginning 53: * and after escapes). 54: * 55: * ex_v*.c Visual/open mode routines... see ex_v.c for a 56: * guide to the overall organization. 57: */ 58: 59: /* 60: * Main procedure. Process arguments and then 61: * transfer control to the main command processing loop 62: * in the routine commands. We are entered as either "ex", "edit" or "vi" 63: * and the distinction is made here. Actually, we are "vi" if 64: * there is a 'v' in our name, and "edit" if there is a 'd' in our 65: * name. For edit we just diddle options; for vi we actually 66: * force an early visual command, setting the external initev so 67: * the q command in visual doesn't give command mode. 68: */ 69: main(ac, av) 70: register int ac; 71: register char *av[]; 72: { 73: char *erpath = EXSTRINGS; 74: register char *cp; 75: register int c; 76: bool recov = 0; 77: bool ivis; 78: bool itag = 0; 79: bool fast = 0; 80: #ifdef TRACE 81: register char *tracef; 82: #endif 83: 84: /* 85: * Immediately grab the tty modes so that we wont 86: * get messed up if an interrupt comes in quickly. 87: */ 88: gTTY(1); 89: #ifndef USG3TTY 90: normf = tty.sg_flags; 91: #else 92: normf = tty; 93: #endif 94: ppid = getpid(); 95: 96: /* 97: * If a 'd' in our name, then set options for edit. 98: */ 99: ivis = any('v', av[0]); /* vi */ 100: #ifdef RDONLY 101: if (any('w', av[0])) /* view */ 102: value(READONLY) = 1; 103: #endif 104: if (any('d', av[0])) { /* edit */ 105: value(OPEN) = 0; 106: value(REPORT) = 1; 107: value(MAGIC) = 0; 108: } 109: 110: /* 111: * Open the error message file. 112: */ 113: draino(); 114: erfile = open(erpath, 0); 115: pstop(); 116: 117: /* 118: * Initialize interrupt handling. 119: */ 120: oldhup = signal(SIGHUP, SIG_IGN); 121: if (oldhup == SIG_DFL) 122: signal(SIGHUP, onhup); 123: oldquit = signal(SIGQUIT, SIG_IGN); 124: ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL; 125: if (signal(SIGTERM, SIG_IGN) == SIG_DFL) 126: signal(SIGTERM, onhup); 127: 128: /* 129: * Initialize end of core pointers. 130: * Normally we avoid breaking back to fendcore after each 131: * file since this can be expensive (much core-core copying). 132: * If your system can scatter load processes you could do 133: * this as ed does, saving a little core, but it will probably 134: * not often make much difference. 135: */ 136: fendcore = (line *) sbrk(0); 137: endcore = fendcore - 2; 138: 139: /* 140: * Process flag arguments. 141: */ 142: ac--, av++; 143: while (ac && av[0][0] == '-') { 144: c = av[0][1]; 145: if (c == 0) { 146: hush = 1; 147: value(AUTOPRINT) = 0; 148: fast++; 149: } else switch (c) { 150: 151: #ifdef TRACE 152: case 'T': 153: if (av[0][2] == 0) 154: tracef = "trace"; 155: else { 156: tracef = tttrace; 157: tracef[8] = av[0][2]; 158: if (tracef[8]) 159: tracef[9] = av[0][3]; 160: else 161: tracef[9] = 0; 162: } 163: trace = fopen(tracef, "w"); 164: if (trace == NULL) 165: printf("Trace create error\n"); 166: setbuf(trace, tracbuf); 167: break; 168: 169: #endif 170: 171: #ifdef RDONLY 172: case 'R': 173: value(READONLY) = 1; 174: break; 175: #endif 176: 177: #ifdef LISPCODE 178: case 'l': 179: value(LISP) = 1; 180: value(SHOWMATCH) = 1; 181: break; 182: #endif 183: 184: case 'r': 185: recov++; 186: break; 187: 188: #ifdef TAGSCODE 189: case 't': 190: if (ac > 1 && av[1][0] != '-') { 191: ac--, av++; 192: itag = 1; 193: /* BUG: should check for too long tag. */ 194: CP(lasttag, av[0]); 195: } 196: break; 197: #endif 198: 199: case 'v': 200: ivis = 1; 201: break; 202: 203: default: 204: smerror("Unknown option %s\n", av[0]); 205: break; 206: } 207: ac--, av++; 208: } 209: if (ac && av[0][0] == '+') { 210: firstpat = &av[0][1]; 211: ac--, av++; 212: } 213: 214: /* 215: * If we are doing a recover and no filename 216: * was given, then execute an exrecover command with 217: * the -r option to type out the list of saved file names. 218: * Otherwise set the remembered file name to the first argument 219: * file name so the "recover" initial command will find it. 220: */ 221: if (recov) { 222: if (ac == 0) { 223: ppid = 0; 224: setrupt(); 225: execl(EXRECOVER, "exrecover", "-r", 0); 226: filioerr(EXRECOVER); 227: exit(1); 228: } 229: CP(savedfile, *av++), ac--; 230: } 231: 232: /* 233: * Initialize the argument list. 234: */ 235: argv0 = av; 236: argc0 = ac; 237: args0 = av[0]; 238: erewind(); 239: 240: /* 241: * Initialize a temporary file (buffer) and 242: * set up terminal environment. Read user startup commands. 243: */ 244: init(); 245: if (setexit() == 0) { 246: setrupt(); 247: intty = isatty(0); 248: value(PROMPT) = intty; 249: if (cp = getenv("SHELL")) 250: CP(shell, cp); 251: if (fast || !intty) 252: setterm("dumb"); 253: else { 254: gettmode(); 255: if ((cp = getenv("TERM")) != 0 && *cp) 256: setterm(cp); 257: } 258: } 259: if (setexit() == 0 && !fast && intty) 260: if ((globp = getenv("EXINIT")) && *globp) 261: commands(1, 1); 262: else if ((cp = getenv("HOME")) != 0 && *cp) 263: source(strcat(strcpy(genbuf, cp), "/.exrc"), 1); 264: 265: /* 266: * Initial processing. Handle tag, recover, and file argument 267: * implied next commands. If going in as 'vi', then don't do 268: * anything, just set initev so we will do it later (from within 269: * visual). 270: */ 271: if (setexit() == 0) { 272: if (recov) 273: globp = "recover"; 274: else if (itag) 275: globp = ivis ? "tag" : "tag|p"; 276: else if (argc) 277: globp = "next"; 278: if (ivis) 279: initev = globp; 280: else if (globp) { 281: inglobal = 1; 282: commands(1, 1); 283: inglobal = 0; 284: } 285: } 286: 287: /* 288: * Vi command... go into visual. 289: * Strange... everything in vi usually happens 290: * before we ever "start". 291: */ 292: if (ivis) { 293: /* 294: * Don't have to be upward compatible with stupidity 295: * of starting editing at line $. 296: */ 297: if (dol > zero) 298: dot = one; 299: globp = "visual"; 300: if (setexit() == 0) 301: commands(1, 1); 302: } 303: 304: /* 305: * Clear out trash in state accumulated by startup, 306: * and then do the main command loop for a normal edit. 307: * If you quit out of a 'vi' command by doing Q or ^\, 308: * you also fall through to here. 309: */ 310: ungetchar(0); 311: globp = 0; 312: initev = 0; 313: setlastchar('\n'); 314: setexit(); 315: commands(0, 0); 316: cleanup(1); 317: exit(0); 318: } 319: 320: /* 321: * Initialization, before editing a new file. 322: * Main thing here is to get a new buffer (in fileinit), 323: * rest is peripheral state resetting. 324: */ 325: init() 326: { 327: register int i; 328: 329: fileinit(); 330: dot = zero = truedol = unddol = dol = fendcore; 331: one = zero+1; 332: undkind = UNDNONE; 333: chng = 0; 334: edited = 0; 335: #ifdef USG 336: signal (SIGHUP, SIG_IGN); 337: #endif 338: #ifdef USG3TTY 339: # ifndef USG 340: signal (SIGHUP, SIG_IGN); 341: # endif 342: #endif 343: for (i = 0; i <= 'z'-'a'+1; i++) 344: names[i] = 1; 345: anymarks = 0; 346: } 347: 348: /* 349: * When a hangup occurs our actions are similar to a preserve 350: * command. If the buffer has not been [Modified], then we do 351: * nothing but remove the temporary files and exit. 352: * Otherwise, we sync the temp file and then attempt a preserve. 353: * If the preserve succeeds, we unlink our temp files. 354: * If the preserve fails, we leave the temp files as they are 355: * as they are a backup even without preservation if they 356: * are not removed. 357: */ 358: onhup() 359: { 360: 361: #ifdef USG 362: /* 363: * USG tty driver can send multiple HUP's!!! 364: */ 365: signal (SIGINT, SIG_IGN); 366: signal (SIGHUP, SIG_IGN); 367: #endif 368: if (chng == 0) { 369: cleanup(1); 370: exit(0); 371: } 372: if (setexit() == 0) { 373: if (preserve()) { 374: cleanup(1); 375: exit(0); 376: } 377: } 378: exit(1); 379: } 380: 381: /* 382: * An interrupt occurred. Drain any output which 383: * is still in the output buffering pipeline. 384: * Catch interrupts again. Unless we are in visual 385: * reset the output state (out of -nl mode, e.g). 386: * Then like a normal error (with the \n before Interrupt 387: * suppressed in visual mode). 388: */ 389: onintr() 390: { 391: 392: #ifndef CBREAK 393: signal(SIGINT, onintr); 394: #else 395: signal(SIGINT, inopen ? vintr : onintr); 396: #endif 397: draino(); 398: if (!inopen) { 399: pstop(); 400: setlastchar('\n'); 401: #ifdef CBREAK 402: } 403: #else 404: } else 405: vraw(); 406: #endif 407: error("\nInterrupt" + inopen); 408: } 409: 410: /* 411: * If we are interruptible, enable interrupts again. 412: * In some critical sections we turn interrupts off, 413: * but not very often. 414: */ 415: setrupt() 416: { 417: 418: if (ruptible) 419: #ifndef CBREAK 420: signal(SIGINT, onintr); 421: #else 422: signal(SIGINT, inopen ? vintr : onintr); 423: #endif 424: } 425: 426: preserve() 427: { 428: 429: synctmp(); 430: pid = fork(); 431: if (pid < 0) 432: return (0); 433: if (pid == 0) { 434: close(0); 435: dup(tfile); 436: execl(EXPRESERVE, "expreserve", (char *) 0); 437: exit(1); 438: } 439: waitfor(); 440: if (rpid == pid && status == 0) 441: return (1); 442: return (0); 443: } 444: 445: #ifndef V6 446: exit(i) 447: int i; 448: { 449: #ifdef TRACE 450: if (trace) 451: fclose(trace); 452: #endif 453: 454: _exit(i); 455: } 456: #endif 457: 458: #ifdef ROOMTOBURN 459: /* 460: * Return last component of unix path name p. 461: */ 462: char * 463: tailpath(p) 464: register char *p; 465: { 466: register char *r; 467: 468: for (r=p; *p; p++) 469: if (*p == '/') 470: r = p+1; 471: return(r); 472: } 473: #endif