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 /*
31 * Copyright (c) 1981 Regents of the University of California
32 */
33
34 #include "ex.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37 #include <regexpr.h>
38 #ifndef PRESUNEUC
39 #include <wctype.h>
40 /* Undef putchar/getchar if they're defined. */
41 #ifdef putchar
42 #undef putchar
43 #endif
44 #ifdef getchar
45 #undef getchar
46 #endif
47 #endif /* PRESUNEUC */
48
49 #ifdef PRESUNEUC
50 #define blank() isspace(wcursor[0])
51 #endif /* PRESUNEUC */
52 #define forbid(a) if (a) goto errlab;
53
54 unsigned char vscandir[2] = { '/', 0 };
55
56 static int get_addr();
57
58 /*
59 * Decode an operator/operand type command.
60 * Eventually we switch to an operator subroutine in ex_vops.c.
61 * The work here is setting up a function variable to point
62 * to the routine we want, and manipulation of the variables
63 * wcursor and wdot, which mark the other end of the affected
64 * area. If wdot is zero, then the current line is the other end,
65 * and if wcursor is zero, then the first non-blank location of the
66 * other line is implied.
67 */
68 void
operate(int c,int cnt)69 operate(int c, int cnt)
70 {
71 wchar_t i;
72 int (*moveop)(), (*deleteop)();
73 int (*opf)();
74 bool subop = 0;
75 unsigned char *oglobp, *ocurs;
76 line *addr;
77 line *odot;
78 int oc;
79 static unsigned char lastFKND;
80 static wchar_t lastFCHR;
81 short d;
82 /* #ifdef PTR_ADDRESSES */
83 int mouse_x;
84 int mouse_y;
85 int oline;
86 /* #endif PTR_ADDRESSES */
87
88 moveop = vmove, deleteop = (int (*)())vdelete;
89 wcursor = cursor;
90 wdot = NOLINE;
91 notecnt = 0;
92 dir = 1;
93 switch (c) {
94
95 /*
96 * d delete operator.
97 */
98 case 'd':
99 moveop = (int (*)())vdelete;
100 deleteop = beep;
101 break;
102
103 /*
104 * s substitute characters, like c\040, i.e. change space.
105 */
106 case 's':
107 ungetkey(' ');
108 subop++;
109 /* FALLTHROUGH */
110
111 /*
112 * c Change operator.
113 */
114 case 'c':
115 if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
116 subop++;
117 moveop = (int (*)())vchange;
118 deleteop = beep;
119 break;
120
121 /*
122 * ! Filter through a UNIX command.
123 */
124 case '!':
125 moveop = vfilter;
126 deleteop = beep;
127 break;
128
129 /*
130 * y Yank operator. Place specified text so that it
131 * can be put back with p/P. Also yanks to named buffers.
132 */
133 case 'y':
134 moveop = vyankit;
135 deleteop = beep;
136 break;
137
138 /*
139 * = Reformat operator (for LISP).
140 */
141 case '=':
142 forbid(!value(vi_LISP));
143 /* FALLTHROUGH */
144
145 /*
146 * > Right shift operator.
147 * < Left shift operator.
148 */
149 case '<':
150 case '>':
151 moveop = vshftop;
152 deleteop = beep;
153 break;
154
155 /*
156 * r Replace character under cursor with single following
157 * character.
158 */
159 case 'r':
160 vmacchng(1);
161 vrep(cnt);
162 return;
163
164 default:
165 goto nocount;
166 }
167 vmacchng(1);
168 /*
169 * Had an operator, so accept another count.
170 * Multiply counts together.
171 */
172 if (isdigit(peekkey()) && peekkey() != '0') {
173 cnt *= vgetcnt();
174 Xcnt = cnt;
175 forbid(cnt <= 0);
176 }
177
178 /*
179 * Get next character, mapping it and saving as
180 * part of command for repeat.
181 */
182 c = map(getesc(), arrows, 0);
183 if (c == 0)
184 return;
185 if (!subop)
186 *lastcp++ = c;
187 nocount:
188 opf = moveop;
189 switch (c) {
190
191 /* #ifdef PTR_ADDRESSES */
192 /*
193 * ^X^_ Netty Mouse positioning hack
194 * ^X^]
195 */
196 case CTRL('X'):
197 /*
198 * Read in mouse stuff
199 */
200 c = getkey(); /* ^_ or ^] */
201 if ((c != CTRL('_')) && (c != (CTRL(']'))))
202 break;
203 getkey(); /* mouse button */
204 mouse_x = get_addr() + 1;
205 mouse_y = get_addr() + 1;
206 if (mouse_y < WTOP)
207 break;
208 if (Pline == numbline)
209 mouse_x -= 8;
210 if (mouse_x < 0)
211 mouse_x = 0;
212 if (mouse_x > WCOLS)
213 break;
214 /*
215 * Find the line on the screen
216 */
217 for (i = 0; i <= WECHO; i++) {
218 if (vlinfo[i].vliny >= mouse_y)
219 break;
220 }
221 if (i > WECHO)
222 break;
223 /*
224 * Look for lines longer than one line - note odd case at zero
225 */
226 if (i) {
227 if (vlinfo[i - 1].vdepth > 1) {
228 mouse_x += WCOLS * (mouse_y -
229 (vlinfo[i].vliny -
230 (vlinfo[i - 1].vdepth - 1)));
231 }
232 }
233 else
234 {
235 mouse_x += WCOLS * (mouse_y - 1);
236 }
237 /*
238 * Set the line
239 */
240 vsave();
241 ocurs = cursor;
242 odot = dot;
243 oline = vcline;
244 operate('H', i);
245 /*
246 * Set the column
247 */
248 getDOT();
249 if (Pline == numbline)
250 mouse_x += 8;
251 vmovcol = mouse_x;
252 vmoving = 1;
253 wcursor = vfindcol(mouse_x);
254 /*
255 * Reset everything so that stuff like delete and change work
256 */
257 wdot = (odot - oline) + i - 1;
258 cursor = ocurs;
259 vcline = oline;
260 dot = odot;
261 getDOT();
262 break;
263 /* #endif PTR_ADDRESSES */
264
265 /*
266 * b Back up a word.
267 * B Back up a word, liberal definition.
268 */
269 case 'b':
270 case 'B':
271 dir = -1;
272 /* FALLTHROUGH */
273
274 /*
275 * w Forward a word.
276 * W Forward a word, liberal definition.
277 */
278 case 'W':
279 case 'w':
280 wdkind = c & ' ';
281 forbid(lfind(2, cnt, opf, (line *)0) < 0);
282 vmoving = 0;
283 break;
284
285 /*
286 * E to end of following blank/nonblank word
287 */
288 case 'E':
289 wdkind = 0;
290 goto ein;
291
292 /*
293 * e To end of following word.
294 */
295 case 'e':
296 wdkind = 1;
297 ein:
298 forbid(lfind(3, cnt - 1, opf, (line *)0) < 0);
299 vmoving = 0;
300 break;
301
302 /*
303 * ( Back an s-expression.
304 */
305 case '(':
306 dir = -1;
307 /* FALLTHROUGH */
308
309 /*
310 * ) Forward an s-expression.
311 */
312 case ')':
313 forbid(lfind(0, cnt, opf, (line *) 0) < 0);
314 markDOT();
315 break;
316
317 /*
318 * { Back an s-expression, but don't stop on atoms.
319 * In text mode, a paragraph. For C, a balanced set
320 * of {}'s.
321 */
322 case '{':
323 dir = -1;
324 /* FALLTHROUGH */
325
326 /*
327 * } Forward an s-expression, but don't stop on atoms.
328 * In text mode, back paragraph. For C, back a balanced
329 * set of {}'s.
330 */
331 case '}':
332 forbid(lfind(1, cnt, opf, (line *) 0) < 0);
333 markDOT();
334 break;
335
336 /*
337 * % To matching () or {}. If not at ( or { scan for
338 * first such after cursor on this line.
339 */
340 case '%':
341 vsave();
342 ocurs = cursor;
343 odot = wdot = dot;
344 oglobp = globp;
345 CATCH
346 i = lmatchp((line *) 0);
347 ONERR
348 globp = oglobp;
349 dot = wdot = odot;
350 cursor = ocurs;
351 splitw = 0;
352 vclean();
353 vjumpto(dot, ocurs, 0);
354 return;
355 ENDCATCH
356 #ifdef TRACE
357 if (trace)
358 fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, "
359 "dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
360 #endif
361 getDOT();
362 forbid(!i);
363 if (opf != vmove)
364 if (dir > 0)
365 wcursor++;
366 else
367 cursor++;
368 else
369 markDOT();
370 vmoving = 0;
371 break;
372
373 /*
374 * [ Back to beginning of defun, i.e. an ( in column 1.
375 * For text, back to a section macro.
376 * For C, back to a { in column 1 (~~ beg of function.)
377 */
378 case '[':
379 dir = -1;
380 /* FALLTHROUGH */
381
382 /*
383 * ] Forward to next defun, i.e. a ( in column 1.
384 * For text, forward section.
385 * For C, forward to a } in column 1 (if delete or such)
386 * or if a move to a { in column 1.
387 */
388 case ']':
389 if (!vglobp)
390 forbid(getkey() != c);
391 #ifndef XPG4
392 forbid(Xhadcnt);
393 #endif
394 vsave();
395 #ifdef XPG4
396 if (cnt > 1) {
397 while (cnt-- > 1) {
398 i = lbrack(c, opf);
399 getDOT();
400 forbid(!i);
401 markDOT();
402 if (ospeed > B300)
403 hold |= HOLDWIG;
404 (*opf)(c);
405 }
406 }
407 #endif /* XPG4 */
408 i = lbrack(c, opf);
409 getDOT();
410 forbid(!i);
411 markDOT();
412 if (ospeed > B300)
413 hold |= HOLDWIG;
414 break;
415
416 /*
417 * , Invert last find with f F t or T, like inverse
418 * of ;.
419 */
420 case ',':
421 forbid(lastFKND == 0);
422 c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
423 i = lastFCHR;
424 if (vglobp == 0)
425 vglobp = (unsigned char *)"";
426 subop++;
427 goto nocount;
428
429 /*
430 * 0 To beginning of real line.
431 */
432 case '0':
433 wcursor = linebuf;
434 vmoving = 0;
435 break;
436
437 /*
438 * ; Repeat last find with f F t or T.
439 */
440 case ';':
441 forbid(lastFKND == 0);
442 c = lastFKND;
443 i = lastFCHR;
444 subop++;
445 goto nocount;
446
447 /*
448 * F Find single character before cursor in current line.
449 * T Like F, but stops before character.
450 */
451 case 'F': /* inverted find */
452 case 'T':
453 dir = -1;
454 /* FALLTHROUGH */
455
456 /*
457 * f Find single character following cursor in current line.
458 * t Like f, but stope before character.
459 */
460 case 'f': /* find */
461 case 't':
462 if (!subop) {
463 int length;
464 wchar_t wchar;
465 length = _mbftowc(lastcp, &wchar, getesc, &Peekkey);
466 if (length <= 0 || wchar == 0) {
467 (void) beep();
468 return;
469 }
470 i = wchar;
471 lastcp += length;
472 }
473 if (vglobp == 0)
474 lastFKND = c, lastFCHR = i;
475 for (; cnt > 0; cnt--)
476 forbid(find(i) == 0);
477 vmoving = 0;
478 switch (c) {
479
480 case 'T':
481 wcursor = nextchr(wcursor);
482 break;
483
484 case 't':
485 wcursor = lastchr(linebuf, wcursor);
486 /* FALLTHROUGH */
487 case 'f':
488 fixup:
489 if (moveop != vmove)
490 wcursor = nextchr(wcursor);
491 break;
492 }
493 break;
494
495 /*
496 * | Find specified print column in current line.
497 */
498 case '|':
499 if (Pline == numbline)
500 cnt += 8;
501 vmovcol = cnt;
502 vmoving = 1;
503 wcursor = vfindcol(cnt);
504 break;
505
506 /*
507 * ^ To beginning of non-white space on line.
508 */
509 case '^':
510 wcursor = vskipwh(linebuf);
511 vmoving = 0;
512 break;
513
514 /*
515 * $ To end of line.
516 */
517 case '$':
518 if (opf == vmove) {
519 vmoving = 1;
520 vmovcol = 20000;
521 } else
522 vmoving = 0;
523 if (cnt > 1) {
524 if (opf == vmove) {
525 wcursor = 0;
526 cnt--;
527 } else
528 wcursor = linebuf;
529 /* This is wrong at EOF */
530 wdot = dot + cnt;
531 break;
532 }
533 if (linebuf[0]) {
534 wcursor = strend(linebuf);
535 wcursor = lastchr(linebuf, wcursor);
536 goto fixup;
537 }
538 wcursor = linebuf;
539 break;
540
541 /*
542 * h Back a character.
543 * ^H Back a character.
544 */
545 case 'h':
546 case CTRL('h'):
547 dir = -1;
548 /* FALLTHROUGH */
549
550 /*
551 * space Forward a character.
552 */
553 case 'l':
554 case ' ':
555 forbid(margin() || opf == vmove && edge());
556 while (cnt > 0 && !margin()) {
557 if (dir == 1)
558 wcursor = nextchr(wcursor);
559 else
560 wcursor = lastchr(linebuf, wcursor);
561 cnt--;
562 }
563 if (margin() && opf == vmove || wcursor < linebuf) {
564 if (dir == 1)
565 wcursor = lastchr(linebuf, wcursor);
566 else
567 wcursor = linebuf;
568 }
569 vmoving = 0;
570 break;
571
572 /*
573 * D Delete to end of line, short for d$.
574 */
575 case 'D':
576 cnt = INF;
577 goto deleteit;
578
579 /*
580 * X Delete character before cursor.
581 */
582 case 'X':
583 dir = -1;
584 /* FALLTHROUGH */
585 deleteit:
586 /*
587 * x Delete character at cursor, leaving cursor where it is.
588 */
589 case 'x':
590 if (margin())
591 goto errlab;
592 vmacchng(1);
593 while (cnt > 0 && !margin()) {
594 if (dir == 1)
595 wcursor = nextchr(wcursor);
596 else
597 wcursor = lastchr(linebuf, wcursor);
598 cnt--;
599 }
600 opf = deleteop;
601 vmoving = 0;
602 break;
603
604 default:
605 /*
606 * Stuttered operators are equivalent to the operator on
607 * a line, thus turn dd into d_.
608 */
609 if (opf == vmove || c != workcmd[0]) {
610 errlab:
611 (void) beep();
612 vmacp = 0;
613 return;
614 }
615 /* FALLTHROUGH */
616
617 /*
618 * _ Target for a line or group of lines.
619 * Stuttering is more convenient; this is mostly
620 * for aesthetics.
621 */
622 case '_':
623 wdot = dot + cnt - 1;
624 vmoving = 0;
625 wcursor = 0;
626 break;
627
628 /*
629 * H To first, home line on screen.
630 * Count is for count'th line rather than first.
631 */
632 case 'H':
633 wdot = (dot - vcline) + cnt - 1;
634 if (opf == vmove)
635 markit(wdot);
636 vmoving = 0;
637 wcursor = 0;
638 break;
639
640 /*
641 * - Backwards lines, to first non-white character.
642 */
643 case '-':
644 wdot = dot - cnt;
645 vmoving = 0;
646 wcursor = 0;
647 break;
648
649 /*
650 * ^P To previous line same column. Ridiculous on the
651 * console of the VAX since it puts console in LSI mode.
652 */
653 case 'k':
654 case CTRL('p'):
655 wdot = dot - cnt;
656 if (vmoving == 0)
657 vmoving = 1, vmovcol = column(cursor);
658 wcursor = 0;
659 break;
660
661 /*
662 * L To last line on screen, or count'th line from the
663 * bottom.
664 */
665 case 'L':
666 wdot = dot + vcnt - vcline - cnt;
667 if (opf == vmove)
668 markit(wdot);
669 vmoving = 0;
670 wcursor = 0;
671 break;
672
673 /*
674 * M To the middle of the screen.
675 */
676 case 'M':
677 wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
678 if (opf == vmove)
679 markit(wdot);
680 vmoving = 0;
681 wcursor = 0;
682 break;
683
684 /*
685 * + Forward line, to first non-white.
686 *
687 * CR Convenient synonym for +.
688 */
689 case '+':
690 case CR:
691 wdot = dot + cnt;
692 vmoving = 0;
693 wcursor = 0;
694 break;
695
696 /*
697 * ^N To next line, same column if possible.
698 *
699 * LF Linefeed is a convenient synonym for ^N.
700 */
701 case CTRL('n'):
702 case 'j':
703 case NL:
704 wdot = dot + cnt;
705 if (vmoving == 0)
706 vmoving = 1, vmovcol = column(cursor);
707 wcursor = 0;
708 break;
709
710 /*
711 * n Search to next match of current pattern.
712 */
713 case 'n':
714 vglobp = vscandir;
715 c = *vglobp++;
716 goto nocount;
717
718 /*
719 * N Like n but in reverse direction.
720 */
721 case 'N':
722 vglobp = vscandir[0] == '/' ? (unsigned char *)"?" :
723 (unsigned char *)"/";
724 c = *vglobp++;
725 goto nocount;
726
727 /*
728 * ' Return to line specified by following mark,
729 * first white position on line.
730 *
731 * ` Return to marked line at remembered column.
732 */
733 case '\'':
734 case '`':
735 d = c;
736 c = getesc();
737 if (c == 0)
738 return;
739 c = markreg(c);
740 forbid(c == 0);
741 wdot = getmark(c);
742 forbid(wdot == NOLINE);
743 forbid(Xhadcnt);
744 vmoving = 0;
745 wcursor = d == '`' ? ncols[c - 'a'] : 0;
746 if (opf == vmove && (wdot != dot ||
747 (d == '`' && wcursor != cursor)))
748 markDOT();
749 if (wcursor) {
750 vsave();
751 getaline(*wdot);
752 if (wcursor > strend(linebuf))
753 wcursor = 0;
754 else {
755 cnt = wcursor - linebuf;
756 /*CSTYLED*/
757 for (wcursor = linebuf; wcursor - linebuf < cnt; )
758 wcursor = nextchr(wcursor);
759 if (wcursor - linebuf > cnt)
760 wcursor = lastchr(linebuf, wcursor);
761 }
762 getDOT();
763 }
764 if (ospeed > B300)
765 hold |= HOLDWIG;
766 break;
767
768 /*
769 * G Goto count'th line, or last line if no count
770 * given.
771 */
772 case 'G':
773 if (!Xhadcnt)
774 cnt = lineDOL();
775 wdot = zero + cnt;
776 forbid(wdot < one || wdot > dol);
777 if (opf == vmove)
778 markit(wdot);
779 vmoving = 0;
780 wcursor = 0;
781 break;
782
783 /*
784 * / Scan forward for following re.
785 * ? Scan backward for following re.
786 */
787 case '/':
788 case '?':
789 forbid(Xhadcnt);
790 vsave();
791 oc = c;
792 ocurs = cursor;
793 odot = dot;
794 wcursor = 0;
795 if (readecho(c))
796 return;
797 if (!vglobp)
798 vscandir[0] = genbuf[0];
799 oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
800 d = peekc;
801 fromsemi:
802 ungetchar(0);
803 fixech();
804 CATCH
805 #ifndef CBREAK
806 /*
807 * Lose typeahead (ick).
808 */
809 vcook();
810 #endif
811 addr = address(cursor);
812 #ifndef CBREAK
813 vraw();
814 #endif
815 ONERR
816 #ifndef CBREAK
817 vraw();
818 #endif
819 slerr:
820 globp = oglobp;
821 dot = odot;
822 cursor = ocurs;
823 ungetchar(d);
824 splitw = 0;
825 vclean();
826 vjumpto(dot, ocurs, 0);
827 return;
828 ENDCATCH
829 if (globp == 0)
830 globp = (unsigned char *)"";
831 else if (peekc)
832 --globp;
833 if (*globp == ';') {
834 /* /foo/;/bar/ */
835 globp++;
836 dot = addr;
837 cursor = (unsigned char *)loc1;
838 goto fromsemi;
839 }
840 dot = odot;
841 ungetchar(d);
842 c = 0;
843 if (*globp == 'z')
844 globp++, c = '\n';
845 if (any(*globp, "^+-."))
846 c = *globp++;
847 i = 0;
848 while (isdigit(*globp))
849 i = i * 10 + *globp++ - '0';
850 if (any(*globp, "^+-."))
851 c = *globp++;
852 if (*globp) {
853 /* random junk after the pattern */
854 (void) beep();
855 goto slerr;
856 }
857 globp = oglobp;
858 splitw = 0;
859 vmoving = 0;
860 wcursor = (unsigned char *)loc1;
861 if (i != 0)
862 vsetsiz(i);
863 if (opf == vmove) {
864 if (state == ONEOPEN || state == HARDOPEN)
865 outline = destline = WBOT;
866 if (addr != dot || (unsigned char *)loc1 != cursor)
867 markDOT();
868 if (loc1 > (char *)linebuf && *loc1 == 0)
869 loc1 = (char *)lastchr(linebuf, loc1);
870 if (c)
871 vjumpto(addr, (unsigned char *)loc1, c);
872 else {
873 vmoving = 0;
874 if (loc1) {
875 vmoving++;
876 vmovcol = column(loc1);
877 }
878 getDOT();
879 if (state == CRTOPEN && addr != dot)
880 vup1();
881 vupdown(addr - dot, NOSTR);
882 }
883 if (oc == '/') { /* forward search */
884 if (dot < odot ||
885 (dot == odot && cursor <= ocurs))
886 warnf(value(vi_TERSE) ?
887 gettext("Search wrapped BOTTOM") :
888 gettext("Search wrapped around BOTTOM of buffer"));
889 } else { /* backward search */
890 if (dot > odot ||
891 (dot == odot && cursor >= ocurs))
892 warnf(value(vi_TERSE) ?
893 gettext("Search wrapped TOP") :
894 gettext("Search wrapped around TOP of buffer"));
895 }
896 return;
897 }
898 lastcp[-1] = 'n';
899 getDOT();
900 wdot = addr;
901 break;
902 }
903 /*
904 * Apply.
905 */
906 if (vreg && wdot == 0)
907 wdot = dot;
908 (*opf)(c);
909 wdot = NOLINE;
910 }
911
912 static void
lfixol()913 lfixol()
914 {
915 unsigned char *savevglobp;
916 int savesplit;
917
918 if (Outchar == vputchar)
919 return;
920
921 /* Show messages */
922 putnl();
923 if (inopen > 0 && clr_eol)
924 vclreol();
925 if (enter_standout_mode && exit_bold)
926 putpad((unsigned char *)enter_standout_mode);
927 lprintf(gettext("[Hit return to continue] "), 0);
928 if (enter_standout_mode && exit_bold)
929 putpad((unsigned char *)exit_bold);
930
931 /* Get key input for confirmation */
932 savevglobp = vglobp;
933 vglobp = 0; /* force typed input */
934 getkey();
935 vglobp = savevglobp;
936
937 /* reset output function */
938 Outchar = vputchar;
939
940 /* Clean up screen */
941 savesplit = splitw;
942 splitw = 0;
943 vclear();
944 vdirty(0, WLINES);
945 vredraw(WTOP);
946 splitw = savesplit;
947 }
948
949 void
warnf(char * str,char * cp)950 warnf(char *str, char *cp)
951 {
952 int saveline, savecol, savesplit;
953
954 saveline = outline;
955 savecol = outcol;
956 savesplit = splitw;
957 splitw = 1;
958 vgoto(WECHO, 0);
959 if (!enter_standout_mode || !exit_bold)
960 dingdong();
961 if (clr_eol)
962 vclreol();
963 if (enter_standout_mode && exit_bold)
964 putpad((unsigned char *)enter_standout_mode);
965 lprintf(str, cp);
966 if (enter_standout_mode && exit_bold)
967 putpad((unsigned char *)exit_bold);
968 lfixol();
969 vgoto(saveline, savecol);
970 splitw = savesplit;
971 }
972
973 /* #ifdef PTR_ADDRESSES */
974 /*
975 * read in a row or column address
976 *
977 */
978 static int
get_addr()979 get_addr()
980 {
981 short c;
982 short next;
983
984 c = getkey();
985 next = 0;
986 switch (c) {
987 case CTRL('A'):
988 next = 96;
989 c = getkey();
990 break;
991
992 case CTRL('B'):
993 next = 192;
994 c = getkey();
995 break;
996 }
997 if (c < ' ')
998 return (-1);
999 return (next + c - ' ');
1000 }
1001 /* #endif PTR_ADDRESSES */
1002
1003 /*
1004 * Find single character c, in direction dir from cursor.
1005 */
1006 int
find(wchar_t c)1007 find(wchar_t c)
1008 {
1009
1010 wchar_t wchar;
1011 int length;
1012 for (;;) {
1013 if (edge())
1014 return (0);
1015 if (dir == 1)
1016 wcursor = nextchr(wcursor);
1017 else
1018 wcursor = lastchr(linebuf, wcursor);
1019 if ((length = mbtowc(&wchar, (char *)wcursor,
1020 MULTI_BYTE_MAX)) > 0 && wchar == c)
1021 return (1);
1022 }
1023 }
1024
1025 /*
1026 * Do a word motion with operator op, and cnt more words
1027 * to go after this.
1028 */
1029 int
word(int (* op)(),int cnt)1030 word(int (*op)(), int cnt)
1031 {
1032 int which;
1033 unsigned char *iwc;
1034 line *iwdot = wdot;
1035 wchar_t wchar;
1036 int length;
1037
1038 if (dir == 1) {
1039 iwc = wcursor;
1040 which = wordch(wcursor);
1041 while (wordof(which, wcursor)) {
1042 length = mbtowc(&wchar, (char *)wcursor,
1043 MULTI_BYTE_MAX);
1044 if (length <= 0)
1045 length = 1;
1046 if (cnt == 1 && op != vmove && wcursor[length] == 0) {
1047 wcursor += length;
1048 break;
1049 }
1050 if (!lnext())
1051 return (0);
1052 if (wcursor == linebuf)
1053 break;
1054 }
1055 /* Unless last segment of a change skip blanks */
1056 if (op != (int (*)())vchange || cnt > 1)
1057 while (!margin() && blank()) {
1058 if (!lnext())
1059 return (0);
1060 }
1061 else
1062 if (wcursor == iwc && iwdot == wdot && *iwc)
1063 wcursor = nextchr(wcursor);
1064 if (op == vmove && margin()) {
1065 wcursor = lastchr(linebuf, wcursor);
1066 #ifdef XPG4
1067 if (wcursor < linebuf) {
1068 wcursor = linebuf;
1069 }
1070 #endif /* XPG4 */
1071 }
1072 } else {
1073 if (!lnext())
1074 return (0);
1075 while (blank())
1076 if (!lnext())
1077 return (0);
1078 if (!margin()) {
1079 which = wordch(wcursor);
1080 while (!margin() && wordof(which, wcursor))
1081 wcursor = lastchr(linebuf, wcursor);
1082 }
1083 #ifdef PRESUNEUC
1084 if (wcursor < linebuf || !wordof(which, wcursor))
1085 wcursor = nextchr(wcursor);
1086 #else
1087 if (wcursor < linebuf)
1088 wcursor++;
1089 else if (!wordof(which, wcursor))
1090 wcursor = nextchr(wcursor);
1091 #endif /* PRESUNEUC */
1092 }
1093 return (1);
1094 }
1095
1096 /*
1097 * To end of word, with operator op and cnt more motions
1098 * remaining after this.
1099 */
1100 int
eend(int (* op)())1101 eend(int (*op)())
1102 {
1103 int which;
1104
1105 if (!lnext())
1106 return (0);
1107 while (blank())
1108 if (!lnext())
1109 return (0);
1110 which = wordch(wcursor);
1111 while (wordof(which, wcursor)) {
1112 if (wcursor[1] == 0) {
1113 wcursor = nextchr(wcursor);
1114 break;
1115 }
1116 if (!lnext())
1117 return (0);
1118 }
1119 if (op == vyankit)
1120 wcursor = lastchr(linebuf, wcursor) + 1;
1121 else if (op != (int (*)())vchange && op != (int (*)())vdelete &&
1122 wcursor > linebuf)
1123 wcursor = lastchr(linebuf, wcursor);
1124 return (1);
1125 }
1126
1127 /*
1128 * Wordof tells whether the character at *wc is in a word of
1129 * kind which (blank/nonblank words are 0, conservative words 1).
1130 */
1131 int
wordof(unsigned char which,unsigned char * wc)1132 wordof(unsigned char which, unsigned char *wc)
1133 {
1134 #ifdef PRESUNEUC
1135
1136 if (isspace(*wc))
1137 #else
1138 wchar_t z;
1139
1140 (void) mbtowc(&z, (char *)wc, MB_LEN_MAX);
1141 if (iswspace(z))
1142 #endif /* PRESUNEUC */
1143 return (0);
1144 return (!wdkind || wordch(wc) == which);
1145 }
1146
1147 /*
1148 * Wordch tells whether character at *wc is a word character
1149 * i.e. an alfa, digit, or underscore.
1150 */
1151 #ifdef PRESUNEUC
1152 #define SS2 0216
1153 #define SS3 0217
1154 #endif /* PRESUNEUC */
1155
1156 int
wordch(unsigned char * wc)1157 wordch(unsigned char *wc)
1158 {
1159 int length;
1160 wchar_t c;
1161
1162 length = mbtowc(&c, (char *)wc, MULTI_BYTE_MAX);
1163 if (length <= 0)
1164 return (0);
1165 if (length > 1)
1166 #ifndef PRESUNEUC
1167 if (wdwc)
1168 return (*wdwc)(c);
1169 else
1170 #endif /* PRESUNEUC */
1171 return (length);
1172 #ifndef PRESUNEUC
1173 return (isalpha(*wc) || isdigit(*wc) || *wc == '_');
1174 #else
1175 return (isalpha(c) || isdigit(c) || c == '_');
1176 #endif /* PRESUNEUC */
1177 }
1178
1179 /*
1180 * Edge tells when we hit the last character in the current line.
1181 */
1182 int
edge(void)1183 edge(void)
1184 {
1185
1186 if (linebuf[0] == 0)
1187 return (1);
1188 if (dir == 1)
1189 return (*(nextchr(wcursor)) == 0);
1190 else
1191 return (wcursor == linebuf);
1192 }
1193
1194 /*
1195 * Margin tells us when we have fallen off the end of the line.
1196 */
1197 int
margin(void)1198 margin(void)
1199 {
1200
1201 return (wcursor < linebuf || wcursor[0] == 0);
1202 }
1203 #ifndef PRESUNEUC
1204
1205 /*
1206 * Blank tells if the cursor is currently on a TAB, RETURN,
1207 * NEWLINE, FORMFEED, bertical tab, or SPACE character from EUC
1208 * primary and supplementary codesets.
1209 */
1210 int
blank(void)1211 blank(void)
1212 {
1213 wchar_t z;
1214
1215 (void) mbtowc(&z, (char *)wcursor, MB_CUR_MAX);
1216 return (iswspace((int)z));
1217 }
1218 #endif /* PRESUNEUC */
1219