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