1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex.c 5.3 09/13/80"; 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 #ifdef TRACE 85 register char *tracef; 86 #endif 87 88 /* 89 * Immediately grab the tty modes so that we wont 90 * get messed up if an interrupt comes in quickly. 91 */ 92 gTTY(1); 93 #ifndef USG3TTY 94 normf = tty.sg_flags; 95 #else 96 normf = tty; 97 #endif 98 ppid = getpid(); 99 /* 100 * Defend against d's, v's, w's, and a's in directories of 101 * path leading to our true name. 102 */ 103 av[0] = tailpath(av[0]); 104 105 /* 106 * Figure out how we were invoked: ex, edit, vi, view. 107 */ 108 ivis = any('v', av[0]); /* "vi" */ 109 if (any('w', av[0])) /* "view" */ 110 value(READONLY) = 1; 111 if (any('d', av[0])) { /* "edit" */ 112 value(OPEN) = 0; 113 value(REPORT) = 1; 114 value(MAGIC) = 0; 115 } 116 117 #ifndef VMUNIX 118 /* 119 * For debugging take files out of . if name is a.out. 120 */ 121 if (av[0][0] == 'a') 122 erpath = tailpath(erpath); 123 #endif 124 /* 125 * Open the error message file. 126 */ 127 draino(); 128 #ifndef VMUNIX 129 erfile = open(erpath+4, 0); 130 if (erfile < 0) { 131 erfile = open(erpath, 0); 132 } 133 #endif 134 pstop(); 135 136 /* 137 * Initialize interrupt handling. 138 */ 139 oldhup = signal(SIGHUP, SIG_IGN); 140 if (oldhup == SIG_DFL) 141 signal(SIGHUP, onhup); 142 oldquit = signal(SIGQUIT, SIG_IGN); 143 ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL; 144 if (signal(SIGTERM, SIG_IGN) == SIG_DFL) 145 signal(SIGTERM, onhup); 146 #ifdef SIGTSTP 147 if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) 148 signal(SIGTSTP, onsusp), dosusp++; 149 #endif 150 151 /* 152 * Initialize end of core pointers. 153 * Normally we avoid breaking back to fendcore after each 154 * file since this can be expensive (much core-core copying). 155 * If your system can scatter load processes you could do 156 * this as ed does, saving a little core, but it will probably 157 * not often make much difference. 158 */ 159 fendcore = (line *) sbrk(0); 160 endcore = fendcore - 2; 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 if (trace == NULL) 192 printf("Trace create error\n"); 193 setbuf(trace, tracbuf); 194 break; 195 196 #endif 197 198 #ifdef LISPCODE 199 case 'l': 200 value(LISP) = 1; 201 value(SHOWMATCH) = 1; 202 break; 203 #endif 204 205 case 'r': 206 recov++; 207 break; 208 209 case 't': 210 if (ac > 1 && av[1][0] != '-') { 211 ac--, av++; 212 itag = 1; 213 /* BUG: should check for too long tag. */ 214 CP(lasttag, av[0]); 215 } 216 break; 217 218 case 'v': 219 ivis = 1; 220 break; 221 222 case 'w': 223 defwind = 0; 224 if (av[0][2] == 0) defwind = 3; 225 else for (cp = &av[0][2]; isdigit(*cp); cp++) 226 defwind = 10*defwind + *cp - '0'; 227 break; 228 229 #ifdef CRYPT 230 case 'x': 231 /* -x: encrypted mode */ 232 xflag = 1; 233 break; 234 #endif 235 236 default: 237 smerror("Unknown option %s\n", av[0]); 238 break; 239 } 240 ac--, av++; 241 } 242 if (ac && av[0][0] == '+') { 243 firstpat = &av[0][1]; 244 ac--, av++; 245 } 246 247 #ifdef CRYPT 248 if(xflag){ 249 key = getpass(KEYPROMPT); 250 kflag = crinit(key, perm); 251 } 252 #endif 253 254 /* 255 * If we are doing a recover and no filename 256 * was given, then execute an exrecover command with 257 * the -r option to type out the list of saved file names. 258 * Otherwise set the remembered file name to the first argument 259 * file name so the "recover" initial command will find it. 260 */ 261 if (recov) { 262 if (ac == 0) { 263 ppid = 0; 264 setrupt(); 265 execl(EXRECOVER, "exrecover", "-r", 0); 266 filioerr(EXRECOVER); 267 exit(1); 268 } 269 CP(savedfile, *av++), ac--; 270 } 271 272 /* 273 * Initialize the argument list. 274 */ 275 argv0 = av; 276 argc0 = ac; 277 args0 = av[0]; 278 erewind(); 279 280 /* 281 * Initialize a temporary file (buffer) and 282 * set up terminal environment. Read user startup commands. 283 */ 284 init(); 285 if (setexit() == 0) { 286 setrupt(); 287 intty = isatty(0); 288 value(PROMPT) = intty; 289 if (cp = getenv("SHELL")) 290 CP(shell, cp); 291 if (fast || !intty) 292 setterm("dumb"); 293 else { 294 gettmode(); 295 if ((cp = getenv("TERM")) != 0 && *cp) 296 setterm(cp); 297 } 298 } 299 if (setexit() == 0 && !fast && intty) 300 if ((globp = getenv("EXINIT")) && *globp) 301 commands(1,1); 302 else { 303 globp = 0; 304 if ((cp = getenv("HOME")) != 0 && *cp) 305 source(strcat(strcpy(genbuf, cp), "/.exrc"), 1); 306 } 307 308 /* 309 * Initial processing. Handle tag, recover, and file argument 310 * implied next commands. If going in as 'vi', then don't do 311 * anything, just set initev so we will do it later (from within 312 * visual). 313 */ 314 if (setexit() == 0) { 315 if (recov) 316 globp = "recover"; 317 else if (itag) 318 globp = ivis ? "tag" : "tag|p"; 319 else if (argc) 320 globp = "next"; 321 if (ivis) 322 initev = globp; 323 else if (globp) { 324 inglobal = 1; 325 commands(1, 1); 326 inglobal = 0; 327 } 328 } 329 330 /* 331 * Vi command... go into visual. 332 * Strange... everything in vi usually happens 333 * before we ever "start". 334 */ 335 if (ivis) { 336 /* 337 * Don't have to be upward compatible with stupidity 338 * of starting editing at line $. 339 */ 340 if (dol > zero) 341 dot = one; 342 globp = "visual"; 343 if (setexit() == 0) 344 commands(1, 1); 345 } 346 347 /* 348 * Clear out trash in state accumulated by startup, 349 * and then do the main command loop for a normal edit. 350 * If you quit out of a 'vi' command by doing Q or ^\, 351 * you also fall through to here. 352 */ 353 ungetchar(0); 354 globp = 0; 355 initev = 0; 356 setlastchar('\n'); 357 setexit(); 358 commands(0, 0); 359 cleanup(1); 360 exit(0); 361 } 362 363 /* 364 * Initialization, before editing a new file. 365 * Main thing here is to get a new buffer (in fileinit), 366 * rest is peripheral state resetting. 367 */ 368 init() 369 { 370 register int i; 371 372 fileinit(); 373 dot = zero = truedol = unddol = dol = fendcore; 374 one = zero+1; 375 undkind = UNDNONE; 376 chng = 0; 377 edited = 0; 378 #ifdef USG 379 signal (SIGHUP, SIG_IGN); 380 #endif 381 #ifdef USG3TTY 382 #ifndef USG 383 signal (SIGHUP, SIG_IGN); 384 #endif 385 #endif 386 for (i = 0; i <= 'z'-'a'+1; i++) 387 names[i] = 1; 388 anymarks = 0; 389 #ifdef CRYPT 390 if(xflag) { 391 xtflag = 1; 392 makekey(key, tperm); 393 } 394 #endif 395 } 396 397 /* 398 * When a hangup occurs our actions are similar to a preserve 399 * command. If the buffer has not been [Modified], then we do 400 * nothing but remove the temporary files and exit. 401 * Otherwise, we sync the temp file and then attempt a preserve. 402 * If the preserve succeeds, we unlink our temp files. 403 * If the preserve fails, we leave the temp files as they are 404 * as they are a backup even without preservation if they 405 * are not removed. 406 */ 407 onhup() 408 { 409 410 /* 411 * USG tty driver can send multiple HUP's!! 412 */ 413 signal(SIGINT, SIG_IGN); 414 signal(SIGHUP, SIG_IGN); 415 if (chng == 0) { 416 cleanup(1); 417 exit(0); 418 } 419 if (setexit() == 0) { 420 if (preserve()) { 421 cleanup(1); 422 exit(0); 423 } 424 } 425 exit(1); 426 } 427 428 /* 429 * An interrupt occurred. Drain any output which 430 * is still in the output buffering pipeline. 431 * Catch interrupts again. Unless we are in visual 432 * reset the output state (out of -nl mode, e.g). 433 * Then like a normal error (with the \n before Interrupt 434 * suppressed in visual mode). 435 */ 436 onintr() 437 { 438 439 #ifndef CBREAK 440 signal(SIGINT, onintr); 441 #else 442 signal(SIGINT, inopen ? vintr : onintr); 443 #endif 444 draino(); 445 if (!inopen) { 446 pstop(); 447 setlastchar('\n'); 448 #ifdef CBREAK 449 } 450 #else 451 } else 452 vraw(); 453 #endif 454 error("\nInterrupt" + inopen); 455 } 456 457 /* 458 * If we are interruptible, enable interrupts again. 459 * In some critical sections we turn interrupts off, 460 * but not very often. 461 */ 462 setrupt() 463 { 464 465 if (ruptible) { 466 #ifndef CBREAK 467 signal(SIGINT, onintr); 468 #else 469 signal(SIGINT, inopen ? vintr : onintr); 470 #endif 471 #ifdef SIGTSTP 472 if (dosusp) 473 signal(SIGTSTP, onsusp); 474 #endif 475 } 476 } 477 478 preserve() 479 { 480 481 #ifdef VMUNIX 482 tflush(); 483 #endif 484 synctmp(); 485 pid = fork(); 486 if (pid < 0) 487 return (0); 488 if (pid == 0) { 489 close(0); 490 dup(tfile); 491 execl(EXPRESERVE, "expreserve", (char *) 0); 492 exit(1); 493 } 494 waitfor(); 495 if (rpid == pid && status == 0) 496 return (1); 497 return (0); 498 } 499 500 #ifndef V6 501 exit(i) 502 int i; 503 { 504 505 # ifdef TRACE 506 if (trace) 507 fclose(trace); 508 # endif 509 _exit(i); 510 } 511 #endif 512 513 /* 514 * Return last component of unix path name p. 515 */ 516 char * 517 tailpath(p) 518 register char *p; 519 { 520 register char *r; 521 522 for (r=p; *p; p++) 523 if (*p == '/') 524 r = p+1; 525 return(r); 526 } 527