1 /* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved.
3 * This software is not subject to any license of the American Telephone
4 * and Telegraph Company or of the Regents of the University of California.
6 * Permission is granted to anyone to use this software for any purpose on
7 * any computer system, and to alter it and redistribute it freely, subject
8 * to the following restrictions:
9 * 1. The authors are not responsible for the consequences of use of this
10 * software, no matter how awful, even if they arise from flaws in it.
11 * 2. The origin of this software must not be misrepresented, either by
12 * explicit claim or by omission. Since few users ever read sources,
13 * credits must appear in the documentation.
14 * 3. Altered versions must be plainly marked as such, and must not be
15 * misrepresented as being the original software. Since few users
16 * ever read sources, credits must appear in the documentation.
17 * 4. This notice may not be removed or altered.
21 ** Main editing routines for editline library.
28 RCSID("$Id: editline.c,v 1.10 2001/09/13 01:19:54 assar Exp $");
31 ** Manifest constants.
33 #define SCREEN_WIDTH 80
34 #define SCREEN_ROWS 24
37 #define CTL(x) ((x) & 0x1F)
38 #define ISCTL(x) ((x) && (x) < ' ')
39 #define UNCTL(x) ((x) + 64)
40 #define META(x) ((x) | 0x80)
41 #define ISMETA(x) ((x) & 0x80)
42 #define UNMETA(x) ((x) & 0x7F)
43 #if !defined(HIST_SIZE)
45 #endif /* !defined(HIST_SIZE) */
48 ** Command status codes.
50 typedef enum _el_STATUS {
51 CSdone, CSeof, CSmove, CSdispatch, CSstay
55 ** The type of case-changing to perform.
62 ** Key to command mapping.
64 typedef struct _KEYMAP {
66 el_STATUS (*Function)();
70 ** Command history structure.
72 typedef struct _HISTORY {
75 unsigned char *Lines[HIST_SIZE];
86 static unsigned char NIL[] = "";
87 static const unsigned char *Input = NIL;
88 static unsigned char *Line;
89 static const char *Prompt;
90 static unsigned char *Yanked;
92 static char NEWLINE[]= CRLF;
102 static KEYMAP Map[33];
103 static KEYMAP MetaMap[16];
104 static size_t Length;
105 static size_t ScreenCount;
106 static size_t ScreenSize;
107 static char *backspace;
111 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
112 int rl_meta_chars = 1;
117 static unsigned char *editinput(void);
118 char *tgetstr(const char*, char**);
119 int tgetent(char*, const char*);
120 int tgetnum(const char*);
123 ** TTY input/output functions.
130 write(1, Screen, ScreenCount);
136 TTYput(unsigned char c)
138 Screen[ScreenCount] = c;
139 if (++ScreenCount >= ScreenSize - 1) {
140 ScreenSize += SCREEN_INC;
141 Screen = realloc(Screen, ScreenSize);
146 TTYputs(const char *p)
153 TTYshow(unsigned char c)
163 else if (rl_meta_chars && ISMETA(c)) {
173 TTYstring(unsigned char *p)
194 } while(e < 0 && errno == EINTR);
224 #if defined(TIOCGWINSZ)
226 #endif /* defined(TIOCGWINSZ) */
229 #if defined(TIOCGWINSZ)
230 /* Perhaps we got resized. */
231 if (ioctl(0, TIOCGWINSZ, &W) >= 0
232 && W.ws_col > 0 && W.ws_row > 0) {
233 TTYwidth = (int)W.ws_col;
234 TTYrows = (int)W.ws_row;
236 #endif /* defined(TIOCGWINSZ) */
241 TTYwidth = TTYrows = 0;
243 if ((term = getenv("TERM")) == NULL)
245 if (tgetent(buff, term) < 0) {
246 TTYwidth = SCREEN_WIDTH;
247 TTYrows = SCREEN_ROWS;
250 tmp = tgetstr("le", &bp);
252 backspace = strdup(tmp);
255 TTYwidth = tgetnum("co");
256 TTYrows = tgetnum("li");
258 #if defined(TIOCGWINSZ)
259 if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
260 TTYwidth = (int)W.ws_col;
261 TTYrows = (int)W.ws_row;
263 #endif /* defined(TIOCGWINSZ) */
265 if (TTYwidth <= 0 || TTYrows <= 0) {
266 TTYwidth = SCREEN_WIDTH;
267 TTYrows = SCREEN_ROWS;
273 ** Print an array of words in columns.
276 columns(int ac, unsigned char **av)
287 /* Find longest name, determine column count from that. */
288 for (longest = 0, i = 0; i < ac; i++)
289 if ((j = strlen((char *)av[i])) > longest)
291 cols = TTYwidth / (longest + 3);
294 for (skip = ac / cols + 1, i = 0; i < skip; i++) {
295 for (j = i; j < ac; j += skip) {
296 for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++)
299 while (++len < longest + 3)
314 for (i = Point, p = Line; --i >= 0; p++)
319 left(el_STATUS Change)
323 if (ISCTL(Line[Point - 1]))
325 else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
330 if (Change == CSmove)
335 right(el_STATUS Change)
337 TTYshow(Line[Point]);
338 if (Change == CSmove)
351 do_macro(unsigned char c)
353 unsigned char name[4];
360 if ((Input = (unsigned char *)getenv((char *)name)) == NULL) {
368 do_forward(el_STATUS move)
376 for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
380 for (; Point < End && isalnum(*p); Point++, p++)
386 } while (++i < Repeat);
400 if (OldPoint != Point) {
401 if ((count = Point - OldPoint) < 0)
404 if ((end = Point + count) > End)
406 for (i = Point, p = &Line[i]; i < end; i++, p++) {
407 if (type == TOupper) {
411 else if (isupper(*p))
422 return do_case(TOlower);
428 return do_case(TOupper);
438 for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
444 else if (rl_meta_chars && ISMETA(*p)) {
451 for (i += extras; i > Point; i--)
458 Point = -strlen(Prompt);
467 insert_string(unsigned char *p)
474 len = strlen((char *)p);
475 if (End + len >= Length) {
476 if ((new = malloc(sizeof(unsigned char) * (Length + len + MEM_INC))) == NULL)
479 memcpy(new, Line, Length);
483 Length += len + MEM_INC;
486 for (q = &Line[Point], i = End - Point; --i >= 0; )
488 memcpy(&Line[Point], p, len);
491 TTYstring(&Line[Point]);
494 return Point == End ? CSstay : CSmove;
498 static unsigned char *
501 return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
504 static unsigned char *
507 return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
511 do_insert_hist(unsigned char *p)
519 return insert_string(p);
523 do_hist(unsigned char *(*move)())
530 if ((p = (*move)()) == NULL)
532 } while (++i < Repeat);
533 return do_insert_hist(p);
539 return do_hist(next_hist);
545 return do_hist(prev_hist);
551 return do_insert_hist(H.Lines[H.Pos = 0]);
557 return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
561 ** Return zero if pat appears as a substring in text.
564 substrcmp(char *text, char *pat, int len)
568 if ((c = *pat) == '\0')
569 return *text == '\0';
570 for ( ; *text; text++)
571 if (*text == c && strncmp(text, pat, len) == 0)
576 static unsigned char *
577 search_hist(unsigned char *search, unsigned char *(*move)())
579 static unsigned char *old_search;
585 /* Save or get remembered search pattern. */
586 if (search && *search) {
589 old_search = (unsigned char *)strdup((char *)search);
592 if (old_search == NULL || *old_search == '\0')
597 /* Set up pattern-finder. */
598 if (*search == '^') {
600 pat = (char *)(search + 1);
604 pat = (char *)search;
608 for (pos = H.Pos; (*move)() != NULL; )
609 if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
610 return H.Lines[H.Pos];
618 static int Searching;
619 const char *old_prompt;
620 unsigned char *(*move)();
631 move = Repeat == NO_ARG ? prev_hist : next_hist;
632 p = search_hist(editinput(), move);
638 return do_insert_hist(p);
651 } while (++i < Repeat);
656 save_yank(int begin, int i)
666 if ((Yanked = malloc(sizeof(unsigned char) * (i + 1))) != NULL) {
667 memcpy(Yanked, &Line[begin], i);
673 delete_string(int count)
678 if (count <= 0 || End == Point)
681 if (count == 1 && Point == End - 1) {
682 /* Optimize common case of delete at end of line. */
691 else if (rl_meta_chars && ISMETA(*p)) {
700 if (Point + count > End && (count = End - Point) <= 0)
704 save_yank(Point, count);
706 for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
710 TTYstring(&Line[Point]);
724 } while (++i < Repeat);
739 } while (++i < Repeat);
741 return delete_string(i);
758 if (Repeat != NO_ARG) {
759 if (Repeat < Point) {
763 delete_string(i - Point);
765 else if (Repeat > Point) {
767 delete_string(Repeat - Point - 1);
772 save_yank(Point, End - Point);
783 unsigned char buff[2];
788 if (Repeat == NO_ARG || Repeat < 2) {
791 return insert_string(buff);
794 if ((p = malloc(Repeat + 1)) == NULL)
796 for (i = Repeat, q = p; --i >= 0; )
800 s = insert_string(p);
811 if ((c = TTYget()) == EOF)
813 /* Also include VT-100 arrows. */
814 if (c == '[' || c == 'O')
815 switch (c = TTYget()) {
816 default: return ring_bell();
817 case EOF: return CSeof;
818 case 'A': return h_prev();
819 case 'B': return h_next();
820 case 'C': return fd_char();
821 case 'D': return bk_char();
825 for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
826 Repeat = Repeat * 10 + c - '0';
834 for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
836 return (*kp->Function)();
842 emacs(unsigned int c)
849 PushBack = UNMETA(c);
852 for (kp = Map; kp->Function; kp++)
855 s = kp->Function ? (*kp->Function)() : insert_char((int)c);
857 /* No pushback means no repeat count; hacky, but true. */
863 TTYspecial(unsigned int c)
868 if (c == rl_erase || c == DEL)
869 return bk_del_char();
878 if (c == rl_intr || c == rl_quit) {
883 if (c == rl_eof && Point == 0 && End == 0)
889 static unsigned char *
895 OldPoint = Point = Mark = End = 0;
898 while ((c = TTYget()) != EOF)
899 switch (TTYspecial(c)) {
928 hist_add(unsigned char *p)
932 if ((p = (unsigned char *)strdup((char *)p)) == NULL)
934 if (H.Size < HIST_SIZE)
935 H.Lines[H.Size++] = p;
938 for (i = 0; i < HIST_SIZE - 1; i++)
939 H.Lines[i] = H.Lines[i + 1];
946 ** For compatibility with FSF readline.
950 rl_reset_terminal(char *p)
960 readline(const char* prompt)
966 if ((Line = malloc(Length)) == NULL)
973 ScreenSize = SCREEN_INC;
974 Screen = malloc(ScreenSize);
975 Prompt = prompt ? prompt : (char *)NIL;
977 if ((line = editinput()) != NULL) {
978 line = (unsigned char *)strdup((char *)line);
984 free(H.Lines[--H.Size]);
991 if (p == NULL || *p == '\0')
994 #if defined(UNIQUE_HISTORY)
995 if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
997 #endif /* defined(UNIQUE_HISTORY) */
998 hist_add((unsigned char *)p);
1015 return delete_string(Repeat == NO_ARG ? 1 : Repeat);
1029 ** Move back to the beginning of the current word and return an
1030 ** allocated copy of it.
1032 static unsigned char *
1035 static char SEPS[] = "#;&|^$=`'{}()<>\n\t ";
1040 for (p = &Line[Point]; p > Line && strchr(SEPS, (char)p[-1]) == NULL; p--)
1042 len = Point - (p - Line) + 1;
1043 if ((new = malloc(len)) == NULL)
1045 memcpy(new, p, len);
1046 new[len - 1] = '\0';
1054 unsigned char *word;
1059 p = (unsigned char *)rl_complete((char *)word, &unique);
1063 s = insert_string(p);
1076 unsigned char *word;
1080 ac = rl_list_possib((char *)word, (char ***)&av);
1108 c = Line[Point - 1];
1110 Line[Point - 1] = Line[Point];
1111 TTYshow(Line[Point - 1]);
1123 return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
1141 return delete_string(Mark - Point);
1156 if ((c = TTYget()) != CTL('X'))
1157 return c == EOF ? CSeof : ring_bell();
1159 if ((c = Mark) <= End) {
1170 if (Yanked && *Yanked)
1171 return insert_string(Yanked);
1182 save_yank(Mark, Point - Mark);
1184 save_yank(Point, Mark - Point);
1196 if ((c = TTYget()) == EOF)
1198 for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1209 return do_forward(CSmove);
1218 if (OldPoint != Point) {
1219 i = Point - OldPoint;
1221 return delete_string(i);
1234 for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1237 for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1242 } while (++i < Repeat);
1251 if (OldPoint != Point)
1252 return delete_string(OldPoint - Point);
1257 argify(unsigned char *line, unsigned char ***avp)
1261 unsigned char **new;
1266 if ((*avp = p = malloc(sizeof(unsigned char*) * i))== NULL)
1269 for (c = line; isspace(*c); c++)
1271 if (*c == '\n' || *c == '\0')
1274 for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1277 if (*c && *c != '\n') {
1279 new = malloc(sizeof(unsigned char*) * (i + MEM_INC));
1284 memcpy(new, p, i * sizeof (char **));
1308 if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
1311 if ((p = (unsigned char *)strdup((char *)p)) == NULL)
1313 ac = argify(p, &av);
1315 if (Repeat != NO_ARG)
1316 s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1318 s = ac ? insert_string(av[ac - 1]) : CSstay;
1326 static KEYMAP Map[33] = {
1327 { CTL('@'), ring_bell },
1328 { CTL('A'), beg_line },
1329 { CTL('B'), bk_char },
1330 { CTL('D'), del_char },
1331 { CTL('E'), end_line },
1332 { CTL('F'), fd_char },
1333 { CTL('G'), ring_bell },
1334 { CTL('H'), bk_del_char },
1335 { CTL('I'), c_complete },
1336 { CTL('J'), accept_line },
1337 { CTL('K'), kill_line },
1338 { CTL('L'), redisplay },
1339 { CTL('M'), accept_line },
1340 { CTL('N'), h_next },
1341 { CTL('O'), ring_bell },
1342 { CTL('P'), h_prev },
1343 { CTL('Q'), ring_bell },
1344 { CTL('R'), h_search },
1345 { CTL('S'), ring_bell },
1346 { CTL('T'), transpose },
1347 { CTL('U'), ring_bell },
1348 { CTL('V'), quote },
1350 { CTL('X'), exchange },
1352 { CTL('Z'), ring_bell },
1354 { CTL(']'), move_to_char },
1355 { CTL('^'), ring_bell },
1356 { CTL('_'), ring_bell },
1360 static KEYMAP MetaMap[16]= {
1361 { CTL('H'), bk_kill_word },
1362 { DEL, bk_kill_word },
1364 { '.', last_argument },
1367 { '?', c_possible },
1369 { 'd', fd_kill_word },
1371 { 'l', case_down_word },
1372 { 'u', case_up_word },
1374 { 'w', copy_region },