chared.c revision 1.53 1 /* $NetBSD: chared.c,v 1.53 2016/04/11 18:56:31 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include "config.h"
36 #if !defined(lint) && !defined(SCCSID)
37 #if 0
38 static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93";
39 #else
40 __RCSID("$NetBSD: chared.c,v 1.53 2016/04/11 18:56:31 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43
44 /*
45 * chared.c: Character editor utilities
46 */
47 #include <ctype.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include "el.h"
52 #include "common.h"
53
54 static void ch__clearmacro (EditLine *);
55
56 /* value to leave unused in line buffer */
57 #define EL_LEAVE 2
58
59 /* cv_undo():
60 * Handle state for the vi undo command
61 */
62 protected void
63 cv_undo(EditLine *el)
64 {
65 c_undo_t *vu = &el->el_chared.c_undo;
66 c_redo_t *r = &el->el_chared.c_redo;
67 size_t size;
68
69 /* Save entire line for undo */
70 size = (size_t)(el->el_line.lastchar - el->el_line.buffer);
71 vu->len = (ssize_t)size;
72 vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer);
73 (void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf));
74
75 /* save command info for redo */
76 r->count = el->el_state.doingarg ? el->el_state.argument : 0;
77 r->action = el->el_chared.c_vcmd.action;
78 r->pos = r->buf;
79 r->cmd = el->el_state.thiscmd;
80 r->ch = el->el_state.thisch;
81 }
82
83 /* cv_yank():
84 * Save yank/delete data for paste
85 */
86 protected void
87 cv_yank(EditLine *el, const wchar_t *ptr, int size)
88 {
89 c_kill_t *k = &el->el_chared.c_kill;
90
91 (void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf));
92 k->last = k->buf + size;
93 }
94
95
96 /* c_insert():
97 * Insert num characters
98 */
99 protected void
100 c_insert(EditLine *el, int num)
101 {
102 wchar_t *cp;
103
104 if (el->el_line.lastchar + num >= el->el_line.limit) {
105 if (!ch_enlargebufs(el, (size_t)num))
106 return; /* can't go past end of buffer */
107 }
108
109 if (el->el_line.cursor < el->el_line.lastchar) {
110 /* if I must move chars */
111 for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
112 cp[num] = *cp;
113 }
114 el->el_line.lastchar += num;
115 }
116
117
118 /* c_delafter():
119 * Delete num characters after the cursor
120 */
121 protected void
122 c_delafter(EditLine *el, int num)
123 {
124
125 if (el->el_line.cursor + num > el->el_line.lastchar)
126 num = (int)(el->el_line.lastchar - el->el_line.cursor);
127
128 if (el->el_map.current != el->el_map.emacs) {
129 cv_undo(el);
130 cv_yank(el, el->el_line.cursor, num);
131 }
132
133 if (num > 0) {
134 wchar_t *cp;
135
136 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
137 *cp = cp[num];
138
139 el->el_line.lastchar -= num;
140 }
141 }
142
143
144 /* c_delafter1():
145 * Delete the character after the cursor, do not yank
146 */
147 protected void
148 c_delafter1(EditLine *el)
149 {
150 wchar_t *cp;
151
152 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
153 *cp = cp[1];
154
155 el->el_line.lastchar--;
156 }
157
158
159 /* c_delbefore():
160 * Delete num characters before the cursor
161 */
162 protected void
163 c_delbefore(EditLine *el, int num)
164 {
165
166 if (el->el_line.cursor - num < el->el_line.buffer)
167 num = (int)(el->el_line.cursor - el->el_line.buffer);
168
169 if (el->el_map.current != el->el_map.emacs) {
170 cv_undo(el);
171 cv_yank(el, el->el_line.cursor - num, num);
172 }
173
174 if (num > 0) {
175 wchar_t *cp;
176
177 for (cp = el->el_line.cursor - num;
178 cp <= el->el_line.lastchar;
179 cp++)
180 *cp = cp[num];
181
182 el->el_line.lastchar -= num;
183 }
184 }
185
186
187 /* c_delbefore1():
188 * Delete the character before the cursor, do not yank
189 */
190 protected void
191 c_delbefore1(EditLine *el)
192 {
193 wchar_t *cp;
194
195 for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++)
196 *cp = cp[1];
197
198 el->el_line.lastchar--;
199 }
200
201
202 /* ce__isword():
203 * Return if p is part of a word according to emacs
204 */
205 protected int
206 ce__isword(wint_t p)
207 {
208 return iswalnum(p) || wcschr(L"*?_-.[]~=", p) != NULL;
209 }
210
211
212 /* cv__isword():
213 * Return if p is part of a word according to vi
214 */
215 protected int
216 cv__isword(wint_t p)
217 {
218 if (iswalnum(p) || p == L'_')
219 return 1;
220 if (iswgraph(p))
221 return 2;
222 return 0;
223 }
224
225
226 /* cv__isWord():
227 * Return if p is part of a big word according to vi
228 */
229 protected int
230 cv__isWord(wint_t p)
231 {
232 return !iswspace(p);
233 }
234
235
236 /* c__prev_word():
237 * Find the previous word
238 */
239 protected wchar_t *
240 c__prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t))
241 {
242 p--;
243
244 while (n--) {
245 while ((p >= low) && !(*wtest)(*p))
246 p--;
247 while ((p >= low) && (*wtest)(*p))
248 p--;
249 }
250
251 /* cp now points to one character before the word */
252 p++;
253 if (p < low)
254 p = low;
255 /* cp now points where we want it */
256 return p;
257 }
258
259
260 /* c__next_word():
261 * Find the next word
262 */
263 protected wchar_t *
264 c__next_word(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t))
265 {
266 while (n--) {
267 while ((p < high) && !(*wtest)(*p))
268 p++;
269 while ((p < high) && (*wtest)(*p))
270 p++;
271 }
272 if (p > high)
273 p = high;
274 /* p now points where we want it */
275 return p;
276 }
277
278 /* cv_next_word():
279 * Find the next word vi style
280 */
281 protected wchar_t *
282 cv_next_word(EditLine *el, wchar_t *p, wchar_t *high, int n,
283 int (*wtest)(wint_t))
284 {
285 int test;
286
287 while (n--) {
288 test = (*wtest)(*p);
289 while ((p < high) && (*wtest)(*p) == test)
290 p++;
291 /*
292 * vi historically deletes with cw only the word preserving the
293 * trailing whitespace! This is not what 'w' does..
294 */
295 if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT))
296 while ((p < high) && iswspace(*p))
297 p++;
298 }
299
300 /* p now points where we want it */
301 if (p > high)
302 return high;
303 else
304 return p;
305 }
306
307
308 /* cv_prev_word():
309 * Find the previous word vi style
310 */
311 protected wchar_t *
312 cv_prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t))
313 {
314 int test;
315
316 p--;
317 while (n--) {
318 while ((p > low) && iswspace(*p))
319 p--;
320 test = (*wtest)(*p);
321 while ((p >= low) && (*wtest)(*p) == test)
322 p--;
323 }
324 p++;
325
326 /* p now points where we want it */
327 if (p < low)
328 return low;
329 else
330 return p;
331 }
332
333
334 /* cv_delfini():
335 * Finish vi delete action
336 */
337 protected void
338 cv_delfini(EditLine *el)
339 {
340 int size;
341 int action = el->el_chared.c_vcmd.action;
342
343 if (action & INSERT)
344 el->el_map.current = el->el_map.key;
345
346 if (el->el_chared.c_vcmd.pos == 0)
347 /* sanity */
348 return;
349
350 size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos);
351 if (size == 0)
352 size = 1;
353 el->el_line.cursor = el->el_chared.c_vcmd.pos;
354 if (action & YANK) {
355 if (size > 0)
356 cv_yank(el, el->el_line.cursor, size);
357 else
358 cv_yank(el, el->el_line.cursor + size, -size);
359 } else {
360 if (size > 0) {
361 c_delafter(el, size);
362 re_refresh_cursor(el);
363 } else {
364 c_delbefore(el, -size);
365 el->el_line.cursor += size;
366 }
367 }
368 el->el_chared.c_vcmd.action = NOP;
369 }
370
371
372 /* cv__endword():
373 * Go to the end of this word according to vi
374 */
375 protected wchar_t *
376 cv__endword(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t))
377 {
378 int test;
379
380 p++;
381
382 while (n--) {
383 while ((p < high) && iswspace(*p))
384 p++;
385
386 test = (*wtest)(*p);
387 while ((p < high) && (*wtest)(*p) == test)
388 p++;
389 }
390 p--;
391 return p;
392 }
393
394 /* ch_init():
395 * Initialize the character editor
396 */
397 protected int
398 ch_init(EditLine *el)
399 {
400 c_macro_t *ma = &el->el_chared.c_macro;
401
402 el->el_line.buffer = el_malloc(EL_BUFSIZ *
403 sizeof(*el->el_line.buffer));
404 if (el->el_line.buffer == NULL)
405 return -1;
406
407 (void) memset(el->el_line.buffer, 0, EL_BUFSIZ *
408 sizeof(*el->el_line.buffer));
409 el->el_line.cursor = el->el_line.buffer;
410 el->el_line.lastchar = el->el_line.buffer;
411 el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE];
412
413 el->el_chared.c_undo.buf = el_malloc(EL_BUFSIZ *
414 sizeof(*el->el_chared.c_undo.buf));
415 if (el->el_chared.c_undo.buf == NULL)
416 return -1;
417 (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ *
418 sizeof(*el->el_chared.c_undo.buf));
419 el->el_chared.c_undo.len = -1;
420 el->el_chared.c_undo.cursor = 0;
421 el->el_chared.c_redo.buf = el_malloc(EL_BUFSIZ *
422 sizeof(*el->el_chared.c_redo.buf));
423 if (el->el_chared.c_redo.buf == NULL)
424 return -1;
425 el->el_chared.c_redo.pos = el->el_chared.c_redo.buf;
426 el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ;
427 el->el_chared.c_redo.cmd = ED_UNASSIGNED;
428
429 el->el_chared.c_vcmd.action = NOP;
430 el->el_chared.c_vcmd.pos = el->el_line.buffer;
431
432 el->el_chared.c_kill.buf = el_malloc(EL_BUFSIZ *
433 sizeof(*el->el_chared.c_kill.buf));
434 if (el->el_chared.c_kill.buf == NULL)
435 return -1;
436 (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ *
437 sizeof(*el->el_chared.c_kill.buf));
438 el->el_chared.c_kill.mark = el->el_line.buffer;
439 el->el_chared.c_kill.last = el->el_chared.c_kill.buf;
440 el->el_chared.c_resizefun = NULL;
441 el->el_chared.c_resizearg = NULL;
442 el->el_chared.c_aliasfun = NULL;
443 el->el_chared.c_aliasarg = NULL;
444
445 el->el_map.current = el->el_map.key;
446
447 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
448 el->el_state.doingarg = 0;
449 el->el_state.metanext = 0;
450 el->el_state.argument = 1;
451 el->el_state.lastcmd = ED_UNASSIGNED;
452
453 ma->level = -1;
454 ma->offset = 0;
455 ma->macro = el_malloc(EL_MAXMACRO * sizeof(*ma->macro));
456 if (ma->macro == NULL)
457 return -1;
458 return 0;
459 }
460
461 /* ch_reset():
462 * Reset the character editor
463 */
464 protected void
465 ch_reset(EditLine *el, int mclear)
466 {
467 el->el_line.cursor = el->el_line.buffer;
468 el->el_line.lastchar = el->el_line.buffer;
469
470 el->el_chared.c_undo.len = -1;
471 el->el_chared.c_undo.cursor = 0;
472
473 el->el_chared.c_vcmd.action = NOP;
474 el->el_chared.c_vcmd.pos = el->el_line.buffer;
475
476 el->el_chared.c_kill.mark = el->el_line.buffer;
477
478 el->el_map.current = el->el_map.key;
479
480 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
481 el->el_state.doingarg = 0;
482 el->el_state.metanext = 0;
483 el->el_state.argument = 1;
484 el->el_state.lastcmd = ED_UNASSIGNED;
485
486 el->el_history.eventno = 0;
487
488 if (mclear)
489 ch__clearmacro(el);
490 }
491
492 static void
493 ch__clearmacro(EditLine *el)
494 {
495 c_macro_t *ma = &el->el_chared.c_macro;
496 while (ma->level >= 0)
497 el_free(ma->macro[ma->level--]);
498 }
499
500 /* ch_enlargebufs():
501 * Enlarge line buffer to be able to hold twice as much characters.
502 * Returns 1 if successful, 0 if not.
503 */
504 protected int
505 ch_enlargebufs(EditLine *el, size_t addlen)
506 {
507 size_t sz, newsz;
508 wchar_t *newbuffer, *oldbuf, *oldkbuf;
509
510 sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE);
511 newsz = sz * 2;
512 /*
513 * If newly required length is longer than current buffer, we need
514 * to make the buffer big enough to hold both old and new stuff.
515 */
516 if (addlen > sz) {
517 while(newsz - sz < addlen)
518 newsz *= 2;
519 }
520
521 /*
522 * Reallocate line buffer.
523 */
524 newbuffer = el_realloc(el->el_line.buffer, newsz * sizeof(*newbuffer));
525 if (!newbuffer)
526 return 0;
527
528 /* zero the newly added memory, leave old data in */
529 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
530
531 oldbuf = el->el_line.buffer;
532
533 el->el_line.buffer = newbuffer;
534 el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
535 el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
536 /* don't set new size until all buffers are enlarged */
537 el->el_line.limit = &newbuffer[sz - EL_LEAVE];
538
539 /*
540 * Reallocate kill buffer.
541 */
542 newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz *
543 sizeof(*newbuffer));
544 if (!newbuffer)
545 return 0;
546
547 /* zero the newly added memory, leave old data in */
548 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
549
550 oldkbuf = el->el_chared.c_kill.buf;
551
552 el->el_chared.c_kill.buf = newbuffer;
553 el->el_chared.c_kill.last = newbuffer +
554 (el->el_chared.c_kill.last - oldkbuf);
555 el->el_chared.c_kill.mark = el->el_line.buffer +
556 (el->el_chared.c_kill.mark - oldbuf);
557
558 /*
559 * Reallocate undo buffer.
560 */
561 newbuffer = el_realloc(el->el_chared.c_undo.buf,
562 newsz * sizeof(*newbuffer));
563 if (!newbuffer)
564 return 0;
565
566 /* zero the newly added memory, leave old data in */
567 (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer));
568 el->el_chared.c_undo.buf = newbuffer;
569
570 newbuffer = el_realloc(el->el_chared.c_redo.buf,
571 newsz * sizeof(*newbuffer));
572 if (!newbuffer)
573 return 0;
574 el->el_chared.c_redo.pos = newbuffer +
575 (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf);
576 el->el_chared.c_redo.lim = newbuffer +
577 (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf);
578 el->el_chared.c_redo.buf = newbuffer;
579
580 if (!hist_enlargebuf(el, sz, newsz))
581 return 0;
582
583 /* Safe to set enlarged buffer size */
584 el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE];
585 if (el->el_chared.c_resizefun)
586 (*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg);
587 return 1;
588 }
589
590 /* ch_end():
591 * Free the data structures used by the editor
592 */
593 protected void
594 ch_end(EditLine *el)
595 {
596 el_free(el->el_line.buffer);
597 el->el_line.buffer = NULL;
598 el->el_line.limit = NULL;
599 el_free(el->el_chared.c_undo.buf);
600 el->el_chared.c_undo.buf = NULL;
601 el_free(el->el_chared.c_redo.buf);
602 el->el_chared.c_redo.buf = NULL;
603 el->el_chared.c_redo.pos = NULL;
604 el->el_chared.c_redo.lim = NULL;
605 el->el_chared.c_redo.cmd = ED_UNASSIGNED;
606 el_free(el->el_chared.c_kill.buf);
607 el->el_chared.c_kill.buf = NULL;
608 ch_reset(el, 1);
609 el_free(el->el_chared.c_macro.macro);
610 el->el_chared.c_macro.macro = NULL;
611 }
612
613
614 /* el_insertstr():
615 * Insert string at cursorI
616 */
617 int
618 el_winsertstr(EditLine *el, const wchar_t *s)
619 {
620 size_t len;
621
622 if (s == NULL || (len = wcslen(s)) == 0)
623 return -1;
624 if (el->el_line.lastchar + len >= el->el_line.limit) {
625 if (!ch_enlargebufs(el, len))
626 return -1;
627 }
628
629 c_insert(el, (int)len);
630 while (*s)
631 *el->el_line.cursor++ = *s++;
632 return 0;
633 }
634
635
636 /* el_deletestr():
637 * Delete num characters before the cursor
638 */
639 void
640 el_deletestr(EditLine *el, int n)
641 {
642 if (n <= 0)
643 return;
644
645 if (el->el_line.cursor < &el->el_line.buffer[n])
646 return;
647
648 c_delbefore(el, n); /* delete before dot */
649 el->el_line.cursor -= n;
650 if (el->el_line.cursor < el->el_line.buffer)
651 el->el_line.cursor = el->el_line.buffer;
652 }
653
654 /* el_cursor():
655 * Move the cursor to the left or the right of the current position
656 */
657 int
658 el_cursor(EditLine *el, int n)
659 {
660 if (n == 0)
661 goto out;
662
663 el->el_line.cursor += n;
664
665 if (el->el_line.cursor < el->el_line.buffer)
666 el->el_line.cursor = el->el_line.buffer;
667 if (el->el_line.cursor > el->el_line.lastchar)
668 el->el_line.cursor = el->el_line.lastchar;
669 out:
670 return (int)(el->el_line.cursor - el->el_line.buffer);
671 }
672
673 /* c_gets():
674 * Get a string
675 */
676 protected int
677 c_gets(EditLine *el, wchar_t *buf, const wchar_t *prompt)
678 {
679 ssize_t len;
680 wchar_t *cp = el->el_line.buffer, ch;
681
682 if (prompt) {
683 len = (ssize_t)wcslen(prompt);
684 (void)memcpy(cp, prompt, (size_t)len * sizeof(*cp));
685 cp += len;
686 }
687 len = 0;
688
689 for (;;) {
690 el->el_line.cursor = cp;
691 *cp = ' ';
692 el->el_line.lastchar = cp + 1;
693 re_refresh(el);
694
695 if (el_wgetc(el, &ch) != 1) {
696 ed_end_of_file(el, 0);
697 len = -1;
698 break;
699 }
700
701 switch (ch) {
702
703 case L'\b': /* Delete and backspace */
704 case 0177:
705 if (len == 0) {
706 len = -1;
707 break;
708 }
709 len--;
710 cp--;
711 continue;
712
713 case 0033: /* ESC */
714 case L'\r': /* Newline */
715 case L'\n':
716 buf[len] = ch;
717 break;
718
719 default:
720 if (len >= (ssize_t)(EL_BUFSIZ - 16))
721 terminal_beep(el);
722 else {
723 buf[len++] = ch;
724 *cp++ = ch;
725 }
726 continue;
727 }
728 break;
729 }
730
731 el->el_line.buffer[0] = '\0';
732 el->el_line.lastchar = el->el_line.buffer;
733 el->el_line.cursor = el->el_line.buffer;
734 return (int)len;
735 }
736
737
738 /* c_hpos():
739 * Return the current horizontal position of the cursor
740 */
741 protected int
742 c_hpos(EditLine *el)
743 {
744 wchar_t *ptr;
745
746 /*
747 * Find how many characters till the beginning of this line.
748 */
749 if (el->el_line.cursor == el->el_line.buffer)
750 return 0;
751 else {
752 for (ptr = el->el_line.cursor - 1;
753 ptr >= el->el_line.buffer && *ptr != '\n';
754 ptr--)
755 continue;
756 return (int)(el->el_line.cursor - ptr - 1);
757 }
758 }
759
760 protected int
761 ch_resizefun(EditLine *el, el_zfunc_t f, void *a)
762 {
763 el->el_chared.c_resizefun = f;
764 el->el_chared.c_resizearg = a;
765 return 0;
766 }
767
768 protected int
769 ch_aliasfun(EditLine *el, el_afunc_t f, void *a)
770 {
771 el->el_chared.c_aliasfun = f;
772 el->el_chared.c_aliasarg = a;
773 return 0;
774 }
775