chared.c revision 1.13 1 /* $NetBSD: chared.c,v 1.13 2001/04/13 01:04:19 lukem 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. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: chared.c,v 1.13 2001/04/13 01:04:19 lukem Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47
48 /*
49 * chared.c: Character editor utilities
50 */
51 #include "sys.h"
52
53 #include <stdlib.h>
54 #include "el.h"
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,int action, size_t size, char *ptr)
64 {
65 c_undo_t *vu = &el->el_chared.c_undo;
66 vu->action = action;
67 vu->ptr = ptr;
68 vu->isize = size;
69 (void) memcpy(vu->buf, vu->ptr, size);
70 #ifdef DEBUG_UNDO
71 (void) fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n",
72 vu->ptr, vu->isize, vu->dsize);
73 #endif
74 }
75
76
77 /* c_insert():
78 * Insert num characters
79 */
80 protected void
81 c_insert(EditLine *el, int num)
82 {
83 char *cp;
84
85 if (el->el_line.lastchar + num >= el->el_line.limit)
86 return; /* can't go past end of buffer */
87
88 if (el->el_line.cursor < el->el_line.lastchar) {
89 /* if I must move chars */
90 for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
91 cp[num] = *cp;
92 }
93 el->el_line.lastchar += num;
94 }
95
96
97 /* c_delafter():
98 * Delete num characters after the cursor
99 */
100 protected void
101 c_delafter(EditLine *el, int num)
102 {
103
104 if (el->el_line.cursor + num > el->el_line.lastchar)
105 num = el->el_line.lastchar - el->el_line.cursor;
106
107 if (num > 0) {
108 char *cp;
109
110 if (el->el_map.current != el->el_map.emacs)
111 cv_undo(el, INSERT, (size_t)num, el->el_line.cursor);
112
113 for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
114 *cp = cp[num];
115
116 el->el_line.lastchar -= num;
117 }
118 }
119
120
121 /* c_delbefore():
122 * Delete num characters before the cursor
123 */
124 protected void
125 c_delbefore(EditLine *el, int num)
126 {
127
128 if (el->el_line.cursor - num < el->el_line.buffer)
129 num = el->el_line.cursor - el->el_line.buffer;
130
131 if (num > 0) {
132 char *cp;
133
134 if (el->el_map.current != el->el_map.emacs)
135 cv_undo(el, INSERT, (size_t)num,
136 el->el_line.cursor - num);
137
138 for (cp = el->el_line.cursor - num;
139 cp <= el->el_line.lastchar;
140 cp++)
141 *cp = cp[num];
142
143 el->el_line.lastchar -= num;
144 }
145 }
146
147
148 /* ce__isword():
149 * Return if p is part of a word according to emacs
150 */
151 protected int
152 ce__isword(int p)
153 {
154 return (isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL);
155 }
156
157
158 /* cv__isword():
159 * Return if p is part of a word according to vi
160 */
161 protected int
162 cv__isword(int p)
163 {
164 return (!isspace(p));
165 }
166
167
168 /* c__prev_word():
169 * Find the previous word
170 */
171 protected char *
172 c__prev_word(char *p, char *low, int n, int (*wtest)(int))
173 {
174 p--;
175
176 while (n--) {
177 while ((p >= low) && !(*wtest)((unsigned char) *p))
178 p--;
179 while ((p >= low) && (*wtest)((unsigned char) *p))
180 p--;
181 }
182
183 /* cp now points to one character before the word */
184 p++;
185 if (p < low)
186 p = low;
187 /* cp now points where we want it */
188 return (p);
189 }
190
191
192 /* c__next_word():
193 * Find the next word
194 */
195 protected char *
196 c__next_word(char *p, char *high, int n, int (*wtest)(int))
197 {
198 while (n--) {
199 while ((p < high) && !(*wtest)((unsigned char) *p))
200 p++;
201 while ((p < high) && (*wtest)((unsigned char) *p))
202 p++;
203 }
204 if (p > high)
205 p = high;
206 /* p now points where we want it */
207 return (p);
208 }
209
210 /* cv_next_word():
211 * Find the next word vi style
212 */
213 protected char *
214 cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int))
215 {
216 int test;
217
218 while (n--) {
219 test = (*wtest)((unsigned char) *p);
220 while ((p < high) && (*wtest)((unsigned char) *p) == test)
221 p++;
222 /*
223 * vi historically deletes with cw only the word preserving the
224 * trailing whitespace! This is not what 'w' does..
225 */
226 if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
227 while ((p < high) && isspace((unsigned char) *p))
228 p++;
229 }
230
231 /* p now points where we want it */
232 if (p > high)
233 return (high);
234 else
235 return (p);
236 }
237
238
239 /* cv_prev_word():
240 * Find the previous word vi style
241 */
242 protected char *
243 cv_prev_word(EditLine *el, char *p, char *low, int n, int (*wtest)(int))
244 {
245 int test;
246
247 while (n--) {
248 p--;
249 /*
250 * vi historically deletes with cb only the word preserving the
251 * leading whitespace! This is not what 'b' does..
252 */
253 if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
254 while ((p > low) && isspace((unsigned char) *p))
255 p--;
256 test = (*wtest)((unsigned char) *p);
257 while ((p >= low) && (*wtest)((unsigned char) *p) == test)
258 p--;
259 p++;
260 while (isspace((unsigned char) *p))
261 p++;
262 }
263
264 /* p now points where we want it */
265 if (p < low)
266 return (low);
267 else
268 return (p);
269 }
270
271
272 #ifdef notdef
273 /* c__number():
274 * Ignore character p points to, return number appearing after that.
275 * A '$' by itself means a big number; "$-" is for negative; '^' means 1.
276 * Return p pointing to last char used.
277 */
278 protected char *
279 c__number(
280 char *p, /* character position */
281 int *num, /* Return value */
282 int dval) /* dval is the number to subtract from like $-3 */
283 {
284 int i;
285 int sign = 1;
286
287 if (*++p == '^') {
288 *num = 1;
289 return (p);
290 }
291 if (*p == '$') {
292 if (*++p != '-') {
293 *num = 0x7fffffff; /* Handle $ */
294 return (--p);
295 }
296 sign = -1; /* Handle $- */
297 ++p;
298 }
299 for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
300 continue;
301 *num = (sign < 0 ? dval - i : i);
302 return (--p);
303 }
304 #endif
305
306 /* cv_delfini():
307 * Finish vi delete action
308 */
309 protected void
310 cv_delfini(EditLine *el)
311 {
312 int size;
313 int oaction;
314
315 if (el->el_chared.c_vcmd.action & INSERT)
316 el->el_map.current = el->el_map.key;
317
318 oaction = el->el_chared.c_vcmd.action;
319 el->el_chared.c_vcmd.action = NOP;
320
321 if (el->el_chared.c_vcmd.pos == 0)
322 return;
323
324
325 if (el->el_line.cursor > el->el_chared.c_vcmd.pos) {
326 size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos);
327 c_delbefore(el, size);
328 el->el_line.cursor = el->el_chared.c_vcmd.pos;
329 re_refresh_cursor(el);
330 } else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) {
331 size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor);
332 c_delafter(el, size);
333 } else {
334 size = 1;
335 c_delafter(el, size);
336 }
337 switch (oaction) {
338 case DELETE|INSERT:
339 el->el_chared.c_undo.action = DELETE|INSERT;
340 break;
341 case DELETE:
342 el->el_chared.c_undo.action = INSERT;
343 break;
344 case NOP:
345 case INSERT:
346 default:
347 EL_ABORT((el->el_errfile, "Bad oaction %d\n", oaction));
348 break;
349 }
350
351
352 el->el_chared.c_undo.ptr = el->el_line.cursor;
353 el->el_chared.c_undo.dsize = size;
354 }
355
356
357 #ifdef notdef
358 /* ce__endword():
359 * Go to the end of this word according to emacs
360 */
361 protected char *
362 ce__endword(char *p, char *high, int n)
363 {
364 p++;
365
366 while (n--) {
367 while ((p < high) && isspace((unsigned char) *p))
368 p++;
369 while ((p < high) && !isspace((unsigned char) *p))
370 p++;
371 }
372
373 p--;
374 return (p);
375 }
376 #endif
377
378
379 /* cv__endword():
380 * Go to the end of this word according to vi
381 */
382 protected char *
383 cv__endword(char *p, char *high, int n)
384 {
385 p++;
386
387 while (n--) {
388 while ((p < high) && isspace((unsigned char) *p))
389 p++;
390
391 if (isalnum((unsigned char) *p))
392 while ((p < high) && isalnum((unsigned char) *p))
393 p++;
394 else
395 while ((p < high) && !(isspace((unsigned char) *p) ||
396 isalnum((unsigned char) *p)))
397 p++;
398 }
399 p--;
400 return (p);
401 }
402
403 /* ch_init():
404 * Initialize the character editor
405 */
406 protected int
407 ch_init(EditLine *el)
408 {
409 el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ);
410 if (el->el_line.buffer == NULL)
411 return (-1);
412
413 (void) memset(el->el_line.buffer, 0, EL_BUFSIZ);
414 el->el_line.cursor = el->el_line.buffer;
415 el->el_line.lastchar = el->el_line.buffer;
416 el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - 2];
417
418 el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ);
419 if (el->el_chared.c_undo.buf == NULL)
420 return (-1);
421 (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
422 el->el_chared.c_undo.action = NOP;
423 el->el_chared.c_undo.isize = 0;
424 el->el_chared.c_undo.dsize = 0;
425 el->el_chared.c_undo.ptr = el->el_line.buffer;
426
427 el->el_chared.c_vcmd.action = NOP;
428 el->el_chared.c_vcmd.pos = el->el_line.buffer;
429 el->el_chared.c_vcmd.ins = el->el_line.buffer;
430
431 el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ);
432 if (el->el_chared.c_kill.buf == NULL)
433 return (-1);
434 (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
435 el->el_chared.c_kill.mark = el->el_line.buffer;
436 el->el_chared.c_kill.last = el->el_chared.c_kill.buf;
437
438 el->el_map.current = el->el_map.key;
439
440 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
441 el->el_state.doingarg = 0;
442 el->el_state.metanext = 0;
443 el->el_state.argument = 1;
444 el->el_state.lastcmd = ED_UNASSIGNED;
445
446 el->el_chared.c_macro.nline = NULL;
447 el->el_chared.c_macro.level = -1;
448 el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO *
449 sizeof(char *));
450 if (el->el_chared.c_macro.macro == NULL)
451 return (-1);
452 return (0);
453 }
454
455 /* ch_reset():
456 * Reset the character editor
457 */
458 protected void
459 ch_reset(EditLine *el)
460 {
461 el->el_line.cursor = el->el_line.buffer;
462 el->el_line.lastchar = el->el_line.buffer;
463
464 el->el_chared.c_undo.action = NOP;
465 el->el_chared.c_undo.isize = 0;
466 el->el_chared.c_undo.dsize = 0;
467 el->el_chared.c_undo.ptr = el->el_line.buffer;
468
469 el->el_chared.c_vcmd.action = NOP;
470 el->el_chared.c_vcmd.pos = el->el_line.buffer;
471 el->el_chared.c_vcmd.ins = el->el_line.buffer;
472
473 el->el_chared.c_kill.mark = el->el_line.buffer;
474
475 el->el_map.current = el->el_map.key;
476
477 el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
478 el->el_state.doingarg = 0;
479 el->el_state.metanext = 0;
480 el->el_state.argument = 1;
481 el->el_state.lastcmd = ED_UNASSIGNED;
482
483 el->el_chared.c_macro.level = -1;
484
485 el->el_history.eventno = 0;
486 }
487
488 /* ch_enlargebufs():
489 * Enlarge line buffer to be able to hold twice as much characters.
490 * Returns 1 if successful, 0 if not.
491 */
492 protected int
493 ch_enlargebufs(el, addlen)
494 EditLine *el;
495 size_t addlen;
496 {
497 size_t sz, newsz;
498 char *newbuffer, *oldbuf, *oldkbuf;
499
500 sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE;
501 newsz = sz * 2;
502 /*
503 * If newly required length is longer than current buffer, we need
504 * to make the buffer big enough to hold both old and new stuff.
505 */
506 if (addlen > sz) {
507 while(newsz - sz < addlen)
508 newsz *= 2;
509 }
510
511 /*
512 * Reallocate line buffer.
513 */
514 newbuffer = el_realloc(el->el_line.buffer, newsz);
515 if (!newbuffer)
516 return 0;
517
518 /* zero the newly added memory, leave old data in */
519 (void) memset(&newbuffer[sz], 0, newsz - sz);
520
521 oldbuf = el->el_line.buffer;
522
523 el->el_line.buffer = newbuffer;
524 el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
525 el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
526 el->el_line.limit = &newbuffer[newsz - EL_LEAVE];
527
528 /*
529 * Reallocate kill buffer.
530 */
531 newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz);
532 if (!newbuffer)
533 return 0;
534
535 /* zero the newly added memory, leave old data in */
536 (void) memset(&newbuffer[sz], 0, newsz - sz);
537
538 oldkbuf = el->el_chared.c_kill.buf;
539
540 el->el_chared.c_kill.buf = newbuffer;
541 el->el_chared.c_kill.last = newbuffer +
542 (el->el_chared.c_kill.last - oldkbuf);
543 el->el_chared.c_kill.mark = el->el_line.buffer +
544 (el->el_chared.c_kill.mark - oldbuf);
545
546 /*
547 * Reallocate undo buffer.
548 */
549 newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz);
550 if (!newbuffer)
551 return 0;
552
553 /* zero the newly added memory, leave old data in */
554 (void) memset(&newbuffer[sz], 0, newsz - sz);
555
556 el->el_chared.c_undo.ptr = el->el_line.buffer +
557 (el->el_chared.c_undo.ptr - oldbuf);
558 el->el_chared.c_undo.buf = newbuffer;
559
560 if (!hist_enlargebuf(el, sz, newsz))
561 return 0;
562
563 return 1;
564 }
565
566 /* ch_end():
567 * Free the data structures used by the editor
568 */
569 protected void
570 ch_end(EditLine *el)
571 {
572 el_free((ptr_t) el->el_line.buffer);
573 el->el_line.buffer = NULL;
574 el->el_line.limit = NULL;
575 el_free((ptr_t) el->el_chared.c_undo.buf);
576 el->el_chared.c_undo.buf = NULL;
577 el_free((ptr_t) el->el_chared.c_kill.buf);
578 el->el_chared.c_kill.buf = NULL;
579 el_free((ptr_t) el->el_chared.c_macro.macro);
580 el->el_chared.c_macro.macro = NULL;
581 ch_reset(el);
582 }
583
584
585 /* el_insertstr():
586 * Insert string at cursorI
587 */
588 public int
589 el_insertstr(EditLine *el, const char *s)
590 {
591 int len;
592
593 if ((len = strlen(s)) == 0)
594 return (-1);
595 if (el->el_line.lastchar + len >= el->el_line.limit) {
596 if (!ch_enlargebufs(el, len))
597 return (-1);
598 }
599
600 c_insert(el, len);
601 while (*s)
602 *el->el_line.cursor++ = *s++;
603 return (0);
604 }
605
606
607 /* el_deletestr():
608 * Delete num characters before the cursor
609 */
610 public void
611 el_deletestr(EditLine *el, int n)
612 {
613 if (n <= 0)
614 return;
615
616 if (el->el_line.cursor < &el->el_line.buffer[n])
617 return;
618
619 c_delbefore(el, n); /* delete before dot */
620 el->el_line.cursor -= n;
621 if (el->el_line.cursor < el->el_line.buffer)
622 el->el_line.cursor = el->el_line.buffer;
623 }
624
625 /* c_gets():
626 * Get a string
627 */
628 protected int
629 c_gets(EditLine *el, char *buf)
630 {
631 char ch;
632 int len = 0;
633
634 for (ch = 0; ch == 0;) {
635 if (el_getc(el, &ch) != 1)
636 return (ed_end_of_file(el, 0));
637 switch (ch) {
638 case 0010: /* Delete and backspace */
639 case 0177:
640 if (len > 1) {
641 *el->el_line.cursor-- = '\0';
642 el->el_line.lastchar = el->el_line.cursor;
643 buf[len--] = '\0';
644 } else {
645 el->el_line.buffer[0] = '\0';
646 el->el_line.lastchar = el->el_line.buffer;
647 el->el_line.cursor = el->el_line.buffer;
648 return (CC_REFRESH);
649 }
650 re_refresh(el);
651 ch = 0;
652 break;
653
654 case 0033: /* ESC */
655 case '\r': /* Newline */
656 case '\n':
657 break;
658
659 default:
660 if (len >= EL_BUFSIZ)
661 term_beep(el);
662 else {
663 buf[len++] = ch;
664 *el->el_line.cursor++ = ch;
665 el->el_line.lastchar = el->el_line.cursor;
666 }
667 re_refresh(el);
668 ch = 0;
669 break;
670 }
671 }
672 buf[len] = ch;
673 return (len);
674 }
675
676
677 /* c_hpos():
678 * Return the current horizontal position of the cursor
679 */
680 protected int
681 c_hpos(EditLine *el)
682 {
683 char *ptr;
684
685 /*
686 * Find how many characters till the beginning of this line.
687 */
688 if (el->el_line.cursor == el->el_line.buffer)
689 return (0);
690 else {
691 for (ptr = el->el_line.cursor - 1;
692 ptr >= el->el_line.buffer && *ptr != '\n';
693 ptr--)
694 continue;
695 return (el->el_line.cursor - ptr - 1);
696 }
697 }
698