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.1.1 (Berkeley) 10/21/90"; 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 261 default: 262 smerror("Unknown option %s\n", av[0]); 263 break; 264 } 265 ac--, av++; 266 } 267 268 #ifdef SIGTSTP 269 if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL) 270 signal(SIGTSTP, onsusp), dosusp++; 271 #endif 272 273 if (ac && av[0][0] == '+') { 274 firstpat = &av[0][1]; 275 ac--, av++; 276 } 277 278 279 /* 280 * If we are doing a recover and no filename 281 * was given, then execute an exrecover command with 282 * the -r option to type out the list of saved file names. 283 * Otherwise set the remembered file name to the first argument 284 * file name so the "recover" initial command will find it. 285 */ 286 if (recov) { 287 if (ac == 0) { 288 ppid = 0; 289 setrupt(); 290 execl(_PATH_EXRECOVER, "exrecover", "-r", 0); 291 filioerr(_PATH_EXRECOVER); 292 ex_exit(1); 293 } 294 CP(savedfile, *av++), ac--; 295 } 296 297 /* 298 * Initialize the argument list. 299 */ 300 argv0 = av; 301 argc0 = ac; 302 args0 = av[0]; 303 erewind(); 304 305 /* 306 * Initialize a temporary file (buffer) and 307 * set up terminal environment. Read user startup commands. 308 */ 309 if (setexit() == 0) { 310 setrupt(); 311 intty = isatty(0); 312 value(PROMPT) = intty; 313 #ifndef vms 314 if (cp = getenv("SHELL")) 315 #else 316 if (cp = getlog("SHELL")) 317 #endif 318 CP(shell, cp); 319 if (fast || !intty) 320 setterm("dumb"); 321 else { 322 gettmode(); 323 #ifndef vms 324 if ((cp = getenv("TERM")) != 0 && *cp) 325 setterm(cp); 326 #else 327 if ((cp = getlog("TERM")) != 0 && *cp) { 328 /* 329 * Can't just use it directly since getlog 330 * returns a pointer to a static buffer that 331 * tgetent() will eventually use 332 */ 333 CP(termtype, cp); 334 setterm(termtype); 335 } 336 #endif 337 } 338 } 339 if (setexit() == 0 && !fast && intty) { 340 #ifndef vms 341 if ((globp = getenv("EXINIT")) && *globp) 342 #else 343 if ((globp = getlog("EXINIT")) && *globp) 344 #endif 345 commands(1,1); 346 else { 347 globp = 0; 348 if ((cp = getenv("HOME")) != 0 && *cp) { 349 (void) strcat(strcpy(genbuf, cp), "/.exrc"); 350 if (iownit(genbuf)) 351 source(genbuf, 1); 352 } 353 } 354 /* 355 * Allow local .exrc too. This loses if . is $HOME, 356 * but nobody should notice unless they do stupid things 357 * like putting a version command in .exrc. Besides, 358 * they should be using EXINIT, not .exrc, right? 359 */ 360 if (iownit(".exrc")) 361 source(".exrc", 1); 362 } 363 #ifdef UNIX_SBRK 364 /* 365 * Initialize end of core pointers. 366 * Normally we avoid breaking back to fendcore after each 367 * file since this can be expensive (much core-core copying). 368 * If your system can scatter load processes you could do 369 * this as ed does, saving a little core, but it will probably 370 * not often make much difference. 371 */ 372 fendcore = (line *) sbrk(0); 373 endcore = fendcore - 2; 374 #else 375 /* 376 * Allocate all the memory we will ever use in one chunk. 377 * This is for system such as VMS where sbrk() does not 378 * guarantee that the memory allocated beyond the end is 379 * consecutive. VMS's RMS does all sorts of memory allocation 380 * and screwed up ex royally because ex assumes that all 381 * memory up to "endcore" belongs to it and RMS has different 382 * ideas. 383 */ 384 fendcore = (line *) malloc((unsigned) 385 value(LINELIMIT) * sizeof (line *)); 386 if (fendcore == NULL) { 387 lprintf("ex: cannot handle %d lines\n", value(LINELIMIT)); 388 lprintf("ex: set \"linelimit\" lower\n"); 389 flush(); 390 ex_exit(1); 391 } 392 endcore = fendcore + (value(LINELIMIT) - 1); 393 #endif 394 init(); /* moved after prev 2 chunks to fix directory option */ 395 396 /* 397 * Initial processing. Handle tag, recover, and file argument 398 * implied next commands. If going in as 'vi', then don't do 399 * anything, just set initev so we will do it later (from within 400 * visual). 401 */ 402 if (setexit() == 0) { 403 if (recov) 404 globp = "recover"; 405 else if (itag) 406 globp = ivis ? "tag" : "tag|p"; 407 else if (argc) 408 globp = "next"; 409 if (ivis) 410 initev = globp; 411 else if (globp) { 412 inglobal = 1; 413 commands(1, 1); 414 inglobal = 0; 415 } 416 } 417 418 /* 419 * Vi command... go into visual. 420 * Strange... everything in vi usually happens 421 * before we ever "start". 422 */ 423 if (ivis) { 424 /* 425 * Don't have to be upward compatible with stupidity 426 * of starting editing at line $. 427 */ 428 if (dol > zero) 429 dot = one; 430 globp = "visual"; 431 if (setexit() == 0) 432 commands(1, 1); 433 } 434 435 /* 436 * Clear out trash in state accumulated by startup, 437 * and then do the main command loop for a normal edit. 438 * If you quit out of a 'vi' command by doing Q or ^\, 439 * you also fall through to here. 440 */ 441 seenprompt = 1; 442 ungetchar(0); 443 globp = 0; 444 initev = 0; 445 setlastchar('\n'); 446 setexit(); 447 commands(0, 0); 448 cleanup(1); 449 ex_exit(0); 450 } 451 452 /* 453 * Initialization, before editing a new file. 454 * Main thing here is to get a new buffer (in fileinit), 455 * rest is peripheral state resetting. 456 */ 457 init() 458 { 459 register int i; 460 461 fileinit(); 462 dot = zero = truedol = unddol = dol = fendcore; 463 one = zero+1; 464 undkind = UNDNONE; 465 chng = 0; 466 edited = 0; 467 for (i = 0; i <= 'z'-'a'+1; i++) 468 names[i] = 1; 469 anymarks = 0; 470 } 471 472 /* 473 * Return last component of unix path name p. 474 */ 475 char * 476 tailpath(p) 477 register char *p; 478 { 479 register char *r; 480 481 for (r=p; *p; p++) 482 if (*p == '/') 483 r = p+1; 484 return(r); 485 } 486 487 /* 488 * Check ownership of file. Return nonzero if it exists and is owned by the 489 * user or the option sourceany is used 490 */ 491 iownit(file) 492 char *file; 493 { 494 struct stat sb; 495 496 if (stat(file, &sb) == 0 && (value(SOURCEANY) || sb.st_uid == getuid())) 497 return(1); 498 else 499 return(0); 500 } 501