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