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