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