1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex.c 7.2 03/27/85"; 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 extern int onemt(); 85 #ifdef TRACE 86 register char *tracef; 87 #endif 88 89 /* 90 * Immediately grab the tty modes so that we wont 91 * get messed up if an interrupt comes in quickly. 92 */ 93 gTTY(1); 94 #ifndef USG3TTY 95 normf = tty.sg_flags; 96 #else 97 normf = tty; 98 #endif 99 ppid = getpid(); 100 /* 101 * Defend against d's, v's, w's, and a's in directories of 102 * path leading to our true name. 103 */ 104 av[0] = tailpath(av[0]); 105 106 /* 107 * Figure out how we were invoked: ex, edit, vi, view. 108 */ 109 ivis = any('v', av[0]); /* "vi" */ 110 if (any('w', av[0])) /* "view" */ 111 value(READONLY) = 1; 112 if (any('d', av[0])) { /* "edit" */ 113 value(OPEN) = 0; 114 value(REPORT) = 1; 115 value(MAGIC) = 0; 116 } 117 118 #ifndef VMUNIX 119 /* 120 * For debugging take files out of . if name is a.out. 121 */ 122 if (av[0][0] == 'a') 123 erpath = tailpath(erpath); 124 #endif 125 /* 126 * Open the error message file. 127 */ 128 draino(); 129 #ifndef VMUNIX 130 erfile = open(erpath+4, 0); 131 if (erfile < 0) { 132 erfile = open(erpath, 0); 133 } 134 #endif 135 pstop(); 136 137 /* 138 * Initialize interrupt handling. 139 */ 140 oldhup = signal(SIGHUP, SIG_IGN); 141 if (oldhup == SIG_DFL) 142 signal(SIGHUP, onhup); 143 oldquit = signal(SIGQUIT, SIG_IGN); 144 ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL; 145 if (signal(SIGTERM, SIG_IGN) == SIG_DFL) 146 signal(SIGTERM, onhup); 147 if (signal(SIGEMT, SIG_IGN) == SIG_DFL) 148 signal(SIGEMT, onemt); 149 150 /* 151 * Process flag arguments. 152 */ 153 ac--, av++; 154 while (ac && av[0][0] == '-') { 155 c = av[0][1]; 156 if (c == 0) { 157 hush = 1; 158 value(AUTOPRINT) = 0; 159 fast++; 160 } else switch (c) { 161 162 case 'R': 163 value(READONLY) = 1; 164 break; 165 166 #ifdef TRACE 167 case 'T': 168 if (av[0][2] == 0) 169 tracef = "trace"; 170 else { 171 tracef = tttrace; 172 tracef[8] = av[0][2]; 173 if (tracef[8]) 174 tracef[9] = av[0][3]; 175 else 176 tracef[9] = 0; 177 } 178 trace = fopen(tracef, "w"); 179 #define tracbuf NULL 180 if (trace == NULL) 181 printf("Trace create error\n"); 182 else 183 setbuf(trace, tracbuf); 184 break; 185 186 #endif 187 188 #ifdef LISPCODE 189 case 'l': 190 value(LISP) = 1; 191 value(SHOWMATCH) = 1; 192 break; 193 #endif 194 195 case 'r': 196 recov++; 197 break; 198 199 case 't': 200 if (ac > 1 && av[1][0] != '-') { 201 ac--, av++; 202 itag = 1; 203 /* BUG: should check for too long tag. */ 204 CP(lasttag, av[0]); 205 } 206 break; 207 208 case 'v': 209 ivis = 1; 210 break; 211 212 case 'w': 213 defwind = 0; 214 if (av[0][2] == 0) defwind = 3; 215 else for (cp = &av[0][2]; isdigit(*cp); cp++) 216 defwind = 10*defwind + *cp - '0'; 217 break; 218 219 #ifdef CRYPT 220 case 'x': 221 /* -x: encrypted mode */ 222 xflag = 1; 223 break; 224 #endif 225 226 default: 227 smerror("Unknown option %s\n", av[0]); 228 break; 229 } 230 ac--, av++; 231 } 232 233 /* 234 * Initialize end of core pointers. 235 * Normally we avoid breaking back to fendcore after each 236 * file since this can be expensive (much core-core copying). 237 * If your system can scatter load processes you could do 238 * this as ed does, saving a little core, but it will probably 239 * not often make much difference. 240 */ 241 fendcore = (line *) sbrk(0); 242 endcore = fendcore - 2; 243 244 #ifdef SIGTSTP 245 if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL) 246 signal(SIGTSTP, onsusp), dosusp++; 247 #endif 248 249 if (ac && av[0][0] == '+') { 250 firstpat = &av[0][1]; 251 ac--, av++; 252 } 253 254 #ifdef CRYPT 255 if(xflag){ 256 key = getpass(KEYPROMPT); 257 kflag = crinit(key, perm); 258 } 259 #endif 260 261 /* 262 * If we are doing a recover and no filename 263 * was given, then execute an exrecover command with 264 * the -r option to type out the list of saved file names. 265 * Otherwise set the remembered file name to the first argument 266 * file name so the "recover" initial command will find it. 267 */ 268 if (recov) { 269 if (ac == 0) { 270 ppid = 0; 271 setrupt(); 272 execl(EXRECOVER, "exrecover", "-r", 0); 273 filioerr(EXRECOVER); 274 exit(1); 275 } 276 CP(savedfile, *av++), ac--; 277 } 278 279 /* 280 * Initialize the argument list. 281 */ 282 argv0 = av; 283 argc0 = ac; 284 args0 = av[0]; 285 erewind(); 286 287 /* 288 * Initialize a temporary file (buffer) and 289 * set up terminal environment. Read user startup commands. 290 */ 291 if (setexit() == 0) { 292 setrupt(); 293 intty = isatty(0); 294 value(PROMPT) = intty; 295 if (cp = getenv("SHELL")) 296 CP(shell, cp); 297 if (fast || !intty) 298 setterm("dumb"); 299 else { 300 gettmode(); 301 if ((cp = getenv("TERM")) != 0 && *cp) 302 setterm(cp); 303 } 304 } 305 if (setexit() == 0 && !fast && intty) { 306 if ((globp = getenv("EXINIT")) && *globp) 307 commands(1,1); 308 else { 309 globp = 0; 310 if ((cp = getenv("HOME")) != 0 && *cp) 311 source(strcat(strcpy(genbuf, cp), "/.exrc"), 1); 312 } 313 /* 314 * Allow local .exrc too. This loses if . is $HOME, 315 * but nobody should notice unless they do stupid things 316 * like putting a version command in .exrc. Besides, 317 * they should be using EXINIT, not .exrc, right? 318 */ 319 source(".exrc", 1); 320 } 321 init(); /* moved after prev 2 chunks to fix directory option */ 322 323 /* 324 * Initial processing. Handle tag, recover, and file argument 325 * implied next commands. If going in as 'vi', then don't do 326 * anything, just set initev so we will do it later (from within 327 * visual). 328 */ 329 if (setexit() == 0) { 330 if (recov) 331 globp = "recover"; 332 else if (itag) 333 globp = ivis ? "tag" : "tag|p"; 334 else if (argc) 335 globp = "next"; 336 if (ivis) 337 initev = globp; 338 else if (globp) { 339 inglobal = 1; 340 commands(1, 1); 341 inglobal = 0; 342 } 343 } 344 345 /* 346 * Vi command... go into visual. 347 * Strange... everything in vi usually happens 348 * before we ever "start". 349 */ 350 if (ivis) { 351 /* 352 * Don't have to be upward compatible with stupidity 353 * of starting editing at line $. 354 */ 355 if (dol > zero) 356 dot = one; 357 globp = "visual"; 358 if (setexit() == 0) 359 commands(1, 1); 360 } 361 362 /* 363 * Clear out trash in state accumulated by startup, 364 * and then do the main command loop for a normal edit. 365 * If you quit out of a 'vi' command by doing Q or ^\, 366 * you also fall through to here. 367 */ 368 seenprompt = 1; 369 ungetchar(0); 370 globp = 0; 371 initev = 0; 372 setlastchar('\n'); 373 setexit(); 374 commands(0, 0); 375 cleanup(1); 376 exit(0); 377 } 378 379 /* 380 * Initialization, before editing a new file. 381 * Main thing here is to get a new buffer (in fileinit), 382 * rest is peripheral state resetting. 383 */ 384 init() 385 { 386 register int i; 387 388 fileinit(); 389 dot = zero = truedol = unddol = dol = fendcore; 390 one = zero+1; 391 undkind = UNDNONE; 392 chng = 0; 393 edited = 0; 394 for (i = 0; i <= 'z'-'a'+1; i++) 395 names[i] = 1; 396 anymarks = 0; 397 #ifdef CRYPT 398 if(xflag) { 399 xtflag = 1; 400 makekey(key, tperm); 401 } 402 #endif 403 } 404 405 /* 406 * Return last component of unix path name p. 407 */ 408 char * 409 tailpath(p) 410 register char *p; 411 { 412 register char *r; 413 414 for (r=p; *p; p++) 415 if (*p == '/') 416 r = p+1; 417 return(r); 418 } 419