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