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