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