tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

editline.c (22625B)


      1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 * Copyright 1992,1993 Simmule Turner and Rich Salz.  All rights reserved.
      8 *
      9 * This software is not subject to any license of the American Telephone
     10 * and Telegraph Company or of the Regents of the University of California.
     11 *
     12 * Permission is granted to anyone to use this software for any purpose on
     13 * any computer system, and to alter it and redistribute it freely, subject
     14 * to the following restrictions:
     15 * 1. The authors are not responsible for the consequences of use of this
     16 *    software, no matter how awful, even if they arise from flaws in it.
     17 * 2. The origin of this software must not be misrepresented, either by
     18 *    explicit claim or by omission.  Since few users ever read sources,
     19 *    credits must appear in the documentation.
     20 * 3. Altered versions must be plainly marked as such, and must not be
     21 *    misrepresented as being the original software.  Since few users
     22 *    ever read sources, credits must appear in the documentation.
     23 * 4. This notice may not be removed or altered.
     24 */
     25 
     26 
     27 /*
     28 **  Main editing routines for editline library.
     29 */
     30 #include "editline.h"
     31 #include <signal.h>
     32 #include <ctype.h>
     33 #include <unistd.h>
     34 
     35 /*
     36 **  Manifest constants.
     37 */
     38 #define SCREEN_WIDTH	80
     39 #define SCREEN_ROWS	24
     40 #define NO_ARG		(-1)
     41 #define DEL		127
     42 #define CTL(x)		((x) & 0x1F)
     43 #define ISCTL(x)	((x) && (x) < ' ')
     44 #define UNCTL(x)	((x) + 64)
     45 #define META(x)		((x) | 0x80)
     46 #define ISMETA(x)	((x) & 0x80)
     47 #define UNMETA(x)	((x) & 0x7F)
     48 #if	!defined(HIST_SIZE)
     49 #define HIST_SIZE	20
     50 #endif	/* !defined(HIST_SIZE) */
     51 
     52 /*
     53 **  Command status codes.
     54 */
     55 typedef enum _STATUS {
     56    CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal
     57 } STATUS;
     58 
     59 /*
     60 **  The type of case-changing to perform.
     61 */
     62 typedef enum _CASE {
     63    TOupper, TOlower
     64 } CASE;
     65 
     66 /*
     67 **  Key to command mapping.
     68 */
     69 typedef struct _KEYMAP {
     70    CHAR	Key;
     71    STATUS	(*Function)();
     72 } KEYMAP;
     73 
     74 /*
     75 **  Command history structure.
     76 */
     77 typedef struct _HISTORY {
     78    int		Size;
     79    int		Pos;
     80    CHAR	*Lines[HIST_SIZE];
     81 } HISTORY;
     82 
     83 /*
     84 **  Globals.
     85 */
     86 unsigned	rl_eof;
     87 unsigned	rl_erase;
     88 unsigned	rl_intr;
     89 unsigned	rl_kill;
     90 unsigned	rl_quit;
     91 
     92 STATIC CHAR		NIL[] = "";
     93 STATIC CONST CHAR	*Input = NIL;
     94 STATIC CHAR		*Line;
     95 STATIC CONST char	*Prompt;
     96 STATIC CHAR		*Yanked;
     97 STATIC char		*Screen;
     98 STATIC CONST char	NEWLINE[]= CRLF;
     99 STATIC HISTORY		H;
    100 STATIC int		Repeat;
    101 STATIC int		End;
    102 STATIC int		Mark;
    103 STATIC int		OldPoint;
    104 STATIC int		Point;
    105 STATIC int		PushBack;
    106 STATIC int		Pushed;
    107 STATIC int		Signal;
    108 FORWARD CONST KEYMAP	Map[32];
    109 FORWARD CONST KEYMAP	MetaMap[16];
    110 STATIC SIZE_T		Length;
    111 STATIC SIZE_T		ScreenCount;
    112 STATIC SIZE_T		ScreenSize;
    113 STATIC char		*backspace;
    114 STATIC int		TTYwidth;
    115 STATIC int		TTYrows;
    116 
    117 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
    118 int		rl_meta_chars = 0;
    119 
    120 /*
    121 **  Declarations.
    122 */
    123 STATIC CHAR	*editinput();
    124 #if	defined(USE_TERMCAP)
    125 #include <stdlib.h>
    126 #include <curses.h>
    127 #include <term.h>
    128 #endif	/* defined(USE_TERMCAP) */
    129 
    130 /*
    131 **  TTY input/output functions.
    132 */
    133 
    134 STATIC void
    135 TTYflush()
    136 {
    137    if (ScreenCount) {
    138        /* Dummy assignment avoids GCC warning on
    139         * "attribute warn_unused_result" */
    140 ssize_t dummy = write(1, Screen, ScreenCount);
    141        (void)dummy;
    142 ScreenCount = 0;
    143    }
    144 }
    145 
    146 STATIC void
    147 TTYput(CHAR c)
    148 {
    149    Screen[ScreenCount] = c;
    150    if (++ScreenCount >= ScreenSize - 1) {
    151 ScreenSize += SCREEN_INC;
    152 RENEW(Screen, char, ScreenSize);
    153    }
    154 }
    155 
    156 STATIC void
    157 TTYputs(CONST CHAR *p)
    158 {
    159    while (*p)
    160 TTYput(*p++);
    161 }
    162 
    163 STATIC void
    164 TTYshow(CHAR c)
    165 {
    166    if (c == DEL) {
    167 TTYput('^');
    168 TTYput('?');
    169    }
    170    else if (ISCTL(c)) {
    171 TTYput('^');
    172 TTYput(UNCTL(c));
    173    }
    174    else if (rl_meta_chars && ISMETA(c)) {
    175 TTYput('M');
    176 TTYput('-');
    177 TTYput(UNMETA(c));
    178    }
    179    else
    180 TTYput(c);
    181 }
    182 
    183 STATIC void
    184 TTYstring(CHAR *p)
    185 {
    186    while (*p)
    187 TTYshow(*p++);
    188 }
    189 
    190 STATIC unsigned int
    191 TTYget()
    192 {
    193    CHAR	c;
    194 
    195    TTYflush();
    196    if (Pushed) {
    197 Pushed = 0;
    198 return PushBack;
    199    }
    200    if (*Input)
    201 return *Input++;
    202    return read(0, &c, (SIZE_T)1) == 1 ? c : EOF;
    203 }
    204 
    205 #define TTYback()	(backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
    206 
    207 STATIC void
    208 TTYbackn(int n)
    209 {
    210    while (--n >= 0)
    211 TTYback();
    212 }
    213 
    214 STATIC void
    215 TTYinfo()
    216 {
    217    static int		init;
    218 #if	defined(USE_TERMCAP)
    219    char		*term;
    220    char		buff[2048];
    221    char		*bp, *p;
    222 #endif	/* defined(USE_TERMCAP) */
    223 #if	defined(TIOCGWINSZ)
    224    struct winsize	W;
    225 #endif	/* defined(TIOCGWINSZ) */
    226 
    227    if (init) {
    228 #if	defined(TIOCGWINSZ)
    229 /* Perhaps we got resized. */
    230 if (ioctl(0, TIOCGWINSZ, &W) >= 0
    231  && W.ws_col > 0 && W.ws_row > 0) {
    232     TTYwidth = (int)W.ws_col;
    233     TTYrows = (int)W.ws_row;
    234 }
    235 #endif	/* defined(TIOCGWINSZ) */
    236 return;
    237    }
    238    init++;
    239 
    240    TTYwidth = TTYrows = 0;
    241 #if	defined(USE_TERMCAP)
    242    bp = &buff[0];
    243    if ((term = getenv("TERM")) == NULL)
    244 term = "dumb";
    245    if (tgetent(buff, term) < 0) {
    246       TTYwidth = SCREEN_WIDTH;
    247       TTYrows = SCREEN_ROWS;
    248       return;
    249    }
    250    p = tgetstr("le", &bp);
    251    backspace = p ? strdup(p) : NULL;
    252    TTYwidth = tgetnum("co");
    253    TTYrows = tgetnum("li");
    254 #endif	/* defined(USE_TERMCAP) */
    255 
    256 #if	defined(TIOCGWINSZ)
    257    if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
    258 TTYwidth = (int)W.ws_col;
    259 TTYrows = (int)W.ws_row;
    260    }
    261 #endif	/* defined(TIOCGWINSZ) */
    262 
    263    if (TTYwidth <= 0 || TTYrows <= 0) {
    264 TTYwidth = SCREEN_WIDTH;
    265 TTYrows = SCREEN_ROWS;
    266    }
    267 }
    268 
    269 
    270 STATIC void
    271 reposition()
    272 {
    273    int		i;
    274    CHAR	*p;
    275 
    276    TTYput('\r');
    277    TTYputs((CONST CHAR *)Prompt);
    278    for (i = Point, p = Line; --i >= 0; p++)
    279 TTYshow(*p);
    280 }
    281 
    282 STATIC void
    283 left(STATUS Change)
    284 {
    285    TTYback();
    286    if (Point) {
    287 if (ISCTL(Line[Point - 1]))
    288     TTYback();
    289        else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
    290     TTYback();
    291     TTYback();
    292 }
    293    }
    294    if (Change == CSmove)
    295 Point--;
    296 }
    297 
    298 STATIC void
    299 right(STATUS Change)
    300 {
    301    TTYshow(Line[Point]);
    302    if (Change == CSmove)
    303 Point++;
    304 }
    305 
    306 STATIC STATUS
    307 ring_bell()
    308 {
    309    TTYput('\07');
    310    TTYflush();
    311    return CSstay;
    312 }
    313 
    314 STATIC STATUS
    315 do_macro(unsigned int c)
    316 {
    317    CHAR		name[4];
    318 
    319    name[0] = '_';
    320    name[1] = c;
    321    name[2] = '_';
    322    name[3] = '\0';
    323 
    324    if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
    325 Input = NIL;
    326 return ring_bell();
    327    }
    328    return CSstay;
    329 }
    330 
    331 STATIC STATUS
    332 do_forward(STATUS move)
    333 {
    334    int		i;
    335    CHAR	*p;
    336 
    337    i = 0;
    338    do {
    339 p = &Line[Point];
    340 for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
    341     if (move == CSmove)
    342 	right(CSstay);
    343 
    344 for (; Point < End && isalnum(*p); Point++, p++)
    345     if (move == CSmove)
    346 	right(CSstay);
    347 
    348 if (Point == End)
    349     break;
    350    } while (++i < Repeat);
    351 
    352    return CSstay;
    353 }
    354 
    355 STATIC STATUS
    356 do_case(CASE type)
    357 {
    358    int		i;
    359    int		end;
    360    int		count;
    361    CHAR	*p;
    362 
    363    (void)do_forward(CSstay);
    364    if (OldPoint != Point) {
    365 if ((count = Point - OldPoint) < 0)
    366     count = -count;
    367 Point = OldPoint;
    368 if ((end = Point + count) > End)
    369     end = End;
    370 for (i = Point, p = &Line[i]; i < end; i++, p++) {
    371     if (type == TOupper) {
    372 	if (islower(*p))
    373 	    *p = toupper(*p);
    374     }
    375     else if (isupper(*p))
    376 	*p = tolower(*p);
    377     right(CSmove);
    378 }
    379    }
    380    return CSstay;
    381 }
    382 
    383 STATIC STATUS
    384 case_down_word()
    385 {
    386    return do_case(TOlower);
    387 }
    388 
    389 STATIC STATUS
    390 case_up_word()
    391 {
    392    return do_case(TOupper);
    393 }
    394 
    395 STATIC void
    396 ceol()
    397 {
    398    int		extras;
    399    int		i;
    400    CHAR	*p;
    401 
    402    for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
    403 TTYput(' ');
    404 if (ISCTL(*p)) {
    405     TTYput(' ');
    406     extras++;
    407 }
    408 else if (rl_meta_chars && ISMETA(*p)) {
    409     TTYput(' ');
    410     TTYput(' ');
    411     extras += 2;
    412 }
    413    }
    414 
    415    for (i += extras; i > Point; i--)
    416 TTYback();
    417 }
    418 
    419 STATIC void
    420 clear_line()
    421 {
    422    Point = -strlen(Prompt);
    423    TTYput('\r');
    424    ceol();
    425    Point = 0;
    426    End = 0;
    427    Line[0] = '\0';
    428 }
    429 
    430 STATIC STATUS
    431 insert_string(CHAR *p)
    432 {
    433    SIZE_T	len;
    434    int		i;
    435    CHAR	*new;
    436    CHAR	*q;
    437 
    438    len = strlen((char *)p);
    439    if (End + len >= Length) {
    440 if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
    441     return CSstay;
    442 if (Length) {
    443     COPYFROMTO(new, Line, Length);
    444     DISPOSE(Line);
    445 }
    446 Line = new;
    447 Length += len + MEM_INC;
    448    }
    449 
    450    for (q = &Line[Point], i = End - Point; --i >= 0; )
    451 q[len + i] = q[i];
    452    COPYFROMTO(&Line[Point], p, len);
    453    End += len;
    454    Line[End] = '\0';
    455    TTYstring(&Line[Point]);
    456    Point += len;
    457 
    458    return Point == End ? CSstay : CSmove;
    459 }
    460 
    461 STATIC STATUS
    462 redisplay()
    463 {
    464    TTYputs((CONST CHAR *)NEWLINE);
    465    TTYputs((CONST CHAR *)Prompt);
    466    TTYstring(Line);
    467    return CSmove;
    468 }
    469 
    470 STATIC STATUS
    471 toggle_meta_mode()
    472 {
    473    rl_meta_chars = ! rl_meta_chars;
    474    return redisplay();
    475 }
    476 
    477 
    478 STATIC CHAR *
    479 next_hist()
    480 {
    481    return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
    482 }
    483 
    484 STATIC CHAR *
    485 prev_hist()
    486 {
    487    return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
    488 }
    489 
    490 STATIC STATUS
    491 do_insert_hist(CHAR *p)
    492 {
    493    if (p == NULL)
    494 return ring_bell();
    495    Point = 0;
    496    reposition();
    497    ceol();
    498    End = 0;
    499    return insert_string(p);
    500 }
    501 
    502 STATIC STATUS
    503 do_hist(CHAR *(*move)())
    504 {
    505    CHAR	*p;
    506    int		i;
    507 
    508    i = 0;
    509    do {
    510 if ((p = (*move)()) == NULL)
    511     return ring_bell();
    512    } while (++i < Repeat);
    513    return do_insert_hist(p);
    514 }
    515 
    516 STATIC STATUS
    517 h_next()
    518 {
    519    return do_hist(next_hist);
    520 }
    521 
    522 STATIC STATUS
    523 h_prev()
    524 {
    525    return do_hist(prev_hist);
    526 }
    527 
    528 STATIC STATUS
    529 h_first()
    530 {
    531    return do_insert_hist(H.Lines[H.Pos = 0]);
    532 }
    533 
    534 STATIC STATUS
    535 h_last()
    536 {
    537    return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
    538 }
    539 
    540 /*
    541 **  Return zero if pat appears as a substring in text.
    542 */
    543 STATIC int
    544 substrcmp(char *text, char *pat, size_t len)
    545 {
    546    char	c;
    547 
    548    if ((c = *pat) == '\0')
    549        return *text == '\0';
    550    for ( ; *text; text++)
    551        if (*text == c && strncmp(text, pat, len) == 0)
    552            return 0;
    553    return 1;
    554 }
    555 
    556 STATIC CHAR *
    557 search_hist(CHAR *search, CHAR *(*move)())
    558 {
    559    static CHAR	*old_search;
    560    int		len;
    561    int		pos;
    562    int		(*match)(char *, char *, size_t);
    563    char	*pat;
    564 
    565    /* Save or get remembered search pattern. */
    566    if (search && *search) {
    567 if (old_search)
    568     DISPOSE(old_search);
    569 old_search = (CHAR *)strdup((char *)search);
    570    }
    571    else {
    572 if (old_search == NULL || *old_search == '\0')
    573            return NULL;
    574 search = old_search;
    575    }
    576 
    577    /* Set up pattern-finder. */
    578    if (*search == '^') {
    579 match = (int(*)(char *, char *, size_t))strncmp;
    580 pat = (char *)(search + 1);
    581    }
    582    else {
    583 match = substrcmp;
    584 pat = (char *)search;
    585    }
    586    len = strlen(pat);
    587 
    588    for (pos = H.Pos; (*move)() != NULL; )
    589 if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
    590            return H.Lines[H.Pos];
    591    H.Pos = pos;
    592    return NULL;
    593 }
    594 
    595 STATIC STATUS
    596 h_search()
    597 {
    598    static int	Searching;
    599    CONST char	*old_prompt;
    600    CHAR	*(*move)();
    601    CHAR	*p;
    602 
    603    if (Searching)
    604 return ring_bell();
    605    Searching = 1;
    606 
    607    clear_line();
    608    old_prompt = Prompt;
    609    Prompt = "Search: ";
    610    TTYputs((CONST CHAR *)Prompt);
    611    move = Repeat == NO_ARG ? prev_hist : next_hist;
    612    p = editinput();
    613    Prompt = old_prompt;
    614    Searching = 0;
    615    TTYputs((CONST CHAR *)Prompt);
    616    if (p == NULL && Signal > 0) {
    617 Signal = 0;
    618 clear_line();
    619 return redisplay();
    620    }
    621    p = search_hist(p, move);
    622    clear_line();
    623    if (p == NULL) {
    624 (void)ring_bell();
    625 return redisplay();
    626    }
    627    return do_insert_hist(p);
    628 }
    629 
    630 STATIC STATUS
    631 fd_char()
    632 {
    633    int		i;
    634 
    635    i = 0;
    636    do {
    637 if (Point >= End)
    638     break;
    639 right(CSmove);
    640    } while (++i < Repeat);
    641    return CSstay;
    642 }
    643 
    644 STATIC void
    645 save_yank(int begin, int i)
    646 {
    647    if (Yanked) {
    648 DISPOSE(Yanked);
    649 Yanked = NULL;
    650    }
    651 
    652    if (i < 1)
    653 return;
    654 
    655    if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
    656 COPYFROMTO(Yanked, &Line[begin], i);
    657 Yanked[i] = '\0';
    658    }
    659 }
    660 
    661 STATIC STATUS
    662 delete_string(int count)
    663 {
    664    int		i;
    665    CHAR	*p;
    666 
    667    if (count <= 0 || End == Point)
    668 return ring_bell();
    669 
    670    if (count == 1 && Point == End - 1) {
    671 /* Optimize common case of delete at end of line. */
    672 End--;
    673 p = &Line[Point];
    674 i = 1;
    675 TTYput(' ');
    676 if (ISCTL(*p)) {
    677     i = 2;
    678     TTYput(' ');
    679 }
    680 else if (rl_meta_chars && ISMETA(*p)) {
    681     i = 3;
    682     TTYput(' ');
    683     TTYput(' ');
    684 }
    685 TTYbackn(i);
    686 *p = '\0';
    687 return CSmove;
    688    }
    689    if (Point + count > End && (count = End - Point) <= 0)
    690 return CSstay;
    691 
    692    if (count > 1)
    693 save_yank(Point, count);
    694 
    695    for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
    696 p[0] = p[count];
    697    ceol();
    698    End -= count;
    699    TTYstring(&Line[Point]);
    700    return CSmove;
    701 }
    702 
    703 STATIC STATUS
    704 bk_char()
    705 {
    706    int		i;
    707 
    708    i = 0;
    709    do {
    710 if (Point == 0)
    711     break;
    712 left(CSmove);
    713    } while (++i < Repeat);
    714 
    715    return CSstay;
    716 }
    717 
    718 STATIC STATUS
    719 bk_del_char()
    720 {
    721    int		i;
    722 
    723    i = 0;
    724    do {
    725 if (Point == 0)
    726     break;
    727 left(CSmove);
    728    } while (++i < Repeat);
    729 
    730    return delete_string(i);
    731 }
    732 
    733 STATIC STATUS
    734 kill_line()
    735 {
    736    int		i;
    737 
    738    if (Repeat != NO_ARG) {
    739 if (Repeat < Point) {
    740     i = Point;
    741     Point = Repeat;
    742     reposition();
    743     (void)delete_string(i - Point);
    744 }
    745 else if (Repeat > Point) {
    746     right(CSmove);
    747     (void)delete_string(Repeat - Point - 1);
    748 }
    749 return CSmove;
    750    }
    751 
    752    save_yank(Point, End - Point);
    753    Line[Point] = '\0';
    754    ceol();
    755    End = Point;
    756    return CSstay;
    757 }
    758 
    759 STATIC STATUS
    760 insert_char(int c)
    761 {
    762    STATUS	s;
    763    CHAR	buff[2];
    764    CHAR	*p;
    765    CHAR	*q;
    766    int		i;
    767 
    768    if (Repeat == NO_ARG || Repeat < 2) {
    769 buff[0] = c;
    770 buff[1] = '\0';
    771 return insert_string(buff);
    772    }
    773 
    774    if ((p = NEW(CHAR, Repeat + 1)) == NULL)
    775 return CSstay;
    776    for (i = Repeat, q = p; --i >= 0; )
    777 *q++ = c;
    778    *q = '\0';
    779    Repeat = 0;
    780    s = insert_string(p);
    781    DISPOSE(p);
    782    return s;
    783 }
    784 
    785 STATIC STATUS
    786 meta()
    787 {
    788    unsigned int	c;
    789    CONST KEYMAP	*kp;
    790 
    791    if ((int)(c = TTYget()) == EOF)
    792 return CSeof;
    793 #if	defined(ANSI_ARROWS)
    794    /* Also include VT-100 arrows. */
    795    if (c == '[' || c == 'O')
    796 switch (c = TTYget()) {
    797 default:	return ring_bell();
    798 case EOF:	return CSeof;
    799 case 'A':	return h_prev();
    800 case 'B':	return h_next();
    801 case 'C':	return fd_char();
    802 case 'D':	return bk_char();
    803 }
    804 #endif	/* defined(ANSI_ARROWS) */
    805 
    806    if (isdigit(c)) {
    807 for (Repeat = c - '0'; (int)(c = TTYget()) != EOF && isdigit(c); )
    808     Repeat = Repeat * 10 + c - '0';
    809 Pushed = 1;
    810 PushBack = c;
    811 return CSstay;
    812    }
    813 
    814    if (isupper(c))
    815 return do_macro(c);
    816    for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
    817 if (kp->Key == c)
    818     return (*kp->Function)();
    819 
    820    return ring_bell();
    821 }
    822 
    823 STATIC STATUS
    824 emacs(unsigned int c)
    825 {
    826    STATUS		s;
    827    const KEYMAP	*kp;
    828 
    829    if (rl_meta_chars && ISMETA(c)) {
    830 Pushed = 1;
    831 PushBack = UNMETA(c);
    832 return meta();
    833    }
    834    for (kp = Map; kp->Function; kp++)
    835 if (kp->Key == c)
    836     break;
    837    s = kp->Function ? (*kp->Function)() : insert_char((int)c);
    838    if (!Pushed)
    839 /* No pushback means no repeat count; hacky, but true. */
    840 Repeat = NO_ARG;
    841    return s;
    842 }
    843 
    844 STATIC STATUS
    845 TTYspecial(unsigned int c)
    846 {
    847    if (ISMETA(c))
    848 return CSdispatch;
    849 
    850    if (c == rl_erase || (int)c == DEL)
    851 return bk_del_char();
    852    if (c == rl_kill) {
    853 if (Point != 0) {
    854     Point = 0;
    855     reposition();
    856 }
    857 Repeat = NO_ARG;
    858 return kill_line();
    859    }
    860    if (c == rl_eof && Point == 0 && End == 0)
    861 return CSeof;
    862    if (c == rl_intr) {
    863 Signal = SIGINT;
    864 return CSsignal;
    865    }
    866    if (c == rl_quit) {
    867 Signal = SIGQUIT;
    868 return CSeof;
    869    }
    870 
    871    return CSdispatch;
    872 }
    873 
    874 STATIC CHAR *
    875 editinput()
    876 {
    877    unsigned int	c;
    878 
    879    Repeat = NO_ARG;
    880    OldPoint = Point = Mark = End = 0;
    881    Line[0] = '\0';
    882 
    883    Signal = -1;
    884    while ((int)(c = TTYget()) != EOF)
    885 switch (TTYspecial(c)) {
    886 case CSdone:
    887     return Line;
    888 case CSeof:
    889     return NULL;
    890 case CSsignal:
    891     return (CHAR *)"";
    892 case CSmove:
    893     reposition();
    894     break;
    895 case CSdispatch:
    896     switch (emacs(c)) {
    897     case CSdone:
    898 	return Line;
    899     case CSeof:
    900 	return NULL;
    901     case CSsignal:
    902 	return (CHAR *)"";
    903     case CSmove:
    904 	reposition();
    905 	break;
    906     case CSdispatch:
    907     case CSstay:
    908 	break;
    909     }
    910     break;
    911 case CSstay:
    912     break;
    913 }
    914    if (strlen((char *)Line))
    915        return Line;
    916    free(Line);
    917    return NULL;
    918 }
    919 
    920 STATIC void
    921 hist_add(CHAR *p)
    922 {
    923    int		i;
    924 
    925    if ((p = (CHAR *)strdup((char *)p)) == NULL)
    926 return;
    927    if (H.Size < HIST_SIZE)
    928 H.Lines[H.Size++] = p;
    929    else {
    930 DISPOSE(H.Lines[0]);
    931 for (i = 0; i < HIST_SIZE - 1; i++)
    932     H.Lines[i] = H.Lines[i + 1];
    933 H.Lines[i] = p;
    934    }
    935    H.Pos = H.Size - 1;
    936 }
    937 
    938 /*
    939 **  For compatibility with FSF readline.
    940 */
    941 /* ARGSUSED0 */
    942 void
    943 rl_reset_terminal(char *p)
    944 {
    945    (void)p;
    946 }
    947 
    948 void
    949 rl_initialize()
    950 {
    951 }
    952 
    953 char *
    954 readline(CONST char *prompt)
    955 {
    956    CHAR	*line;
    957    int		s;
    958 
    959    if (Line == NULL) {
    960 Length = MEM_INC;
    961 if ((Line = NEW(CHAR, Length)) == NULL)
    962     return NULL;
    963    }
    964 
    965    TTYinfo();
    966    rl_ttyset(0);
    967    hist_add(NIL);
    968    ScreenSize = SCREEN_INC;
    969    Screen = NEW(char, ScreenSize);
    970    Prompt = prompt ? prompt : (char *)NIL;
    971    TTYputs((CONST CHAR *)Prompt);
    972    if ((line = editinput()) != NULL) {
    973 line = (CHAR *)strdup((char *)line);
    974 TTYputs((CONST CHAR *)NEWLINE);
    975 TTYflush();
    976    }
    977    rl_ttyset(1);
    978    DISPOSE(Screen);
    979    DISPOSE(H.Lines[--H.Size]);
    980    if (Signal > 0) {
    981 s = Signal;
    982 Signal = 0;
    983 (void)kill(getpid(), s);
    984    }
    985    return (char *)line;
    986 }
    987 
    988 void
    989 add_history(char *p)
    990 {
    991    if (p == NULL || *p == '\0')
    992 return;
    993 
    994 #if	defined(UNIQUE_HISTORY)
    995    if (H.Size && strcmp(p, (char *)H.Lines[H.Size - 1]) == 0)
    996        return;
    997 #endif	/* defined(UNIQUE_HISTORY) */
    998    hist_add((CHAR *)p);
    999 }
   1000 
   1001 
   1002 STATIC STATUS
   1003 beg_line()
   1004 {
   1005    if (Point) {
   1006 Point = 0;
   1007 return CSmove;
   1008    }
   1009    return CSstay;
   1010 }
   1011 
   1012 STATIC STATUS
   1013 del_char()
   1014 {
   1015    return delete_string(Repeat == NO_ARG ? 1 : Repeat);
   1016 }
   1017 
   1018 STATIC STATUS
   1019 end_line()
   1020 {
   1021    if (Point != End) {
   1022 Point = End;
   1023 return CSmove;
   1024    }
   1025    return CSstay;
   1026 }
   1027 
   1028 STATIC STATUS
   1029 accept_line()
   1030 {
   1031    Line[End] = '\0';
   1032    return CSdone;
   1033 }
   1034 
   1035 STATIC STATUS
   1036 transpose()
   1037 {
   1038    CHAR	c;
   1039 
   1040    if (Point) {
   1041 if (Point == End)
   1042     left(CSmove);
   1043 c = Line[Point - 1];
   1044 left(CSstay);
   1045 Line[Point - 1] = Line[Point];
   1046 TTYshow(Line[Point - 1]);
   1047 Line[Point++] = c;
   1048 TTYshow(c);
   1049    }
   1050    return CSstay;
   1051 }
   1052 
   1053 STATIC STATUS
   1054 quote()
   1055 {
   1056    unsigned int	c;
   1057 
   1058    return (int)(c = TTYget()) == EOF ? CSeof : insert_char((int)c);
   1059 }
   1060 
   1061 STATIC STATUS
   1062 wipe()
   1063 {
   1064    int		i;
   1065 
   1066    if (Mark > End)
   1067 return ring_bell();
   1068 
   1069    if (Point > Mark) {
   1070 i = Point;
   1071 Point = Mark;
   1072 Mark = i;
   1073 reposition();
   1074    }
   1075 
   1076    return delete_string(Mark - Point);
   1077 }
   1078 
   1079 STATIC STATUS
   1080 mk_set()
   1081 {
   1082    Mark = Point;
   1083    return CSstay;
   1084 }
   1085 
   1086 STATIC STATUS
   1087 exchange()
   1088 {
   1089    unsigned int	c;
   1090 
   1091    if ((c = TTYget()) != CTL('X'))
   1092 return (int)c == EOF ? CSeof : ring_bell();
   1093 
   1094    if ((int)(c = Mark) <= End) {
   1095 Mark = Point;
   1096 Point = c;
   1097 return CSmove;
   1098    }
   1099    return CSstay;
   1100 }
   1101 
   1102 STATIC STATUS
   1103 yank()
   1104 {
   1105    if (Yanked && *Yanked)
   1106 return insert_string(Yanked);
   1107    return CSstay;
   1108 }
   1109 
   1110 STATIC STATUS
   1111 copy_region()
   1112 {
   1113    if (Mark > End)
   1114 return ring_bell();
   1115 
   1116    if (Point > Mark)
   1117 save_yank(Mark, Point - Mark);
   1118    else
   1119 save_yank(Point, Mark - Point);
   1120 
   1121    return CSstay;
   1122 }
   1123 
   1124 STATIC STATUS
   1125 move_to_char()
   1126 {
   1127    unsigned int	c;
   1128    int			i;
   1129    CHAR		*p;
   1130 
   1131    if ((int)(c = TTYget()) == EOF)
   1132 return CSeof;
   1133    for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
   1134 if (*p == c) {
   1135     Point = i;
   1136     return CSmove;
   1137 }
   1138    return CSstay;
   1139 }
   1140 
   1141 STATIC STATUS
   1142 fd_word()
   1143 {
   1144    return do_forward(CSmove);
   1145 }
   1146 
   1147 STATIC STATUS
   1148 fd_kill_word()
   1149 {
   1150    int		i;
   1151 
   1152    (void)do_forward(CSstay);
   1153    if (OldPoint != Point) {
   1154 i = Point - OldPoint;
   1155 Point = OldPoint;
   1156 return delete_string(i);
   1157    }
   1158    return CSstay;
   1159 }
   1160 
   1161 STATIC STATUS
   1162 bk_word()
   1163 {
   1164    int		i;
   1165    CHAR	*p;
   1166 
   1167    i = 0;
   1168    do {
   1169 for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
   1170     left(CSmove);
   1171 
   1172 for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
   1173     left(CSmove);
   1174 
   1175 if (Point == 0)
   1176     break;
   1177    } while (++i < Repeat);
   1178 
   1179    return CSstay;
   1180 }
   1181 
   1182 STATIC STATUS
   1183 bk_kill_word()
   1184 {
   1185    (void)bk_word();
   1186    if (OldPoint != Point)
   1187 return delete_string(OldPoint - Point);
   1188    return CSstay;
   1189 }
   1190 
   1191 STATIC int
   1192 argify(CHAR *line, CHAR ***avp)
   1193 {
   1194    CHAR	*c;
   1195    CHAR	**p;
   1196    CHAR	**new;
   1197    int		ac;
   1198    int		i;
   1199 
   1200    i = MEM_INC;
   1201    if ((*avp = p = NEW(CHAR*, i))== NULL)
   1202  return 0;
   1203 
   1204    for (c = line; isspace(*c); c++)
   1205 continue;
   1206    if (*c == '\n' || *c == '\0')
   1207 return 0;
   1208 
   1209    for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
   1210 if (isspace(*c)) {
   1211     *c++ = '\0';
   1212     if (*c && *c != '\n') {
   1213 	if (ac + 1 == i) {
   1214 	    new = NEW(CHAR*, i + MEM_INC);
   1215 	    if (new == NULL) {
   1216 		p[ac] = NULL;
   1217 		return ac;
   1218 	    }
   1219 	    COPYFROMTO(new, p, i * sizeof (char **));
   1220 	    i += MEM_INC;
   1221 	    DISPOSE(p);
   1222 	    *avp = p = new;
   1223 	}
   1224 	p[ac++] = c;
   1225     }
   1226 }
   1227 else
   1228     c++;
   1229    }
   1230    *c = '\0';
   1231    p[ac] = NULL;
   1232    return ac;
   1233 }
   1234 
   1235 STATIC STATUS
   1236 last_argument()
   1237 {
   1238    CHAR	**av;
   1239    CHAR	*p;
   1240    STATUS	s;
   1241    int		ac;
   1242 
   1243    if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
   1244 return ring_bell();
   1245 
   1246    if ((p = (CHAR *)strdup((char *)p)) == NULL)
   1247 return CSstay;
   1248    ac = argify(p, &av);
   1249 
   1250    if (Repeat != NO_ARG)
   1251 s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
   1252    else
   1253 s = ac ? insert_string(av[ac - 1]) : CSstay;
   1254 
   1255    if (ac)
   1256 DISPOSE(av);
   1257    DISPOSE(p);
   1258    return s;
   1259 }
   1260 
   1261 STATIC CONST KEYMAP Map[32] = {
   1262    {	CTL('@'),	ring_bell	},
   1263    {	CTL('A'),	beg_line	},
   1264    {	CTL('B'),	bk_char		},
   1265    {	CTL('D'),	del_char	},
   1266    {	CTL('E'),	end_line	},
   1267    {	CTL('F'),	fd_char		},
   1268    {	CTL('G'),	ring_bell	},
   1269    {	CTL('H'),	bk_del_char	},
   1270    {	CTL('J'),	accept_line	},
   1271    {	CTL('K'),	kill_line	},
   1272    {	CTL('L'),	redisplay	},
   1273    {	CTL('M'),	accept_line	},
   1274    {	CTL('N'),	h_next		},
   1275    {	CTL('O'),	ring_bell	},
   1276    {	CTL('P'),	h_prev		},
   1277    {	CTL('Q'),	ring_bell	},
   1278    {	CTL('R'),	h_search	},
   1279    {	CTL('S'),	ring_bell	},
   1280    {	CTL('T'),	transpose	},
   1281    {	CTL('U'),	ring_bell	},
   1282    {	CTL('V'),	quote		},
   1283    {	CTL('W'),	wipe		},
   1284    {	CTL('X'),	exchange	},
   1285    {	CTL('Y'),	yank		},
   1286    {	CTL('Z'),	ring_bell	},
   1287    {	CTL('['),	meta		},
   1288    {	CTL(']'),	move_to_char	},
   1289    {	CTL('^'),	ring_bell	},
   1290    {	CTL('_'),	ring_bell	},
   1291    {	0,		NULL		}
   1292 };
   1293 
   1294 STATIC CONST KEYMAP MetaMap[16]= {
   1295    {	CTL('H'),	bk_kill_word	},
   1296    {	DEL,		bk_kill_word	},
   1297    {	' ',		mk_set		},
   1298    {	'.',		last_argument	},
   1299    {	'<',		h_first		},
   1300    {	'>',		h_last		},
   1301    {	'b',		bk_word		},
   1302    {	'd',		fd_kill_word	},
   1303    {	'f',		fd_word		},
   1304    {	'l',		case_down_word	},
   1305    {	'm',		toggle_meta_mode },
   1306    {	'u',		case_up_word	},
   1307    {	'y',		yank		},
   1308    {	'w',		copy_region	},
   1309    {	0,		NULL		}
   1310 };