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 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include "ex.h" 35 #include "ex_tty.h" 36 #include "ex_vis.h" 37 #ifndef PRESUNEUC 38 #include <wctype.h> 39 /* Undef putchar/getchar if they're defined. */ 40 #ifdef putchar 41 # undef putchar 42 #endif 43 #ifdef getchar 44 # undef getchar 45 #endif 46 #endif /* PRESUNEUC */ 47 48 extern size_t strlcpy(char *, const char *, size_t); 49 50 /* 51 * Low level routines for operations sequences, 52 * and mostly, insert mode (and a subroutine 53 * to read an input line, including in the echo area.) 54 */ 55 extern unsigned char *vUA1, *vUA2; /* extern; also in ex_vops.c */ 56 extern unsigned char *vUD1, *vUD2; /* extern; also in ex_vops.c */ 57 58 #ifdef XPG6 59 /* XPG6 assertion 313 & 254 [count]r\n : Also used in ex_vmain.c */ 60 extern int redisplay; 61 #endif 62 63 int vmaxrep(unsigned char, int); 64 static void imultlinerep(int, line *, int, int); 65 static void omultlinerep(int, line *, int); 66 #ifdef XPG6 67 static void rmultlinerep(int, int); 68 #endif 69 void fixdisplay(void); 70 71 /* 72 * Obleeperate characters in hardcopy 73 * open with \'s. 74 */ 75 void 76 bleep(int i, unsigned char *cp) 77 { 78 79 i -= lcolumn(nextchr(cp)); 80 do 81 putchar('\\' | QUOTE); 82 while (--i >= 0); 83 rubble = 1; 84 } 85 86 /* 87 * Common code for middle part of delete 88 * and change operating on parts of lines. 89 */ 90 int 91 vdcMID(void) 92 { 93 unsigned char *cp; 94 95 squish(); 96 setLAST(); 97 if (FIXUNDO) 98 vundkind = VCHNG, CP(vutmp, linebuf); 99 if (wcursor < cursor) 100 cp = wcursor, wcursor = cursor, cursor = cp; 101 vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor; 102 /* 103 * XPG6 assertion 273: Set vmcurs so that undo positions the 104 * cursor column correctly when we've moved off the initial line 105 * that was changed, as with the C, c, and s commands, 106 * when G has moved us off the line, or when a 107 * multi-line change was done. 108 */ 109 fixundo(); 110 return (lcolumn(wcursor)); 111 } 112 113 /* 114 * Take text from linebuf and stick it 115 * in the VBSIZE buffer BUF. Used to save 116 * deleted text of part of line. 117 */ 118 void 119 takeout(unsigned char *BUF) 120 { 121 unsigned char *cp; 122 123 if (wcursor < linebuf) 124 wcursor = linebuf; 125 if (cursor == wcursor) { 126 (void) beep(); 127 return; 128 } 129 if (wcursor < cursor) { 130 cp = wcursor; 131 wcursor = cursor; 132 cursor = cp; 133 } 134 setBUF(BUF); 135 if ((unsigned char)BUF[128] == 0200) 136 (void) beep(); 137 } 138 139 /* 140 * Are we at the end of the printed representation of the 141 * line? Used internally in hardcopy open. 142 */ 143 int 144 ateopr(void) 145 { 146 wchar_t i, c; 147 wchar_t *cp = vtube[destline] + destcol; 148 149 for (i = WCOLS - destcol; i > 0; i--) { 150 c = *cp++; 151 if (c == 0) { 152 /* 153 * Optimization to consider returning early, saving 154 * CPU time. We have to make a special check that 155 * we aren't missing a mode indicator. 156 */ 157 if (destline == WECHO && destcol < WCOLS-11 && vtube[WECHO][WCOLS-20]) 158 return 0; 159 return (1); 160 } 161 if (c != ' ' && (c & QUOTE) == 0) 162 return (0); 163 } 164 return (1); 165 } 166 167 /* 168 * Append. 169 * 170 * This routine handles the top level append, doing work 171 * as each new line comes in, and arranging repeatability. 172 * It also handles append with repeat counts, and calculation 173 * of autoindents for new lines. 174 */ 175 bool vaifirst; 176 bool gobbled; 177 unsigned char *ogcursor; 178 179 static int INSCDCNT; /* number of ^D's (backtabs) in insertion buffer */ 180 181 static int inscdcnt; /* 182 * count of ^D's (backtabs) not seen yet when doing 183 * repeat of insertion 184 */ 185 186 void 187 vappend(int ch, int cnt, int indent) 188 { 189 int i; 190 unsigned char *gcursor; 191 bool escape; 192 int repcnt, savedoomed; 193 short oldhold = hold; 194 int savecnt = cnt; 195 line *startsrcline; 196 int startsrccol, endsrccol; 197 int gotNL = 0; 198 int imultlinecnt = 0; 199 int omultlinecnt = 0; 200 201 if ((savecnt > 1) && (ch == 'o' || ch == 'O')) { 202 omultlinecnt = 1; 203 } 204 #ifdef XPG6 205 if ((savecnt > 1) && (ch == 'a' || ch == 'A' || ch == 'i' || ch == 'I')) 206 imultlinecnt = 1; 207 #endif /* XPG6 */ 208 209 /* 210 * Before a move in hardopen when the line is dirty 211 * or we are in the middle of the printed representation, 212 * we retype the line to the left of the cursor so the 213 * insert looks clean. 214 */ 215 216 if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { 217 rubble = 1; 218 gcursor = cursor; 219 i = *gcursor; 220 *gcursor = ' '; 221 wcursor = gcursor; 222 (void) vmove(); 223 *gcursor = i; 224 } 225 /* 226 * If vrep() passed indent = 0, this is the 'r' command, 227 * so don't autoindent until the last char. 228 */ 229 vaifirst = indent == 0; 230 231 /* 232 * Handle replace character by (eventually) 233 * limiting the number of input characters allowed 234 * in the vgetline routine. 235 */ 236 if (ch == 'r') 237 repcnt = 2; 238 else 239 repcnt = 0; 240 241 /* 242 * If an autoindent is specified, then 243 * generate a mixture of blanks to tabs to implement 244 * it and place the cursor after the indent. 245 * Text read by the vgetline routine will be placed in genbuf, 246 * so the indent is generated there. 247 */ 248 if (value(vi_AUTOINDENT) && indent != 0) { 249 unsigned char x; 250 gcursor = genindent(indent); 251 *gcursor = 0; 252 vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); 253 } else { 254 gcursor = genbuf; 255 *gcursor = 0; 256 if (ch == 'o') 257 vfixcurs(); 258 } 259 260 /* 261 * Prepare for undo. Pointers delimit inserted portion of line. 262 */ 263 vUA1 = vUA2 = cursor; 264 265 /* 266 * If we are not in a repeated command and a ^@ comes in 267 * then this means the previous inserted text. 268 * If there is none or it was too long to be saved, 269 * then beep() and also arrange to undo any damage done 270 * so far (e.g. if we are a change.) 271 */ 272 switch (ch) { 273 case 'r': 274 break; 275 case 'a': 276 /* 277 * TRANSLATION_NOTE 278 * "A" is a terse mode message corresponding to 279 * "APPEND MODE". 280 * Translated message of "A" must be 1 character (not byte). 281 * Or, just leave it. 282 */ 283 if (value(vi_TERSE)) { 284 vshowmode(gettext("A")); 285 } else { 286 vshowmode(gettext("APPEND MODE")); 287 } 288 break; 289 case 's': 290 /* 291 * TRANSLATION_NOTE 292 * "S" is a terse mode message corresponding to 293 * "SUBSTITUTE MODE". 294 * Translated message of "S" must be 1 character (not byte). 295 * Or, just leave it. 296 */ 297 if (value(vi_TERSE)) { 298 vshowmode(gettext("S")); 299 } else { 300 vshowmode(gettext("SUBSTITUTE MODE")); 301 } 302 break; 303 case 'c': 304 /* 305 * TRANSLATION_NOTE 306 * "C" is a terse mode message corresponding to 307 * "CHANGE MODE". 308 * Translated message of "C" must be 1 character (not byte). 309 * Or, just leave it. 310 */ 311 if (value(vi_TERSE)) { 312 vshowmode(gettext("C")); 313 } else { 314 vshowmode(gettext("CHANGE MODE")); 315 } 316 break; 317 case 'R': 318 /* 319 * TRANSLATION_NOTE 320 * "R" is a terse mode message corresponding to 321 * "REPLACE MODE". 322 * Translated message of "R" must be 1 character (not byte). 323 * Or, just leave it. 324 */ 325 if (value(vi_TERSE)) { 326 vshowmode(gettext("R")); 327 } else { 328 vshowmode(gettext("REPLACE MODE")); 329 } 330 break; 331 case 'o': 332 /* 333 * TRANSLATION_NOTE 334 * "O" is a terse mode message corresponding to 335 * "OPEN MODE". 336 * Translated message of "O" must be 1 character (not byte). 337 * Or, just leave it. 338 */ 339 if (value(vi_TERSE)) { 340 vshowmode(gettext("O")); 341 } else { 342 vshowmode(gettext("OPEN MODE")); 343 } 344 break; 345 case 'i': 346 /* 347 * TRANSLATION_NOTE 348 * "I" is a terse mode message corresponding to 349 * "INSERT MODE" and the following "INPUT MODE". 350 * Translated message of "I" must be 1 character (not byte). 351 * Or, just leave it. 352 */ 353 if (value(vi_TERSE)) { 354 vshowmode(gettext("I")); 355 } else { 356 vshowmode(gettext("INSERT MODE")); 357 } 358 break; 359 default: 360 /* 361 * TRANSLATION_NOTE 362 * "I" is a terse mode message corresponding to 363 * "INPUT MODE" and the previous "INSERT MODE". 364 * Translated message of "I" must be 1 character (not byte). 365 * Or, just leave it. 366 */ 367 if (value(vi_TERSE)) { 368 vshowmode(gettext("I")); 369 } else { 370 vshowmode(gettext("INPUT MODE")); 371 } 372 } 373 ixlatctl(1); 374 if ((vglobp && *vglobp == 0) || peekbr()) { 375 if (INS[128] == 0200) { 376 (void) beep(); 377 if (!splitw) 378 ungetkey('u'); 379 doomed = 0; 380 hold = oldhold; 381 return; 382 } 383 /* 384 * Unread input from INS. 385 * An escape will be generated at end of string. 386 * Hold off n^^2 type update on dumb terminals. 387 */ 388 vglobp = INS; 389 inscdcnt = INSCDCNT; 390 hold |= HOLDQIK; 391 } else if (vglobp == 0) { 392 /* 393 * Not a repeated command, get 394 * a new inserted text for repeat. 395 */ 396 INS[0] = 0; 397 INS[128] = 0; 398 INSCDCNT = 0; 399 } 400 401 /* 402 * For wrapmargin to hack away second space after a '.' 403 * when the first space caused a line break we keep 404 * track that this happened in gobblebl, which says 405 * to gobble up a blank silently. 406 */ 407 gobblebl = 0; 408 409 startsrcline = dot; 410 startsrccol = cursor - linebuf; 411 412 /* 413 * Text gathering loop. 414 * New text goes into genbuf starting at gcursor. 415 * cursor preserves place in linebuf where text will eventually go. 416 */ 417 if (*cursor == 0 || state == CRTOPEN) 418 hold |= HOLDROL; 419 for (;;) { 420 if (ch == 'r' && repcnt == 0) 421 escape = 0; 422 else { 423 ixlatctl(1); 424 /* 425 * When vgetline() returns, gcursor is 426 * pointing to '\0' and vgetline() has 427 * read an ESCAPE or NL. 428 */ 429 gcursor = vgetline(repcnt, gcursor, &escape, ch); 430 if (escape == '\n') { 431 gotNL = 1; 432 #ifdef XPG6 433 if (ch == 'r') { 434 /* 435 * XPG6 assertion 313 [count]r\n : 436 * Arrange to set cursor correctly. 437 */ 438 endsrccol = gcursor - genbuf - 1; 439 } 440 #endif /* XPG6 */ 441 } else { 442 /* 443 * Upon escape, gcursor is pointing to '\0' 444 * terminating the string in genbuf. 445 */ 446 endsrccol = gcursor - genbuf - 1; 447 } 448 ixlatctl(0); 449 450 /* 451 * After an append, stick information 452 * about the ^D's and ^^D's and 0^D's in 453 * the repeated text buffer so repeated 454 * inserts of stuff indented with ^D as backtab's 455 * can work. 456 */ 457 if (HADUP) 458 addtext("^"); 459 else if (HADZERO) 460 addtext("0"); 461 if(!vglobp) 462 INSCDCNT = CDCNT; 463 while (CDCNT > 0) { 464 addtext("\004"); 465 CDCNT--; 466 } 467 if (gobbled) 468 addtext(" "); 469 addtext(ogcursor); 470 } 471 repcnt = 0; 472 473 /* 474 * Smash the generated and preexisting indents together 475 * and generate one cleanly made out of tabs and spaces 476 * if we are using autoindent and this isn't 'r' command. 477 */ 478 if (!vaifirst && value(vi_AUTOINDENT)) { 479 i = fixindent(indent); 480 if (!HADUP) 481 indent = i; 482 gcursor = strend(genbuf); 483 } 484 485 /* 486 * Set cnt to 1 to avoid repeating the text on the same line. 487 * Do this for commands 'i', 'I', 'a', and 'A', if we're 488 * inserting anything with a newline for XPG6. Always do this 489 * for commands 'o' and 'O'. 490 */ 491 if ((imultlinecnt && gotNL) || omultlinecnt) { 492 cnt = 1; 493 } 494 495 /* 496 * Limit the repetition count based on maximum 497 * possible line length; do output implied 498 * by further count (> 1) and cons up the new line 499 * in linebuf. 500 */ 501 cnt = vmaxrep(ch, cnt); 502 /* 503 * cursor points to linebuf 504 * Copy remaining old text (cursor) in original 505 * line to after new text (gcursor + 1) in genbuf. 506 */ 507 CP(gcursor + 1, cursor); 508 /* 509 * For [count] r \n command, when replacing [count] chars 510 * with '\n', this loop replaces [count] chars with "". 511 */ 512 do { 513 /* cp new text (genbuf) into linebuf (cursor) */ 514 CP(cursor, genbuf); 515 if (cnt > 1) { 516 int oldhold = hold; 517 518 Outchar = vinschar; 519 hold |= HOLDQIK; 520 viprintf("%s", genbuf); 521 hold = oldhold; 522 Outchar = vputchar; 523 } 524 /* point cursor after new text in linebuf */ 525 cursor += gcursor - genbuf; 526 } while (--cnt > 0); 527 endim(); 528 vUA2 = cursor; 529 /* add the remaining old text after the cursor */ 530 if (escape != '\n') 531 CP(cursor, gcursor + 1); 532 533 /* 534 * If doomed characters remain, clobber them, 535 * and reopen the line to get the display exact. 536 * eg. c$ to change to end of line 537 */ 538 if (state != HARDOPEN) { 539 DEPTH(vcline) = 0; 540 savedoomed = doomed; 541 if (doomed > 0) { 542 int cind = cindent(); 543 544 physdc(cind, cind + doomed); 545 doomed = 0; 546 } 547 if(MB_CUR_MAX > 1) 548 rewrite = _ON; 549 i = vreopen(LINE(vcline), lineDOT(), vcline); 550 if(MB_CUR_MAX > 1) 551 rewrite = _OFF; 552 #ifdef TRACE 553 if (trace) 554 fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed); 555 #endif 556 if (ch == 'R') 557 doomed = savedoomed; 558 } 559 560 /* 561 * Unless we are continuing on to another line 562 * (got a NL), break out of the for loop (got 563 * an ESCAPE). 564 */ 565 if (escape != '\n') { 566 vshowmode(""); 567 break; 568 } 569 570 /* 571 * Set up for the new line. 572 * First save the current line, then construct a new 573 * first image for the continuation line consisting 574 * of any new autoindent plus the pushed ahead text. 575 */ 576 killU(); 577 addtext(gobblebl ? " " : "\n"); 578 /* save vutmp (for undo state) into temp file */ 579 vsave(); 580 cnt = 1; 581 if (value(vi_AUTOINDENT)) { 582 if (value(vi_LISP)) 583 indent = lindent(dot + 1); 584 else 585 if (!HADUP && vaifirst) 586 indent = whitecnt(linebuf); 587 vaifirst = 0; 588 strcLIN(vpastwh(gcursor + 1)); 589 gcursor = genindent(indent); 590 *gcursor = 0; 591 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) 592 gcursor = genbuf; 593 CP(gcursor, linebuf); 594 } else { 595 /* 596 * Put gcursor at start of genbuf to wipe 597 * out previous line in preparation for 598 * the next vgetline() loop. 599 */ 600 CP(genbuf, gcursor + 1); 601 gcursor = genbuf; 602 } 603 604 /* 605 * If we started out as a single line operation and are now 606 * turning into a multi-line change, then we had better yank 607 * out dot before it changes so that undo will work 608 * correctly later. 609 */ 610 if (FIXUNDO && vundkind == VCHNG) { 611 vremote(1, yank, 0); 612 undap1--; 613 } 614 615 /* 616 * Now do the append of the new line in the buffer, 617 * and update the display, ie: append genbuf to 618 * the file after dot. If slowopen 619 * we don't do very much. 620 */ 621 vdoappend(genbuf); 622 vundkind = VMANYINS; 623 vcline++; 624 if (state != VISUAL) 625 vshow(dot, NOLINE); 626 else { 627 i += LINE(vcline - 1); 628 vopen(dot, i); 629 if (value(vi_SLOWOPEN)) 630 vscrap(); 631 else 632 vsync1(LINE(vcline)); 633 } 634 switch (ch) { 635 case 'r': 636 break; 637 case 'a': 638 if (value(vi_TERSE)) { 639 vshowmode(gettext("A")); 640 } else { 641 vshowmode(gettext("APPEND MODE")); 642 } 643 break; 644 case 's': 645 if (value(vi_TERSE)) { 646 vshowmode(gettext("S")); 647 } else { 648 vshowmode(gettext("SUBSTITUTE MODE")); 649 } 650 break; 651 case 'c': 652 if (value(vi_TERSE)) { 653 vshowmode(gettext("C")); 654 } else { 655 vshowmode(gettext("CHANGE MODE")); 656 } 657 break; 658 case 'R': 659 if (value(vi_TERSE)) { 660 vshowmode(gettext("R")); 661 } else { 662 vshowmode(gettext("REPLACE MODE")); 663 } 664 break; 665 case 'i': 666 if (value(vi_TERSE)) { 667 vshowmode(gettext("I")); 668 } else { 669 vshowmode(gettext("INSERT MODE")); 670 } 671 break; 672 case 'o': 673 if (value(vi_TERSE)) { 674 vshowmode(gettext("O")); 675 } else { 676 vshowmode(gettext("OPEN MODE")); 677 } 678 break; 679 default: 680 if (value(vi_TERSE)) { 681 vshowmode(gettext("I")); 682 } else { 683 vshowmode(gettext("INPUT MODE")); 684 } 685 } 686 strcLIN(gcursor); 687 /* zero genbuf */ 688 *gcursor = 0; 689 cursor = linebuf; 690 vgotoCL(nqcolumn(cursor - 1, genbuf)); 691 } /* end for (;;) loop in vappend() */ 692 693 if (imultlinecnt && gotNL) { 694 imultlinerep(savecnt, startsrcline, startsrccol, endsrccol); 695 } else if (omultlinecnt) { 696 omultlinerep(savecnt, startsrcline, endsrccol); 697 #ifdef XPG6 698 } else if (savecnt > 1 && ch == 'r' && gotNL) { 699 /* 700 * XPG6 assertion 313 & 254 : Position cursor for [count]r\n 701 * then insert [count -1] newlines. 702 */ 703 endsrccol = gcursor - genbuf - 1; 704 rmultlinerep(savecnt, endsrccol); 705 #endif /* XPG6 */ 706 } 707 708 /* 709 * All done with insertion, position the cursor 710 * and sync the screen. 711 */ 712 hold = oldhold; 713 if ((imultlinecnt && gotNL) || omultlinecnt) { 714 fixdisplay(); 715 #ifdef XPG6 716 } else if (savecnt > 1 && ch == 'r' && gotNL) { 717 fixdisplay(); 718 /* 719 * XPG6 assertion 313 & 254 [count]r\n : Set flag to call 720 * fixdisplay() after operate() has finished. To be sure that 721 * the text (after the last \n followed by an indent) is always 722 * displayed, fixdisplay() is called right before getting 723 * the next command. 724 */ 725 redisplay = 1; 726 #endif /* XPG6 */ 727 } else if (cursor > linebuf) { 728 cursor = lastchr(linebuf, cursor); 729 #ifdef XPG6 730 /* 731 * XPG6 assertion 313 & 254 [count]r\n : 732 * For 'r' command, when the replacement char causes new 733 * lines to be created, point cursor to first non-blank. 734 * The old code, ie: cursor = lastchr(linebuf, cursor); 735 * set cursor to the blank before the first non-blank 736 * for r\n 737 */ 738 if (ch == 'r' && gotNL && isblank((int)*cursor)) 739 ++cursor; 740 #endif /* XPG6 */ 741 } 742 if (state != HARDOPEN) 743 vsyncCL(); 744 else if (cursor > linebuf) 745 back1(); 746 doomed = 0; 747 wcursor = cursor; 748 (void) vmove(); 749 } 750 751 /* 752 * XPG6 753 * To repeat multi-line input for [count]a, [count]A, [count]i, [count]I, 754 * or a subsequent [count]. : 755 * insert input count-1 more times. 756 */ 757 758 static void 759 imultlinerep(int savecnt, line *startsrcline, int startsrccol, int endsrccol) 760 { 761 int tmpcnt = 2; /* 1st insert counts as 1 repeat */ 762 line *srcline, *endsrcline; 763 size_t destsize = LBSIZE - endsrccol - 1; 764 765 endsrcline = dot; 766 767 /* Save linebuf into temp file before moving off the line. */ 768 vsave(); 769 770 /* 771 * At this point the temp file contains the first iteration of 772 * a multi-line insert, and we need to repeat it savecnt - 1 773 * more times in the temp file. dot is the last line in the 774 * first iteration of the insert. Decrement dot so that 775 * vdoappend() will append each new line before the last line. 776 */ 777 --dot; 778 --vcline; 779 /* 780 * Use genbuf to rebuild the last line in the 1st iteration 781 * of the repeated insert, then copy this line to the temp file. 782 */ 783 (void) strlcpy((char *)genbuf, (char *)linebuf, sizeof (genbuf)); 784 getline(*startsrcline); 785 if (strlcpy((char *)(genbuf + endsrccol + 1), 786 (char *)(linebuf + startsrccol), destsize) >= destsize) { 787 error(gettext("Line too long")); 788 } 789 vdoappend(genbuf); 790 vcline++; 791 /* 792 * Loop from the second line of the first iteration 793 * through endsrcline, appending after dot. 794 */ 795 ++startsrcline; 796 797 while (tmpcnt <= savecnt) { 798 for (srcline = startsrcline; srcline <= endsrcline; 799 ++srcline) { 800 if ((tmpcnt == savecnt) && 801 (srcline == endsrcline)) { 802 /* 803 * The last line is already in place, 804 * just make it the current line. 805 */ 806 vcline++; 807 dot++; 808 getDOT(); 809 cursor = linebuf + endsrccol; 810 } else { 811 getline(*srcline); 812 /* copy linebuf to temp file */ 813 vdoappend(linebuf); 814 vcline++; 815 } 816 } 817 ++tmpcnt; 818 } 819 } 820 821 /* 822 * To repeat input for [count]o, [count]O, or a subsequent [count]. : 823 * append input count-1 more times to the end of the already added 824 * text, each time starting on a new line. 825 */ 826 827 static void 828 omultlinerep(int savecnt, line *startsrcline, int endsrccol) 829 { 830 int tmpcnt = 2; /* 1st insert counts as 1 repeat */ 831 line *srcline, *endsrcline; 832 833 endsrcline = dot; 834 /* Save linebuf into temp file before moving off the line. */ 835 vsave(); 836 837 /* 838 * Loop from the first line of the first iteration 839 * through endsrcline, appending after dot. 840 */ 841 while (tmpcnt <= savecnt) { 842 for (srcline = startsrcline; srcline <= endsrcline; ++srcline) { 843 getline(*srcline); 844 /* copy linebuf to temp file */ 845 vdoappend(linebuf); 846 vcline++; 847 } 848 ++tmpcnt; 849 } 850 cursor = linebuf + endsrccol; 851 } 852 853 #ifdef XPG6 854 /* 855 * XPG6 assertion 313 & 254 : To repeat '\n' for [count]r\n 856 * insert '\n' savecnt-1 more times before the already added '\n'. 857 */ 858 859 static void 860 rmultlinerep(int savecnt, int endsrccol) 861 { 862 int tmpcnt = 2; /* 1st replacement counts as 1 repeat */ 863 864 /* Save linebuf into temp file before moving off the line. */ 865 vsave(); 866 /* 867 * At this point the temp file contains the line followed by '\n', 868 * which is preceded by indentation if autoindent is set. 869 * '\n' must be repeated [savecnt - 1] more times in the temp file. 870 * dot is the current line containing the '\n'. Decrement dot so that 871 * vdoappend() will append each '\n' before the current '\n'. 872 * This will allow only the last line to contain any autoindent 873 * characters. 874 */ 875 --dot; 876 --vcline; 877 878 /* 879 * Append after dot. 880 */ 881 while (tmpcnt <= savecnt) { 882 linebuf[0] = '\0'; 883 /* append linebuf below current line in temp file */ 884 vdoappend(linebuf); 885 vcline++; 886 ++tmpcnt; 887 } 888 /* set the current line to the line after the last '\n' */ 889 ++dot; 890 ++vcline; 891 /* point cursor after (linebuf + endsrccol) */ 892 vcursaft(linebuf + endsrccol); 893 } 894 #endif /* XPG6 */ 895 896 /* 897 * Similiar to a ctrl-l, however always vrepaint() in case the last line 898 * of the repeat would exceed the bottom of the screen. 899 */ 900 901 void 902 fixdisplay(void) 903 { 904 vclear(); 905 vdirty(0, vcnt); 906 if (state != VISUAL) { 907 vclean(); 908 vcnt = 0; 909 vmoveto(dot, cursor, 0); 910 } else { 911 vredraw(WTOP); 912 vrepaint(cursor); 913 vfixcurs(); 914 } 915 } 916 917 /* 918 * Subroutine for vgetline to back up a single character position, 919 * backwards around end of lines (vgoto can't hack columns which are 920 * less than 0 in general). 921 */ 922 void 923 back1(void) 924 { 925 926 vgoto(destline - 1, WCOLS + destcol - 1); 927 } 928 929 /* 930 * Get a line into genbuf after gcursor. 931 * Cnt limits the number of input characters 932 * accepted and is used for handling the replace 933 * single character command. Aescaped is the location 934 * where we stick a termination indicator (whether we 935 * ended with an ESCAPE or a newline/return. 936 * 937 * We do erase-kill type processing here and also 938 * are careful about the way we do this so that it is 939 * repeatable. (I.e. so that your kill doesn't happen, 940 * when you repeat an insert if it was escaped with \ the 941 * first time you did it. commch is the command character 942 * involved, including the prompt for readline. 943 */ 944 unsigned char * 945 vgetline(cnt, gcursor, aescaped, commch) 946 int cnt; 947 unsigned char *gcursor; 948 bool *aescaped; 949 unsigned char commch; 950 { 951 int c, ch; 952 unsigned char *cp, *pcp; 953 int x, y, iwhite, backsl=0; 954 unsigned char *iglobp; 955 int (*OO)() = Outchar; 956 int length, width; 957 unsigned char multic[MULTI_BYTE_MAX+1]; 958 wchar_t wchar = 0; 959 unsigned char *p; 960 int len; 961 962 963 /* 964 * Clear the output state and counters 965 * for autoindent backwards motion (counts of ^D, etc.) 966 * Remember how much white space at beginning of line so 967 * as not to allow backspace over autoindent. 968 */ 969 970 *aescaped = 0; 971 ogcursor = gcursor; 972 flusho(); 973 CDCNT = 0; 974 HADUP = 0; 975 HADZERO = 0; 976 gobbled = 0; 977 iwhite = whitecnt(genbuf); 978 iglobp = vglobp; 979 980 /* 981 * Clear abbreviation recursive-use count 982 */ 983 abbrepcnt = 0; 984 /* 985 * Carefully avoid using vinschar in the echo area. 986 */ 987 if (splitw) 988 Outchar = vputchar; 989 else { 990 Outchar = vinschar; 991 vprepins(); 992 } 993 for (;;) { 994 length = 0; 995 backsl = 0; 996 if (gobblebl) 997 gobblebl--; 998 if (cnt != 0) { 999 cnt--; 1000 if (cnt == 0) 1001 goto vadone; 1002 } 1003 c = getkey(); 1004 if (c != ATTN) 1005 c &= 0377; 1006 ch = c; 1007 maphopcnt = 0; 1008 if (vglobp == 0 && Peekkey == 0 && commch != 'r') 1009 while ((ch = map(c, immacs, commch)) != c) { 1010 c = ch; 1011 if (!value(vi_REMAP)) 1012 break; 1013 if (++maphopcnt > 256) 1014 error(gettext("Infinite macro loop")); 1015 } 1016 if (!iglobp) { 1017 1018 /* 1019 * Erase-kill type processing. 1020 * Only happens if we were not reading 1021 * from untyped input when we started. 1022 * Map users erase to ^H, kill to -1 for switch. 1023 */ 1024 if (c == tty.c_cc[VERASE]) 1025 c = CTRL('h'); 1026 else if (c == tty.c_cc[VKILL]) 1027 c = -1; 1028 switch (c) { 1029 1030 /* 1031 * ^? Interrupt drops you back to visual 1032 * command mode with an unread interrupt 1033 * still in the input buffer. 1034 * 1035 * ^\ Quit does the same as interrupt. 1036 * If you are a ex command rather than 1037 * a vi command this will drop you 1038 * back to command mode for sure. 1039 */ 1040 case ATTN: 1041 case QUIT: 1042 ungetkey(c); 1043 goto vadone; 1044 1045 /* 1046 * ^H Backs up a character in the input. 1047 * 1048 * BUG: Can't back around line boundaries. 1049 * This is hard because stuff has 1050 * already been saved for repeat. 1051 */ 1052 case CTRL('h'): 1053 bakchar: 1054 cp = lastchr(ogcursor, gcursor); 1055 if (cp < ogcursor) { 1056 if (splitw) { 1057 /* 1058 * Backspacing over readecho 1059 * prompt. Pretend delete but 1060 * don't beep. 1061 */ 1062 ungetkey(c); 1063 goto vadone; 1064 } 1065 (void) beep(); 1066 continue; 1067 } 1068 goto vbackup; 1069 1070 /* 1071 * ^W Back up a white/non-white word. 1072 */ 1073 case CTRL('w'): 1074 wdkind = 1; 1075 for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) 1076 continue; 1077 pcp = lastchr(ogcursor, cp); 1078 for (c = wordch(pcp); 1079 cp > ogcursor && wordof(c, pcp); cp = pcp, pcp = lastchr(ogcursor, cp)) 1080 continue; 1081 goto vbackup; 1082 1083 /* 1084 * users kill Kill input on this line, back to 1085 * the autoindent. 1086 */ 1087 case -1: 1088 cp = ogcursor; 1089 vbackup: 1090 if (cp == gcursor) { 1091 (void) beep(); 1092 continue; 1093 } 1094 endim(); 1095 *cp = 0; 1096 c = cindent(); 1097 vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); 1098 1099 if (doomed >= 0) 1100 doomed += c - cindent(); 1101 gcursor = cp; 1102 continue; 1103 1104 /* 1105 * \ Followed by erase or kill 1106 * maps to just the erase or kill. 1107 */ 1108 case '\\': 1109 x = destcol, y = destline; 1110 putchar('\\'); 1111 vcsync(); 1112 c = getkey(); 1113 if (c == tty.c_cc[VERASE] 1114 || c == tty.c_cc[VKILL]) 1115 { 1116 vgoto(y, x); 1117 if (doomed >= 0) 1118 doomed++; 1119 multic[0] = wchar = c; 1120 length = 1; 1121 goto def; 1122 } 1123 ungetkey(c), c = '\\'; 1124 backsl = 1; 1125 break; 1126 1127 /* 1128 * ^Q Super quote following character 1129 * Only ^@ is verboten (trapped at 1130 * a lower level) and \n forces a line 1131 * split so doesn't really go in. 1132 * 1133 * ^V Synonym for ^Q 1134 */ 1135 case CTRL('q'): 1136 case CTRL('v'): 1137 x = destcol, y = destline; 1138 putchar('^'); 1139 vgoto(y, x); 1140 c = getkey(); 1141 #ifdef USG 1142 if (c == ATTN) 1143 c = tty.c_cc[VINTR]; 1144 #endif 1145 if (c != NL) { 1146 if (doomed >= 0) 1147 doomed++; 1148 multic[0] = wchar = c; 1149 length = 1; 1150 goto def; 1151 } 1152 break; 1153 } 1154 } 1155 1156 /* 1157 * If we get a blank not in the echo area 1158 * consider splitting the window in the wrapmargin. 1159 */ 1160 if(!backsl) { 1161 ungetkey(c); 1162 if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1163 (void) beep(); 1164 continue; 1165 } 1166 } else { 1167 length = 1; 1168 multic[0] = '\\'; 1169 } 1170 1171 if (c != NL && !splitw) { 1172 if (c == ' ' && gobblebl) { 1173 gobbled = 1; 1174 continue; 1175 } 1176 if ((width = wcwidth(wchar)) <= 0) 1177 width = (wchar <= 0177 ? 1 : 4); 1178 if (value(vi_WRAPMARGIN) && 1179 (outcol + width - 1 >= OCOLUMNS - value(vi_WRAPMARGIN) || 1180 backsl && outcol==0) && 1181 commch != 'r') { 1182 /* 1183 * At end of word and hit wrapmargin. 1184 * Move the word to next line and keep going. 1185 */ 1186 unsigned char *wp; 1187 int bytelength; 1188 #ifndef PRESUNEUC 1189 unsigned char *tgcursor; 1190 wchar_t wc1, wc2; 1191 tgcursor = gcursor; 1192 #endif /* PRESUNEUC */ 1193 wdkind = 1; 1194 strncpy(gcursor, multic, length); 1195 gcursor += length; 1196 if (backsl) { 1197 #ifdef PRESUNEUC 1198 if((length = mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1199 #else 1200 if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1201 #endif /* PRESUNEUC */ 1202 (void) beep(); 1203 continue; 1204 } 1205 strncpy(gcursor, multic, length); 1206 gcursor += length; 1207 } 1208 *gcursor = 0; 1209 /* 1210 * Find end of previous word if we are past it. 1211 */ 1212 for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--) 1213 ; 1214 #ifdef PRESUNEUC 1215 /* find screen width of previous word */ 1216 width = 0; 1217 for(wp = cp; *wp; ) 1218 #else 1219 /* count screen width of pending characters */ 1220 width = 0; 1221 for(wp = tgcursor; wp < cp;) 1222 #endif /* PRESUNEUC */ 1223 if((bytelength = mbtowc(&wchar, (char *)wp, MULTI_BYTE_MAX)) < 0) { 1224 width+=4; 1225 wp++; 1226 } else { 1227 int curwidth = wcwidth(wchar); 1228 if(curwidth <= 0) 1229 width += (*wp < 0200 ? 2 : 4); 1230 else 1231 width += curwidth; 1232 wp += bytelength; 1233 } 1234 1235 #ifdef PRESUNEUC 1236 if (outcol+(backsl?OCOLUMNS:0) - width >= OCOLUMNS - value(vi_WRAPMARGIN)) { 1237 #else 1238 if (outcol+(backsl?OCOLUMNS:0) + width -1 >= OCOLUMNS - value(vi_WRAPMARGIN)) { 1239 #endif /* PRESUNEUC */ 1240 /* 1241 * Find beginning of previous word. 1242 */ 1243 #ifdef PRESUNEUC 1244 for (; cp>ogcursor && !isspace(cp[-1]); cp--) 1245 ; 1246 #else 1247 wc1 = wc2 = 0; 1248 while (cp>ogcursor) { 1249 if (isspace(cp[-1])) { 1250 break; 1251 } 1252 if (!multibyte) { 1253 cp--; 1254 continue; 1255 } 1256 wp = (unsigned char *)(cp - 1257 MB_CUR_MAX); 1258 if (wp < ogcursor) 1259 wp = ogcursor; 1260 while (cp > wp) { 1261 /* 7tabs */if (wc2) { 1262 /* 7tabs */ if ((bytelength = mbtowc(&wc1, (char *)wp, cp-wp)) != cp-wp) { 1263 /* 7tabs */ wp++; 1264 /* 7tabs */ wc1 = 0; 1265 /* 7tabs */ continue; 1266 /* 7tabs */ } 1267 /* 7tabs */} else { 1268 /* 7tabs */ if ((bytelength = mbtowc(&wc2, (char *)wp, cp-wp)) != cp-wp) { 1269 /* 7tabs */ wp++; 1270 /* 7tabs */ wc2 = 0; 1271 /* 7tabs */ continue; 1272 /* 7tabs */ } 1273 /* 7tabs */} 1274 /* 7tabs */if (wc1) { 1275 /* 7tabs */ if (wdbdg && (!iswascii(wc1) || !iswascii(wc2))) { 1276 /* 7tabs */ if ((*wdbdg)(wc1, wc2, 2) < 5) { 1277 /* 7tabs */ goto ws; 1278 /* 7tabs */ } 1279 /* 7tabs */ } 1280 /* 7tabs */ wc2 = wc1; 1281 /* 7tabs */ wc1 = 0; 1282 /* 7tabs */ cp -= bytelength - 1; 1283 /* 7tabs */ break; 1284 /* 7tabs */} else { 1285 /* 7tabs */ cp -= bytelength - 1; 1286 /* 7tabs */ break; 1287 /* 7tabs */} 1288 } 1289 cp--; 1290 } 1291 ws: 1292 #endif /* PRESUNEUC */ 1293 if (cp <= ogcursor) { 1294 /* 1295 * There is a single word that 1296 * is too long to fit. Just 1297 * let it pass, but beep for 1298 * each new letter to warn 1299 * the luser. 1300 */ 1301 gcursor -= length; 1302 c = *gcursor; 1303 *gcursor = 0; 1304 (void) beep(); 1305 goto dontbreak; 1306 } 1307 /* 1308 * Save it for next line. 1309 */ 1310 macpush(cp, 0); 1311 #ifdef PRESUNEUC 1312 cp--; 1313 #endif /* PRESUNEUC */ 1314 } 1315 macpush("\n", 0); 1316 /* 1317 * Erase white space before the word. 1318 */ 1319 while (cp > ogcursor && isspace(cp[-1])) 1320 cp--; /* skip blank */ 1321 gobblebl = 3; 1322 goto vbackup; 1323 } 1324 dontbreak:; 1325 } 1326 1327 /* 1328 * Word abbreviation mode. 1329 */ 1330 if (anyabbrs && gcursor > ogcursor && !wordch(multic) && wordch(lastchr(ogcursor, gcursor))) { 1331 int wdtype, abno; 1332 1333 multic[length] = 0; 1334 wdkind = 1; 1335 cp = lastchr(ogcursor, gcursor); 1336 pcp = lastchr(ogcursor, cp); 1337 for (wdtype = wordch(pcp); 1338 cp > ogcursor && wordof(wdtype, pcp); cp = pcp, pcp = lastchr(ogcursor, pcp)) 1339 ; 1340 *gcursor = 0; 1341 for (abno=0; abbrevs[abno].mapto; abno++) { 1342 if (eq(cp, abbrevs[abno].cap)) { 1343 if(abbrepcnt == 0) { 1344 if(reccnt(abbrevs[abno].cap, abbrevs[abno].mapto)) 1345 abbrepcnt = 1; 1346 macpush(multic, 0); 1347 macpush(abbrevs[abno].mapto); 1348 goto vbackup; 1349 } else 1350 abbrepcnt = 0; 1351 } 1352 } 1353 } 1354 1355 switch (c) { 1356 1357 /* 1358 * ^M Except in repeat maps to \n. 1359 */ 1360 case CR: 1361 if (vglobp) { 1362 multic[0] = wchar = c; 1363 length = 1; 1364 goto def; 1365 } 1366 c = '\n'; 1367 /* presto chango ... */ 1368 1369 /* 1370 * \n Start new line. 1371 */ 1372 case NL: 1373 *aescaped = c; 1374 goto vadone; 1375 1376 /* 1377 * escape End insert unless repeat and more to repeat. 1378 */ 1379 case ESCAPE: 1380 if (lastvgk) { 1381 multic[0] = wchar = c; 1382 length = 1; 1383 goto def; 1384 } 1385 goto vadone; 1386 1387 /* 1388 * ^D Backtab. 1389 * ^T Software forward tab. 1390 * 1391 * Unless in repeat where this means these 1392 * were superquoted in. 1393 */ 1394 case CTRL('t'): 1395 if (vglobp) { 1396 multic[0] = wchar = c; 1397 length = 1; 1398 goto def; 1399 } 1400 /* fall into ... */ 1401 1402 *gcursor = 0; 1403 cp = vpastwh(genbuf); 1404 c = whitecnt(genbuf); 1405 if (ch == CTRL('t')) { 1406 /* 1407 * ^t just generates new indent replacing 1408 * current white space rounded up to soft 1409 * tab stop increment. 1410 */ 1411 if (cp != gcursor) 1412 /* 1413 * BUG: Don't hack ^T except 1414 * right after initial 1415 * white space. 1416 */ 1417 continue; 1418 cp = genindent(iwhite = backtab(c + value(vi_SHIFTWIDTH) + 1)); 1419 ogcursor = cp; 1420 goto vbackup; 1421 } 1422 /* 1423 * ^D works only if we are at the (end of) the 1424 * generated autoindent. We count the ^D for repeat 1425 * purposes. 1426 */ 1427 case CTRL('d'): 1428 /* check if ^d was superquoted in */ 1429 if(vglobp && inscdcnt <= 0) { 1430 multic[0] = wchar = c; 1431 length = 1; 1432 goto def; 1433 } 1434 if(vglobp) 1435 inscdcnt--; 1436 *gcursor = 0; 1437 cp = vpastwh(genbuf); 1438 c = whitecnt(genbuf); 1439 if (c == iwhite && c != 0) 1440 if (cp == gcursor) { 1441 iwhite = backtab(c); 1442 CDCNT++; 1443 ogcursor = cp = genindent(iwhite); 1444 goto vbackup; 1445 } else if (&cp[1] == gcursor && 1446 (*cp == '^' || *cp == '0')) { 1447 /* 1448 * ^^D moves to margin, then back 1449 * to current indent on next line. 1450 * 1451 * 0^D moves to margin and then 1452 * stays there. 1453 */ 1454 HADZERO = *cp == '0'; 1455 ogcursor = cp = genbuf; 1456 HADUP = 1 - HADZERO; 1457 CDCNT = 1; 1458 endim(); 1459 back1(); 1460 (void) vputchar(' '); 1461 goto vbackup; 1462 } 1463 1464 if (vglobp && vglobp - iglobp >= 2) { 1465 if ((p = vglobp - MB_CUR_MAX) < iglobp) 1466 p = iglobp; 1467 for ( ; p < &vglobp[-2]; p += len) { 1468 if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0) 1469 len = 1; 1470 } 1471 if ((p == &vglobp[-2]) && 1472 (*p == '^' || *p == '0') && 1473 gcursor == ogcursor + 1) 1474 goto bakchar; 1475 } 1476 continue; 1477 1478 default: 1479 /* 1480 * Possibly discard control inputs. 1481 */ 1482 if (!vglobp && junk(c)) { 1483 (void) beep(); 1484 continue; 1485 } 1486 def: 1487 if (!backsl) { 1488 putchar(wchar); 1489 flush(); 1490 } 1491 if (gcursor + length - 1 > &genbuf[LBSIZE - 2]) 1492 error(gettext("Line too long")); 1493 (void)strncpy(gcursor, multic, length); 1494 gcursor += length; 1495 vcsync(); 1496 if (value(vi_SHOWMATCH) && !iglobp) 1497 if (c == ')' || c == '}') 1498 lsmatch(gcursor); 1499 continue; 1500 } 1501 } 1502 vadone: 1503 *gcursor = 0; 1504 if (Outchar != termchar) 1505 Outchar = OO; 1506 endim(); 1507 return (gcursor); 1508 } 1509 1510 int vgetsplit(); 1511 unsigned char *vsplitpt; 1512 1513 /* 1514 * Append the line in buffer at lp 1515 * to the buffer after dot. 1516 */ 1517 void 1518 vdoappend(unsigned char *lp) 1519 { 1520 int oing = inglobal; 1521 1522 vsplitpt = lp; 1523 inglobal = 1; 1524 (void)append(vgetsplit, dot); 1525 inglobal = oing; 1526 } 1527 1528 /* 1529 * Subroutine for vdoappend to pass to append. 1530 */ 1531 int 1532 vgetsplit(void) 1533 { 1534 1535 if (vsplitpt == 0) 1536 return (EOF); 1537 strcLIN(vsplitpt); 1538 vsplitpt = 0; 1539 return (0); 1540 } 1541 1542 /* 1543 * Vmaxrep determines the maximum repetition factor 1544 * allowed that will yield total line length less than 1545 * LBSIZE characters and also does hacks for the R command. 1546 */ 1547 int 1548 vmaxrep(unsigned char ch, int cnt) 1549 { 1550 int len; 1551 unsigned char *cp; 1552 int repcnt, oldcnt, replen; 1553 if (cnt > LBSIZE - 2) 1554 cnt = LBSIZE - 2; 1555 if (ch == 'R') { 1556 len = strlen(cursor); 1557 oldcnt = 0; 1558 for(cp = cursor; *cp; ) { 1559 oldcnt++; 1560 cp = nextchr(cp); 1561 } 1562 repcnt = 0; 1563 for(cp = genbuf; *cp; ) { 1564 repcnt++; 1565 cp = nextchr(cp); 1566 } 1567 /* 1568 * if number of characters in replacement string 1569 * (repcnt) is less than number of characters following 1570 * cursor (oldcnt), find end of repcnt 1571 * characters after cursor 1572 */ 1573 if(repcnt < oldcnt) { 1574 for(cp = cursor; repcnt > 0; repcnt--) 1575 cp = nextchr(cp); 1576 len = cp - cursor; 1577 } 1578 CP(cursor, cursor + len); 1579 vUD2 += len; 1580 } 1581 len = strlen(linebuf); 1582 replen = strlen(genbuf); 1583 if (len + cnt * replen <= LBSIZE - 2) 1584 return (cnt); 1585 cnt = (LBSIZE - 2 - len) / replen; 1586 if (cnt == 0) { 1587 vsave(); 1588 error(gettext("Line too long")); 1589 } 1590 return (cnt); 1591 } 1592 1593 /* 1594 * Determine how many occurrences of word 'CAP' are in 'MAPTO'. To be 1595 * considered an occurrence there must be both a nonword-prefix, a 1596 * complete match of 'CAP' within 'MAPTO', and a nonword-suffix. 1597 * Note that the beginning and end of 'MAPTO' are considered to be 1598 * valid nonword delimiters. 1599 */ 1600 int 1601 reccnt(unsigned char *cap, unsigned char *mapto) 1602 { 1603 int i, cnt, final; 1604 1605 cnt = 0; 1606 final = strlen(mapto) - strlen(cap); 1607 1608 for (i=0; i <= final; i++) 1609 if ((strncmp(cap, mapto+i, strlen(cap)) == 0) /* match */ 1610 && (i == 0 || !wordch(&mapto[i-1])) /* prefix ok */ 1611 && (i == final || !wordch(&mapto[i+strlen(cap)]))) /* suffix ok */ 1612 cnt++; 1613 return (cnt); 1614 } 1615 1616