1 /* $OpenBSD: chared.c,v 1.28 2017/04/12 18:24:37 tb Exp $ */
2 /* $NetBSD: chared.c,v 1.28 2009/12/30 22:37:40 christos Exp $ */
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * chared.c: Character editor utilities
49 /* value to leave unused in line buffer */
53 * Handle state for the vi undo command
58 c_undo_t
*vu
= &el
->el_chared
.c_undo
;
59 c_redo_t
*r
= &el
->el_chared
.c_redo
;
62 /* Save entire line for undo */
63 size
= el
->el_line
.lastchar
- el
->el_line
.buffer
;
65 vu
->cursor
= (int)(el
->el_line
.cursor
- el
->el_line
.buffer
);
66 (void)memcpy(vu
->buf
, el
->el_line
.buffer
, size
* sizeof(*vu
->buf
));
68 /* save command info for redo */
69 r
->count
= el
->el_state
.doingarg
? el
->el_state
.argument
: 0;
70 r
->action
= el
->el_chared
.c_vcmd
.action
;
72 r
->cmd
= el
->el_state
.thiscmd
;
73 r
->ch
= el
->el_state
.thisch
;
77 * Save yank/delete data for paste
80 cv_yank(EditLine
*el
, const wchar_t *ptr
, int size
)
82 c_kill_t
*k
= &el
->el_chared
.c_kill
;
84 (void)memcpy(k
->buf
, ptr
, size
* sizeof(*k
->buf
));
85 k
->last
= k
->buf
+ size
;
90 * Insert num characters
93 c_insert(EditLine
*el
, int num
)
97 if (el
->el_line
.lastchar
+ num
>= el
->el_line
.limit
) {
98 if (!ch_enlargebufs(el
, (size_t)num
))
99 return; /* can't go past end of buffer */
102 if (el
->el_line
.cursor
< el
->el_line
.lastchar
) {
103 /* if I must move chars */
104 for (cp
= el
->el_line
.lastchar
; cp
>= el
->el_line
.cursor
; cp
--)
107 el
->el_line
.lastchar
+= num
;
112 * Delete num characters after the cursor
115 c_delafter(EditLine
*el
, int num
)
118 if (el
->el_line
.cursor
+ num
> el
->el_line
.lastchar
)
119 num
= (int)(el
->el_line
.lastchar
- el
->el_line
.cursor
);
121 if (el
->el_map
.current
!= el
->el_map
.emacs
) {
123 cv_yank(el
, el
->el_line
.cursor
, num
);
129 for (cp
= el
->el_line
.cursor
; cp
<= el
->el_line
.lastchar
; cp
++)
132 el
->el_line
.lastchar
-= num
;
138 * Delete the character after the cursor, do not yank
141 c_delafter1(EditLine
*el
)
145 for (cp
= el
->el_line
.cursor
; cp
<= el
->el_line
.lastchar
; cp
++)
148 el
->el_line
.lastchar
--;
153 * Delete num characters before the cursor
156 c_delbefore(EditLine
*el
, int num
)
159 if (el
->el_line
.cursor
- num
< el
->el_line
.buffer
)
160 num
= (int)(el
->el_line
.cursor
- el
->el_line
.buffer
);
162 if (el
->el_map
.current
!= el
->el_map
.emacs
) {
164 cv_yank(el
, el
->el_line
.cursor
- num
, num
);
170 for (cp
= el
->el_line
.cursor
- num
;
171 cp
<= el
->el_line
.lastchar
;
175 el
->el_line
.lastchar
-= num
;
181 * Delete the character before the cursor, do not yank
184 c_delbefore1(EditLine
*el
)
188 for (cp
= el
->el_line
.cursor
- 1; cp
<= el
->el_line
.lastchar
; cp
++)
191 el
->el_line
.lastchar
--;
196 * Return if p is part of a word according to emacs
201 return iswalnum(p
) || wcschr(L
"*?_-.[]~=", p
) != NULL
;
206 * Return if p is part of a word according to vi
211 if (iswalnum(p
) || p
== L
'_')
220 * Return if p is part of a big word according to vi
230 * Find the previous word
233 c__prev_word(wchar_t *p
, wchar_t *low
, int n
, int (*wtest
)(wint_t))
238 while ((p
>= low
) && !(*wtest
)(*p
))
240 while ((p
>= low
) && (*wtest
)(*p
))
244 /* cp now points to one character before the word */
248 /* cp now points where we want it */
257 c__next_word(wchar_t *p
, wchar_t *high
, int n
, int (*wtest
)(wint_t))
260 while ((p
< high
) && !(*wtest
)(*p
))
262 while ((p
< high
) && (*wtest
)(*p
))
267 /* p now points where we want it */
272 * Find the next word vi style
275 cv_next_word(EditLine
*el
, wchar_t *p
, wchar_t *high
, int n
,
276 int (*wtest
)(wint_t))
282 while ((p
< high
) && (*wtest
)(*p
) == test
)
285 * vi historically deletes with cw only the word preserving the
286 * trailing whitespace! This is not what 'w' does..
288 if (n
|| el
->el_chared
.c_vcmd
.action
!= (DELETE
|INSERT
))
289 while ((p
< high
) && iswspace(*p
))
293 /* p now points where we want it */
302 * Find the previous word vi style
305 cv_prev_word(wchar_t *p
, wchar_t *low
, int n
, int (*wtest
)(wint_t))
311 while ((p
> low
) && iswspace(*p
))
314 while ((p
>= low
) && (*wtest
)(*p
) == test
)
319 /* p now points where we want it */
328 * Finish vi delete action
331 cv_delfini(EditLine
*el
)
334 int action
= el
->el_chared
.c_vcmd
.action
;
337 el
->el_map
.current
= el
->el_map
.key
;
339 if (el
->el_chared
.c_vcmd
.pos
== 0)
343 size
= (int)(el
->el_line
.cursor
- el
->el_chared
.c_vcmd
.pos
);
346 el
->el_line
.cursor
= el
->el_chared
.c_vcmd
.pos
;
349 cv_yank(el
, el
->el_line
.cursor
, size
);
351 cv_yank(el
, el
->el_line
.cursor
+ size
, -size
);
354 c_delafter(el
, size
);
355 re_refresh_cursor(el
);
357 c_delbefore(el
, -size
);
358 el
->el_line
.cursor
+= size
;
361 el
->el_chared
.c_vcmd
.action
= NOP
;
366 * Go to the end of this word according to vi
369 cv__endword(wchar_t *p
, wchar_t *high
, int n
, int (*wtest
)(wint_t))
376 while ((p
< high
) && iswspace(*p
))
380 while ((p
< high
) && (*wtest
)(*p
) == test
)
388 * Initialize the character editor
391 ch_init(EditLine
*el
)
393 el
->el_line
.buffer
= calloc(EL_BUFSIZ
, sizeof(*el
->el_line
.buffer
));
394 if (el
->el_line
.buffer
== NULL
)
396 el
->el_line
.cursor
= el
->el_line
.buffer
;
397 el
->el_line
.lastchar
= el
->el_line
.buffer
;
398 el
->el_line
.limit
= &el
->el_line
.buffer
[EL_BUFSIZ
- EL_LEAVE
];
400 el
->el_chared
.c_undo
.buf
= calloc(EL_BUFSIZ
,
401 sizeof(*el
->el_chared
.c_undo
.buf
));
402 if (el
->el_chared
.c_undo
.buf
== NULL
)
404 el
->el_chared
.c_undo
.len
= -1;
405 el
->el_chared
.c_undo
.cursor
= 0;
407 el
->el_chared
.c_redo
.buf
= reallocarray(NULL
, EL_BUFSIZ
,
408 sizeof(*el
->el_chared
.c_redo
.buf
));
409 if (el
->el_chared
.c_redo
.buf
== NULL
)
411 el
->el_chared
.c_redo
.pos
= el
->el_chared
.c_redo
.buf
;
412 el
->el_chared
.c_redo
.lim
= el
->el_chared
.c_redo
.buf
+ EL_BUFSIZ
;
413 el
->el_chared
.c_redo
.cmd
= ED_UNASSIGNED
;
415 el
->el_chared
.c_vcmd
.action
= NOP
;
416 el
->el_chared
.c_vcmd
.pos
= el
->el_line
.buffer
;
418 el
->el_chared
.c_kill
.buf
= calloc(EL_BUFSIZ
,
419 sizeof(*el
->el_chared
.c_kill
.buf
));
420 if (el
->el_chared
.c_kill
.buf
== NULL
)
422 el
->el_chared
.c_kill
.mark
= el
->el_line
.buffer
;
423 el
->el_chared
.c_kill
.last
= el
->el_chared
.c_kill
.buf
;
424 el
->el_chared
.c_resizefun
= NULL
;
425 el
->el_chared
.c_resizearg
= NULL
;
427 el
->el_map
.current
= el
->el_map
.key
;
429 el
->el_state
.inputmode
= MODE_INSERT
; /* XXX: save a default */
430 el
->el_state
.doingarg
= 0;
431 el
->el_state
.metanext
= 0;
432 el
->el_state
.argument
= 1;
433 el
->el_state
.lastcmd
= ED_UNASSIGNED
;
439 * Reset the character editor
442 ch_reset(EditLine
*el
)
444 el
->el_line
.cursor
= el
->el_line
.buffer
;
445 el
->el_line
.lastchar
= el
->el_line
.buffer
;
447 el
->el_chared
.c_undo
.len
= -1;
448 el
->el_chared
.c_undo
.cursor
= 0;
450 el
->el_chared
.c_vcmd
.action
= NOP
;
451 el
->el_chared
.c_vcmd
.pos
= el
->el_line
.buffer
;
453 el
->el_chared
.c_kill
.mark
= el
->el_line
.buffer
;
455 el
->el_map
.current
= el
->el_map
.key
;
457 el
->el_state
.inputmode
= MODE_INSERT
; /* XXX: save a default */
458 el
->el_state
.doingarg
= 0;
459 el
->el_state
.metanext
= 0;
460 el
->el_state
.argument
= 1;
461 el
->el_state
.lastcmd
= ED_UNASSIGNED
;
463 el
->el_history
.eventno
= 0;
467 * Enlarge line buffer to be able to hold twice as much characters.
468 * Returns 1 if successful, 0 if not.
471 ch_enlargebufs(EditLine
*el
, size_t addlen
)
474 wchar_t *newbuffer
, *oldbuf
, *oldkbuf
;
476 sz
= el
->el_line
.limit
- el
->el_line
.buffer
+ EL_LEAVE
;
479 * If newly required length is longer than current buffer, we need
480 * to make the buffer big enough to hold both old and new stuff.
483 while(newsz
- sz
< addlen
)
488 * Reallocate line buffer.
490 newbuffer
= recallocarray(el
->el_line
.buffer
, sz
, newsz
,
495 oldbuf
= el
->el_line
.buffer
;
497 el
->el_line
.buffer
= newbuffer
;
498 el
->el_line
.cursor
= newbuffer
+ (el
->el_line
.cursor
- oldbuf
);
499 el
->el_line
.lastchar
= newbuffer
+ (el
->el_line
.lastchar
- oldbuf
);
500 /* don't set new size until all buffers are enlarged */
501 el
->el_line
.limit
= &newbuffer
[sz
- EL_LEAVE
];
504 * Reallocate kill buffer.
506 newbuffer
= recallocarray(el
->el_chared
.c_kill
.buf
, sz
, newsz
,
511 oldkbuf
= el
->el_chared
.c_kill
.buf
;
513 el
->el_chared
.c_kill
.buf
= newbuffer
;
514 el
->el_chared
.c_kill
.last
= newbuffer
+
515 (el
->el_chared
.c_kill
.last
- oldkbuf
);
516 el
->el_chared
.c_kill
.mark
= el
->el_line
.buffer
+
517 (el
->el_chared
.c_kill
.mark
- oldbuf
);
520 * Reallocate undo buffer.
522 newbuffer
= recallocarray(el
->el_chared
.c_undo
.buf
, sz
, newsz
,
526 el
->el_chared
.c_undo
.buf
= newbuffer
;
528 newbuffer
= reallocarray(el
->el_chared
.c_redo
.buf
,
529 newsz
, sizeof(*newbuffer
));
532 el
->el_chared
.c_redo
.pos
= newbuffer
+
533 (el
->el_chared
.c_redo
.pos
- el
->el_chared
.c_redo
.buf
);
534 el
->el_chared
.c_redo
.lim
= newbuffer
+
535 (el
->el_chared
.c_redo
.lim
- el
->el_chared
.c_redo
.buf
);
536 el
->el_chared
.c_redo
.buf
= newbuffer
;
538 if (!hist_enlargebuf(el
, sz
, newsz
))
541 /* Safe to set enlarged buffer size */
542 el
->el_line
.limit
= &el
->el_line
.buffer
[newsz
- EL_LEAVE
];
543 if (el
->el_chared
.c_resizefun
)
544 (*el
->el_chared
.c_resizefun
)(el
, el
->el_chared
.c_resizearg
);
549 * Free the data structures used by the editor
554 free(el
->el_line
.buffer
);
555 el
->el_line
.buffer
= NULL
;
556 el
->el_line
.limit
= NULL
;
557 free(el
->el_chared
.c_undo
.buf
);
558 el
->el_chared
.c_undo
.buf
= NULL
;
559 free(el
->el_chared
.c_redo
.buf
);
560 el
->el_chared
.c_redo
.buf
= NULL
;
561 el
->el_chared
.c_redo
.pos
= NULL
;
562 el
->el_chared
.c_redo
.lim
= NULL
;
563 el
->el_chared
.c_redo
.cmd
= ED_UNASSIGNED
;
564 free(el
->el_chared
.c_kill
.buf
);
565 el
->el_chared
.c_kill
.buf
= NULL
;
571 * Insert string at cursorI
574 el_winsertstr(EditLine
*el
, const wchar_t *s
)
578 if ((len
= wcslen(s
)) == 0)
580 if (el
->el_line
.lastchar
+ len
>= el
->el_line
.limit
) {
581 if (!ch_enlargebufs(el
, len
))
585 c_insert(el
, (int)len
);
587 *el
->el_line
.cursor
++ = *s
++;
593 * Delete num characters before the cursor
596 el_deletestr(EditLine
*el
, int n
)
601 if (el
->el_line
.cursor
< &el
->el_line
.buffer
[n
])
604 c_delbefore(el
, n
); /* delete before dot */
605 el
->el_line
.cursor
-= n
;
606 if (el
->el_line
.cursor
< el
->el_line
.buffer
)
607 el
->el_line
.cursor
= el
->el_line
.buffer
;
614 c_gets(EditLine
*el
, wchar_t *buf
, const wchar_t *prompt
)
617 wchar_t *cp
= el
->el_line
.buffer
, ch
;
620 len
= wcslen(prompt
);
621 (void)memcpy(cp
, prompt
, len
* sizeof(*cp
));
627 el
->el_line
.cursor
= cp
;
629 el
->el_line
.lastchar
= cp
+ 1;
632 if (el_wgetc(el
, &ch
) != 1) {
633 ed_end_of_file(el
, 0);
640 case L
'\b': /* Delete and backspace */
651 case L
'\r': /* Newline */
657 if (len
>= EL_BUFSIZ
- 16)
668 el
->el_line
.buffer
[0] = '\0';
669 el
->el_line
.lastchar
= el
->el_line
.buffer
;
670 el
->el_line
.cursor
= el
->el_line
.buffer
;
676 * Return the current horizontal position of the cursor
684 * Find how many characters till the beginning of this line.
686 if (el
->el_line
.cursor
== el
->el_line
.buffer
)
689 for (ptr
= el
->el_line
.cursor
- 1;
690 ptr
>= el
->el_line
.buffer
&& *ptr
!= '\n';
693 return (int)(el
->el_line
.cursor
- ptr
- 1);
698 ch_resizefun(EditLine
*el
, el_zfunc_t f
, void *a
)
700 el
->el_chared
.c_resizefun
= f
;
701 el
->el_chared
.c_resizearg
= a
;