update bug -- once lines resolved into the underlying DB, can't use
[nvi.git] / vi / v_txt.c
blob2eacd96b0b4a02e72dc31190d9c75b70d4b41a62
1 /*-
2 * Copyright (c) 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: v_txt.c,v 8.69 1993/12/22 13:03:42 bostic Exp $ (Berkeley) $Date: 1993/12/22 13:03:42 $";
10 #endif /* not lint */
12 #include <sys/types.h>
13 #include <sys/time.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
21 #include "vi.h"
22 #include "seq.h"
23 #include "vcmd.h"
25 static int txt_abbrev __P((SCR *, TEXT *, int *, ARG_CHAR_T));
26 static void txt_ai_resolve __P((SCR *, TEXT *));
27 static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int));
28 static void txt_err __P((SCR *, EXF *, TEXTH *));
29 static int txt_hex __P((SCR *, TEXT *, int *, ARG_CHAR_T));
30 static int txt_indent __P((SCR *, TEXT *));
31 static int txt_margin __P((SCR *, TEXT *, int *, ARG_CHAR_T));
32 static int txt_outdent __P((SCR *, TEXT *));
33 static void txt_showmatch __P((SCR *, EXF *));
34 static int txt_resolve __P((SCR *, EXF *, TEXTH *));
36 /* Cursor character (space is hard to track on the screen). */
37 #if defined(DEBUG) && 0
38 #undef CURSOR_CH
39 #define CURSOR_CH '+'
40 #endif
42 /* Local version of BINC. */
43 #define TBINC(sp, lp, llen, nlen) { \
44 if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \
45 goto err; \
49 * newtext --
50 * Read in text from the user.
52 * !!!
53 * Historic vi always used:
55 * ^D: autoindent deletion
56 * ^H: last character deletion
57 * ^W: last word deletion
58 * ^V: quote the next character
60 * regardless of the user's choices for these characters. The user's erase
61 * and kill characters worked in addition to these characters. Ex was not
62 * completely consistent with this, as it did map the scroll command to the
63 * user's EOF character.
65 * This implementation does not use fixed characters, but uses whatever the
66 * user specified as described by the termios structure. I'm getting away
67 * with something here, but I think I'm unlikely to get caught.
69 * !!!
70 * Historic vi did a special screen optimization for tab characters. For
71 * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of
72 * the string when it was displayed. Because this implementation redisplays
73 * the entire line on each keystroke, the "bcd" gets pushed to the right as
74 * we ignore that the user has "promised" to change the rest of the characters.
75 * Users have noticed, but this isn't worth fixing, and, the way that the
76 * historic vi did it results in an even worse bug. Given the keystrokes
77 * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
78 * on the second <esc> key.
80 int
81 v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags)
82 SCR *sp;
83 EXF *ep;
84 TEXTH *tiqh;
85 MARK *tm; /* To MARK. */
86 const char *lp; /* Input line. */
87 const size_t len; /* Input line length. */
88 MARK *rp; /* Return MARK. */
89 int prompt; /* Prompt to display. */
90 recno_t ai_line; /* Line number to use for autoindent count. */
91 u_int flags; /* TXT_ flags. */
93 /* State of abbreviation checks. */
94 enum { A_NOTSET, A_SPACE, A_NOTSPACE } abb;
95 /* State of the "[^0]^D" sequences. */
96 enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st;
97 /* State of the hex input character. */
98 enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex;
99 /* State of quotation. */
100 enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted;
101 CH ikey; /* Input character structure. */
102 CHAR_T ch; /* Input character. */
103 GS *gp; /* Global pointer. */
104 TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */
105 size_t rcol; /* 0-N: insert offset in the replay buffer. */
106 size_t col; /* Current column. */
107 u_long margin; /* Wrapmargin value. */
108 u_int iflags; /* Input flags. */
109 int ab_cnt; /* Abbreviation count. */
110 int eval; /* Routine return value. */
111 int replay; /* If replaying a set of input. */
112 int showmatch; /* Showmatch set on this character. */
113 int testnr; /* Test first character for nul replay. */
114 int max, tmp;
115 char *p;
118 * Set the input flag, so tabs get displayed correctly
119 * and everyone knows that the text buffer is in use.
121 F_SET(sp, S_INPUT);
123 /* Set return value. */
124 eval = 0;
127 * Get one TEXT structure with some initial buffer space, reusing
128 * the last one if it's big enough. (All TEXT bookkeeping fields
129 * default to 0 -- text_init() handles this.) If changing a line,
130 * copy it into the TEXT buffer.
132 if (tiqh->cqh_first != (void *)tiqh) {
133 tp = tiqh->cqh_first;
134 if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) {
135 text_lfree(tiqh);
136 goto newtp;
138 tp->ai = tp->insert = tp->offset = tp->owrite = 0;
139 if (lp != NULL) {
140 tp->len = len;
141 memmove(tp->lb, lp, len);
142 } else
143 tp->len = 0;
144 } else {
145 newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
146 return (1);
147 CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
150 /* Set the starting line number. */
151 tp->lno = sp->lno;
154 * Set the insert and overwrite counts. If overwriting characters,
155 * do insertion afterward. If not overwriting characters, assume
156 * doing insertion. If change is to a mark, emphasize it with an
157 * END_CH.
159 if (len) {
160 if (LF_ISSET(TXT_OVERWRITE)) {
161 tp->owrite = tm->cno - sp->cno;
162 tp->insert = len - tm->cno;
163 } else
164 tp->insert = len - sp->cno;
166 if (LF_ISSET(TXT_EMARK))
167 tp->lb[tm->cno - 1] = END_CH;
171 * Many of the special cases in this routine are to handle autoindent
172 * support. Somebody decided that it would be a good idea if "^^D"
173 * and "0^D" deleted all of the autoindented characters. In an editor
174 * that takes single character input from the user, this wasn't a very
175 * good idea. Note also that "^^D" resets the next lines' autoindent,
176 * but "0^D" doesn't.
178 * We assume that autoindent only happens on empty lines, so insert
179 * and overwrite will be zero. If doing autoindent, figure out how
180 * much indentation we need and fill it in. Update input column and
181 * screen cursor as necessary.
183 if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
184 if (txt_auto(sp, ep, ai_line, NULL, 0, tp))
185 return (1);
186 sp->cno = tp->ai;
187 } else {
189 * The cc and S commands have a special feature -- leading
190 * <blank> characters are handled as autoindent characters.
191 * Beauty!
193 if (LF_ISSET(TXT_AICHARS)) {
194 tp->offset = 0;
195 tp->ai = sp->cno;
196 } else
197 tp->offset = sp->cno;
200 /* If getting a command buffer from the user, there may be a prompt. */
201 if (LF_ISSET(TXT_PROMPT)) {
202 tp->lb[sp->cno++] = prompt;
203 ++tp->len;
204 ++tp->offset;
208 * If appending after the end-of-line, add a space into the buffer
209 * and move the cursor right. This space is inserted, i.e. pushed
210 * along, and then deleted when the line is resolved. Assumes that
211 * the cursor is already positioned at the end of the line. This
212 * avoids the nastiness of having the cursor reside on a magical
213 * column, i.e. a column that doesn't really exist. The only down
214 * side is that we may wrap lines or scroll the screen before it's
215 * strictly necessary. Not a big deal.
217 if (LF_ISSET(TXT_APPENDEOL)) {
218 tp->lb[sp->cno] = CURSOR_CH;
219 ++tp->len;
220 ++tp->insert;
224 * Historic practice is that the wrapmargin value was a distance
225 * from the RIGHT-HAND column, not the left. It's more useful to
226 * us as a distance from the left-hand column.
228 * !!!
229 * Replay commands are not affected by wrapmargin values. What
230 * I found surprising was that people actually depend on it, as
231 * in this gem of a macro which centers lines:
233 * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p
235 * XXX
236 * Setting margin causes a significant performance hit. Normally
237 * we don't update the screen if there are keys waiting, but we
238 * have to if margin is set, otherwise the screen routines don't
239 * know where the cursor is.
241 if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN))
242 margin = 0;
243 else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
244 margin = sp->cols - margin;
246 /* Initialize abbreviations checks. */
247 if (F_ISSET(sp, S_ABBREV) && LF_ISSET(TXT_MAPINPUT)) {
248 abb = A_NOTSPACE;
249 ab_cnt = 0;
250 } else
251 abb = A_NOTSET;
254 * Set up the dot command. Dot commands are done by saving the
255 * actual characters and replaying the input. We have to push
256 * the characters onto the key stack and then handle them normally,
257 * otherwise things like wrapmargin will fail.
259 * XXX
260 * It would be nice if we could swallow backspaces and such, but
261 * it's not all that easy to do. Another possibility would be to
262 * recognize full line insertions, which could be performed quickly,
263 * without replay.
265 nullreplay:
266 rcol = 0;
267 if (replay = LF_ISSET(TXT_REPLAY)) {
269 * !!!
270 * Historically, it wasn't an error to replay non-existent
271 * input. This test is necessary, we get here by the user
272 * doing an input command followed by a nul.
274 * !!!
275 * Historically, vi did not remap or reabbreviate replayed
276 * input. It did, however, beep at you if you changed an
277 * abbreviation and then replayed the input. We're not that
278 * compatible.
280 if (VIP(sp)->rep == NULL)
281 return (0);
282 if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP))
283 return (1);
284 testnr = 0;
285 abb = A_NOTSET;
286 LF_CLR(TXT_RECORD);
287 } else
288 testnr = 1;
290 iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
291 for (gp = sp->gp, showmatch = 0,
292 carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) {
294 * Reset the line and update the screen. (The txt_showmatch()
295 * code refreshes the screen for us.) Don't refresh unless
296 * we're about to wait on a character or we need to know where
297 * the cursor really is.
299 if (showmatch || margin || !KEYS_WAITING(sp)) {
300 if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
301 goto err;
302 if (showmatch) {
303 showmatch = 0;
304 txt_showmatch(sp, ep);
305 } else if (sp->s_refresh(sp, ep))
306 goto err;
309 /* Get the next character. */
310 next_ch: if (term_key(sp, &ikey, iflags) != INP_OK)
311 goto err;
312 ch = ikey.ch;
314 /* Abbreviation check. See comment in txt_abbrev(). */
315 #define MAX_ABBREVIATION_EXPANSION 256
316 if (ikey.flags & CH_ABBREVIATED) {
317 if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) {
318 term_ab_flush(sp,
319 "Abbreviation exceeded maximum number of characters");
320 ab_cnt = 0;
321 continue;
323 } else
324 ab_cnt = 0;
327 * !!!
328 * Historic feature. If the first character of the input is
329 * a nul, replay the previous input. This isn't documented
330 * anywhere, and is a great test of vi clones.
332 if (ch == '\0' && testnr) {
333 LF_SET(TXT_REPLAY);
334 goto nullreplay;
336 testnr = 0;
339 * Check to see if the character fits into the input (and
340 * replay, if necessary) buffers. It isn't necessary to
341 * have tp->len bytes, since it doesn't consider overwrite
342 * characters, but not worth fixing.
344 if (LF_ISSET(TXT_RECORD)) {
345 TBINC(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1);
346 VIP(sp)->rep[rcol++] = ch;
348 TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
351 * If the character was quoted, replace the last character
352 * (the literal mark) with the new character. If quoted
353 * by someone else, simply insert the character.
355 * !!!
356 * Extension -- if the quoted character is HEX_CH, enter hex
357 * mode. If the user enters "<HEX_CH>[isxdigit()]*" we will
358 * try to use the value as a character. Anything else resets
359 * hex mode.
361 if (ikey.flags & CH_QUOTED)
362 goto ins_ch;
363 if (quoted == Q_THISCHAR) {
364 --sp->cno;
365 ++tp->owrite;
366 quoted = Q_NOTSET;
368 if (ch == HEX_CH)
369 hex = H_NEXTCHAR;
370 goto ins_ch;
373 switch (ikey.value) {
374 case K_CR:
375 case K_NL: /* New line. */
376 #define LINE_RESOLVE { \
377 /* \
378 * Handle abbreviations. If there was one, \
379 * discard the replay characters. \
380 */ \
381 if (abb == A_NOTSPACE && !replay) { \
382 if (txt_abbrev(sp, tp, &tmp, ch)) \
383 goto err; \
384 if (tmp) { \
385 if (LF_ISSET(TXT_RECORD)) \
386 rcol -= tmp; \
387 goto next_ch; \
390 if (abb != A_NOTSET) \
391 abb = A_SPACE; \
392 /* Handle hex numbers. */ \
393 if (hex == H_INHEX) { \
394 if (txt_hex(sp, tp, &tmp, ch)) \
395 goto err; \
396 if (tmp) { \
397 hex = H_NOTSET; \
398 goto next_ch; \
401 /* \
402 * The 'R' command returns any overwriteable \
403 * characters in the first line to the original \
404 * characters.
405 */ \
406 if (LF_ISSET(TXT_REPLACE) && tp->owrite && \
407 tp == tiqh->cqh_first) { \
408 memmove(tp->lb + sp->cno, \
409 lp + sp->cno, tp->owrite); \
410 tp->insert += tp->owrite; \
411 tp->owrite = 0; \
413 /* Delete any appended cursor. */ \
414 if (LF_ISSET(TXT_APPENDEOL)) { \
415 --tp->len; \
416 --tp->insert; \
419 LINE_RESOLVE;
421 /* CR returns from the vi command line. */
422 if (LF_ISSET(TXT_CR)) {
424 * If a script window and not the colon
425 * line, push a <cr> so it gets executed.
427 if (F_ISSET(sp, S_SCRIPT) &&
428 !LF_ISSET(TXT_INFOLINE))
429 (void)term_push(sp,
430 "\r", 1, 0, CH_NOMAP);
431 goto k_escape;
435 * Historic practice was to delete any <blank>
436 * characters following the inserted newline.
437 * This affects the 'R', 'c', and 's' commands.
439 for (p = tp->lb + sp->cno + tp->owrite;
440 tp->insert && isblank(*p);
441 ++p, ++tp->owrite, --tp->insert);
444 * Move any remaining insert characters into
445 * a new TEXT structure.
447 if ((ntp = text_init(sp,
448 tp->lb + sp->cno + tp->owrite,
449 tp->insert, tp->insert + 32)) == NULL)
450 goto err;
451 CIRCLEQ_INSERT_TAIL(tiqh, ntp, q);
453 /* Set bookkeeping for the new line. */
454 ntp->lno = tp->lno + 1;
455 ntp->insert = tp->insert;
458 * Note if the user inserted any characters on this
459 * line. Done before calling txt_ai_resolve() because
460 * it changes the value of sp->cno without making the
461 * corresponding changes to tp->ai.
463 tmp = sp->cno <= tp->ai;
466 * Resolve autoindented characters for the old line.
467 * Reset the autoindent line value. 0^D keeps the ai
468 * line from changing, ^D changes the level, even if
469 * there are no characters in the old line. Note,
470 * if using the current tp structure, use the cursor
471 * as the length, the user may have erased autoindent
472 * characters.
474 if (LF_ISSET(TXT_AUTOINDENT)) {
475 txt_ai_resolve(sp, tp);
477 if (carat_st == C_NOCHANGE) {
478 if (txt_auto(sp, ep,
479 OOBLNO, &ait, ait.ai, ntp))
480 goto err;
481 FREE_SPACE(sp, ait.lb, ait.lb_len);
482 } else
483 if (txt_auto(sp, ep,
484 OOBLNO, tp, sp->cno, ntp))
485 goto err;
486 carat_st = C_NOTSET;
490 * If the user hasn't entered any characters, delete
491 * any autoindent characters.
493 * !!!
494 * Historic vi didn't get the insert test right, if
495 * there were characters after the cursor, entering
496 * a <cr> left the autoindent characters on the line.
498 if (tmp)
499 sp->cno = 0;
501 /* Reset bookkeeping for the old line. */
502 tp->len = sp->cno;
503 tp->ai = tp->insert = tp->owrite = 0;
505 /* New cursor position. */
506 sp->cno = ntp->ai;
508 /* New lines are TXT_APPENDEOL if nothing to insert. */
509 if (ntp->insert == 0) {
510 TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
511 LF_SET(TXT_APPENDEOL);
512 ntp->lb[sp->cno] = CURSOR_CH;
513 ++ntp->insert;
514 ++ntp->len;
517 /* Update the old line. */
518 if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
519 goto err;
521 /* Swap old and new TEXT's. */
522 tp = ntp;
524 /* Reset the cursor. */
525 sp->lno = tp->lno;
527 /* Update the new line. */
528 if (sp->s_change(sp, ep, tp->lno, LINE_INSERT))
529 goto err;
531 /* Set the renumber bit. */
532 F_SET(sp, S_RENUMBER);
534 /* Refresh if nothing waiting. */
535 if ((margin || !KEYS_WAITING(sp)) &&
536 sp->s_refresh(sp, ep))
537 goto err;
538 goto next_ch;
539 case K_ESCAPE: /* Escape. */
540 if (!LF_ISSET(TXT_ESCAPE))
541 goto ins_ch;
543 LINE_RESOLVE;
546 * If there aren't any trailing characters in the line
547 * and the user hasn't entered any characters, delete
548 * the autoindent characters.
550 if (!tp->insert && sp->cno <= tp->ai) {
551 tp->len = tp->owrite = 0;
552 sp->cno = 0;
553 } else if (LF_ISSET(TXT_AUTOINDENT))
554 txt_ai_resolve(sp, tp);
556 /* If there are insert characters, copy them down. */
557 k_escape: if (tp->insert && tp->owrite)
558 memmove(tp->lb + sp->cno,
559 tp->lb + sp->cno + tp->owrite, tp->insert);
560 tp->len -= tp->owrite;
563 * Delete any lines that were inserted into the text
564 * structure and then erased.
566 while (tp->q.cqe_next != (void *)tiqh) {
567 ntp = tp->q.cqe_next;
568 CIRCLEQ_REMOVE(tiqh, ntp, q);
569 text_free(ntp);
573 * If not resolving the lines into the file, end
574 * it with a nul.
576 * XXX
577 * This is wrong, should pass back a length.
579 if (LF_ISSET(TXT_RESOLVE)) {
580 if (txt_resolve(sp, ep, tiqh))
581 goto err;
583 * Clear input flag -- input buffer no longer
584 * valid.
586 F_CLR(sp, S_INPUT);
587 } else {
588 TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
589 tp->lb[tp->len] = '\0';
593 * Set the return cursor position to rest on the last
594 * inserted character.
596 if (rp != NULL) {
597 rp->lno = tp->lno;
598 rp->cno = sp->cno ? sp->cno - 1 : 0;
599 if (sp->s_change(sp, ep, rp->lno, LINE_RESET))
600 goto err;
602 goto ret;
603 case K_CARAT: /* Delete autoindent chars. */
604 if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
605 carat_st = C_CARATSET;
606 goto ins_ch;
607 case K_ZERO: /* Delete autoindent chars. */
608 if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
609 carat_st = C_ZEROSET;
610 goto ins_ch;
611 case K_VEOF: /* Delete autoindent char. */
613 * If in the first column or no characters to erase,
614 * ignore the ^D (this matches historic practice). If
615 * not doing autoindent or already inserted non-ai
616 * characters, it's a literal. The latter test is done
617 * in the switch, as the CARAT forms are N + 1, not N.
619 if (!LF_ISSET(TXT_AUTOINDENT))
620 goto ins_ch;
621 if (sp->cno == 0 || tp->ai == 0)
622 break;
623 switch (carat_st) {
624 case C_CARATSET: /* ^^D */
625 if (sp->cno > tp->ai + tp->offset + 1)
626 goto ins_ch;
628 /* Save the ai string for later. */
629 ait.lb = NULL;
630 ait.lb_len = 0;
631 TBINC(sp, ait.lb, ait.lb_len, tp->ai);
632 memmove(ait.lb, tp->lb, tp->ai);
633 ait.ai = ait.len = tp->ai;
635 carat_st = C_NOCHANGE;
636 goto leftmargin;
637 case C_ZEROSET: /* 0^D */
638 if (sp->cno > tp->ai + tp->offset + 1)
639 goto ins_ch;
640 carat_st = C_NOTSET;
641 leftmargin: tp->lb[sp->cno - 1] = ' ';
642 tp->owrite += sp->cno - tp->offset;
643 tp->ai = 0;
644 sp->cno = tp->offset;
645 break;
646 case C_NOTSET: /* ^D */
647 if (sp->cno > tp->ai + tp->offset)
648 goto ins_ch;
649 (void)txt_outdent(sp, tp);
650 break;
651 default:
652 abort();
654 break;
655 case K_VERASE: /* Erase the last character. */
657 * If can erase over the prompt, return. Len is 0
658 * if backspaced over the prompt, 1 if only CR entered.
660 if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) {
661 tp->len = 0;
662 goto ret;
666 * If at the beginning of the line, try and drop back
667 * to a previously inserted line.
669 if (sp->cno == 0) {
670 if ((ntp = txt_backup(sp,
671 ep, tiqh, tp, flags)) == NULL)
672 goto err;
673 tp = ntp;
674 break;
677 /* If nothing to erase, bell the user. */
678 if (sp->cno <= tp->offset) {
679 msgq(sp, M_BERR,
680 "No more characters to erase.");
681 break;
684 /* Drop back one character. */
685 --sp->cno;
688 * Increment overwrite, decrement ai if deleted.
690 * !!!
691 * Historic vi did not permit users to use erase
692 * characters to delete autoindent characters.
694 ++tp->owrite;
695 if (sp->cno < tp->ai)
696 --tp->ai;
697 break;
698 case K_VWERASE: /* Skip back one word. */
700 * If at the beginning of the line, try and drop back
701 * to a previously inserted line.
703 if (sp->cno == 0) {
704 if ((ntp = txt_backup(sp,
705 ep, tiqh, tp, flags)) == NULL)
706 goto err;
707 tp = ntp;
711 * If at offset, nothing to erase so bell the user.
713 if (sp->cno <= tp->offset) {
714 msgq(sp, M_BERR,
715 "No more characters to erase.");
716 break;
720 * First werase goes back to any autoindent
721 * and second werase goes back to the offset.
723 * !!!
724 * Historic vi did not permit users to use erase
725 * characters to delete autoindent characters.
727 if (tp->ai && sp->cno > tp->ai)
728 max = tp->ai;
729 else {
730 tp->ai = 0;
731 max = tp->offset;
734 /* Skip over trailing space characters. */
735 while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) {
736 --sp->cno;
737 ++tp->owrite;
739 if (sp->cno == max)
740 break;
742 * There are three types of word erase found on UNIX
743 * systems. They can be identified by how the string
744 * /a/b/c is treated -- as 1, 3, or 6 words. Historic
745 * vi had two classes of characters, and strings were
746 * delimited by them and <blank>'s, so, 6 words. The
747 * historic tty interface used <blank>'s to delimit
748 * strings, so, 1 word. The algorithm offered in the
749 * 4.4BSD tty interface (as stty altwerase) treats it
750 * as 3 words -- there are two classes of characters,
751 * and strings are delimited by them and <blank>'s.
752 * The difference is that the type of the first erased
753 * character erased is ignored, which is exactly right
754 * when erasing pathname components. Here, the options
755 * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD
756 * tty interface and the historic tty driver behavior,
757 * respectively, and the default is the same as the
758 * historic vi behavior.
760 if (LF_ISSET(TXT_TTYWERASE))
761 while (sp->cno > max) {
762 --sp->cno;
763 ++tp->owrite;
764 if (isblank(tp->lb[sp->cno - 1]))
765 break;
767 else {
768 if (LF_ISSET(TXT_ALTWERASE)) {
769 --sp->cno;
770 ++tp->owrite;
771 if (isblank(tp->lb[sp->cno - 1]))
772 break;
774 if (sp->cno > max)
775 tmp = inword(tp->lb[sp->cno - 1]);
776 while (sp->cno > max) {
777 --sp->cno;
778 ++tp->owrite;
779 if (tmp != inword(tp->lb[sp->cno - 1])
780 || isblank(tp->lb[sp->cno - 1]))
781 break;
784 break;
785 case K_VKILL: /* Restart this line. */
787 * If at the beginning of the line, try and drop back
788 * to a previously inserted line.
790 if (sp->cno == 0) {
791 if ((ntp = txt_backup(sp,
792 ep, tiqh, tp, flags)) == NULL)
793 goto err;
794 tp = ntp;
797 /* If at offset, nothing to erase so bell the user. */
798 if (sp->cno <= tp->offset) {
799 msgq(sp, M_BERR,
800 "No more characters to erase.");
801 break;
805 * First kill goes back to any autoindent
806 * and second kill goes back to the offset.
808 * !!!
809 * Historic vi did not permit users to use erase
810 * characters to delete autoindent characters.
812 if (tp->ai && sp->cno > tp->ai)
813 max = tp->ai;
814 else {
815 tp->ai = 0;
816 max = tp->offset;
818 tp->owrite += sp->cno - max;
819 sp->cno = max;
820 break;
821 case K_CNTRLT: /* Add autoindent char. */
822 if (!LF_ISSET(TXT_CNTRLT))
823 goto ins_ch;
824 if (txt_indent(sp, tp))
825 goto err;
826 goto ebuf_chk;
827 case K_CNTRLZ:
828 (void)sp->s_suspend(sp);
829 break;
830 #ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT
831 case K_FORMFEED:
832 F_SET(sp, S_REFRESH);
833 break;
834 #endif
835 case K_RIGHTBRACE:
836 case K_RIGHTPAREN:
837 showmatch = LF_ISSET(TXT_SHOWMATCH);
838 goto ins_ch;
839 case K_VLNEXT: /* Quote the next character. */
840 /* If in hex mode, see if we've entered a hex value. */
841 if (hex == H_INHEX) {
842 if (txt_hex(sp, tp, &tmp, ch))
843 goto err;
844 if (tmp) {
845 hex = H_NOTSET;
846 goto next_ch;
849 ch = '^';
850 quoted = Q_NEXTCHAR;
851 /* FALLTHROUGH */
852 default: /* Insert the character. */
853 ins_ch: /*
854 * If entering a space character after a word, check
855 * for abbreviations. If there was one, discard the
856 * replay characters.
858 if (isblank(ch) && abb == A_NOTSPACE && !replay) {
859 if (txt_abbrev(sp, tp, &tmp, ch))
860 goto err;
861 if (tmp) {
862 if (LF_ISSET(TXT_RECORD))
863 rcol -= tmp;
864 goto next_ch;
867 /* If in hex mode, see if we've entered a hex value. */
868 if (hex == H_INHEX && !isxdigit(ch)) {
869 if (txt_hex(sp, tp, &tmp, ch))
870 goto err;
871 if (tmp) {
872 hex = H_NOTSET;
873 goto next_ch;
876 /* Check to see if we've crossed the margin. */
877 if (margin) {
878 if (sp->s_column(sp, ep, &col))
879 goto err;
880 if (col >= margin) {
881 if (txt_margin(sp, tp, &tmp, ch))
882 goto err;
883 if (tmp)
884 goto next_ch;
887 if (abb != A_NOTSET)
888 abb = isblank(ch) ? A_SPACE : A_NOTSPACE;
890 if (tp->owrite) /* Overwrite a character. */
891 --tp->owrite;
892 else if (tp->insert) { /* Insert a character. */
893 ++tp->len;
894 if (tp->insert == 1)
895 tp->lb[sp->cno + 1] = tp->lb[sp->cno];
896 else
897 memmove(tp->lb + sp->cno + 1,
898 tp->lb + sp->cno, tp->insert);
901 tp->lb[sp->cno++] = ch;
904 * If we've reached the end of the buffer, then we
905 * need to switch into insert mode. This happens
906 * when there's a change to a mark and the user puts
907 * in more characters than the length of the motion.
909 ebuf_chk: if (sp->cno >= tp->len) {
910 TBINC(sp, tp->lb, tp->lb_len, tp->len + 1);
911 LF_SET(TXT_APPENDEOL);
912 tp->lb[sp->cno] = CURSOR_CH;
913 ++tp->insert;
914 ++tp->len;
917 if (hex == H_NEXTCHAR)
918 hex = H_INHEX;
919 if (quoted == Q_NEXTCHAR)
920 quoted = Q_THISCHAR;
921 break;
923 #if defined(DEBUG) && 1
924 if (sp->cno + tp->insert + tp->owrite != tp->len)
925 msgq(sp, M_ERR,
926 "len %u != cno: %u ai: %u insert %u overwrite %u",
927 tp->len, sp->cno, tp->ai, tp->insert, tp->owrite);
928 tp->len = sp->cno + tp->insert + tp->owrite;
929 #endif
932 /* Clear input flag. */
933 ret: F_CLR(sp, S_INPUT);
935 if (LF_ISSET(TXT_RECORD))
936 VIP(sp)->rep_cnt = rcol;
937 return (eval);
939 /* Error jump. */
940 err: eval = 1;
941 txt_err(sp, ep, tiqh);
942 goto ret;
946 * txt_abbrev --
947 * Handle abbreviations.
949 static int
950 txt_abbrev(sp, tp, didsubp, pushc)
951 SCR *sp;
952 TEXT *tp;
953 int *didsubp;
954 ARG_CHAR_T pushc;
956 CHAR_T ch;
957 SEQ *qp;
958 size_t len, off;
959 char *p;
961 /* Find the beginning of this "word". */
962 for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
963 if (isblank(*p)) {
964 ++p;
965 break;
967 ++len;
968 if (off == tp->ai || off == tp->offset)
969 break;
972 /* Check for any abbreviations. */
973 if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) {
974 *didsubp = 0;
975 return (0);
979 * Push the abbreviation onto the tty stack. Historically, characters
980 * resulting from an abbreviation expansion were themselves subject to
981 * map expansions, O_SHOWMATCH matching etc. This means the expanded
982 * characters will be re-tested for abbreviations. It's difficult to
983 * know what historic practice in this case was, since abbreviations
984 * were applied to :colon command lines, so entering abbreviations that
985 * looped was tricky, although possible. In addition, obvious loops
986 * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will
987 * silently only implement and/or display the last abbreviation.)
989 * This implementation doesn't recover well from such abbreviations.
990 * The main input loop counts abbreviated characters, and, when it
991 * reaches a limit, discards any abbreviated characters on the queue.
992 * It's difficult to back up to the original position, as the replay
993 * queue would have to be adjusted, and the line state when an initial
994 * abbreviated character was received would have to be saved.
996 ch = pushc;
997 if (term_push(sp, &ch, 1, 0, CH_ABBREVIATED))
998 return (1);
999 if (term_push(sp, qp->output, qp->olen, 0, CH_ABBREVIATED))
1000 return (1);
1003 * Move the cursor to the start of the abbreviation,
1004 * adjust the length.
1006 sp->cno -= len;
1007 tp->len -= len;
1009 /* Copy any insert characters back. */
1010 if (tp->insert)
1011 memmove(tp->lb + sp->cno + tp->owrite,
1012 tp->lb + sp->cno + tp->owrite + len, tp->insert);
1015 * We return the length of the abbreviated characters. This is so
1016 * the calling routine can replace the replay characters with the
1017 * abbreviation. This means that subsequent '.' commands will produce
1018 * the same text, regardless of intervening :[un]abbreviate commands.
1019 * This is historic practice.
1021 *didsubp = len;
1022 return (0);
1025 /* Offset to next column of stop size. */
1026 #define STOP_OFF(c, stop) (stop - (c) % stop)
1029 * txt_ai_resolve --
1030 * When a line is resolved by <esc> or <cr>, review autoindent
1031 * characters.
1033 static void
1034 txt_ai_resolve(sp, tp)
1035 SCR *sp;
1036 TEXT *tp;
1038 u_long ts;
1039 int del;
1040 size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
1041 char *p;
1044 * If the line is empty, has an offset, or no autoindent
1045 * characters, we're done.
1047 if (!tp->len || tp->offset || !tp->ai)
1048 return;
1051 * The autoindent characters plus any leading <blank> characters
1052 * in the line are resolved into the minimum number of characters.
1053 * Historic practice.
1055 ts = O_VAL(sp, O_TABSTOP);
1057 /* Figure out the last <blank> screen column. */
1058 for (p = tp->lb, scno = 0, len = tp->len,
1059 spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
1060 if (*p == '\t') {
1061 if (spaces)
1062 tab_after_sp = 1;
1063 scno += STOP_OFF(scno, ts);
1064 } else {
1065 ++spaces;
1066 ++scno;
1070 * If there are no spaces, or no tabs after spaces and less than
1071 * ts spaces, it's already minimal.
1073 if (!spaces || !tab_after_sp && spaces < ts)
1074 return;
1076 /* Count up spaces/tabs needed to get to the target. */
1077 for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs)
1078 cno += STOP_OFF(cno, ts);
1079 spaces = scno - cno;
1082 * Figure out how many characters we're dropping -- if we're not
1083 * dropping any, it's already minimal, we're done.
1085 old = p - tp->lb;
1086 new = spaces + tabs;
1087 if (old == new)
1088 return;
1090 /* Shift the rest of the characters down, adjust the counts. */
1091 del = old - new;
1092 memmove(p - del, p, tp->len - old);
1093 sp->cno -= del;
1094 tp->len -= del;
1096 /* Fill in space/tab characters. */
1097 for (p = tp->lb; tabs--;)
1098 *p++ = '\t';
1099 while (spaces--)
1100 *p++ = ' ';
1104 * txt_auto --
1105 * Handle autoindent. If aitp isn't NULL, use it, otherwise,
1106 * retrieve the line.
1109 txt_auto(sp, ep, lno, aitp, len, tp)
1110 SCR *sp;
1111 EXF *ep;
1112 recno_t lno;
1113 size_t len;
1114 TEXT *aitp, *tp;
1116 size_t nlen;
1117 char *p, *t;
1119 if (aitp == NULL) {
1120 if ((p = t = file_gline(sp, ep, lno, &len)) == NULL)
1121 return (0);
1122 } else
1123 p = t = aitp->lb;
1124 for (nlen = 0; len; ++p) {
1125 if (!isblank(*p))
1126 break;
1127 /* If last character is a space, it counts. */
1128 if (--len == 0) {
1129 ++p;
1130 break;
1134 /* No indentation. */
1135 if (p == t)
1136 return (0);
1138 /* Set count. */
1139 nlen = p - t;
1141 /* Make sure the buffer's big enough. */
1142 BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
1144 /* Copy the indentation into the new buffer. */
1145 memmove(tp->lb + nlen, tp->lb, tp->len);
1146 memmove(tp->lb, t, nlen);
1147 tp->len += nlen;
1149 /* Return the additional length. */
1150 tp->ai = nlen;
1151 return (0);
1155 * txt_backup --
1156 * Back up to the previously edited line.
1158 static TEXT *
1159 txt_backup(sp, ep, tiqh, tp, flags)
1160 SCR *sp;
1161 EXF *ep;
1162 TEXTH *tiqh;
1163 TEXT *tp;
1164 u_int flags;
1166 TEXT *ntp;
1167 size_t col;
1169 if (tp->q.cqe_prev == (void *)tiqh) {
1170 msgq(sp, M_BERR, "Already at the beginning of the insert");
1171 return (tp);
1174 /* Update the old line on the screen. */
1175 if (sp->s_change(sp, ep, tp->lno, LINE_DELETE))
1176 return (NULL);
1178 /* Get a handle on the previous TEXT structure. */
1179 ntp = tp->q.cqe_prev;
1181 /* Make sure that we can get enough space. */
1182 if (LF_ISSET(TXT_APPENDEOL) && ntp->len + 1 > ntp->lb_len &&
1183 binc(sp, &ntp->lb, &ntp->lb_len, ntp->len + 1))
1184 return (NULL);
1187 * Release current TEXT; now committed to the swap, nothing
1188 * better fail.
1190 CIRCLEQ_REMOVE(tiqh, tp, q);
1191 text_free(tp);
1193 /* Swap TEXT's. */
1194 tp = ntp;
1196 /* Set bookkeeping information. */
1197 col = tp->len;
1198 if (LF_ISSET(TXT_APPENDEOL)) {
1199 tp->lb[col] = CURSOR_CH;
1200 ++tp->insert;
1201 ++tp->len;
1203 sp->lno = tp->lno;
1204 sp->cno = col;
1205 return (tp);
1209 * txt_err --
1210 * Handle an error during input processing.
1212 static void
1213 txt_err(sp, ep, tiqh)
1214 SCR *sp;
1215 EXF *ep;
1216 TEXTH *tiqh;
1218 recno_t lno;
1219 size_t len;
1222 * The problem with input processing is that the cursor is at an
1223 * indeterminate position since some input may have been lost due
1224 * to a malloc error. So, try to go back to the place from which
1225 * the cursor started, knowing that it may no longer be available.
1227 * We depend on at least one line number being set in the text
1228 * chain.
1230 for (lno = tiqh->cqh_first->lno;
1231 file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno);
1233 sp->lno = lno == 0 ? 1 : lno;
1234 sp->cno = 0;
1236 /* Redraw the screen, just in case. */
1237 F_SET(sp, S_REDRAW);
1241 * txt_hex --
1242 * Let the user insert any character value they want.
1244 * !!!
1245 * This is an extension. The pattern "^Vx[0-9a-fA-F]*" is a way
1246 * for the user to specify a character value which their keyboard
1247 * may not be able to enter.
1249 static int
1250 txt_hex(sp, tp, was_hex, pushc)
1251 SCR *sp;
1252 TEXT *tp;
1253 int *was_hex;
1254 ARG_CHAR_T pushc;
1256 CHAR_T ch, savec;
1257 size_t len, off;
1258 u_long value;
1259 char *p, *wp;
1262 * Null-terminate the string. Since nul isn't a legal hex value,
1263 * this should be okay, and lets us use a local routine, which
1264 * presumably understands the character set, to convert the value.
1266 savec = tp->lb[sp->cno];
1267 tp->lb[sp->cno] = 0;
1269 /* Find the previous HEX_CH. */
1270 for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
1271 if (*p == HEX_CH) {
1272 wp = p + 1;
1273 break;
1275 ++len;
1276 /* If not on this line, there's nothing to do. */
1277 if (off == tp->ai || off == tp->offset)
1278 goto nothex;
1281 /* If no length, then it wasn't a hex value. */
1282 if (len == 0)
1283 goto nothex;
1285 /* Get the value. */
1286 value = strtol(wp, NULL, 16);
1287 if (value == LONG_MIN || value == LONG_MAX || value > MAX_CHAR_T) {
1288 nothex: tp->lb[sp->cno] = savec;
1289 *was_hex = 0;
1290 return (0);
1293 ch = pushc;
1294 if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
1295 return (1);
1296 ch = value;
1297 if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
1298 return (1);
1300 tp->lb[sp->cno] = savec;
1302 /* Move the cursor to the start of the hex value, adjust the length. */
1303 sp->cno -= len + 1;
1304 tp->len -= len + 1;
1306 /* Copy any insert characters back. */
1307 if (tp->insert)
1308 memmove(tp->lb + sp->cno + tp->owrite,
1309 tp->lb + sp->cno + tp->owrite + len + 1, tp->insert);
1311 *was_hex = 1;
1312 return (0);
1316 * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements
1317 * to the next or previous shiftwidth value, i.e. for a 1-based numbering,
1318 * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to
1319 * the 10th column, and ^D moves it back.
1321 * !!!
1322 * The ^T and ^D characters in historical vi only had special meaning when
1323 * they were the first characters typed after entering text input mode.
1324 * Since normal erase characters couldn't erase autoindent (in this case
1325 * ^T) characters, this meant that inserting text into previously existing
1326 * text was quite strange, ^T only worked if it was the first keystroke,
1327 * and then it could only be erased by using ^D. This implementation treats
1328 * ^T specially anywhere it occurs in the input, and permits the standard
1329 * erase characters to erase characters inserted using it.
1331 * XXX
1332 * Technically, txt_indent, txt_outdent should part of the screen interface,
1333 * as they require knowledge of the size of a space character on the screen.
1334 * (Not the size of tabs, because tabs are logically composed of spaces.)
1335 * They're left in the text code because they're complicated, not to mention
1336 * the gruesome awareness that if spaces aren't a single column on the screen
1337 * for any language, we're into some serious, ah, for lack of a better word,
1338 * "issues".
1342 * txt_indent --
1343 * Handle ^T indents.
1345 static int
1346 txt_indent(sp, tp)
1347 SCR *sp;
1348 TEXT *tp;
1350 u_long sw, ts;
1351 size_t cno, off, scno, spaces, tabs;
1353 ts = O_VAL(sp, O_TABSTOP);
1354 sw = O_VAL(sp, O_SHIFTWIDTH);
1356 /* Get the current screen column. */
1357 for (off = scno = 0; off < sp->cno; ++off)
1358 if (tp->lb[off] == '\t')
1359 scno += STOP_OFF(scno, ts);
1360 else
1361 ++scno;
1363 /* Count up spaces/tabs needed to get to the target. */
1364 for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0;
1365 cno + STOP_OFF(cno, ts) <= scno; ++tabs)
1366 cno += STOP_OFF(cno, ts);
1367 spaces = scno - cno;
1369 /* Put space/tab characters in place of any overwrite characters. */
1370 for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai)
1371 tp->lb[sp->cno++] = '\t';
1372 for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai)
1373 tp->lb[sp->cno++] = ' ';
1375 if (!tabs && !spaces)
1376 return (0);
1378 /* Make sure there's enough room. */
1379 BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs);
1381 /* Move the insert characters out of the way. */
1382 if (tp->insert)
1383 memmove(tp->lb + sp->cno + spaces + tabs,
1384 tp->lb + sp->cno, tp->insert);
1386 /* Add new space/tab characters. */
1387 for (; tabs--; ++tp->len, ++tp->ai)
1388 tp->lb[sp->cno++] = '\t';
1389 for (; spaces--; ++tp->len, ++tp->ai)
1390 tp->lb[sp->cno++] = ' ';
1391 return (0);
1395 * txt_outdent --
1396 * Handle ^D outdents.
1399 static int
1400 txt_outdent(sp, tp)
1401 SCR *sp;
1402 TEXT *tp;
1404 u_long sw, ts;
1405 size_t cno, off, scno, spaces;
1407 ts = O_VAL(sp, O_TABSTOP);
1408 sw = O_VAL(sp, O_SHIFTWIDTH);
1410 /* Get the current screen column. */
1411 for (off = scno = 0; off < sp->cno; ++off)
1412 if (tp->lb[off] == '\t')
1413 scno += STOP_OFF(scno, ts);
1414 else
1415 ++scno;
1417 /* Get the previous shiftwidth column. */
1418 for (cno = scno; --scno % sw != 0;);
1420 /* Decrement characters until less than or equal to that slot. */
1421 for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite)
1422 if (tp->lb[--off] == '\t')
1423 cno -= STOP_OFF(cno, ts);
1424 else
1425 --cno;
1427 /* Spaces needed to get to the target. */
1428 spaces = scno - cno;
1430 /* Maybe just a delete. */
1431 if (spaces == 0)
1432 return (0);
1434 /* Make sure there's enough room. */
1435 BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces);
1437 /* Use up any overwrite characters. */
1438 for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite)
1439 tp->lb[sp->cno++] = ' ';
1441 /* Maybe that was enough. */
1442 if (spaces == 0)
1443 return (0);
1445 /* Move the insert characters out of the way. */
1446 if (tp->insert)
1447 memmove(tp->lb + sp->cno + spaces,
1448 tp->lb + sp->cno, tp->insert);
1450 /* Add new space characters. */
1451 for (; spaces--; ++tp->len, ++tp->ai)
1452 tp->lb[sp->cno++] = ' ';
1453 return (0);
1457 * txt_resolve --
1458 * Resolve the input text chain into the file.
1460 static int
1461 txt_resolve(sp, ep, tiqh)
1462 SCR *sp;
1463 EXF *ep;
1464 TEXTH *tiqh;
1466 TEXT *tp;
1467 recno_t lno;
1469 /* The first line replaces a current line. */
1470 tp = tiqh->cqh_first;
1471 if (file_sline(sp, ep, tp->lno, tp->lb, tp->len))
1472 return (1);
1474 /* All subsequent lines are appended into the file. */
1475 for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno)
1476 if (file_aline(sp, ep, 0, lno, tp->lb, tp->len))
1477 return (1);
1478 return (0);
1482 * txt_showmatch --
1483 * Show a character match.
1485 * !!!
1486 * Historic vi tried to display matches even in the :colon command line.
1487 * I think not.
1489 static void
1490 txt_showmatch(sp, ep)
1491 SCR *sp;
1492 EXF *ep;
1494 struct timeval second;
1495 VCS cs;
1496 MARK m;
1497 fd_set zero;
1498 int cnt, endc, startc;
1501 * Do a refresh first, in case the v_ntext() code hasn't done
1502 * one in awhile, so the user can see what we're complaining
1503 * about.
1505 if (sp->s_refresh(sp, ep))
1506 return;
1508 * We don't display the match if it's not on the screen. Find
1509 * out what the first character on the screen is.
1511 if (sp->s_position(sp, ep, &m, 0, P_TOP))
1512 return;
1514 /* Initialize the getc() interface. */
1515 cs.cs_lno = sp->lno;
1516 cs.cs_cno = sp->cno - 1;
1517 if (cs_init(sp, ep, &cs))
1518 return;
1519 startc = (endc = cs.cs_ch) == ')' ? '(' : '{';
1521 /* Search for the match. */
1522 for (cnt = 1;;) {
1523 if (cs_prev(sp, ep, &cs))
1524 return;
1525 if (cs.cs_lno < m.lno ||
1526 cs.cs_lno == m.lno && cs.cs_cno < m.cno)
1527 return;
1528 if (cs.cs_flags != 0) {
1529 if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
1530 (void)sp->s_bell(sp);
1531 return;
1533 continue;
1535 if (cs.cs_ch == endc)
1536 ++cnt;
1537 else if (cs.cs_ch == startc && --cnt == 0)
1538 break;
1541 /* Move to the match. */
1542 m.lno = sp->lno;
1543 m.cno = sp->cno;
1544 sp->lno = cs.cs_lno;
1545 sp->cno = cs.cs_cno;
1546 (void)sp->s_refresh(sp, ep);
1549 * Sleep(3) is eight system calls. Do it fast -- besides,
1550 * I don't want to wait an entire second.
1552 FD_ZERO(&zero);
1553 second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10;
1554 second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L;
1555 (void)select(0, &zero, &zero, &zero, &second);
1557 /* Return to the current location. */
1558 sp->lno = m.lno;
1559 sp->cno = m.cno;
1560 (void)sp->s_refresh(sp, ep);
1564 * txt_margin --
1565 * Handle margin wrap.
1567 * !!!
1568 * Historic vi belled the user each time a character was entered after
1569 * crossing the margin until a space was entered which could be used to
1570 * break the line. I don't, it tends to wake the cats.
1572 static int
1573 txt_margin(sp, tp, didbreak, pushc)
1574 SCR *sp;
1575 TEXT *tp;
1576 int *didbreak;
1577 ARG_CHAR_T pushc;
1579 CHAR_T ch;
1580 size_t len, off, tlen;
1581 char *p, *wp;
1583 /* Find the closest previous blank. */
1584 for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
1585 if (isblank(*p)) {
1586 wp = p + 1;
1587 break;
1589 ++len;
1590 /* If it's the beginning of the line, there's nothing to do. */
1591 if (off == tp->ai || off == tp->offset) {
1592 *didbreak = 0;
1593 return (0);
1598 * Historic practice is to delete any trailing whitespace
1599 * from the previous line.
1601 for (tlen = len;; --p, --off) {
1602 if (!isblank(*p))
1603 break;
1604 ++tlen;
1605 if (off == tp->ai || off == tp->offset)
1606 break;
1609 ch = pushc;
1610 if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED))
1611 return (1);
1612 if (len && term_push(sp, wp, len, 0, CH_NOMAP | CH_QUOTED))
1613 return (1);
1614 ch = '\n';
1615 if (term_push(sp, &ch, 1, 0, CH_NOMAP))
1616 return (1);
1618 sp->cno -= tlen;
1619 tp->owrite += tlen;
1620 *didbreak = 1;
1621 return (0);