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