1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab /* 12a5f0fb15SPaul Saab * Functions which manipulate the command buffer. 13a5f0fb15SPaul Saab * Used only by command() and related functions. 14a5f0fb15SPaul Saab */ 15a5f0fb15SPaul Saab 16a5f0fb15SPaul Saab #include "less.h" 17a5f0fb15SPaul Saab #include "cmd.h" 186dcb072bSXin LI #include "charset.h" 196dcb072bSXin LI #if HAVE_STAT 206dcb072bSXin LI #include <sys/stat.h> 216dcb072bSXin LI #endif 22a5f0fb15SPaul Saab 23a5f0fb15SPaul Saab extern int sc_width; 246dcb072bSXin LI extern int utf_mode; 25b7780dbeSXin LI extern int no_hist_dups; 26b7780dbeSXin LI extern int marks_modified; 27a5f0fb15SPaul Saab 28a5f0fb15SPaul Saab static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 29a5f0fb15SPaul Saab static int cmd_col; /* Current column of the cursor */ 30a5f0fb15SPaul Saab static int prompt_col; /* Column of cursor just after prompt */ 31a5f0fb15SPaul Saab static char *cp; /* Pointer into cmdbuf */ 32a5f0fb15SPaul Saab static int cmd_offset; /* Index into cmdbuf of first displayed char */ 33*c77c4889SXin LI static lbool literal; /* Next input char should not be interpreted */ 34*c77c4889SXin LI static size_t updown_match; /* Prefix length in up/down movement */ 35*c77c4889SXin LI static lbool have_updown_match = FALSE; 36a5f0fb15SPaul Saab 37a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 38d713e089SXin LI static int cmd_complete(int action); 39a5f0fb15SPaul Saab /* 40a5f0fb15SPaul Saab * These variables are statics used by cmd_complete. 41a5f0fb15SPaul Saab */ 42*c77c4889SXin LI static lbool in_completion = FALSE; 43a5f0fb15SPaul Saab static char *tk_text; 44a5f0fb15SPaul Saab static char *tk_original; 45*c77c4889SXin LI static constant char *tk_ipoint; 46*c77c4889SXin LI static constant char *tk_trial = NULL; 47a5f0fb15SPaul Saab static struct textlist tk_tlist; 48a5f0fb15SPaul Saab #endif 49a5f0fb15SPaul Saab 50a5f0fb15SPaul Saab static int cmd_left(); 51a5f0fb15SPaul Saab static int cmd_right(); 52a5f0fb15SPaul Saab 53a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 54a5f0fb15SPaul Saab public char openquote = '"'; 55a5f0fb15SPaul Saab public char closequote = '"'; 56a5f0fb15SPaul Saab #endif 57a5f0fb15SPaul Saab 58a5f0fb15SPaul Saab #if CMD_HISTORY 596dcb072bSXin LI 606dcb072bSXin LI /* History file */ 616dcb072bSXin LI #define HISTFILE_FIRST_LINE ".less-history-file:" 626dcb072bSXin LI #define HISTFILE_SEARCH_SECTION ".search" 636dcb072bSXin LI #define HISTFILE_SHELL_SECTION ".shell" 64b7780dbeSXin LI #define HISTFILE_MARK_SECTION ".mark" 656dcb072bSXin LI 66a5f0fb15SPaul Saab /* 67a5f0fb15SPaul Saab * A mlist structure represents a command history. 68a5f0fb15SPaul Saab */ 69a5f0fb15SPaul Saab struct mlist 70a5f0fb15SPaul Saab { 71a5f0fb15SPaul Saab struct mlist *next; 72a5f0fb15SPaul Saab struct mlist *prev; 73a5f0fb15SPaul Saab struct mlist *curr_mp; 74a5f0fb15SPaul Saab char *string; 75*c77c4889SXin LI lbool modified; 76a5f0fb15SPaul Saab }; 77a5f0fb15SPaul Saab 78a5f0fb15SPaul Saab /* 79a5f0fb15SPaul Saab * These are the various command histories that exist. 80a5f0fb15SPaul Saab */ 81a5f0fb15SPaul Saab struct mlist mlist_search = 827f074f9cSXin LI { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; 83f6b74a7dSXin LI public void *ml_search = (void *) &mlist_search; 84a5f0fb15SPaul Saab 85a5f0fb15SPaul Saab struct mlist mlist_examine = 867f074f9cSXin LI { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; 87f6b74a7dSXin LI public void *ml_examine = (void *) &mlist_examine; 88a5f0fb15SPaul Saab 89a5f0fb15SPaul Saab #if SHELL_ESCAPE || PIPEC 90a5f0fb15SPaul Saab struct mlist mlist_shell = 917f074f9cSXin LI { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; 92f6b74a7dSXin LI public void *ml_shell = (void *) &mlist_shell; 93a5f0fb15SPaul Saab #endif 94a5f0fb15SPaul Saab 95a5f0fb15SPaul Saab #else /* CMD_HISTORY */ 96a5f0fb15SPaul Saab 97a5f0fb15SPaul Saab /* If CMD_HISTORY is off, these are just flags. */ 98f6b74a7dSXin LI public void *ml_search = (void *)1; 99f6b74a7dSXin LI public void *ml_examine = (void *)2; 100a5f0fb15SPaul Saab #if SHELL_ESCAPE || PIPEC 101f6b74a7dSXin LI public void *ml_shell = (void *)3; 102a5f0fb15SPaul Saab #endif 103a5f0fb15SPaul Saab 104a5f0fb15SPaul Saab #endif /* CMD_HISTORY */ 105a5f0fb15SPaul Saab 106a5f0fb15SPaul Saab /* 107a5f0fb15SPaul Saab * History for the current command. 108a5f0fb15SPaul Saab */ 109a5f0fb15SPaul Saab static struct mlist *curr_mlist = NULL; 110a5f0fb15SPaul Saab static int curr_cmdflags; 111a5f0fb15SPaul Saab 1126dcb072bSXin LI static char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; 1136dcb072bSXin LI static int cmd_mbc_buf_len; 1146dcb072bSXin LI static int cmd_mbc_buf_index; 1156dcb072bSXin LI 116a5f0fb15SPaul Saab 117a5f0fb15SPaul Saab /* 118a5f0fb15SPaul Saab * Reset command buffer (to empty). 119a5f0fb15SPaul Saab */ 120d713e089SXin LI public void cmd_reset(void) 121a5f0fb15SPaul Saab { 122a5f0fb15SPaul Saab cp = cmdbuf; 123a5f0fb15SPaul Saab *cp = '\0'; 124a5f0fb15SPaul Saab cmd_col = 0; 125a5f0fb15SPaul Saab cmd_offset = 0; 126*c77c4889SXin LI literal = FALSE; 1276dcb072bSXin LI cmd_mbc_buf_len = 0; 128*c77c4889SXin LI have_updown_match = FALSE; 129a5f0fb15SPaul Saab } 130a5f0fb15SPaul Saab 131a5f0fb15SPaul Saab /* 1327f074f9cSXin LI * Clear command line. 133a5f0fb15SPaul Saab */ 134d713e089SXin LI public void clear_cmd(void) 135a5f0fb15SPaul Saab { 136a5f0fb15SPaul Saab cmd_col = prompt_col = 0; 1376dcb072bSXin LI cmd_mbc_buf_len = 0; 138*c77c4889SXin LI have_updown_match = FALSE; 139a5f0fb15SPaul Saab } 140a5f0fb15SPaul Saab 141a5f0fb15SPaul Saab /* 142a5f0fb15SPaul Saab * Display a string, usually as a prompt for input into the command buffer. 143a5f0fb15SPaul Saab */ 144d713e089SXin LI public void cmd_putstr(constant char *s) 145a5f0fb15SPaul Saab { 1466dcb072bSXin LI LWCHAR prev_ch = 0; 1476dcb072bSXin LI LWCHAR ch; 1481ea31627SRobert Watson constant char *endline = s + strlen(s); 1496dcb072bSXin LI while (*s != '\0') 1506dcb072bSXin LI { 151*c77c4889SXin LI constant char *os = s; 152f6b74a7dSXin LI int width; 153*c77c4889SXin LI ch = step_charc(&s, +1, endline); 154*c77c4889SXin LI while (os < s) 155*c77c4889SXin LI putchr(*os++); 1566dcb072bSXin LI if (!utf_mode) 157f6b74a7dSXin LI width = 1; 158f6b74a7dSXin LI else if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 159f6b74a7dSXin LI width = 0; 160f6b74a7dSXin LI else 161f6b74a7dSXin LI width = is_wide_char(ch) ? 2 : 1; 1626dcb072bSXin LI cmd_col += width; 1636dcb072bSXin LI prompt_col += width; 1646dcb072bSXin LI prev_ch = ch; 1656dcb072bSXin LI } 166a5f0fb15SPaul Saab } 167a5f0fb15SPaul Saab 168a5f0fb15SPaul Saab /* 169a5f0fb15SPaul Saab * How many characters are in the command buffer? 170a5f0fb15SPaul Saab */ 171d713e089SXin LI public int len_cmdbuf(void) 172a5f0fb15SPaul Saab { 173*c77c4889SXin LI constant char *s = cmdbuf; 174*c77c4889SXin LI constant char *endline = s + strlen(s); 1756dcb072bSXin LI int len = 0; 1766dcb072bSXin LI 1776dcb072bSXin LI while (*s != '\0') 1786dcb072bSXin LI { 179*c77c4889SXin LI step_charc(&s, +1, endline); 1806dcb072bSXin LI len++; 1816dcb072bSXin LI } 1826dcb072bSXin LI return (len); 1836dcb072bSXin LI } 1846dcb072bSXin LI 1856dcb072bSXin LI /* 1866dcb072bSXin LI * Common part of cmd_step_right() and cmd_step_left(). 187f6b74a7dSXin LI * {{ Returning pwidth and bswidth separately is a historical artifact 188f6b74a7dSXin LI * since they're always the same. Maybe clean this up someday. }} 1896dcb072bSXin LI */ 190*c77c4889SXin LI static constant char * cmd_step_common(char *p, LWCHAR ch, size_t len, int *pwidth, int *bswidth) 1916dcb072bSXin LI { 192*c77c4889SXin LI constant char *pr; 193f6b74a7dSXin LI int width; 1946dcb072bSXin LI 1956dcb072bSXin LI if (len == 1) 1966dcb072bSXin LI { 197*c77c4889SXin LI pr = prchar(ch); 198f6b74a7dSXin LI width = (int) strlen(pr); 1996dcb072bSXin LI } else 2006dcb072bSXin LI { 2016dcb072bSXin LI pr = prutfchar(ch); 2026dcb072bSXin LI if (is_composing_char(ch)) 203f6b74a7dSXin LI width = 0; 204f6b74a7dSXin LI else if (is_ubin_char(ch)) 205f6b74a7dSXin LI width = (int) strlen(pr); 206f6b74a7dSXin LI else 2076dcb072bSXin LI { 2086dcb072bSXin LI LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 2096dcb072bSXin LI if (is_combining_char(prev_ch, ch)) 210f6b74a7dSXin LI width = 0; 211f6b74a7dSXin LI else 212f6b74a7dSXin LI width = is_wide_char(ch) ? 2 : 1; 213f6b74a7dSXin LI } 214f6b74a7dSXin LI } 2156dcb072bSXin LI if (pwidth != NULL) 216f6b74a7dSXin LI *pwidth = width; 2176dcb072bSXin LI if (bswidth != NULL) 218f6b74a7dSXin LI *bswidth = width; 2196dcb072bSXin LI return (pr); 2206dcb072bSXin LI } 2216dcb072bSXin LI 2226dcb072bSXin LI /* 2236dcb072bSXin LI * Step a pointer one character right in the command buffer. 2246dcb072bSXin LI */ 225*c77c4889SXin LI static constant char * cmd_step_right(char **pp, int *pwidth, int *bswidth) 2266dcb072bSXin LI { 2276dcb072bSXin LI char *p = *pp; 228f6b74a7dSXin LI LWCHAR ch = step_char(pp, +1, p + strlen(p)); 2296dcb072bSXin LI 230*c77c4889SXin LI return cmd_step_common(p, ch, ptr_diff(*pp, p), pwidth, bswidth); 2316dcb072bSXin LI } 2326dcb072bSXin LI 2336dcb072bSXin LI /* 2346dcb072bSXin LI * Step a pointer one character left in the command buffer. 2356dcb072bSXin LI */ 236*c77c4889SXin LI static constant char * cmd_step_left(char **pp, int *pwidth, int *bswidth) 2376dcb072bSXin LI { 2386dcb072bSXin LI char *p = *pp; 239f6b74a7dSXin LI LWCHAR ch = step_char(pp, -1, cmdbuf); 2406dcb072bSXin LI 241*c77c4889SXin LI return cmd_step_common(*pp, ch, ptr_diff(p, *pp), pwidth, bswidth); 242a5f0fb15SPaul Saab } 243a5f0fb15SPaul Saab 244a5f0fb15SPaul Saab /* 2452235c7feSXin LI * Put the cursor at "home" (just after the prompt), 2462235c7feSXin LI * and set cp to the corresponding char in cmdbuf. 2472235c7feSXin LI */ 248d713e089SXin LI static void cmd_home(void) 2492235c7feSXin LI { 2502235c7feSXin LI while (cmd_col > prompt_col) 2512235c7feSXin LI { 2522235c7feSXin LI int width, bswidth; 2532235c7feSXin LI 2542235c7feSXin LI cmd_step_left(&cp, &width, &bswidth); 2552235c7feSXin LI while (bswidth-- > 0) 2562235c7feSXin LI putbs(); 2572235c7feSXin LI cmd_col -= width; 2582235c7feSXin LI } 2592235c7feSXin LI 2602235c7feSXin LI cp = &cmdbuf[cmd_offset]; 2612235c7feSXin LI } 2622235c7feSXin LI 2632235c7feSXin LI /* 264a5f0fb15SPaul Saab * Repaint the line from cp onwards. 265a5f0fb15SPaul Saab * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 266a5f0fb15SPaul Saab */ 267d713e089SXin LI public void cmd_repaint(constant char *old_cp) 268a5f0fb15SPaul Saab { 269a5f0fb15SPaul Saab /* 270a5f0fb15SPaul Saab * Repaint the line from the current position. 271a5f0fb15SPaul Saab */ 2722235c7feSXin LI if (old_cp == NULL) 2732235c7feSXin LI { 2742235c7feSXin LI old_cp = cp; 2752235c7feSXin LI cmd_home(); 2762235c7feSXin LI } 277a5f0fb15SPaul Saab clear_eol(); 2786dcb072bSXin LI while (*cp != '\0') 279a5f0fb15SPaul Saab { 2806dcb072bSXin LI char *np = cp; 2816dcb072bSXin LI int width; 282*c77c4889SXin LI constant char *pr = cmd_step_right(&np, &width, NULL); 2836dcb072bSXin LI if (cmd_col + width >= sc_width) 284a5f0fb15SPaul Saab break; 2856dcb072bSXin LI cp = np; 2866dcb072bSXin LI putstr(pr); 2876dcb072bSXin LI cmd_col += width; 2886dcb072bSXin LI } 2896dcb072bSXin LI while (*cp != '\0') 2906dcb072bSXin LI { 2916dcb072bSXin LI char *np = cp; 2926dcb072bSXin LI int width; 293*c77c4889SXin LI constant char *pr = cmd_step_right(&np, &width, NULL); 2946dcb072bSXin LI if (width > 0) 2956dcb072bSXin LI break; 2966dcb072bSXin LI cp = np; 2976dcb072bSXin LI putstr(pr); 298a5f0fb15SPaul Saab } 299a5f0fb15SPaul Saab 300a5f0fb15SPaul Saab /* 301a5f0fb15SPaul Saab * Back up the cursor to the correct position. 302a5f0fb15SPaul Saab */ 303a5f0fb15SPaul Saab while (cp > old_cp) 304a5f0fb15SPaul Saab cmd_left(); 305a5f0fb15SPaul Saab } 306a5f0fb15SPaul Saab 307a5f0fb15SPaul Saab /* 308a5f0fb15SPaul Saab * Shift the cmdbuf display left a half-screen. 309a5f0fb15SPaul Saab */ 310d713e089SXin LI static void cmd_lshift(void) 311a5f0fb15SPaul Saab { 312a5f0fb15SPaul Saab char *s; 313a5f0fb15SPaul Saab char *save_cp; 314a5f0fb15SPaul Saab int cols; 315a5f0fb15SPaul Saab 316a5f0fb15SPaul Saab /* 317a5f0fb15SPaul Saab * Start at the first displayed char, count how far to the 318a5f0fb15SPaul Saab * right we'd have to move to reach the center of the screen. 319a5f0fb15SPaul Saab */ 320a5f0fb15SPaul Saab s = cmdbuf + cmd_offset; 321a5f0fb15SPaul Saab cols = 0; 322a5f0fb15SPaul Saab while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 3236dcb072bSXin LI { 3246dcb072bSXin LI int width; 3256dcb072bSXin LI cmd_step_right(&s, &width, NULL); 3266dcb072bSXin LI cols += width; 3276dcb072bSXin LI } 3286dcb072bSXin LI while (*s != '\0') 3296dcb072bSXin LI { 3306dcb072bSXin LI int width; 3316dcb072bSXin LI char *ns = s; 3326dcb072bSXin LI cmd_step_right(&ns, &width, NULL); 3336dcb072bSXin LI if (width > 0) 3346dcb072bSXin LI break; 3356dcb072bSXin LI s = ns; 3366dcb072bSXin LI } 337a5f0fb15SPaul Saab 338a15691bfSXin LI cmd_offset = (int) (s - cmdbuf); 339a5f0fb15SPaul Saab save_cp = cp; 340a5f0fb15SPaul Saab cmd_home(); 341a5f0fb15SPaul Saab cmd_repaint(save_cp); 342a5f0fb15SPaul Saab } 343a5f0fb15SPaul Saab 344a5f0fb15SPaul Saab /* 345a5f0fb15SPaul Saab * Shift the cmdbuf display right a half-screen. 346a5f0fb15SPaul Saab */ 347d713e089SXin LI static void cmd_rshift(void) 348a5f0fb15SPaul Saab { 349a5f0fb15SPaul Saab char *s; 350a5f0fb15SPaul Saab char *save_cp; 351a5f0fb15SPaul Saab int cols; 352a5f0fb15SPaul Saab 353a5f0fb15SPaul Saab /* 354a5f0fb15SPaul Saab * Start at the first displayed char, count how far to the 355a5f0fb15SPaul Saab * left we'd have to move to traverse a half-screen width 356a5f0fb15SPaul Saab * of displayed characters. 357a5f0fb15SPaul Saab */ 358a5f0fb15SPaul Saab s = cmdbuf + cmd_offset; 359a5f0fb15SPaul Saab cols = 0; 360a5f0fb15SPaul Saab while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 361a5f0fb15SPaul Saab { 3626dcb072bSXin LI int width; 3636dcb072bSXin LI cmd_step_left(&s, &width, NULL); 3646dcb072bSXin LI cols += width; 365a5f0fb15SPaul Saab } 366a5f0fb15SPaul Saab 367a15691bfSXin LI cmd_offset = (int) (s - cmdbuf); 368a5f0fb15SPaul Saab save_cp = cp; 369a5f0fb15SPaul Saab cmd_home(); 370a5f0fb15SPaul Saab cmd_repaint(save_cp); 371a5f0fb15SPaul Saab } 372a5f0fb15SPaul Saab 373a5f0fb15SPaul Saab /* 374a5f0fb15SPaul Saab * Move cursor right one character. 375a5f0fb15SPaul Saab */ 376d713e089SXin LI static int cmd_right(void) 377a5f0fb15SPaul Saab { 378*c77c4889SXin LI constant char *pr; 3796dcb072bSXin LI char *ncp; 3806dcb072bSXin LI int width; 381a5f0fb15SPaul Saab 382a5f0fb15SPaul Saab if (*cp == '\0') 383a5f0fb15SPaul Saab { 3846dcb072bSXin LI /* Already at the end of the line. */ 385a5f0fb15SPaul Saab return (CC_OK); 386a5f0fb15SPaul Saab } 3876dcb072bSXin LI ncp = cp; 3886dcb072bSXin LI pr = cmd_step_right(&ncp, &width, NULL); 3896dcb072bSXin LI if (cmd_col + width >= sc_width) 390a5f0fb15SPaul Saab cmd_lshift(); 3916dcb072bSXin LI else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 392a5f0fb15SPaul Saab cmd_lshift(); 3936dcb072bSXin LI cp = ncp; 3946dcb072bSXin LI cmd_col += width; 3956dcb072bSXin LI putstr(pr); 3966dcb072bSXin LI while (*cp != '\0') 3976dcb072bSXin LI { 3986dcb072bSXin LI pr = cmd_step_right(&ncp, &width, NULL); 3996dcb072bSXin LI if (width > 0) 4006dcb072bSXin LI break; 4016dcb072bSXin LI putstr(pr); 4026dcb072bSXin LI cp = ncp; 4036dcb072bSXin LI } 404a5f0fb15SPaul Saab return (CC_OK); 405a5f0fb15SPaul Saab } 406a5f0fb15SPaul Saab 407a5f0fb15SPaul Saab /* 408a5f0fb15SPaul Saab * Move cursor left one character. 409a5f0fb15SPaul Saab */ 410d713e089SXin LI static int cmd_left(void) 411a5f0fb15SPaul Saab { 4126dcb072bSXin LI char *ncp; 413b2ea2440SXin LI int width = 0; 414b2ea2440SXin LI int bswidth = 0; 415a5f0fb15SPaul Saab 416a5f0fb15SPaul Saab if (cp <= cmdbuf) 417a5f0fb15SPaul Saab { 418a5f0fb15SPaul Saab /* Already at the beginning of the line */ 419a5f0fb15SPaul Saab return (CC_OK); 420a5f0fb15SPaul Saab } 4216dcb072bSXin LI ncp = cp; 4226dcb072bSXin LI while (ncp > cmdbuf) 4236dcb072bSXin LI { 4246dcb072bSXin LI cmd_step_left(&ncp, &width, &bswidth); 4256dcb072bSXin LI if (width > 0) 4266dcb072bSXin LI break; 4276dcb072bSXin LI } 4286dcb072bSXin LI if (cmd_col < prompt_col + width) 429a5f0fb15SPaul Saab cmd_rshift(); 4306dcb072bSXin LI cp = ncp; 4316dcb072bSXin LI cmd_col -= width; 4326dcb072bSXin LI while (bswidth-- > 0) 433a5f0fb15SPaul Saab putbs(); 434a5f0fb15SPaul Saab return (CC_OK); 435a5f0fb15SPaul Saab } 436a5f0fb15SPaul Saab 437a5f0fb15SPaul Saab /* 438a5f0fb15SPaul Saab * Insert a char into the command buffer, at the current position. 439a5f0fb15SPaul Saab */ 440*c77c4889SXin LI static int cmd_ichar(constant char *cs, size_t clen) 441a5f0fb15SPaul Saab { 442a5f0fb15SPaul Saab char *s; 443a5f0fb15SPaul Saab 4446dcb072bSXin LI if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 445a5f0fb15SPaul Saab { 4466dcb072bSXin LI /* No room in the command buffer for another char. */ 447a5f0fb15SPaul Saab bell(); 448a5f0fb15SPaul Saab return (CC_ERROR); 449a5f0fb15SPaul Saab } 450a5f0fb15SPaul Saab 451a5f0fb15SPaul Saab /* 4526dcb072bSXin LI * Make room for the new character (shift the tail of the buffer right). 453a5f0fb15SPaul Saab */ 454a5f0fb15SPaul Saab for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 4556dcb072bSXin LI s[clen] = s[0]; 4566dcb072bSXin LI /* 4576dcb072bSXin LI * Insert the character into the buffer. 4586dcb072bSXin LI */ 4596dcb072bSXin LI for (s = cp; s < cp + clen; s++) 4606dcb072bSXin LI *s = *cs++; 461a5f0fb15SPaul Saab /* 462a5f0fb15SPaul Saab * Reprint the tail of the line from the inserted char. 463a5f0fb15SPaul Saab */ 464*c77c4889SXin LI have_updown_match = FALSE; 465a5f0fb15SPaul Saab cmd_repaint(cp); 466a5f0fb15SPaul Saab cmd_right(); 467a5f0fb15SPaul Saab return (CC_OK); 468a5f0fb15SPaul Saab } 469a5f0fb15SPaul Saab 470a5f0fb15SPaul Saab /* 471a5f0fb15SPaul Saab * Backspace in the command buffer. 472a5f0fb15SPaul Saab * Delete the char to the left of the cursor. 473a5f0fb15SPaul Saab */ 474d713e089SXin LI static int cmd_erase(void) 475a5f0fb15SPaul Saab { 4761ea31627SRobert Watson char *s; 4776dcb072bSXin LI int clen; 478a5f0fb15SPaul Saab 479a5f0fb15SPaul Saab if (cp == cmdbuf) 480a5f0fb15SPaul Saab { 481a5f0fb15SPaul Saab /* 482a5f0fb15SPaul Saab * Backspace past beginning of the buffer: 483a5f0fb15SPaul Saab * this usually means abort the command. 484a5f0fb15SPaul Saab */ 485a5f0fb15SPaul Saab return (CC_QUIT); 486a5f0fb15SPaul Saab } 487a5f0fb15SPaul Saab /* 488a5f0fb15SPaul Saab * Move cursor left (to the char being erased). 489a5f0fb15SPaul Saab */ 4906dcb072bSXin LI s = cp; 491a5f0fb15SPaul Saab cmd_left(); 492a15691bfSXin LI clen = (int) (s - cp); 4936dcb072bSXin LI 494a5f0fb15SPaul Saab /* 495a5f0fb15SPaul Saab * Remove the char from the buffer (shift the buffer left). 496a5f0fb15SPaul Saab */ 4976dcb072bSXin LI for (s = cp; ; s++) 4986dcb072bSXin LI { 4996dcb072bSXin LI s[0] = s[clen]; 5006dcb072bSXin LI if (s[0] == '\0') 5016dcb072bSXin LI break; 5026dcb072bSXin LI } 5036dcb072bSXin LI 504a5f0fb15SPaul Saab /* 505a5f0fb15SPaul Saab * Repaint the buffer after the erased char. 506a5f0fb15SPaul Saab */ 507*c77c4889SXin LI have_updown_match = FALSE; 508a5f0fb15SPaul Saab cmd_repaint(cp); 509a5f0fb15SPaul Saab 510a5f0fb15SPaul Saab /* 511a5f0fb15SPaul Saab * We say that erasing the entire command string causes us 512a5f0fb15SPaul Saab * to abort the current command, if CF_QUIT_ON_ERASE is set. 513a5f0fb15SPaul Saab */ 514a5f0fb15SPaul Saab if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 515a5f0fb15SPaul Saab return (CC_QUIT); 516a5f0fb15SPaul Saab return (CC_OK); 517a5f0fb15SPaul Saab } 518a5f0fb15SPaul Saab 519a5f0fb15SPaul Saab /* 520a5f0fb15SPaul Saab * Delete the char under the cursor. 521a5f0fb15SPaul Saab */ 522d713e089SXin LI static int cmd_delete(void) 523a5f0fb15SPaul Saab { 524a5f0fb15SPaul Saab if (*cp == '\0') 525a5f0fb15SPaul Saab { 5266dcb072bSXin LI /* At end of string; there is no char under the cursor. */ 527a5f0fb15SPaul Saab return (CC_OK); 528a5f0fb15SPaul Saab } 529a5f0fb15SPaul Saab /* 530a5f0fb15SPaul Saab * Move right, then use cmd_erase. 531a5f0fb15SPaul Saab */ 532a5f0fb15SPaul Saab cmd_right(); 533a5f0fb15SPaul Saab cmd_erase(); 534a5f0fb15SPaul Saab return (CC_OK); 535a5f0fb15SPaul Saab } 536a5f0fb15SPaul Saab 537a5f0fb15SPaul Saab /* 538a5f0fb15SPaul Saab * Delete the "word" to the left of the cursor. 539a5f0fb15SPaul Saab */ 540d713e089SXin LI static int cmd_werase(void) 541a5f0fb15SPaul Saab { 542a5f0fb15SPaul Saab if (cp > cmdbuf && cp[-1] == ' ') 543a5f0fb15SPaul Saab { 544a5f0fb15SPaul Saab /* 545a5f0fb15SPaul Saab * If the char left of cursor is a space, 546a5f0fb15SPaul Saab * erase all the spaces left of cursor (to the first non-space). 547a5f0fb15SPaul Saab */ 548a5f0fb15SPaul Saab while (cp > cmdbuf && cp[-1] == ' ') 549a5f0fb15SPaul Saab (void) cmd_erase(); 550a5f0fb15SPaul Saab } else 551a5f0fb15SPaul Saab { 552a5f0fb15SPaul Saab /* 553a5f0fb15SPaul Saab * If the char left of cursor is not a space, 554a5f0fb15SPaul Saab * erase all the nonspaces left of cursor (the whole "word"). 555a5f0fb15SPaul Saab */ 556a5f0fb15SPaul Saab while (cp > cmdbuf && cp[-1] != ' ') 557a5f0fb15SPaul Saab (void) cmd_erase(); 558a5f0fb15SPaul Saab } 559a5f0fb15SPaul Saab return (CC_OK); 560a5f0fb15SPaul Saab } 561a5f0fb15SPaul Saab 562a5f0fb15SPaul Saab /* 563a5f0fb15SPaul Saab * Delete the "word" under the cursor. 564a5f0fb15SPaul Saab */ 565d713e089SXin LI static int cmd_wdelete(void) 566a5f0fb15SPaul Saab { 567a5f0fb15SPaul Saab if (*cp == ' ') 568a5f0fb15SPaul Saab { 569a5f0fb15SPaul Saab /* 570a5f0fb15SPaul Saab * If the char under the cursor is a space, 571a5f0fb15SPaul Saab * delete it and all the spaces right of cursor. 572a5f0fb15SPaul Saab */ 573a5f0fb15SPaul Saab while (*cp == ' ') 574a5f0fb15SPaul Saab (void) cmd_delete(); 575a5f0fb15SPaul Saab } else 576a5f0fb15SPaul Saab { 577a5f0fb15SPaul Saab /* 578a5f0fb15SPaul Saab * If the char under the cursor is not a space, 579a5f0fb15SPaul Saab * delete it and all nonspaces right of cursor (the whole word). 580a5f0fb15SPaul Saab */ 581a5f0fb15SPaul Saab while (*cp != ' ' && *cp != '\0') 582a5f0fb15SPaul Saab (void) cmd_delete(); 583a5f0fb15SPaul Saab } 584a5f0fb15SPaul Saab return (CC_OK); 585a5f0fb15SPaul Saab } 586a5f0fb15SPaul Saab 587a5f0fb15SPaul Saab /* 588a5f0fb15SPaul Saab * Delete all chars in the command buffer. 589a5f0fb15SPaul Saab */ 590d713e089SXin LI static int cmd_kill(void) 591a5f0fb15SPaul Saab { 592a5f0fb15SPaul Saab if (cmdbuf[0] == '\0') 593a5f0fb15SPaul Saab { 5946dcb072bSXin LI /* Buffer is already empty; abort the current command. */ 595a5f0fb15SPaul Saab return (CC_QUIT); 596a5f0fb15SPaul Saab } 597a5f0fb15SPaul Saab cmd_offset = 0; 598a5f0fb15SPaul Saab cmd_home(); 599a5f0fb15SPaul Saab *cp = '\0'; 600*c77c4889SXin LI have_updown_match = FALSE; 601a5f0fb15SPaul Saab cmd_repaint(cp); 602a5f0fb15SPaul Saab 603a5f0fb15SPaul Saab /* 604a5f0fb15SPaul Saab * We say that erasing the entire command string causes us 605a5f0fb15SPaul Saab * to abort the current command, if CF_QUIT_ON_ERASE is set. 606a5f0fb15SPaul Saab */ 607a5f0fb15SPaul Saab if (curr_cmdflags & CF_QUIT_ON_ERASE) 608a5f0fb15SPaul Saab return (CC_QUIT); 609a5f0fb15SPaul Saab return (CC_OK); 610a5f0fb15SPaul Saab } 611a5f0fb15SPaul Saab 612a5f0fb15SPaul Saab /* 613a5f0fb15SPaul Saab * Select an mlist structure to be the current command history. 614a5f0fb15SPaul Saab */ 615d713e089SXin LI public void set_mlist(void *mlist, int cmdflags) 616a5f0fb15SPaul Saab { 6177374caaaSXin LI #if CMD_HISTORY 618a5f0fb15SPaul Saab curr_mlist = (struct mlist *) mlist; 619a5f0fb15SPaul Saab curr_cmdflags = cmdflags; 6206dcb072bSXin LI 6216dcb072bSXin LI /* Make sure the next up-arrow moves to the last string in the mlist. */ 6226dcb072bSXin LI if (curr_mlist != NULL) 6236dcb072bSXin LI curr_mlist->curr_mp = curr_mlist; 6247374caaaSXin LI #endif 625a5f0fb15SPaul Saab } 626a5f0fb15SPaul Saab 627a5f0fb15SPaul Saab #if CMD_HISTORY 628a5f0fb15SPaul Saab /* 629a5f0fb15SPaul Saab * Move up or down in the currently selected command history list. 63096e55cc7SXin LI * Only consider entries whose first updown_match chars are equal to 63196e55cc7SXin LI * cmdbuf's corresponding chars. 632a5f0fb15SPaul Saab */ 633d713e089SXin LI static int cmd_updown(int action) 634a5f0fb15SPaul Saab { 635f6b74a7dSXin LI constant char *s; 63696e55cc7SXin LI struct mlist *ml; 637a5f0fb15SPaul Saab 638a5f0fb15SPaul Saab if (curr_mlist == NULL) 639a5f0fb15SPaul Saab { 640a5f0fb15SPaul Saab /* 641a5f0fb15SPaul Saab * The current command has no history list. 642a5f0fb15SPaul Saab */ 643a5f0fb15SPaul Saab bell(); 644a5f0fb15SPaul Saab return (CC_OK); 645a5f0fb15SPaul Saab } 64696e55cc7SXin LI 647*c77c4889SXin LI if (!have_updown_match) 64896e55cc7SXin LI { 649*c77c4889SXin LI updown_match = ptr_diff(cp, cmdbuf); 650*c77c4889SXin LI have_updown_match = TRUE; 65196e55cc7SXin LI } 65296e55cc7SXin LI 653a5f0fb15SPaul Saab /* 65496e55cc7SXin LI * Find the next history entry which matches. 655a5f0fb15SPaul Saab */ 65696e55cc7SXin LI for (ml = curr_mlist->curr_mp;;) 65796e55cc7SXin LI { 65896e55cc7SXin LI ml = (action == EC_UP) ? ml->prev : ml->next; 65996e55cc7SXin LI if (ml == curr_mlist) 66096e55cc7SXin LI { 661a5f0fb15SPaul Saab /* 66296e55cc7SXin LI * We reached the end (or beginning) of the list. 66396e55cc7SXin LI */ 66496e55cc7SXin LI break; 66596e55cc7SXin LI } 66696e55cc7SXin LI if (strncmp(cmdbuf, ml->string, updown_match) == 0) 66796e55cc7SXin LI { 66896e55cc7SXin LI /* 66996e55cc7SXin LI * This entry matches; stop here. 670a5f0fb15SPaul Saab * Copy the entry into cmdbuf and echo it on the screen. 671a5f0fb15SPaul Saab */ 67296e55cc7SXin LI curr_mlist->curr_mp = ml; 67396e55cc7SXin LI s = ml->string; 674a5f0fb15SPaul Saab if (s == NULL) 675a5f0fb15SPaul Saab s = ""; 676b7780dbeSXin LI cmd_offset = 0; 67796e55cc7SXin LI cmd_home(); 67896e55cc7SXin LI clear_eol(); 6797bd2567cSXin LI strcpy(cmdbuf, s); 6806dcb072bSXin LI for (cp = cmdbuf; *cp != '\0'; ) 681a5f0fb15SPaul Saab cmd_right(); 682a5f0fb15SPaul Saab return (CC_OK); 683a5f0fb15SPaul Saab } 68496e55cc7SXin LI } 68596e55cc7SXin LI /* 68696e55cc7SXin LI * We didn't find a history entry that matches. 68796e55cc7SXin LI */ 68896e55cc7SXin LI bell(); 68996e55cc7SXin LI return (CC_OK); 69096e55cc7SXin LI } 691*c77c4889SXin LI 692*c77c4889SXin LI /* 693*c77c4889SXin LI * Yet another lesson in the evils of global variables. 694*c77c4889SXin LI */ 695*c77c4889SXin LI public ssize_t save_updown_match(void) 696*c77c4889SXin LI { 697*c77c4889SXin LI if (!have_updown_match) 698*c77c4889SXin LI return (ssize_t)(-1); 699*c77c4889SXin LI return (ssize_t) updown_match; 700*c77c4889SXin LI } 701*c77c4889SXin LI 702*c77c4889SXin LI public void restore_updown_match(ssize_t udm) 703*c77c4889SXin LI { 704*c77c4889SXin LI updown_match = udm; 705*c77c4889SXin LI have_updown_match = (udm != (ssize_t)(-1)); 706*c77c4889SXin LI } 707*c77c4889SXin LI #endif /* CMD_HISTORY */ 708a5f0fb15SPaul Saab 709a5f0fb15SPaul Saab /* 710b7780dbeSXin LI * 711b7780dbeSXin LI */ 712d713e089SXin LI static void ml_link(struct mlist *mlist, struct mlist *ml) 713b7780dbeSXin LI { 714b7780dbeSXin LI ml->next = mlist; 715b7780dbeSXin LI ml->prev = mlist->prev; 716b7780dbeSXin LI mlist->prev->next = ml; 717b7780dbeSXin LI mlist->prev = ml; 718b7780dbeSXin LI } 719b7780dbeSXin LI 720b7780dbeSXin LI /* 721b7780dbeSXin LI * 722b7780dbeSXin LI */ 723d713e089SXin LI static void ml_unlink(struct mlist *ml) 724b7780dbeSXin LI { 725b7780dbeSXin LI ml->prev->next = ml->next; 726b7780dbeSXin LI ml->next->prev = ml->prev; 727b7780dbeSXin LI } 728b7780dbeSXin LI 729b7780dbeSXin LI /* 730a15691bfSXin LI * Add a string to an mlist. 731a5f0fb15SPaul Saab */ 732*c77c4889SXin LI public void cmd_addhist(struct mlist *mlist, constant char *cmd, lbool modified) 733a5f0fb15SPaul Saab { 734a5f0fb15SPaul Saab #if CMD_HISTORY 735a5f0fb15SPaul Saab struct mlist *ml; 736a5f0fb15SPaul Saab 737a5f0fb15SPaul Saab /* 738a5f0fb15SPaul Saab * Don't save a trivial command. 739a5f0fb15SPaul Saab */ 740a5f0fb15SPaul Saab if (strlen(cmd) == 0) 741a5f0fb15SPaul Saab return; 7426dcb072bSXin LI 743b7780dbeSXin LI if (no_hist_dups) 744b7780dbeSXin LI { 745b7780dbeSXin LI struct mlist *next = NULL; 746b7780dbeSXin LI for (ml = mlist->next; ml->string != NULL; ml = next) 747b7780dbeSXin LI { 748b7780dbeSXin LI next = ml->next; 749b7780dbeSXin LI if (strcmp(ml->string, cmd) == 0) 750b7780dbeSXin LI { 751b7780dbeSXin LI ml_unlink(ml); 752b7780dbeSXin LI free(ml->string); 753b7780dbeSXin LI free(ml); 754b7780dbeSXin LI } 755b7780dbeSXin LI } 756b7780dbeSXin LI } 757b7780dbeSXin LI 758a5f0fb15SPaul Saab /* 7596dcb072bSXin LI * Save the command unless it's a duplicate of the 7606dcb072bSXin LI * last command in the history. 761a5f0fb15SPaul Saab */ 7626dcb072bSXin LI ml = mlist->prev; 7636dcb072bSXin LI if (ml == mlist || strcmp(ml->string, cmd) != 0) 764a5f0fb15SPaul Saab { 765a5f0fb15SPaul Saab /* 766a5f0fb15SPaul Saab * Did not find command in history. 767a5f0fb15SPaul Saab * Save the command and put it at the end of the history list. 768a5f0fb15SPaul Saab */ 769a5f0fb15SPaul Saab ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 770a5f0fb15SPaul Saab ml->string = save(cmd); 771a15691bfSXin LI ml->modified = modified; 772b7780dbeSXin LI ml_link(mlist, ml); 773a5f0fb15SPaul Saab } 774a5f0fb15SPaul Saab /* 775a5f0fb15SPaul Saab * Point to the cmd just after the just-accepted command. 776a5f0fb15SPaul Saab * Thus, an UPARROW will always retrieve the previous command. 777a5f0fb15SPaul Saab */ 778a5f0fb15SPaul Saab mlist->curr_mp = ml->next; 779a5f0fb15SPaul Saab #endif 780a5f0fb15SPaul Saab } 781a5f0fb15SPaul Saab 782a5f0fb15SPaul Saab /* 783a5f0fb15SPaul Saab * Accept the command in the command buffer. 784a5f0fb15SPaul Saab * Add it to the currently selected history list. 785a5f0fb15SPaul Saab */ 786d713e089SXin LI public void cmd_accept(void) 787a5f0fb15SPaul Saab { 788a5f0fb15SPaul Saab #if CMD_HISTORY 789a5f0fb15SPaul Saab /* 790a5f0fb15SPaul Saab * Nothing to do if there is no currently selected history list. 791a5f0fb15SPaul Saab */ 792b7780dbeSXin LI if (curr_mlist == NULL || curr_mlist == ml_examine) 793a5f0fb15SPaul Saab return; 794*c77c4889SXin LI cmd_addhist(curr_mlist, cmdbuf, TRUE); 795*c77c4889SXin LI curr_mlist->modified = TRUE; 796a5f0fb15SPaul Saab #endif 797a5f0fb15SPaul Saab } 798a5f0fb15SPaul Saab 799a5f0fb15SPaul Saab /* 800a5f0fb15SPaul Saab * Try to perform a line-edit function on the command buffer, 801a5f0fb15SPaul Saab * using a specified char as a line-editing command. 802a5f0fb15SPaul Saab * Returns: 803a5f0fb15SPaul Saab * CC_PASS The char does not invoke a line edit function. 804a5f0fb15SPaul Saab * CC_OK Line edit function done. 805a5f0fb15SPaul Saab * CC_QUIT The char requests the current command to be aborted. 806a5f0fb15SPaul Saab */ 807*c77c4889SXin LI static int cmd_edit(char c) 808a5f0fb15SPaul Saab { 809a5f0fb15SPaul Saab int action; 810a5f0fb15SPaul Saab int flags; 811a5f0fb15SPaul Saab 812a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 813a5f0fb15SPaul Saab #define not_in_completion() in_completion = 0 814a5f0fb15SPaul Saab #else 815d713e089SXin LI #define not_in_completion(void) 816a5f0fb15SPaul Saab #endif 817a5f0fb15SPaul Saab 818a5f0fb15SPaul Saab /* 819a5f0fb15SPaul Saab * See if the char is indeed a line-editing command. 820a5f0fb15SPaul Saab */ 821a5f0fb15SPaul Saab flags = 0; 822a5f0fb15SPaul Saab #if CMD_HISTORY 823a5f0fb15SPaul Saab if (curr_mlist == NULL) 824a5f0fb15SPaul Saab /* 825a5f0fb15SPaul Saab * No current history; don't accept history manipulation cmds. 826a5f0fb15SPaul Saab */ 8272235c7feSXin LI flags |= ECF_NOHISTORY; 828a5f0fb15SPaul Saab #endif 829a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 83095270f73SXin LI if (curr_mlist == ml_search || curr_mlist == NULL) 831a5f0fb15SPaul Saab /* 83295270f73SXin LI * Don't accept file-completion cmds in contexts 83395270f73SXin LI * such as search pattern, digits, long option name, etc. 834a5f0fb15SPaul Saab */ 8352235c7feSXin LI flags |= ECF_NOCOMPLETE; 836a5f0fb15SPaul Saab #endif 837a5f0fb15SPaul Saab 838a5f0fb15SPaul Saab action = editchar(c, flags); 839a5f0fb15SPaul Saab 840a5f0fb15SPaul Saab switch (action) 841a5f0fb15SPaul Saab { 8422235c7feSXin LI case A_NOACTION: 8432235c7feSXin LI return (CC_OK); 844a5f0fb15SPaul Saab case EC_RIGHT: 845a5f0fb15SPaul Saab not_in_completion(); 846a5f0fb15SPaul Saab return (cmd_right()); 847a5f0fb15SPaul Saab case EC_LEFT: 848a5f0fb15SPaul Saab not_in_completion(); 849a5f0fb15SPaul Saab return (cmd_left()); 850a5f0fb15SPaul Saab case EC_W_RIGHT: 851a5f0fb15SPaul Saab not_in_completion(); 852a5f0fb15SPaul Saab while (*cp != '\0' && *cp != ' ') 853a5f0fb15SPaul Saab cmd_right(); 854a5f0fb15SPaul Saab while (*cp == ' ') 855a5f0fb15SPaul Saab cmd_right(); 856a5f0fb15SPaul Saab return (CC_OK); 857a5f0fb15SPaul Saab case EC_W_LEFT: 858a5f0fb15SPaul Saab not_in_completion(); 859a5f0fb15SPaul Saab while (cp > cmdbuf && cp[-1] == ' ') 860a5f0fb15SPaul Saab cmd_left(); 861a5f0fb15SPaul Saab while (cp > cmdbuf && cp[-1] != ' ') 862a5f0fb15SPaul Saab cmd_left(); 863a5f0fb15SPaul Saab return (CC_OK); 864a5f0fb15SPaul Saab case EC_HOME: 865a5f0fb15SPaul Saab not_in_completion(); 866a5f0fb15SPaul Saab cmd_offset = 0; 867a5f0fb15SPaul Saab cmd_home(); 868a5f0fb15SPaul Saab cmd_repaint(cp); 869a5f0fb15SPaul Saab return (CC_OK); 870a5f0fb15SPaul Saab case EC_END: 871a5f0fb15SPaul Saab not_in_completion(); 872a5f0fb15SPaul Saab while (*cp != '\0') 873a5f0fb15SPaul Saab cmd_right(); 874a5f0fb15SPaul Saab return (CC_OK); 875a5f0fb15SPaul Saab case EC_INSERT: 876a5f0fb15SPaul Saab not_in_completion(); 877a5f0fb15SPaul Saab return (CC_OK); 878a5f0fb15SPaul Saab case EC_BACKSPACE: 879a5f0fb15SPaul Saab not_in_completion(); 880a5f0fb15SPaul Saab return (cmd_erase()); 881a5f0fb15SPaul Saab case EC_LINEKILL: 882a5f0fb15SPaul Saab not_in_completion(); 883a5f0fb15SPaul Saab return (cmd_kill()); 88433096f16SXin LI case EC_ABORT: 88533096f16SXin LI not_in_completion(); 88633096f16SXin LI (void) cmd_kill(); 88733096f16SXin LI return (CC_QUIT); 888a5f0fb15SPaul Saab case EC_W_BACKSPACE: 889a5f0fb15SPaul Saab not_in_completion(); 890a5f0fb15SPaul Saab return (cmd_werase()); 891a5f0fb15SPaul Saab case EC_DELETE: 892a5f0fb15SPaul Saab not_in_completion(); 893a5f0fb15SPaul Saab return (cmd_delete()); 894a5f0fb15SPaul Saab case EC_W_DELETE: 895a5f0fb15SPaul Saab not_in_completion(); 896a5f0fb15SPaul Saab return (cmd_wdelete()); 897a5f0fb15SPaul Saab case EC_LITERAL: 898*c77c4889SXin LI literal = TRUE; 899a5f0fb15SPaul Saab return (CC_OK); 900a5f0fb15SPaul Saab #if CMD_HISTORY 901a5f0fb15SPaul Saab case EC_UP: 902a5f0fb15SPaul Saab case EC_DOWN: 903a5f0fb15SPaul Saab not_in_completion(); 904a5f0fb15SPaul Saab return (cmd_updown(action)); 905a5f0fb15SPaul Saab #endif 906a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 907a5f0fb15SPaul Saab case EC_F_COMPLETE: 908a5f0fb15SPaul Saab case EC_B_COMPLETE: 909a5f0fb15SPaul Saab case EC_EXPAND: 910a5f0fb15SPaul Saab return (cmd_complete(action)); 911a5f0fb15SPaul Saab #endif 912a5f0fb15SPaul Saab default: 913a5f0fb15SPaul Saab not_in_completion(); 914a5f0fb15SPaul Saab return (CC_PASS); 915a5f0fb15SPaul Saab } 916a5f0fb15SPaul Saab } 917a5f0fb15SPaul Saab 918a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 919a5f0fb15SPaul Saab /* 920a5f0fb15SPaul Saab * Insert a string into the command buffer, at the current position. 921a5f0fb15SPaul Saab */ 922*c77c4889SXin LI static int cmd_istr(constant char *str) 923a5f0fb15SPaul Saab { 924*c77c4889SXin LI constant char *endline = str + strlen(str); 925*c77c4889SXin LI constant char *s; 926a5f0fb15SPaul Saab int action; 927a5f0fb15SPaul Saab 9286dcb072bSXin LI for (s = str; *s != '\0'; ) 929a5f0fb15SPaul Saab { 930*c77c4889SXin LI constant char *os = s; 931*c77c4889SXin LI step_charc(&s, +1, endline); 932*c77c4889SXin LI action = cmd_ichar(os, ptr_diff(s, os)); 933a5f0fb15SPaul Saab if (action != CC_OK) 934a5f0fb15SPaul Saab return (action); 935a5f0fb15SPaul Saab } 936a5f0fb15SPaul Saab return (CC_OK); 937a5f0fb15SPaul Saab } 938a5f0fb15SPaul Saab 939a5f0fb15SPaul Saab /* 940a5f0fb15SPaul Saab * Find the beginning and end of the "current" word. 941a5f0fb15SPaul Saab * This is the word which the cursor (cp) is inside or at the end of. 942a5f0fb15SPaul Saab * Return pointer to the beginning of the word and put the 943a5f0fb15SPaul Saab * cursor at the end of the word. 944a5f0fb15SPaul Saab */ 945d713e089SXin LI static char * delimit_word(void) 946a5f0fb15SPaul Saab { 947a5f0fb15SPaul Saab char *word; 948a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 949a5f0fb15SPaul Saab char *p; 950*c77c4889SXin LI int delim_quoted = FALSE; 951*c77c4889SXin LI int meta_quoted = FALSE; 952f6b74a7dSXin LI constant char *esc = get_meta_escape(); 953*c77c4889SXin LI size_t esclen = strlen(esc); 954a5f0fb15SPaul Saab #endif 955a5f0fb15SPaul Saab 956a5f0fb15SPaul Saab /* 957a5f0fb15SPaul Saab * Move cursor to end of word. 958a5f0fb15SPaul Saab */ 959a5f0fb15SPaul Saab if (*cp != ' ' && *cp != '\0') 960a5f0fb15SPaul Saab { 961a5f0fb15SPaul Saab /* 962a5f0fb15SPaul Saab * Cursor is on a nonspace. 963a5f0fb15SPaul Saab * Move cursor right to the next space. 964a5f0fb15SPaul Saab */ 965a5f0fb15SPaul Saab while (*cp != ' ' && *cp != '\0') 966a5f0fb15SPaul Saab cmd_right(); 967a5f0fb15SPaul Saab } else if (cp > cmdbuf && cp[-1] != ' ') 968a5f0fb15SPaul Saab { 969a5f0fb15SPaul Saab /* 970a5f0fb15SPaul Saab * Cursor is on a space, and char to the left is a nonspace. 971a5f0fb15SPaul Saab * We're already at the end of the word. 972a5f0fb15SPaul Saab */ 973a5f0fb15SPaul Saab ; 974000ba3e8STim J. Robbins #if 0 975a5f0fb15SPaul Saab } else 976a5f0fb15SPaul Saab { 977a5f0fb15SPaul Saab /* 978a5f0fb15SPaul Saab * Cursor is on a space and char to the left is a space. 979a5f0fb15SPaul Saab * Huh? There's no word here. 980a5f0fb15SPaul Saab */ 981a5f0fb15SPaul Saab return (NULL); 982000ba3e8STim J. Robbins #endif 983a5f0fb15SPaul Saab } 984a5f0fb15SPaul Saab /* 985000ba3e8STim J. Robbins * Find the beginning of the word which the cursor is in. 986a5f0fb15SPaul Saab */ 987a5f0fb15SPaul Saab if (cp == cmdbuf) 988a5f0fb15SPaul Saab return (NULL); 989a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 990a5f0fb15SPaul Saab /* 991a5f0fb15SPaul Saab * If we have an unbalanced quote (that is, an open quote 992a5f0fb15SPaul Saab * without a corresponding close quote), we return everything 993a5f0fb15SPaul Saab * from the open quote, including spaces. 994a5f0fb15SPaul Saab */ 995000ba3e8STim J. Robbins for (word = cmdbuf; word < cp; word++) 996000ba3e8STim J. Robbins if (*word != ' ') 997000ba3e8STim J. Robbins break; 998000ba3e8STim J. Robbins if (word >= cp) 999000ba3e8STim J. Robbins return (cp); 1000a5f0fb15SPaul Saab for (p = cmdbuf; p < cp; p++) 1001a5f0fb15SPaul Saab { 1002000ba3e8STim J. Robbins if (meta_quoted) 1003a5f0fb15SPaul Saab { 1004*c77c4889SXin LI meta_quoted = FALSE; 1005000ba3e8STim J. Robbins } else if (esclen > 0 && p + esclen < cp && 1006000ba3e8STim J. Robbins strncmp(p, esc, esclen) == 0) 1007a5f0fb15SPaul Saab { 1008*c77c4889SXin LI meta_quoted = TRUE; 1009000ba3e8STim J. Robbins p += esclen - 1; 1010000ba3e8STim J. Robbins } else if (delim_quoted) 1011000ba3e8STim J. Robbins { 1012000ba3e8STim J. Robbins if (*p == closequote) 1013*c77c4889SXin LI delim_quoted = FALSE; 1014000ba3e8STim J. Robbins } else /* (!delim_quoted) */ 1015000ba3e8STim J. Robbins { 1016000ba3e8STim J. Robbins if (*p == openquote) 1017*c77c4889SXin LI delim_quoted = TRUE; 1018000ba3e8STim J. Robbins else if (*p == ' ') 1019000ba3e8STim J. Robbins word = p+1; 1020a5f0fb15SPaul Saab } 1021a5f0fb15SPaul Saab } 1022a5f0fb15SPaul Saab #endif 1023a5f0fb15SPaul Saab return (word); 1024a5f0fb15SPaul Saab } 1025a5f0fb15SPaul Saab 1026a5f0fb15SPaul Saab /* 1027a5f0fb15SPaul Saab * Set things up to enter completion mode. 1028a5f0fb15SPaul Saab * Expand the word under the cursor into a list of filenames 1029a5f0fb15SPaul Saab * which start with that word, and set tk_text to that list. 1030a5f0fb15SPaul Saab */ 1031d713e089SXin LI static void init_compl(void) 1032a5f0fb15SPaul Saab { 1033a5f0fb15SPaul Saab char *word; 1034a5f0fb15SPaul Saab char c; 1035a5f0fb15SPaul Saab 1036a5f0fb15SPaul Saab /* 1037a5f0fb15SPaul Saab * Get rid of any previous tk_text. 1038a5f0fb15SPaul Saab */ 1039a5f0fb15SPaul Saab if (tk_text != NULL) 1040a5f0fb15SPaul Saab { 1041a5f0fb15SPaul Saab free(tk_text); 1042a5f0fb15SPaul Saab tk_text = NULL; 1043a5f0fb15SPaul Saab } 1044a5f0fb15SPaul Saab /* 1045a5f0fb15SPaul Saab * Find the original (uncompleted) word in the command buffer. 1046a5f0fb15SPaul Saab */ 1047a5f0fb15SPaul Saab word = delimit_word(); 1048a5f0fb15SPaul Saab if (word == NULL) 1049a5f0fb15SPaul Saab return; 1050a5f0fb15SPaul Saab /* 1051a5f0fb15SPaul Saab * Set the insertion point to the point in the command buffer 1052a5f0fb15SPaul Saab * where the original (uncompleted) word now sits. 1053a5f0fb15SPaul Saab */ 1054a5f0fb15SPaul Saab tk_ipoint = word; 1055a5f0fb15SPaul Saab /* 1056a5f0fb15SPaul Saab * Save the original (uncompleted) word 1057a5f0fb15SPaul Saab */ 1058a5f0fb15SPaul Saab if (tk_original != NULL) 1059a5f0fb15SPaul Saab free(tk_original); 1060*c77c4889SXin LI tk_original = (char *) ecalloc(ptr_diff(cp,word)+1, sizeof(char)); 1061*c77c4889SXin LI strncpy(tk_original, word, ptr_diff(cp,word)); 1062a5f0fb15SPaul Saab /* 1063a5f0fb15SPaul Saab * Get the expanded filename. 1064a5f0fb15SPaul Saab * This may result in a single filename, or 1065a5f0fb15SPaul Saab * a blank-separated list of filenames. 1066a5f0fb15SPaul Saab */ 1067a5f0fb15SPaul Saab c = *cp; 1068a5f0fb15SPaul Saab *cp = '\0'; 1069000ba3e8STim J. Robbins if (*word != openquote) 1070000ba3e8STim J. Robbins { 1071a5f0fb15SPaul Saab tk_text = fcomplete(word); 1072000ba3e8STim J. Robbins } else 1073000ba3e8STim J. Robbins { 1074e2449719SXin LI #if MSDOS_COMPILER 1075e2449719SXin LI char *qword = NULL; 1076e2449719SXin LI #else 1077000ba3e8STim J. Robbins char *qword = shell_quote(word+1); 1078e2449719SXin LI #endif 1079000ba3e8STim J. Robbins if (qword == NULL) 1080000ba3e8STim J. Robbins tk_text = fcomplete(word+1); 1081000ba3e8STim J. Robbins else 1082000ba3e8STim J. Robbins { 1083000ba3e8STim J. Robbins tk_text = fcomplete(qword); 1084000ba3e8STim J. Robbins free(qword); 1085000ba3e8STim J. Robbins } 1086000ba3e8STim J. Robbins } 1087a5f0fb15SPaul Saab *cp = c; 1088a5f0fb15SPaul Saab } 1089a5f0fb15SPaul Saab 1090a5f0fb15SPaul Saab /* 1091a5f0fb15SPaul Saab * Return the next word in the current completion list. 1092a5f0fb15SPaul Saab */ 1093*c77c4889SXin LI static constant char * next_compl(int action, constant char *prev) 1094a5f0fb15SPaul Saab { 1095a5f0fb15SPaul Saab switch (action) 1096a5f0fb15SPaul Saab { 1097a5f0fb15SPaul Saab case EC_F_COMPLETE: 1098a5f0fb15SPaul Saab return (forw_textlist(&tk_tlist, prev)); 1099a5f0fb15SPaul Saab case EC_B_COMPLETE: 1100a5f0fb15SPaul Saab return (back_textlist(&tk_tlist, prev)); 1101a5f0fb15SPaul Saab } 1102a5f0fb15SPaul Saab /* Cannot happen */ 1103a5f0fb15SPaul Saab return ("?"); 1104a5f0fb15SPaul Saab } 1105a5f0fb15SPaul Saab 1106a5f0fb15SPaul Saab /* 1107a5f0fb15SPaul Saab * Complete the filename before (or under) the cursor. 1108a5f0fb15SPaul Saab * cmd_complete may be called multiple times. The global in_completion 1109a5f0fb15SPaul Saab * remembers whether this call is the first time (create the list), 1110a5f0fb15SPaul Saab * or a subsequent time (step thru the list). 1111a5f0fb15SPaul Saab */ 1112d713e089SXin LI static int cmd_complete(int action) 1113a5f0fb15SPaul Saab { 1114*c77c4889SXin LI constant char *s; 1115a5f0fb15SPaul Saab 1116a5f0fb15SPaul Saab if (!in_completion || action == EC_EXPAND) 1117a5f0fb15SPaul Saab { 1118a5f0fb15SPaul Saab /* 1119a5f0fb15SPaul Saab * Expand the word under the cursor and 1120a5f0fb15SPaul Saab * use the first word in the expansion 1121a5f0fb15SPaul Saab * (or the entire expansion if we're doing EC_EXPAND). 1122a5f0fb15SPaul Saab */ 1123a5f0fb15SPaul Saab init_compl(); 1124a5f0fb15SPaul Saab if (tk_text == NULL) 1125a5f0fb15SPaul Saab { 1126a5f0fb15SPaul Saab bell(); 1127a5f0fb15SPaul Saab return (CC_OK); 1128a5f0fb15SPaul Saab } 1129a5f0fb15SPaul Saab if (action == EC_EXPAND) 1130a5f0fb15SPaul Saab { 1131a5f0fb15SPaul Saab /* 1132a5f0fb15SPaul Saab * Use the whole list. 1133a5f0fb15SPaul Saab */ 1134a5f0fb15SPaul Saab tk_trial = tk_text; 1135a5f0fb15SPaul Saab } else 1136a5f0fb15SPaul Saab { 1137a5f0fb15SPaul Saab /* 1138a5f0fb15SPaul Saab * Use the first filename in the list. 1139a5f0fb15SPaul Saab */ 1140*c77c4889SXin LI in_completion = TRUE; 1141a5f0fb15SPaul Saab init_textlist(&tk_tlist, tk_text); 1142a5f0fb15SPaul Saab tk_trial = next_compl(action, (char*)NULL); 1143a5f0fb15SPaul Saab } 1144a5f0fb15SPaul Saab } else 1145a5f0fb15SPaul Saab { 1146a5f0fb15SPaul Saab /* 1147a5f0fb15SPaul Saab * We already have a completion list. 1148a5f0fb15SPaul Saab * Use the next/previous filename from the list. 1149a5f0fb15SPaul Saab */ 1150a5f0fb15SPaul Saab tk_trial = next_compl(action, tk_trial); 1151a5f0fb15SPaul Saab } 1152a5f0fb15SPaul Saab 1153a5f0fb15SPaul Saab /* 1154a5f0fb15SPaul Saab * Remove the original word, or the previous trial completion. 1155a5f0fb15SPaul Saab */ 1156a5f0fb15SPaul Saab while (cp > tk_ipoint) 1157a5f0fb15SPaul Saab (void) cmd_erase(); 1158a5f0fb15SPaul Saab 1159a5f0fb15SPaul Saab if (tk_trial == NULL) 1160a5f0fb15SPaul Saab { 1161a5f0fb15SPaul Saab /* 1162a5f0fb15SPaul Saab * There are no more trial completions. 1163a5f0fb15SPaul Saab * Insert the original (uncompleted) filename. 1164a5f0fb15SPaul Saab */ 1165*c77c4889SXin LI in_completion = FALSE; 1166a5f0fb15SPaul Saab if (cmd_istr(tk_original) != CC_OK) 1167a5f0fb15SPaul Saab goto fail; 1168a5f0fb15SPaul Saab } else 1169a5f0fb15SPaul Saab { 1170a5f0fb15SPaul Saab /* 1171a5f0fb15SPaul Saab * Insert trial completion. 1172a5f0fb15SPaul Saab */ 1173a5f0fb15SPaul Saab if (cmd_istr(tk_trial) != CC_OK) 1174a5f0fb15SPaul Saab goto fail; 1175a5f0fb15SPaul Saab /* 1176a5f0fb15SPaul Saab * If it is a directory, append a slash. 1177a5f0fb15SPaul Saab */ 1178a5f0fb15SPaul Saab if (is_dir(tk_trial)) 1179a5f0fb15SPaul Saab { 1180a5f0fb15SPaul Saab if (cp > cmdbuf && cp[-1] == closequote) 1181a5f0fb15SPaul Saab (void) cmd_erase(); 1182a5f0fb15SPaul Saab s = lgetenv("LESSSEPARATOR"); 1183a5f0fb15SPaul Saab if (s == NULL) 1184a5f0fb15SPaul Saab s = PATHNAME_SEP; 1185a5f0fb15SPaul Saab if (cmd_istr(s) != CC_OK) 1186a5f0fb15SPaul Saab goto fail; 1187a5f0fb15SPaul Saab } 1188a5f0fb15SPaul Saab } 1189a5f0fb15SPaul Saab 1190a5f0fb15SPaul Saab return (CC_OK); 1191a5f0fb15SPaul Saab 1192a5f0fb15SPaul Saab fail: 1193*c77c4889SXin LI in_completion = FALSE; 1194a5f0fb15SPaul Saab bell(); 1195a5f0fb15SPaul Saab return (CC_OK); 1196a5f0fb15SPaul Saab } 1197a5f0fb15SPaul Saab 1198a5f0fb15SPaul Saab #endif /* TAB_COMPLETE_FILENAME */ 1199a5f0fb15SPaul Saab 1200a5f0fb15SPaul Saab /* 1201a5f0fb15SPaul Saab * Process a single character of a multi-character command, such as 1202a5f0fb15SPaul Saab * a number, or the pattern of a search command. 1203a5f0fb15SPaul Saab * Returns: 1204a5f0fb15SPaul Saab * CC_OK The char was accepted. 1205a5f0fb15SPaul Saab * CC_QUIT The char requests the command to be aborted. 1206a5f0fb15SPaul Saab * CC_ERROR The char could not be accepted due to an error. 1207a5f0fb15SPaul Saab */ 1208*c77c4889SXin LI public int cmd_char(char c) 1209a5f0fb15SPaul Saab { 1210a5f0fb15SPaul Saab int action; 1211*c77c4889SXin LI size_t len; 12126dcb072bSXin LI 12136dcb072bSXin LI if (!utf_mode) 12146dcb072bSXin LI { 12156dcb072bSXin LI cmd_mbc_buf[0] = c; 12166dcb072bSXin LI len = 1; 12176dcb072bSXin LI } else 12186dcb072bSXin LI { 12196dcb072bSXin LI /* Perform strict validation in all possible cases. */ 12206dcb072bSXin LI if (cmd_mbc_buf_len == 0) 12216dcb072bSXin LI { 12226dcb072bSXin LI retry: 12236dcb072bSXin LI cmd_mbc_buf_index = 1; 12246dcb072bSXin LI *cmd_mbc_buf = c; 12256dcb072bSXin LI if (IS_ASCII_OCTET(c)) 12266dcb072bSXin LI cmd_mbc_buf_len = 1; 1227b2ea2440SXin LI #if MSDOS_COMPILER || OS2 1228*c77c4889SXin LI else if (c == '\340' && IS_ASCII_OCTET(peekcc())) 1229b2ea2440SXin LI { 1230b2ea2440SXin LI /* Assume a special key. */ 1231b2ea2440SXin LI cmd_mbc_buf_len = 1; 1232b2ea2440SXin LI } 1233b2ea2440SXin LI #endif 12346dcb072bSXin LI else if (IS_UTF8_LEAD(c)) 12356dcb072bSXin LI { 12366dcb072bSXin LI cmd_mbc_buf_len = utf_len(c); 12376dcb072bSXin LI return (CC_OK); 12386dcb072bSXin LI } else 12396dcb072bSXin LI { 12406dcb072bSXin LI /* UTF8_INVALID or stray UTF8_TRAIL */ 12416dcb072bSXin LI bell(); 12426dcb072bSXin LI return (CC_ERROR); 12436dcb072bSXin LI } 12446dcb072bSXin LI } else if (IS_UTF8_TRAIL(c)) 12456dcb072bSXin LI { 12466dcb072bSXin LI cmd_mbc_buf[cmd_mbc_buf_index++] = c; 12476dcb072bSXin LI if (cmd_mbc_buf_index < cmd_mbc_buf_len) 12486dcb072bSXin LI return (CC_OK); 1249a15691bfSXin LI if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index)) 12506dcb072bSXin LI { 12516dcb072bSXin LI /* complete, but not well formed (non-shortest form), sequence */ 12526dcb072bSXin LI cmd_mbc_buf_len = 0; 12536dcb072bSXin LI bell(); 12546dcb072bSXin LI return (CC_ERROR); 12556dcb072bSXin LI } 12566dcb072bSXin LI } else 12576dcb072bSXin LI { 12586dcb072bSXin LI /* Flush incomplete (truncated) sequence. */ 12596dcb072bSXin LI cmd_mbc_buf_len = 0; 12606dcb072bSXin LI bell(); 12616dcb072bSXin LI /* Handle new char. */ 12626dcb072bSXin LI goto retry; 12636dcb072bSXin LI } 12646dcb072bSXin LI 1265*c77c4889SXin LI len = (size_t) cmd_mbc_buf_len; /*{{type-issue}}*/ 12666dcb072bSXin LI cmd_mbc_buf_len = 0; 12676dcb072bSXin LI } 1268a5f0fb15SPaul Saab 1269a5f0fb15SPaul Saab if (literal) 1270a5f0fb15SPaul Saab { 1271a5f0fb15SPaul Saab /* 1272a5f0fb15SPaul Saab * Insert the char, even if it is a line-editing char. 1273a5f0fb15SPaul Saab */ 1274*c77c4889SXin LI literal = FALSE; 12756dcb072bSXin LI return (cmd_ichar(cmd_mbc_buf, len)); 1276a5f0fb15SPaul Saab } 1277a5f0fb15SPaul Saab 1278a5f0fb15SPaul Saab /* 12796dcb072bSXin LI * See if it is a line-editing character. 1280a5f0fb15SPaul Saab */ 12816dcb072bSXin LI if (in_mca() && len == 1) 1282a5f0fb15SPaul Saab { 1283a5f0fb15SPaul Saab action = cmd_edit(c); 1284a5f0fb15SPaul Saab switch (action) 1285a5f0fb15SPaul Saab { 1286a5f0fb15SPaul Saab case CC_OK: 1287a5f0fb15SPaul Saab case CC_QUIT: 1288a5f0fb15SPaul Saab return (action); 1289a5f0fb15SPaul Saab case CC_PASS: 1290a5f0fb15SPaul Saab break; 1291a5f0fb15SPaul Saab } 1292a5f0fb15SPaul Saab } 1293a5f0fb15SPaul Saab 1294a5f0fb15SPaul Saab /* 1295a5f0fb15SPaul Saab * Insert the char into the command buffer. 1296a5f0fb15SPaul Saab */ 12976dcb072bSXin LI return (cmd_ichar(cmd_mbc_buf, len)); 1298a5f0fb15SPaul Saab } 1299a5f0fb15SPaul Saab 1300a5f0fb15SPaul Saab /* 1301a5f0fb15SPaul Saab * Return the number currently in the command buffer. 1302a5f0fb15SPaul Saab */ 1303*c77c4889SXin LI public LINENUM cmd_int(mutable long *frac) 1304a5f0fb15SPaul Saab { 1305*c77c4889SXin LI constant char *p; 1306000ba3e8STim J. Robbins LINENUM n = 0; 1307*c77c4889SXin LI lbool err; 1308000ba3e8STim J. Robbins 13097f074f9cSXin LI for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 131095270f73SXin LI { 1311d713e089SXin LI if (ckd_mul(&n, n, 10) || ckd_add(&n, n, *p - '0')) 131295270f73SXin LI { 131395270f73SXin LI error("Integer is too big", NULL_PARG); 131495270f73SXin LI return (0); 131595270f73SXin LI } 131695270f73SXin LI } 13177f074f9cSXin LI *frac = 0; 13187f074f9cSXin LI if (*p++ == '.') 13197f074f9cSXin LI { 13207f074f9cSXin LI *frac = getfraction(&p, NULL, &err); 13217f074f9cSXin LI /* {{ do something if err is set? }} */ 13227f074f9cSXin LI } 1323000ba3e8STim J. Robbins return (n); 1324a5f0fb15SPaul Saab } 1325a5f0fb15SPaul Saab 1326a5f0fb15SPaul Saab /* 1327a5f0fb15SPaul Saab * Return a pointer to the command buffer. 1328a5f0fb15SPaul Saab */ 1329*c77c4889SXin LI public constant char * get_cmdbuf(void) 1330a5f0fb15SPaul Saab { 133195270f73SXin LI if (cmd_mbc_buf_index < cmd_mbc_buf_len) 133295270f73SXin LI /* Don't return buffer containing an incomplete multibyte char. */ 133395270f73SXin LI return (NULL); 1334a5f0fb15SPaul Saab return (cmdbuf); 1335a5f0fb15SPaul Saab } 13366dcb072bSXin LI 13377374caaaSXin LI #if CMD_HISTORY 13387f074f9cSXin LI /* 13397f074f9cSXin LI * Return the last (most recent) string in the current command history. 13407f074f9cSXin LI */ 1341*c77c4889SXin LI public constant char * cmd_lastpattern(void) 13427f074f9cSXin LI { 13437f074f9cSXin LI if (curr_mlist == NULL) 13447f074f9cSXin LI return (NULL); 13457f074f9cSXin LI return (curr_mlist->curr_mp->prev->string); 13467f074f9cSXin LI } 13477374caaaSXin LI #endif 13487f074f9cSXin LI 13496dcb072bSXin LI #if CMD_HISTORY 13506dcb072bSXin LI /* 1351a15691bfSXin LI */ 1352d713e089SXin LI static int mlist_size(struct mlist *ml) 1353a15691bfSXin LI { 1354a15691bfSXin LI int size = 0; 1355a15691bfSXin LI for (ml = ml->next; ml->string != NULL; ml = ml->next) 1356a15691bfSXin LI ++size; 1357a15691bfSXin LI return size; 1358a15691bfSXin LI } 1359a15691bfSXin LI 1360a15691bfSXin LI /* 13616dcb072bSXin LI * Get the name of the history file. 13626dcb072bSXin LI */ 1363*c77c4889SXin LI static char * histfile_find(lbool must_exist) 136495270f73SXin LI { 1365*c77c4889SXin LI constant char *home = lgetenv("HOME"); 136695270f73SXin LI char *name = NULL; 136795270f73SXin LI 136895270f73SXin LI /* Try in $XDG_STATE_HOME, then in $HOME/.local/state, then in $XDG_DATA_HOME, then in $HOME. */ 136995270f73SXin LI #if OS2 137095270f73SXin LI if (isnullenv(home)) 137195270f73SXin LI home = lgetenv("INIT"); 137295270f73SXin LI #endif 137395270f73SXin LI name = dirfile(lgetenv("XDG_STATE_HOME"), &LESSHISTFILE[1], must_exist); 137495270f73SXin LI if (name == NULL) 137595270f73SXin LI { 137695270f73SXin LI char *dir = dirfile(home, ".local/state", 1); 137795270f73SXin LI if (dir != NULL) 137895270f73SXin LI { 137995270f73SXin LI name = dirfile(dir, &LESSHISTFILE[1], must_exist); 138095270f73SXin LI free(dir); 138195270f73SXin LI } 138295270f73SXin LI } 138395270f73SXin LI if (name == NULL) 138495270f73SXin LI name = dirfile(lgetenv("XDG_DATA_HOME"), &LESSHISTFILE[1], must_exist); 138595270f73SXin LI if (name == NULL) 138695270f73SXin LI name = dirfile(home, LESSHISTFILE, must_exist); 138795270f73SXin LI return (name); 138895270f73SXin LI } 138995270f73SXin LI 1390*c77c4889SXin LI static char * histfile_name(lbool must_exist) 13916dcb072bSXin LI { 1392*c77c4889SXin LI constant char *name; 1393*c77c4889SXin LI char *wname; 13946dcb072bSXin LI 13956dcb072bSXin LI /* See if filename is explicitly specified by $LESSHISTFILE. */ 13966dcb072bSXin LI name = lgetenv("LESSHISTFILE"); 1397b7780dbeSXin LI if (!isnullenv(name)) 13986dcb072bSXin LI { 13997f074f9cSXin LI if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 14006dcb072bSXin LI /* $LESSHISTFILE == "-" means don't use a history file. */ 14016dcb072bSXin LI return (NULL); 14026dcb072bSXin LI return (save(name)); 14036dcb072bSXin LI } 14046dcb072bSXin LI 1405a15691bfSXin LI /* See if history file is disabled in the build. */ 1406a15691bfSXin LI if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0) 1407a15691bfSXin LI return (NULL); 1408a15691bfSXin LI 1409*c77c4889SXin LI wname = NULL; 141030a1828cSXin LI if (!must_exist) 141130a1828cSXin LI { 141230a1828cSXin LI /* If we're writing the file and the file already exists, use it. */ 1413*c77c4889SXin LI wname = histfile_find(TRUE); 14146dcb072bSXin LI } 1415*c77c4889SXin LI if (wname == NULL) 1416*c77c4889SXin LI wname = histfile_find(must_exist); 1417*c77c4889SXin LI return (wname); 14186dcb072bSXin LI } 14196dcb072bSXin LI 14206dcb072bSXin LI /* 1421a15691bfSXin LI * Read a .lesshst file and call a callback for each line in the file. 14226dcb072bSXin LI */ 1423*c77c4889SXin LI static void read_cmdhist2(void (*action)(void*,struct mlist*,constant char*), void *uparam, int skip_search, int skip_shell) 14246dcb072bSXin LI { 14256dcb072bSXin LI struct mlist *ml = NULL; 14266dcb072bSXin LI char line[CMDBUF_SIZE]; 14276dcb072bSXin LI char *filename; 14286dcb072bSXin LI FILE *f; 1429a15691bfSXin LI int *skip = NULL; 14306dcb072bSXin LI 1431*c77c4889SXin LI filename = histfile_name(TRUE); 14326dcb072bSXin LI if (filename == NULL) 14336dcb072bSXin LI return; 14346dcb072bSXin LI f = fopen(filename, "r"); 14356dcb072bSXin LI free(filename); 14366dcb072bSXin LI if (f == NULL) 14376dcb072bSXin LI return; 14386dcb072bSXin LI if (fgets(line, sizeof(line), f) == NULL || 14396dcb072bSXin LI strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 14406dcb072bSXin LI { 14416dcb072bSXin LI fclose(f); 14426dcb072bSXin LI return; 14436dcb072bSXin LI } 14446dcb072bSXin LI while (fgets(line, sizeof(line), f) != NULL) 14456dcb072bSXin LI { 1446*c77c4889SXin LI char *p; 14476dcb072bSXin LI for (p = line; *p != '\0'; p++) 14486dcb072bSXin LI { 14496dcb072bSXin LI if (*p == '\n' || *p == '\r') 14506dcb072bSXin LI { 14516dcb072bSXin LI *p = '\0'; 14526dcb072bSXin LI break; 14536dcb072bSXin LI } 14546dcb072bSXin LI } 14556dcb072bSXin LI if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1456a15691bfSXin LI { 14576dcb072bSXin LI ml = &mlist_search; 1458a15691bfSXin LI skip = &skip_search; 1459a15691bfSXin LI } else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1460efd72c2eSXin LI { 1461efd72c2eSXin LI #if SHELL_ESCAPE || PIPEC 14626dcb072bSXin LI ml = &mlist_shell; 1463a15691bfSXin LI skip = &skip_shell; 1464efd72c2eSXin LI #else 1465efd72c2eSXin LI ml = NULL; 1466a15691bfSXin LI skip = NULL; 14676dcb072bSXin LI #endif 1468b7780dbeSXin LI } else if (strcmp(line, HISTFILE_MARK_SECTION) == 0) 1469b7780dbeSXin LI { 1470b7780dbeSXin LI ml = NULL; 1471efd72c2eSXin LI } else if (*line == '"') 14726dcb072bSXin LI { 14736dcb072bSXin LI if (ml != NULL) 1474a15691bfSXin LI { 1475a15691bfSXin LI if (skip != NULL && *skip > 0) 1476a15691bfSXin LI --(*skip); 1477a15691bfSXin LI else 1478a15691bfSXin LI (*action)(uparam, ml, line+1); 1479a15691bfSXin LI } 1480b7780dbeSXin LI } else if (*line == 'm') 1481b7780dbeSXin LI { 1482b7780dbeSXin LI (*action)(uparam, NULL, line); 14836dcb072bSXin LI } 14846dcb072bSXin LI } 14856dcb072bSXin LI fclose(f); 1486a15691bfSXin LI } 1487a15691bfSXin LI 1488*c77c4889SXin LI static void read_cmdhist(void (*action)(void*,struct mlist*,constant char*), void *uparam, lbool skip_search, lbool skip_shell) 1489a15691bfSXin LI { 1490*c77c4889SXin LI if (!secure_allow(SF_HISTORY)) 149195270f73SXin LI return; 1492a15691bfSXin LI read_cmdhist2(action, uparam, skip_search, skip_shell); 1493a15691bfSXin LI (*action)(uparam, NULL, NULL); /* signal end of file */ 1494a15691bfSXin LI } 1495a15691bfSXin LI 1496*c77c4889SXin LI static void addhist_init(void *uparam, struct mlist *ml, constant char *string) 1497a15691bfSXin LI { 1498*c77c4889SXin LI (void) uparam; 1499b7780dbeSXin LI if (ml != NULL) 1500a15691bfSXin LI cmd_addhist(ml, string, 0); 1501b7780dbeSXin LI else if (string != NULL) 1502*c77c4889SXin LI restore_mark(string); 1503a15691bfSXin LI } 1504a15691bfSXin LI #endif /* CMD_HISTORY */ 1505a15691bfSXin LI 1506a15691bfSXin LI /* 1507a15691bfSXin LI * Initialize history from a .lesshist file. 1508a15691bfSXin LI */ 1509d713e089SXin LI public void init_cmdhist(void) 1510a15691bfSXin LI { 1511a15691bfSXin LI #if CMD_HISTORY 1512a15691bfSXin LI read_cmdhist(&addhist_init, NULL, 0, 0); 15136dcb072bSXin LI #endif /* CMD_HISTORY */ 15146dcb072bSXin LI } 15156dcb072bSXin LI 15166dcb072bSXin LI /* 1517a15691bfSXin LI * Write the header for a section of the history file. 15186dcb072bSXin LI */ 15196dcb072bSXin LI #if CMD_HISTORY 1520d713e089SXin LI static void write_mlist_header(struct mlist *ml, FILE *f) 15216dcb072bSXin LI { 1522a15691bfSXin LI if (ml == &mlist_search) 1523a15691bfSXin LI fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1524a15691bfSXin LI #if SHELL_ESCAPE || PIPEC 1525a15691bfSXin LI else if (ml == &mlist_shell) 1526a15691bfSXin LI fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1527a15691bfSXin LI #endif 15286dcb072bSXin LI } 1529a15691bfSXin LI 1530a15691bfSXin LI /* 1531a15691bfSXin LI * Write all modified entries in an mlist to the history file. 1532a15691bfSXin LI */ 1533d713e089SXin LI static void write_mlist(struct mlist *ml, FILE *f) 1534a15691bfSXin LI { 15356dcb072bSXin LI for (ml = ml->next; ml->string != NULL; ml = ml->next) 1536a15691bfSXin LI { 1537a15691bfSXin LI if (!ml->modified) 1538a15691bfSXin LI continue; 15396dcb072bSXin LI fprintf(f, "\"%s\n", ml->string); 1540*c77c4889SXin LI ml->modified = FALSE; 1541a15691bfSXin LI } 1542*c77c4889SXin LI ml->modified = FALSE; /* entire mlist is now unmodified */ 1543a15691bfSXin LI } 1544a15691bfSXin LI 1545a15691bfSXin LI /* 1546a15691bfSXin LI * Make a temp name in the same directory as filename. 1547a15691bfSXin LI */ 1548*c77c4889SXin LI static char * make_tempname(constant char *filename) 1549a15691bfSXin LI { 1550a15691bfSXin LI char lastch; 1551a15691bfSXin LI char *tempname = ecalloc(1, strlen(filename)+1); 1552a15691bfSXin LI strcpy(tempname, filename); 1553a15691bfSXin LI lastch = tempname[strlen(tempname)-1]; 1554a15691bfSXin LI tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q'; 1555a15691bfSXin LI return tempname; 1556a15691bfSXin LI } 1557a15691bfSXin LI 1558a15691bfSXin LI struct save_ctx 1559a15691bfSXin LI { 1560a15691bfSXin LI struct mlist *mlist; 1561a15691bfSXin LI FILE *fout; 1562a15691bfSXin LI }; 1563a15691bfSXin LI 1564a15691bfSXin LI /* 1565a15691bfSXin LI * Copy entries from the saved history file to a new file. 1566a15691bfSXin LI * At the end of each mlist, append any new entries 1567a15691bfSXin LI * created during this session. 1568a15691bfSXin LI */ 1569*c77c4889SXin LI static void copy_hist(void *uparam, struct mlist *ml, constant char *string) 1570a15691bfSXin LI { 1571a15691bfSXin LI struct save_ctx *ctx = (struct save_ctx *) uparam; 1572a15691bfSXin LI 1573b7780dbeSXin LI if (ml != NULL && ml != ctx->mlist) { 1574a15691bfSXin LI /* We're changing mlists. */ 1575a15691bfSXin LI if (ctx->mlist) 1576a15691bfSXin LI /* Append any new entries to the end of the current mlist. */ 1577a15691bfSXin LI write_mlist(ctx->mlist, ctx->fout); 1578a15691bfSXin LI /* Write the header for the new mlist. */ 1579a15691bfSXin LI ctx->mlist = ml; 1580a15691bfSXin LI write_mlist_header(ctx->mlist, ctx->fout); 1581a15691bfSXin LI } 1582b7780dbeSXin LI 1583b7780dbeSXin LI if (string == NULL) /* End of file */ 1584a15691bfSXin LI { 1585a15691bfSXin LI /* Write any sections that were not in the original file. */ 1586a15691bfSXin LI if (mlist_search.modified) 1587a15691bfSXin LI { 1588a15691bfSXin LI write_mlist_header(&mlist_search, ctx->fout); 1589a15691bfSXin LI write_mlist(&mlist_search, ctx->fout); 1590a15691bfSXin LI } 1591a15691bfSXin LI #if SHELL_ESCAPE || PIPEC 1592a15691bfSXin LI if (mlist_shell.modified) 1593a15691bfSXin LI { 1594a15691bfSXin LI write_mlist_header(&mlist_shell, ctx->fout); 1595a15691bfSXin LI write_mlist(&mlist_shell, ctx->fout); 1596a15691bfSXin LI } 1597a15691bfSXin LI #endif 1598b7780dbeSXin LI } else if (ml != NULL) 1599b7780dbeSXin LI { 1600b7780dbeSXin LI /* Copy mlist entry. */ 1601b7780dbeSXin LI fprintf(ctx->fout, "\"%s\n", string); 1602a15691bfSXin LI } 1603b7780dbeSXin LI /* Skip marks */ 16046dcb072bSXin LI } 16056dcb072bSXin LI #endif /* CMD_HISTORY */ 16066dcb072bSXin LI 16076dcb072bSXin LI /* 1608a15691bfSXin LI * Make a file readable only by its owner. 16096dcb072bSXin LI */ 1610d713e089SXin LI static void make_file_private(FILE *f) 16117374caaaSXin LI { 1612a15691bfSXin LI #if HAVE_FCHMOD 1613*c77c4889SXin LI lbool do_chmod = TRUE; 16147374caaaSXin LI #if HAVE_STAT 16157374caaaSXin LI struct stat statbuf; 16167374caaaSXin LI int r = fstat(fileno(f), &statbuf); 16177374caaaSXin LI if (r < 0 || !S_ISREG(statbuf.st_mode)) 16187374caaaSXin LI /* Don't chmod if not a regular file. */ 1619*c77c4889SXin LI do_chmod = FALSE; 16207374caaaSXin LI #endif 16217374caaaSXin LI if (do_chmod) 16226dcb072bSXin LI fchmod(fileno(f), 0600); 1623a15691bfSXin LI #endif 16247374caaaSXin LI } 16256dcb072bSXin LI 1626a15691bfSXin LI /* 1627a15691bfSXin LI * Does the history file need to be updated? 1628a15691bfSXin LI */ 162995270f73SXin LI #if CMD_HISTORY 1630*c77c4889SXin LI static lbool histfile_modified(void) 1631a15691bfSXin LI { 1632a15691bfSXin LI if (mlist_search.modified) 1633*c77c4889SXin LI return TRUE; 16346dcb072bSXin LI #if SHELL_ESCAPE || PIPEC 1635a15691bfSXin LI if (mlist_shell.modified) 1636*c77c4889SXin LI return TRUE; 16376dcb072bSXin LI #endif 1638b7780dbeSXin LI if (marks_modified) 1639*c77c4889SXin LI return TRUE; 1640*c77c4889SXin LI return FALSE; 1641a15691bfSXin LI } 164295270f73SXin LI #endif 16436dcb072bSXin LI 1644a15691bfSXin LI /* 1645a15691bfSXin LI * Update the .lesshst file. 1646a15691bfSXin LI */ 1647d713e089SXin LI public void save_cmdhist(void) 1648a15691bfSXin LI { 1649a15691bfSXin LI #if CMD_HISTORY 1650a15691bfSXin LI char *histname; 1651a15691bfSXin LI char *tempname; 1652a15691bfSXin LI int skip_search; 1653a15691bfSXin LI int skip_shell; 1654a15691bfSXin LI struct save_ctx ctx; 1655*c77c4889SXin LI constant char *s; 1656a15691bfSXin LI FILE *fout = NULL; 1657a15691bfSXin LI int histsize = 0; 1658a15691bfSXin LI 1659*c77c4889SXin LI if (!secure_allow(SF_HISTORY) || !histfile_modified()) 1660a15691bfSXin LI return; 166130a1828cSXin LI histname = histfile_name(0); 1662a15691bfSXin LI if (histname == NULL) 1663a15691bfSXin LI return; 1664a15691bfSXin LI tempname = make_tempname(histname); 1665a15691bfSXin LI fout = fopen(tempname, "w"); 1666a15691bfSXin LI if (fout != NULL) 1667a15691bfSXin LI { 1668a15691bfSXin LI make_file_private(fout); 1669a15691bfSXin LI s = lgetenv("LESSHISTSIZE"); 1670a15691bfSXin LI if (s != NULL) 1671a15691bfSXin LI histsize = atoi(s); 1672a15691bfSXin LI if (histsize <= 0) 1673a15691bfSXin LI histsize = 100; 1674a15691bfSXin LI skip_search = mlist_size(&mlist_search) - histsize; 1675a15691bfSXin LI #if SHELL_ESCAPE || PIPEC 1676a15691bfSXin LI skip_shell = mlist_size(&mlist_shell) - histsize; 1677a15691bfSXin LI #endif 1678a15691bfSXin LI fprintf(fout, "%s\n", HISTFILE_FIRST_LINE); 1679a15691bfSXin LI ctx.fout = fout; 1680a15691bfSXin LI ctx.mlist = NULL; 1681b7780dbeSXin LI read_cmdhist(©_hist, &ctx, skip_search, skip_shell); 1682b7780dbeSXin LI save_marks(fout, HISTFILE_MARK_SECTION); 1683a15691bfSXin LI fclose(fout); 1684a15691bfSXin LI #if MSDOS_COMPILER==WIN32C 1685a15691bfSXin LI /* 1686a15691bfSXin LI * Windows rename doesn't remove an existing file, 1687a15691bfSXin LI * making it useless for atomic operations. Sigh. 1688a15691bfSXin LI */ 1689a15691bfSXin LI remove(histname); 1690a15691bfSXin LI #endif 1691a15691bfSXin LI rename(tempname, histname); 1692a15691bfSXin LI } 1693a15691bfSXin LI free(tempname); 1694a15691bfSXin LI free(histname); 16956dcb072bSXin LI #endif /* CMD_HISTORY */ 16966dcb072bSXin LI } 1697