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