1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* Copyright (c) 1981 Regents of the University of California */ 27 28 /* 29 * Copyright 2000, 2003 Sun Microsystems, Inc. All rights reserved. 30 * Use is subject to license terms. 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ex.h" 36 #include "ex_argv.h" 37 #include "ex_temp.h" 38 #include "ex_tty.h" 39 #include "ex_vis.h" 40 41 extern bool pflag, nflag; /* extern; also in ex_cmds.c */ 42 extern int poffset; /* extern; also in ex_cmds.c */ 43 extern short slevel; /* extern; has level of source() */ 44 45 /* 46 * Subroutines for major command loop. 47 */ 48 49 /* 50 * Is there a single letter indicating a named buffer next? 51 */ 52 cmdreg() 53 { 54 register int c = 0; 55 register int wh = skipwh(); 56 57 #ifdef XPG4 58 if (wh && isalpha(c = peekchar()) && isascii(c) && !isdigit(c)) 59 #else /* XPG4 */ 60 if (wh && isalpha(c = peekchar()) && isascii(c)) 61 #endif /* XPG4 */ 62 c = getchar(); 63 64 #ifdef XPG4 65 if (isdigit(c)) { 66 c = 0; 67 } 68 #endif /* XPG4 */ 69 return (c); 70 } 71 72 /* 73 * Tell whether the character ends a command 74 */ 75 endcmd(ch) 76 int ch; 77 { 78 switch (ch) { 79 80 case '\n': 81 case EOF: 82 endline = 1; 83 return (1); 84 85 case '|': 86 case '"': 87 endline = 0; 88 return (1); 89 } 90 return (0); 91 } 92 93 /* 94 * Insist on the end of the command. 95 */ 96 eol() 97 { 98 99 if (!skipend()) 100 error(value(vi_TERSE) ? gettext("Extra chars") : 101 gettext("Extra characters at end of command")); 102 ignnEOF(); 103 } 104 105 #ifdef XPG4 106 /* 107 * Print out the message in the error message file at str, 108 * with i an integer argument to printf. 109 */ 110 /*VARARGS2*/ 111 error(str, i) 112 register unsigned char *str; 113 int i; 114 { 115 116 errcnt++; 117 noerror(str, i); 118 } 119 120 /* 121 * noerror(): like error(), but doesn't inc errcnt. 122 * the reason why we created this routine, instead of fixing up errcnt 123 * after error() is called, is because we will do a longjmp, and 124 * not a return. it does other things closing file i/o, reset, etc; 125 * so we follow those procedures. 126 */ 127 /*VARARGS2*/ 128 noerror(str, i) 129 register unsigned char *str; 130 int i; 131 { 132 133 error0(); 134 merror(str, i); 135 if (writing) { 136 serror(gettext(" [Warning - %s is incomplete]"), file); 137 writing = 0; 138 } 139 error1(str); 140 } 141 142 #else /* !XPG4 */ 143 /* 144 * Print out the message in the error message file at str, 145 * with i an integer argument to printf. 146 */ 147 /*VARARGS2*/ 148 error(str, i) 149 register unsigned char *str; 150 int i; 151 { 152 153 errcnt++; 154 error0(); 155 merror(str, i); 156 if (writing) { 157 serror(gettext(" [Warning - %s is incomplete]"), file); 158 writing = 0; 159 } 160 error1(str); 161 } 162 #endif /* XPG4 */ 163 164 /* 165 * Rewind the argument list. 166 */ 167 erewind() 168 { 169 170 argc = argc0; 171 argv = argv0; 172 args = args0; 173 if (argc > 1 && !hush && cur_term) { 174 printf(mesg(value(vi_TERSE) ? gettext("%d files") : 175 gettext("%d files to edit")), argc); 176 if (inopen) 177 putchar(' '); 178 else 179 putNFL(); 180 } 181 } 182 183 /* 184 * Guts of the pre-printing error processing. 185 * If in visual and catching errors, then we don't mung up the internals, 186 * just fixing up the echo area for the print. 187 * Otherwise we reset a number of externals, and discard unused input. 188 */ 189 error0() 190 { 191 192 if (laste) { 193 #ifdef VMUNIX 194 tlaste(); 195 #endif 196 laste = 0; 197 sync(); 198 } 199 if (vcatch) { 200 if (splitw == 0) 201 fixech(); 202 if (!enter_standout_mode || !exit_bold) 203 dingdong(); 204 return; 205 } 206 if (input) { 207 input = strend(input) - 1; 208 if (*input == '\n') 209 setlastchar('\n'); 210 input = 0; 211 } 212 setoutt(); 213 flush(); 214 resetflav(); 215 if (!enter_standout_mode || !exit_bold) 216 dingdong(); 217 if (inopen) { 218 /* 219 * We are coming out of open/visual ungracefully. 220 * Restore columns, undo, and fix tty mode. 221 */ 222 columns = OCOLUMNS; 223 undvis(); 224 ostop(normf); 225 /* ostop should be doing this 226 putpad(cursor_normal); 227 putpad(key_eol); 228 */ 229 putnl(); 230 } 231 inopen = 0; 232 holdcm = 0; 233 } 234 235 /* 236 * Post error printing processing. 237 * Close the i/o file if left open. 238 * If catching in visual then throw to the visual catch, 239 * else if a child after a fork, then exit. 240 * Otherwise, in the normal command mode error case, 241 * finish state reset, and throw to top. 242 */ 243 error1(str) 244 unsigned char *str; 245 { 246 bool die; 247 extern short ttyindes; 248 249 if ((io > 0) && (io != ttyindes)) { 250 close(io); 251 io = -1; 252 } 253 254 die = (getpid() != ppid); /* Only children die */ 255 inappend = inglobal = 0; 256 globp = vglobp = vmacp = 0; 257 if (vcatch && !die) { 258 inopen = 1; 259 vcatch = 0; 260 if (str) 261 noonl(); 262 fixol(); 263 if (slevel > 0) 264 reset(); 265 longjmp(vreslab,1); 266 } 267 if (str && !vcatch) 268 putNFL(); 269 if (die) 270 exit(++errcnt); 271 lseek(0, 0L, 2); 272 if (inglobal) 273 setlastchar('\n'); 274 275 if (inexrc) { 276 lprintf(gettext("Error detected in .exrc.[Hit return to continue] "), 0); 277 putNFL(); 278 getkey(); 279 } 280 281 while ((lastchar() != '\n') && (lastchar() != EOF)) 282 ignchar(); 283 ungetchar(0); 284 endline = 1; 285 reset(); 286 } 287 288 fixol() 289 { 290 if (Outchar != vputchar) { 291 flush(); 292 if (state == ONEOPEN || state == HARDOPEN) 293 outline = destline = 0; 294 Outchar = vputchar; 295 vcontin(1); 296 /* 297 * Outchar could be set to termchar() through vcontin(). 298 * So reset it again. 299 */ 300 Outchar = vputchar; 301 } else { 302 if (destcol) 303 vclreol(); 304 vclean(); 305 } 306 } 307 308 /* 309 * Does an ! character follow in the command stream? 310 */ 311 exclam() 312 { 313 314 if (peekchar() == '!') { 315 ignchar(); 316 return (1); 317 } 318 return (0); 319 } 320 321 /* 322 * Make an argument list for e.g. next. 323 */ 324 makargs() 325 { 326 327 glob(&frob); 328 argc0 = frob.argc0; 329 argv0 = frob.argv; 330 args0 = argv0[0]; 331 erewind(); 332 } 333 334 /* 335 * Advance to next file in argument list. 336 */ 337 next() 338 { 339 extern short isalt; /* defined in ex_io.c */ 340 341 if (argc == 0) 342 error(value(vi_TERSE) ? gettext("No more files") : 343 gettext("No more files to edit")); 344 morargc = argc; 345 isalt = (strcmp(altfile, args)==0) + 1; 346 if (savedfile[0]) 347 CP(altfile, savedfile); 348 (void) strlcpy(savedfile, args, sizeof (savedfile)); 349 argc--; 350 args = argv ? *++argv : strend(args) + 1; 351 #if i386 || i286 352 destcol = 0; 353 #endif 354 } 355 356 /* 357 * Eat trailing flags and offsets after a command, 358 * saving for possible later post-command prints. 359 */ 360 donewline() 361 { 362 register int c; 363 364 resetflav(); 365 for (;;) { 366 c = getchar(); 367 switch (c) { 368 369 case '^': 370 case '-': 371 poffset--; 372 break; 373 374 case '+': 375 poffset++; 376 break; 377 378 case 'l': 379 listf++; 380 break; 381 382 case '#': 383 nflag++; 384 break; 385 386 case 'p': 387 listf = 0; 388 break; 389 390 case ' ': 391 case '\t': 392 continue; 393 394 case '"': 395 comment(); 396 setflav(); 397 return; 398 399 default: 400 if (!endcmd(c)) 401 serror(value(vi_TERSE) ? gettext("Extra chars") : 402 gettext("Extra characters at end of \"%s\" command"), Command); 403 if (c == EOF) 404 ungetchar(c); 405 setflav(); 406 return; 407 } 408 pflag++; 409 } 410 } 411 412 /* 413 * Before quit or respec of arg list, check that there are 414 * no more files in the arg list. 415 */ 416 nomore() 417 { 418 419 if (argc == 0 || morargc == argc) 420 return(0); 421 morargc = argc; 422 if (argc == 1) { 423 merror(value(vi_TERSE) ? gettext("1 more file") : 424 gettext("1 more file to edit"), argc); 425 } else { 426 merror(value(vi_TERSE) ? gettext("%d more files") : 427 gettext("%d more files to edit"), argc); 428 } 429 return(1); 430 } 431 432 /* 433 * Before edit of new file check that either an ! follows 434 * or the file has not been changed. 435 */ 436 quickly() 437 { 438 439 if (exclam()) 440 return (1); 441 if (chng && dol > zero) { 442 /* 443 chng = 0; 444 */ 445 xchng = 0; 446 error(value(vi_TERSE) ? gettext("No write") : 447 gettext("No write since last change (:%s! overrides)"), Command); 448 } 449 return (0); 450 } 451 452 /* 453 * Reset the flavor of the output to print mode with no numbering. 454 */ 455 resetflav() 456 { 457 458 if (inopen) 459 return; 460 listf = 0; 461 nflag = 0; 462 pflag = 0; 463 poffset = 0; 464 setflav(); 465 } 466 467 /* 468 * Print an error message with a %s type argument to printf. 469 * Message text comes from error message file. 470 */ 471 serror(str, cp) 472 register unsigned char *str; 473 unsigned char *cp; 474 { 475 476 error0(); 477 smerror(str, cp); 478 error1(str); 479 } 480 481 /* 482 * Set the flavor of the output based on the flags given 483 * and the number and list options to either number or not number lines 484 * and either use normally decoded (ARPAnet standard) characters or list mode, 485 * where end of lines are marked and tabs print as ^I. 486 */ 487 setflav() 488 { 489 490 if (inopen) 491 return; 492 setnumb(nflag || value(vi_NUMBER)); 493 setlist(listf || value(vi_LIST)); 494 if (!inopen) 495 setoutt(); 496 } 497 498 /* 499 * Skip white space and tell whether command ends then. 500 */ 501 skipend() 502 { 503 504 pastwh(); 505 return (endcmd(peekchar()) && peekchar() != '"'); 506 } 507 508 /* 509 * Set the command name for non-word commands. 510 */ 511 tailspec(c) 512 int c; 513 { 514 static unsigned char foocmd[2]; 515 516 foocmd[0] = c; 517 Command = foocmd; 518 } 519 520 /* 521 * Try to read off the rest of the command word. 522 * If alphabetics follow, then this is not the command we seek. 523 */ 524 tail(comm) 525 unsigned char *comm; 526 { 527 528 tailprim(comm, 1, 0); 529 } 530 531 tail2of(comm) 532 unsigned char *comm; 533 { 534 535 tailprim(comm, 2, 0); 536 } 537 538 unsigned char tcommand[20]; 539 540 tailprim(comm, i, notinvis) 541 register unsigned char *comm; 542 int i; 543 bool notinvis; 544 { 545 register unsigned char *cp; 546 register int c; 547 548 Command = comm; 549 for (cp = tcommand; i > 0; i--) 550 *cp++ = *comm++; 551 while (*comm && peekchar() == *comm) 552 *cp++ = getchar(), comm++; 553 c = peekchar(); 554 if (notinvis || (isalpha(c) && isascii(c))) { 555 /* 556 * Of the trailing lp funny business, only dl and dp 557 * survive the move from ed to ex. 558 */ 559 if (tcommand[0] == 'd' && any(c, "lp")) 560 goto ret; 561 if (tcommand[0] == 's' && any(c, "gcr")) 562 goto ret; 563 while (cp < &tcommand[19] && isalpha(c = peekchar()) && isascii(c)) 564 *cp++ = getchar(); 565 *cp = 0; 566 if (notinvis) 567 serror(value(vi_TERSE) ? gettext("What?") : 568 gettext("%s: No such command from open/visual"), tcommand); 569 else 570 serror(value(vi_TERSE) ? gettext("What?") : 571 gettext("%s: Not an editor command"), tcommand); 572 } 573 ret: 574 *cp = 0; 575 } 576 577 /* 578 * Continue after a : command from open/visual. 579 */ 580 vcontin(ask) 581 bool ask; 582 { 583 584 if (vcnt > 0) 585 vcnt = -vcnt; 586 if (inopen) { 587 if (state != VISUAL) { 588 /* 589 * We don't know what a shell command may have left on 590 * the screen, so we move the cursor to the right place 591 * and then put out a newline. But this makes an extra 592 * blank line most of the time so we only do it for :sh 593 * since the prompt gets left on the screen. 594 * 595 * BUG: :!echo longer than current line \\c 596 * will mess it up. 597 */ 598 if (state == CRTOPEN) { 599 termreset(); 600 vgoto(WECHO, 0); 601 } 602 if (!ask) { 603 putch('\r'); 604 putch('\n'); 605 } 606 return; 607 } 608 if (ask) { 609 merror(gettext("[Hit return to continue] ")); 610 flush(); 611 } 612 #ifndef CBREAK 613 vraw(); 614 #endif 615 if (ask) { 616 #ifdef notdef 617 /* 618 * Gobble ^Q/^S since the tty driver should be eating 619 * them (as far as the user can see) 620 */ 621 while (peekkey() == CTRL('Q') || peekkey() == CTRL('S')) 622 ignore(getkey()); 623 #endif 624 if(getkey() == ':') { 625 /* Extra newlines, but no other way */ 626 putch('\n'); 627 outline = WECHO; 628 ungetkey(':'); 629 } 630 } 631 vclrech(1); 632 if (Peekkey != ':') { 633 fixterm(); 634 putpad(enter_ca_mode); 635 tostart(); 636 } 637 } 638 } 639 640 /* 641 * Put out a newline (before a shell escape) 642 * if in open/visual. 643 */ 644 vnfl() 645 { 646 647 if (inopen) { 648 if (state != VISUAL && state != CRTOPEN && destline <= WECHO) 649 vclean(); 650 else 651 vmoveitup(1, 0); 652 vgoto(WECHO, 0); 653 vclrbyte(vtube[WECHO], WCOLS); 654 tostop(); 655 /* replaced by the ostop above 656 putpad(cursor_normal); 657 putpad(key_eol); 658 */ 659 } 660 flush(); 661 } 662