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