OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / elvis-tiny / input.c
1 /* input.c */
2
3 /* Author:
4  *      Steve Kirkendall
5  *      14407 SW Teal Blvd. #C
6  *      Beaverton, OR 97005
7  *      kirkenda@cs.pdx.edu
8  */
9
10
11 /* This file contains the input() function, which implements vi's INPUT mode.
12  * It also contains the code that supports digraphs.
13  */
14
15 #include <ctype.h>
16 #include "config.h"
17 #include "vi.h"
18
19
20 #ifndef NO_DIGRAPH
21 static struct _DIG
22 {
23         struct _DIG     *next;
24         char            key1;
25         char            key2;
26         char            dig;
27         char            save;
28 } *digs;
29
30 char digraph(key1, key2)
31         char    key1;   /* the underlying character */
32         char    key2;   /* the second character */
33 {
34         int             newkey;
35         REG struct _DIG *dp;
36
37         /* if digraphs are disabled, then just return the new char */
38         if (!*o_digraph)
39         {
40                 return key2;
41         }
42
43         /* remember the new key, so we can return it if this isn't a digraph */
44         newkey = key2;
45
46         /* sort key1 and key2, so that their original order won't matter */
47         if (key1 > key2)
48         {
49                 key2 = key1;
50                 key1 = newkey;
51         }
52
53         /* scan through the digraph chart */
54         for (dp = digs;
55              dp && (dp->key1 != key1 || dp->key2 != key2);
56              dp = dp->next)
57         {
58         }
59
60         /* if this combination isn't in there, just use the new key */
61         if (!dp)
62         {
63                 return newkey;
64         }
65
66         /* else use the digraph key */
67         return dp->dig;
68 }
69
70 /* this function lists or defines digraphs */
71 void do_digraph(bang, extra)
72         int     bang;
73         char    extra[];
74 {
75         int             dig;
76         REG struct _DIG *dp;
77         struct _DIG     *prev;
78         static int      user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
79         char            listbuf[8];
80
81         /* if "extra" is NULL, then we've reached the end of the built-ins */
82         if (!extra)
83         {
84                 user_defined = TRUE;
85                 return;
86         }
87
88         /* if no args, then display the existing digraphs */
89         if (*extra < ' ')
90         {
91                 listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
92                 listbuf[7] = '\0';
93                 for (dig = 0, dp = digs; dp; dp = dp->next)
94                 {
95                         if (dp->save || bang)
96                         {
97                                 dig += 7;
98                                 if (dig >= COLS)
99                                 {
100                                         addch('\n');
101                                         exrefresh();
102                                         dig = 7;
103                                 }
104                                 listbuf[3] = dp->key1;
105                                 listbuf[4] = dp->key2;
106                                 listbuf[6] = dp->dig;
107                                 qaddstr(listbuf);
108                         }
109                 }
110                 addch('\n');
111                 exrefresh();
112                 return;
113         }
114
115         /* make sure we have at least two characters */
116         if (!extra[1])
117         {
118                 msg("Digraphs must be composed of two characters");
119                 return;
120         }
121
122         /* sort key1 and key2, so that their original order won't matter */
123         if (extra[0] > extra[1])
124         {
125                 dig = extra[0];
126                 extra[0] = extra[1];
127                 extra[1] = dig;
128         }
129
130         /* locate the new digraph character */
131         for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
132         {
133         }
134         dig = extra[dig];
135         if (!bang && dig)
136         {
137                 dig |= 0x80;
138         }
139
140         /* search for the digraph */
141         for (prev = (struct _DIG *)0, dp = digs;
142              dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
143              prev = dp, dp = dp->next)
144         {
145         }
146
147         /* deleting the digraph? */
148         if (!dig)
149         {
150                 if (!dp)
151                 {
152 #ifndef CRUNCH
153                         msg("%c%c not a digraph", extra[0], extra[1]);
154 #endif
155                         return;
156                 }
157                 if (prev)
158                         prev->next = dp->next;
159                 else
160                         digs = dp->next;
161                 free(dp);
162                 return;
163         }
164
165         /* if necessary, create a new digraph struct for the new digraph */
166         if (dig && !dp)
167         {
168                 dp = (struct _DIG *)malloc(sizeof *dp);
169                 if (!dp)
170                 {
171                         msg("Out of space in the digraph table");
172                         return;
173                 }
174                 if (prev)
175                         prev->next = dp;
176                 else
177                         digs = dp;
178                 dp->next = (struct _DIG *)0;
179         }
180
181         /* assign it the new digraph value */
182         dp->key1 = extra[0];
183         dp->key2 = extra[1];
184         dp->dig = dig;
185         dp->save = user_defined;
186 }
187
188 # ifndef NO_MKEXRC
189 void savedigs(fd)
190         int             fd;
191 {
192         static char     buf[] = "digraph! XX Y\n";
193         REG struct _DIG *dp;
194
195         for (dp = digs; dp; dp = dp->next)
196         {
197                 if (dp->save)
198                 {
199                         buf[9] = dp->key1;
200                         buf[10] = dp->key2;
201                         buf[12] = dp->dig;
202                         write(fd, buf, (unsigned)14);
203                 }
204         }
205 }
206 # endif
207 #endif
208
209
210 #ifndef NO_ABBR
211 static struct _AB
212 {
213         struct _AB      *next;
214         char            *large;         /* the expanded form */
215         char            small[1];       /* the abbreviated form (appended to struct) */
216 }
217         *abbrev;
218
219 /* This functions lists or defines abbreviations */
220 void do_abbr(extra)
221         char    *extra;
222 {
223         int             smlen;  /* length of the small form */
224         int             lrg;    /* index of the start of the large form */
225         REG struct _AB  *ab;    /* used to move through the abbrev list */
226         struct _AB      *prev;
227
228         /* no arguments? */
229         if (!*extra)
230         {
231                 /* list all current abbreviations */
232                 for (ab = abbrev; ab; ab = ab->next)
233                 {
234                         qaddstr("abbr ");
235                         qaddstr(ab->small);
236                         qaddch(' ');
237                         qaddstr(ab->large);
238                         addch('\n');
239                         exrefresh();
240                 }
241                 return;
242         }
243
244         /* else one or more arguments.  Parse the first & look up in abbrev[] */
245         for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
246         {
247         }
248         for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
249         {
250                 if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
251                 {
252                         break;
253                 }
254         }
255
256         /* locate the start of the large form, if any */
257         for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
258         {
259         }
260
261         /* only one arg? */
262         if (!extra[lrg])
263         {
264                 /* trying to undo an abbreviation which doesn't exist? */
265                 if (!ab)
266                 {
267 #ifndef CRUNCH
268                         msg("\"%s\" not an abbreviation", extra);
269 #endif
270                         return;
271                 }
272
273                 /* undo the abbreviation */
274                 if (prev)
275                         prev->next = ab->next;
276                 else
277                         abbrev = ab->next;
278                 free(ab->large);
279                 free(ab);
280
281                 return;
282         }
283
284         /* multiple args - [re]define an abbreviation */
285         if (ab)
286         {
287                 /* redefining - free the old large form */
288                 free(ab->large);
289         }
290         else
291         {
292                 /* adding a new definition - make a new struct */
293                 ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
294 #ifndef CRUNCH
295                 if (!ab)
296                 {
297                         msg("Out of memory -- Sorry");
298                         return;
299                 }
300 #endif
301                 strncpy(ab->small, extra, smlen);
302                 ab->small[smlen] = '\0';
303                 ab->next = (struct _AB *)0;
304                 if (prev)
305                         prev->next = ab;
306                 else
307                         abbrev = ab;
308         }
309
310         /* store the new form */
311         ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
312         strcpy(ab->large, &extra[lrg]);
313 }
314
315
316 # ifndef NO_MKEXRC
317 /* This function is called from cmd_mkexrc() to save the abbreviations */
318 void saveabbr(fd)
319         int     fd;     /* fd to which the :abbr commands should be written */
320 {
321         REG struct _AB  *ab;
322
323         for (ab = abbrev; ab; ab = ab->next)
324         {
325                 twrite(fd, "abbr ", 5);
326                 twrite(fd, ab->small, strlen(ab->small));
327                 twrite(fd, " ", 1);
328                 twrite(fd, ab->large, strlen(ab->large));
329                 twrite(fd, "\n", 1);
330         }
331 }
332 # endif
333
334 /* This function should be called before each char is inserted.  If the next
335  * char is non-alphanumeric and we're at the end of a word, then that word
336  * is checked against the abbrev[] array and expanded, if appropriate.  Upon
337  * returning from this function, the new char still must be inserted.
338  */
339 static MARK expandabbr(m, ch)
340         MARK            m;      /* the cursor position */
341         int             ch;     /* the character to insert */
342 {
343         char            *word;  /* where the word starts */
344         int             len;    /* length of the word */
345         REG struct _AB  *ab;
346
347         /* if no abbreviations are in effect, or ch is aphanumeric, then
348          * don't do anything
349          */
350         if (!abbrev || !isascii(ch) || isalnum(ch))
351         {
352                 return m;
353         }
354
355         /* see where the preceding word starts */
356         pfetch(markline(m));
357         for (word = ptext + markidx(m), len = 0;
358              --word >= ptext && (!isascii(*word) || isalnum(*word));
359              len++)
360         {
361         }
362         word++;
363
364         /* if zero-length, then it isn't a word, really -- so nothing */
365         if (len == 0)
366         {
367                 return m;
368         }
369
370         /* look it up in the abbrev list */
371         for (ab = abbrev; ab; ab = ab->next)
372         {
373                 if (!strncmp(ab->small, word, len) && !ab->small[len])
374                 {
375                         break;
376                 }
377         }
378
379         /* not an abbreviation? then do nothing */
380         if (!ab)
381         {
382                 return m;
383         }
384
385         /* else replace the small form with the large form */
386         add(m, ab->large);
387         delete(m - len, m);
388
389         /* return with the cursor after the end of the large form */
390         return m - len + strlen(ab->large);
391 }
392 #endif
393
394                 
395 /* This function allows the user to replace an existing (possibly zero-length)
396  * chunk of text with typed-in text.  It returns the MARK of the last character
397  * that the user typed in.
398  */
399 MARK input(from, to, when)
400         MARK    from;   /* where to start inserting text */
401         MARK    to;     /* extent of text to delete */
402         int     when;   /* either WHEN_VIINP or WHEN_VIREP */
403 {
404         char    key[2]; /* key char followed by '\0' char */
405         char    *build; /* used in building a newline+indent string */
406         char    *scan;  /* used while looking at the indent chars of a line */
407         MARK    m;      /* some place in the text */
408 #ifndef NO_EXTENSIONS
409         int     quit = FALSE;   /* boolean: are we exiting after this? */
410 #endif
411
412 #ifdef DEBUG
413         /* if "from" and "to" are reversed, complain */
414         if (from > to)
415         {
416                 msg("ERROR: input(%ld:%d, %ld:%d)",
417                         markline(from), markidx(from),
418                         markline(to), markidx(to));
419                 return MARK_UNSET;
420         }
421 #endif
422
423         key[1] = 0;
424
425         /* if we're replacing text with new text, save the old stuff */
426         /* (Alas, there is no easy way to save text for replace mode) */
427         if (from != to)
428         {
429                 cut(from, to);
430         }
431
432         ChangeText
433         {
434                 /* if doing a dot command, then reuse the previous text */
435                 if (doingdot)
436                 {
437                         /* delete the text that's there now */
438                         if (from != to)
439                         {
440                                 delete(from, to);
441                         }
442
443                         /* insert the previous text */
444                         cutname('.');
445                         cursor = paste(from, FALSE, TRUE) + 1L;
446                 }
447                 else /* interactive version */
448                 {
449                         /* if doing a change within the line... */
450                         if (from != to && markline(from) == markline(to))
451                         {
452                                 /* mark the end of the text with a "$" */
453                                 change(to - 1, to, "$");
454                         }
455                         else
456                         {
457                                 /* delete the old text right off */
458                                 if (from != to)
459                                 {
460                                         delete(from, to);
461                                 }
462                                 to = from;
463                         }
464
465                         /* handle autoindent of the first line, maybe */
466                         cursor = from;
467                         if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
468                         {
469                                 /* Only autoindent blank lines. */
470                                 pfetch(markline(cursor));
471                                 if (plen == 0)
472                                 {
473                                         /* Okay, we really want to autoindent */
474                                         pfetch(markline(cursor) - 1L);
475                                         for (scan = ptext, build = tmpblk.c;
476                                              *scan == ' ' || *scan == '\t';
477                                              )
478                                         {
479                                                 *build++ = *scan++;
480                                         }
481                                         if (build > tmpblk.c)
482                                         {
483                                                 *build = '\0';
484                                                 add(cursor, tmpblk.c);
485                                                 cursor += (build - tmpblk.c);
486                                         }
487                                 }
488                         }
489
490                         /* repeatedly add characters from the user */
491                         for (;;)
492                         {
493                                 /* Get a character */
494                                 redraw(cursor, TRUE);
495 #ifdef DEBUG
496                                 msg("cursor=%ld.%d, to=%ld.%d",
497                                         markline(cursor), markidx(cursor),
498                                         markline(to), markidx(to));
499 #endif
500                                 key[0] = getkey(when);
501
502                                 /* if whitespace & wrapmargin is set & we're
503                                  * past the warpmargin, then change the
504                                  * whitespace character into a newline
505                                  */
506                                 if ((*key == ' ' || *key == '\t')
507                                  && *o_wrapmargin != 0)
508                                 {
509                                         pfetch(markline(cursor));
510                                         if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
511                                         {
512                                                 *key = '\n';
513                                         }
514                                 }
515
516                                 /* process it */
517                                 switch (*key)
518                                 {
519 #ifndef NO_EXTENSIONS
520                                   case 0: /* special movement mapped keys */
521                                         *key = getkey(0);
522                                         switch (*key)
523                                         {
524                                           case 'h':     m = m_left(cursor, 0L);         break;
525                                           case 'j':
526                                           case 'k':     m = m_updnto(cursor, 0L, *key); break;
527                                           case 'l':     m = cursor + 1;                 break;
528                                           case 'b':     m = m_bword(cursor, 0L);        break;
529                                           case 'w':     m = m_fword(cursor, 0L);        break;
530                                           case '^':     m = m_front(cursor, 0L);        break;
531                                           case '$':     m = m_rear(cursor, 0L);         break;
532                                           case ctrl('B'):
533                                           case ctrl('F'):
534                                                         m = m_scroll(cursor, 0L, *key); break;
535                                           case 'x':     m = v_xchar(cursor, 0L);        break;
536                                           case 'i':     m = to = from = cursor;         break;
537                                           default:      m = MARK_UNSET;                 break;
538                                         }
539                                         /* adjust the moved cursor */
540                                         m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
541                                         if (*key == '$' || (*key == 'l' && m <= cursor))
542                                         {
543                                                 m++;
544                                         }
545                                         /* if the cursor is reasonable, use it */
546                                         if (m == MARK_UNSET)
547                                         {
548                                                 beep();
549                                         }
550                                         else
551                                         {
552                                                 if (to > cursor)
553                                                 {
554                                                         delete(cursor, to);
555                                                         redraw(cursor, TRUE);
556                                                 }
557                                                 from = to = cursor = m;
558                                         }
559                                         break;
560
561                                   case ctrl('Z'):
562                                         if (getkey(0) == ctrl('Z'))
563                                         {
564                                                 quit = TRUE;
565                                                 goto BreakBreak;
566                                         }
567                                         break;
568 #endif
569
570                                   case ctrl('['):
571 #ifndef NO_ABBR
572                                         cursor = expandabbr(cursor, ctrl('['));
573 #endif
574                                         goto BreakBreak;
575
576                                   case ctrl('U'):
577                                         if (markline(cursor) == markline(from))
578                                         {
579                                                 cursor = from;
580                                         }
581                                         else
582                                         {
583                                                 cursor &= ~(BLKSIZE - 1);
584                                         }
585                                         break;
586
587                                   case ctrl('D'):
588                                   case ctrl('T'):
589                                         if (to > cursor)
590                                         {
591                                                 delete(cursor, to);
592                                         }
593                                         mark[27] = cursor;
594                                         cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
595                                         if (mark[27])
596                                         {
597                                                 cursor = mark[27];
598                                         }
599                                         else
600                                         {
601                                                 cursor = m_front(cursor, 0L);
602                                         }
603                                         to = cursor;
604                                         break;
605
606                                   case '\b':
607                                         if (cursor <= from)
608                                         {
609                                                 beep();
610                                         }
611                                         else if (markidx(cursor) == 0)
612                                         {
613                                                 cursor -= BLKSIZE;
614                                                 pfetch(markline(cursor));
615                                                 cursor += plen;
616                                         }
617                                         else
618                                         {
619                                                 cursor--;
620                                         }
621                                         break;
622
623                                   case ctrl('W'):
624                                         m = m_bword(cursor, 1L);
625                                         if (markline(m) == markline(cursor) && m >= from)
626                                         {
627                                                 cursor = m;
628                                                 if (from > cursor)
629                                                 {
630                                                         from = cursor;
631                                                 }
632                                         }
633                                         else
634                                         {
635                                                 beep();
636                                         }
637                                         break;
638
639                                   case '\n':
640 #if OSK
641                                   case '\l':
642 #else                             
643                                   case '\r':
644 #endif
645 #ifndef NO_ABBR
646                                         cursor = expandabbr(cursor, '\n');
647 #endif
648                                         build = tmpblk.c;
649                                         *build++ = '\n';
650                                         if (*o_autoindent)
651                                         {
652                                                 /* figure out indent for next line */
653                                                 pfetch(markline(cursor));
654                                                 for (scan = ptext; *scan == ' ' || *scan == '\t'; )
655                                                 {
656                                                         *build++ = *scan++;
657                                                 }
658
659                                                 /* remove indent from this line, if blank */
660                                                 if (!*scan && plen > 0)
661                                                 {
662                                                         to = cursor &= ~(BLKSIZE - 1);
663                                                         delete(cursor, cursor + plen);
664                                                 }
665                                         }
666                                         *build = 0;
667                                         if (cursor >= to && when != WHEN_VIREP)
668                                         {
669                                                 add(cursor, tmpblk.c);
670                                         }
671                                         else
672                                         {
673                                                 change(cursor, to, tmpblk.c);
674                                         }
675                                         redraw(cursor, TRUE);
676                                         to = cursor = (cursor & ~(BLKSIZE - 1))
677                                                         + BLKSIZE
678                                                         + (int)(build - tmpblk.c) - 1;
679                                         break;
680
681                                   case ctrl('A'):
682                                   case ctrl('P'):
683                                         if (cursor < to)
684                                         {
685                                                 delete(cursor, to);
686                                         }
687                                         if (*key == ctrl('A'))
688                                         {
689                                                 cutname('.');
690                                         }
691                                         to = cursor = paste(cursor, FALSE, TRUE) + 1L;
692                                         break;
693
694                                   case ctrl('V'):
695                                         if (cursor >= to && when != WHEN_VIREP)
696                                         {
697                                                 add(cursor, "^");
698                                         }
699                                         else
700                                         {
701                                                 change(cursor, to, "^");
702                                                 to = cursor + 1;
703                                         }
704                                         redraw(cursor, TRUE);
705                                         *key = getkey(0);
706                                         if (*key == '\n')
707                                         {
708                                                 /* '\n' too hard to handle */
709 #if OSK
710                                                 *key = '\l';
711 #else
712                                                 *key = '\r';
713 #endif
714                                         }
715                                         change(cursor, cursor + 1, key);
716                                         cursor++;
717                                         if (cursor > to)
718                                         {
719                                                 to = cursor;
720                                         }
721                                         break;
722
723                                   case ctrl('L'):
724                                   case ctrl('R'):
725                                         redraw(MARK_UNSET, FALSE);
726                                         break;
727
728                                   default:
729                                         if (cursor >= to && when != WHEN_VIREP)
730                                         {
731 #ifndef NO_ABBR
732                                                 cursor = expandabbr(cursor, *key);
733 #endif
734                                                 add(cursor, key);
735                                                 cursor++;
736                                                 to = cursor;
737                                         }
738                                         else
739                                         {
740                                                 pfetch(markline(cursor));
741                                                 if (markidx(cursor) == plen)
742                                                 {
743 #ifndef NO_ABBR
744                                                         cursor = expandabbr(cursor, *key);
745 #endif
746                                                         add(cursor, key);
747                                                 }
748                                                 else
749                                                 {
750 #ifndef NO_DIGRAPH
751                                                         *key = digraph(ptext[markidx(cursor)], *key);
752 #endif
753 #ifndef NO_ABBR
754                                                         cursor = expandabbr(cursor, *key);
755 #endif
756                                                         change(cursor, cursor + 1, key);
757                                                 }
758                                                 cursor++;
759                                         }
760 #ifndef NO_SHOWMATCH
761                                         /* show matching "({[" if neceesary */
762                                         if (*o_showmatch && strchr(")}]", *key))
763                                         {
764                                                 redraw(cursor, TRUE);
765                                                 m = m_match(cursor - 1, 0L);
766                                                 if (markline(m) >= topline
767                                                  && markline(m) <= botline)
768                                                 {
769                                                         redraw(m, TRUE);
770                                                         refresh();
771                                                         sleep(1);
772                                                 }
773                                         }
774 #endif
775                                 } /* end switch(*key) */
776                         } /* end for(;;) */
777 BreakBreak:;
778
779                         /* delete any excess characters */
780                         if (cursor < to)
781                         {
782                                 delete(cursor, to);
783                         }
784
785                 } /* end if doingdot else */
786
787         } /* end ChangeText */
788
789         /* put the new text into a cut buffer for possible reuse */
790         if (!doingdot)
791         {
792                 blksync();
793                 cutname('.');
794                 cut(from, cursor);
795         }
796
797         /* move to last char that we inputted, unless it was newline */
798         if (markidx(cursor) != 0)
799         {
800                 cursor--;
801         }
802         redraw(cursor, FALSE);
803
804 #ifndef NO_EXTENSIONS
805         if (quit)
806         {
807                 /* if this is a nested "do", then cut it short */
808                 abortdo();
809
810                 /* exit, unless we can't write out the file */
811                 cursor = v_xit(cursor, 0L, 'Z');
812         }
813 #endif
814
815         rptlines = 0L;
816         return cursor;
817 }