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