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