1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Functions which manipulate the command buffer. 1360786Sps * Used only by command() and related functions. 1460786Sps */ 1560786Sps 1660786Sps#include "less.h" 1760786Sps#include "cmd.h" 18161475Sdelphij#include "charset.h" 19161475Sdelphij#if HAVE_STAT 20161475Sdelphij#include <sys/stat.h> 21161475Sdelphij#endif 2260786Sps 2360786Spsextern int sc_width; 24161475Sdelphijextern int utf_mode; 2560786Sps 2660786Spsstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 2760786Spsstatic int cmd_col; /* Current column of the cursor */ 2860786Spsstatic int prompt_col; /* Column of cursor just after prompt */ 2960786Spsstatic char *cp; /* Pointer into cmdbuf */ 3060786Spsstatic int cmd_offset; /* Index into cmdbuf of first displayed char */ 3160786Spsstatic int literal; /* Next input char should not be interpreted */ 32237613Sdelphijstatic int updown_match = -1; /* Prefix length in up/down movement */ 3360786Sps 3460786Sps#if TAB_COMPLETE_FILENAME 3560786Spsstatic int cmd_complete(); 3660786Sps/* 3760786Sps * These variables are statics used by cmd_complete. 3860786Sps */ 3960786Spsstatic int in_completion = 0; 4060786Spsstatic char *tk_text; 4160786Spsstatic char *tk_original; 4260786Spsstatic char *tk_ipoint; 43330571Sdelphijstatic char *tk_trial = NULL; 4460786Spsstatic struct textlist tk_tlist; 4560786Sps#endif 4660786Sps 4760786Spsstatic int cmd_left(); 4860786Spsstatic int cmd_right(); 4960786Sps 5060786Sps#if SPACES_IN_FILENAMES 5160786Spspublic char openquote = '"'; 5260786Spspublic char closequote = '"'; 5360786Sps#endif 5460786Sps 5560786Sps#if CMD_HISTORY 56161475Sdelphij 57161475Sdelphij/* History file */ 58161475Sdelphij#define HISTFILE_FIRST_LINE ".less-history-file:" 59161475Sdelphij#define HISTFILE_SEARCH_SECTION ".search" 60161475Sdelphij#define HISTFILE_SHELL_SECTION ".shell" 61161475Sdelphij 6260786Sps/* 6360786Sps * A mlist structure represents a command history. 6460786Sps */ 6560786Spsstruct mlist 6660786Sps{ 6760786Sps struct mlist *next; 6860786Sps struct mlist *prev; 6960786Sps struct mlist *curr_mp; 7060786Sps char *string; 71170256Sdelphij int modified; 7260786Sps}; 7360786Sps 7460786Sps/* 7560786Sps * These are the various command histories that exist. 7660786Sps */ 7760786Spsstruct mlist mlist_search = 78170256Sdelphij { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; 79330571Sdelphijpublic void *ml_search = (void *) &mlist_search; 8060786Sps 8160786Spsstruct mlist mlist_examine = 82170256Sdelphij { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; 83330571Sdelphijpublic void *ml_examine = (void *) &mlist_examine; 8460786Sps 8560786Sps#if SHELL_ESCAPE || PIPEC 8660786Spsstruct mlist mlist_shell = 87170256Sdelphij { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; 88330571Sdelphijpublic void *ml_shell = (void *) &mlist_shell; 8960786Sps#endif 9060786Sps 9160786Sps#else /* CMD_HISTORY */ 9260786Sps 9360786Sps/* If CMD_HISTORY is off, these are just flags. */ 94330571Sdelphijpublic void *ml_search = (void *)1; 95330571Sdelphijpublic void *ml_examine = (void *)2; 9660786Sps#if SHELL_ESCAPE || PIPEC 97330571Sdelphijpublic void *ml_shell = (void *)3; 9860786Sps#endif 9960786Sps 10060786Sps#endif /* CMD_HISTORY */ 10160786Sps 10260786Sps/* 10360786Sps * History for the current command. 10460786Sps */ 10560786Spsstatic struct mlist *curr_mlist = NULL; 10660786Spsstatic int curr_cmdflags; 10760786Sps 108161475Sdelphijstatic char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; 109161475Sdelphijstatic int cmd_mbc_buf_len; 110161475Sdelphijstatic int cmd_mbc_buf_index; 11160786Sps 112161475Sdelphij 11360786Sps/* 11460786Sps * Reset command buffer (to empty). 11560786Sps */ 11660786Sps public void 11760786Spscmd_reset() 11860786Sps{ 11960786Sps cp = cmdbuf; 12060786Sps *cp = '\0'; 12160786Sps cmd_col = 0; 12260786Sps cmd_offset = 0; 12360786Sps literal = 0; 124161475Sdelphij cmd_mbc_buf_len = 0; 125237613Sdelphij updown_match = -1; 12660786Sps} 12760786Sps 12860786Sps/* 129170256Sdelphij * Clear command line. 13060786Sps */ 13160786Sps public void 13260786Spsclear_cmd() 13360786Sps{ 13460786Sps cmd_col = prompt_col = 0; 135161475Sdelphij cmd_mbc_buf_len = 0; 136237613Sdelphij updown_match = -1; 13760786Sps} 13860786Sps 13960786Sps/* 14060786Sps * Display a string, usually as a prompt for input into the command buffer. 14160786Sps */ 14260786Sps public void 14360786Spscmd_putstr(s) 144330571Sdelphij constant char *s; 14560786Sps{ 146161475Sdelphij LWCHAR prev_ch = 0; 147161475Sdelphij LWCHAR ch; 148330571Sdelphij constant char *endline = s + strlen(s); 149161475Sdelphij while (*s != '\0') 150161475Sdelphij { 151330571Sdelphij char *ns = (char *) s; 152330571Sdelphij int width; 153161475Sdelphij ch = step_char(&ns, +1, endline); 154161475Sdelphij while (s < ns) 155161475Sdelphij putchr(*s++); 156161475Sdelphij if (!utf_mode) 157330571Sdelphij width = 1; 158330571Sdelphij else if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 159330571Sdelphij width = 0; 160330571Sdelphij else 161330571Sdelphij width = is_wide_char(ch) ? 2 : 1; 162330571Sdelphij cmd_col += width; 163330571Sdelphij prompt_col += width; 164161475Sdelphij prev_ch = ch; 165161475Sdelphij } 16660786Sps} 16760786Sps 16860786Sps/* 16960786Sps * How many characters are in the command buffer? 17060786Sps */ 17160786Sps public int 17260786Spslen_cmdbuf() 17360786Sps{ 174161475Sdelphij char *s = cmdbuf; 175161475Sdelphij char *endline = s + strlen(s); 176161475Sdelphij int len = 0; 177161475Sdelphij 178161475Sdelphij while (*s != '\0') 179161475Sdelphij { 180161475Sdelphij step_char(&s, +1, endline); 181161475Sdelphij len++; 182161475Sdelphij } 183161475Sdelphij return (len); 18460786Sps} 18560786Sps 18660786Sps/* 187161475Sdelphij * Common part of cmd_step_right() and cmd_step_left(). 188330571Sdelphij * {{ Returning pwidth and bswidth separately is a historical artifact 189330571Sdelphij * since they're always the same. Maybe clean this up someday. }} 190161475Sdelphij */ 191161475Sdelphij static char * 192161475Sdelphijcmd_step_common(p, ch, len, pwidth, bswidth) 193161475Sdelphij char *p; 194161475Sdelphij LWCHAR ch; 195161475Sdelphij int len; 196161475Sdelphij int *pwidth; 197161475Sdelphij int *bswidth; 198161475Sdelphij{ 199161475Sdelphij char *pr; 200330571Sdelphij int width; 201161475Sdelphij 202161475Sdelphij if (len == 1) 203161475Sdelphij { 204161475Sdelphij pr = prchar((int) ch); 205330571Sdelphij width = (int) strlen(pr); 206161475Sdelphij } else 207161475Sdelphij { 208161475Sdelphij pr = prutfchar(ch); 209330571Sdelphij if (is_composing_char(ch)) 210330571Sdelphij width = 0; 211330571Sdelphij else if (is_ubin_char(ch)) 212330571Sdelphij width = (int) strlen(pr); 213330571Sdelphij else 214161475Sdelphij { 215330571Sdelphij LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 216330571Sdelphij if (is_combining_char(prev_ch, ch)) 217330571Sdelphij width = 0; 218330571Sdelphij else 219330571Sdelphij width = is_wide_char(ch) ? 2 : 1; 220161475Sdelphij } 221161475Sdelphij } 222330571Sdelphij if (pwidth != NULL) 223330571Sdelphij *pwidth = width; 224330571Sdelphij if (bswidth != NULL) 225330571Sdelphij *bswidth = width; 226161475Sdelphij return (pr); 227161475Sdelphij} 228161475Sdelphij 229161475Sdelphij/* 230161475Sdelphij * Step a pointer one character right in the command buffer. 231161475Sdelphij */ 232161475Sdelphij static char * 233161475Sdelphijcmd_step_right(pp, pwidth, bswidth) 234161475Sdelphij char **pp; 235161475Sdelphij int *pwidth; 236161475Sdelphij int *bswidth; 237161475Sdelphij{ 238161475Sdelphij char *p = *pp; 239161475Sdelphij LWCHAR ch = step_char(pp, +1, p + strlen(p)); 240161475Sdelphij 241161475Sdelphij return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 242161475Sdelphij} 243161475Sdelphij 244161475Sdelphij/* 245161475Sdelphij * Step a pointer one character left in the command buffer. 246161475Sdelphij */ 247161475Sdelphij static char * 248161475Sdelphijcmd_step_left(pp, pwidth, bswidth) 249161475Sdelphij char **pp; 250161475Sdelphij int *pwidth; 251161475Sdelphij int *bswidth; 252161475Sdelphij{ 253161475Sdelphij char *p = *pp; 254161475Sdelphij LWCHAR ch = step_char(pp, -1, cmdbuf); 255161475Sdelphij 256161475Sdelphij return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 257161475Sdelphij} 258161475Sdelphij 259161475Sdelphij/* 26060786Sps * Repaint the line from cp onwards. 26160786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 26260786Sps */ 26360786Sps static void 26460786Spscmd_repaint(old_cp) 265330571Sdelphij constant char *old_cp; 26660786Sps{ 26760786Sps /* 26860786Sps * Repaint the line from the current position. 26960786Sps */ 27060786Sps clear_eol(); 271161475Sdelphij while (*cp != '\0') 27260786Sps { 273161475Sdelphij char *np = cp; 274161475Sdelphij int width; 275161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 276161475Sdelphij if (cmd_col + width >= sc_width) 27760786Sps break; 278161475Sdelphij cp = np; 279161475Sdelphij putstr(pr); 280161475Sdelphij cmd_col += width; 28160786Sps } 282161475Sdelphij while (*cp != '\0') 283161475Sdelphij { 284161475Sdelphij char *np = cp; 285161475Sdelphij int width; 286161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 287161475Sdelphij if (width > 0) 288161475Sdelphij break; 289161475Sdelphij cp = np; 290161475Sdelphij putstr(pr); 291161475Sdelphij } 29260786Sps 29360786Sps /* 29460786Sps * Back up the cursor to the correct position. 29560786Sps */ 29660786Sps while (cp > old_cp) 29760786Sps cmd_left(); 29860786Sps} 29960786Sps 30060786Sps/* 30160786Sps * Put the cursor at "home" (just after the prompt), 30260786Sps * and set cp to the corresponding char in cmdbuf. 30360786Sps */ 30460786Sps static void 30560786Spscmd_home() 30660786Sps{ 30760786Sps while (cmd_col > prompt_col) 30860786Sps { 309161475Sdelphij int width, bswidth; 310161475Sdelphij 311161475Sdelphij cmd_step_left(&cp, &width, &bswidth); 312161475Sdelphij while (bswidth-- > 0) 313161475Sdelphij putbs(); 314161475Sdelphij cmd_col -= width; 31560786Sps } 31660786Sps 31760786Sps cp = &cmdbuf[cmd_offset]; 31860786Sps} 31960786Sps 32060786Sps/* 32160786Sps * Shift the cmdbuf display left a half-screen. 32260786Sps */ 32360786Sps static void 32460786Spscmd_lshift() 32560786Sps{ 32660786Sps char *s; 32760786Sps char *save_cp; 32860786Sps int cols; 32960786Sps 33060786Sps /* 33160786Sps * Start at the first displayed char, count how far to the 33260786Sps * right we'd have to move to reach the center of the screen. 33360786Sps */ 33460786Sps s = cmdbuf + cmd_offset; 33560786Sps cols = 0; 33660786Sps while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 337161475Sdelphij { 338161475Sdelphij int width; 339161475Sdelphij cmd_step_right(&s, &width, NULL); 340161475Sdelphij cols += width; 341161475Sdelphij } 342161475Sdelphij while (*s != '\0') 343161475Sdelphij { 344161475Sdelphij int width; 345161475Sdelphij char *ns = s; 346161475Sdelphij cmd_step_right(&ns, &width, NULL); 347161475Sdelphij if (width > 0) 348161475Sdelphij break; 349161475Sdelphij s = ns; 350161475Sdelphij } 35160786Sps 352294286Sdelphij cmd_offset = (int) (s - cmdbuf); 35360786Sps save_cp = cp; 35460786Sps cmd_home(); 35560786Sps cmd_repaint(save_cp); 35660786Sps} 35760786Sps 35860786Sps/* 35960786Sps * Shift the cmdbuf display right a half-screen. 36060786Sps */ 36160786Sps static void 36260786Spscmd_rshift() 36360786Sps{ 36460786Sps char *s; 36560786Sps char *save_cp; 36660786Sps int cols; 36760786Sps 36860786Sps /* 36960786Sps * Start at the first displayed char, count how far to the 37060786Sps * left we'd have to move to traverse a half-screen width 37160786Sps * of displayed characters. 37260786Sps */ 37360786Sps s = cmdbuf + cmd_offset; 37460786Sps cols = 0; 37560786Sps while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 37660786Sps { 377161475Sdelphij int width; 378161475Sdelphij cmd_step_left(&s, &width, NULL); 379161475Sdelphij cols += width; 38060786Sps } 38160786Sps 382294286Sdelphij cmd_offset = (int) (s - cmdbuf); 38360786Sps save_cp = cp; 38460786Sps cmd_home(); 38560786Sps cmd_repaint(save_cp); 38660786Sps} 38760786Sps 38860786Sps/* 38960786Sps * Move cursor right one character. 39060786Sps */ 39160786Sps static int 39260786Spscmd_right() 39360786Sps{ 394161475Sdelphij char *pr; 395161475Sdelphij char *ncp; 396161475Sdelphij int width; 39760786Sps 39860786Sps if (*cp == '\0') 39960786Sps { 400161475Sdelphij /* Already at the end of the line. */ 40160786Sps return (CC_OK); 40260786Sps } 403161475Sdelphij ncp = cp; 404161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 405161475Sdelphij if (cmd_col + width >= sc_width) 40660786Sps cmd_lshift(); 407161475Sdelphij else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 40860786Sps cmd_lshift(); 409161475Sdelphij cp = ncp; 410161475Sdelphij cmd_col += width; 411161475Sdelphij putstr(pr); 412161475Sdelphij while (*cp != '\0') 413161475Sdelphij { 414161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 415161475Sdelphij if (width > 0) 416161475Sdelphij break; 417161475Sdelphij putstr(pr); 418161475Sdelphij cp = ncp; 419161475Sdelphij } 42060786Sps return (CC_OK); 42160786Sps} 42260786Sps 42360786Sps/* 42460786Sps * Move cursor left one character. 42560786Sps */ 42660786Sps static int 42760786Spscmd_left() 42860786Sps{ 429161475Sdelphij char *ncp; 430330571Sdelphij int width = 0; 431330571Sdelphij int bswidth = 0; 432330571Sdelphij 43360786Sps if (cp <= cmdbuf) 43460786Sps { 43560786Sps /* Already at the beginning of the line */ 43660786Sps return (CC_OK); 43760786Sps } 438161475Sdelphij ncp = cp; 439161475Sdelphij while (ncp > cmdbuf) 440161475Sdelphij { 441161475Sdelphij cmd_step_left(&ncp, &width, &bswidth); 442161475Sdelphij if (width > 0) 443161475Sdelphij break; 444161475Sdelphij } 445161475Sdelphij if (cmd_col < prompt_col + width) 44660786Sps cmd_rshift(); 447161475Sdelphij cp = ncp; 448161475Sdelphij cmd_col -= width; 449161475Sdelphij while (bswidth-- > 0) 45060786Sps putbs(); 45160786Sps return (CC_OK); 45260786Sps} 45360786Sps 45460786Sps/* 45560786Sps * Insert a char into the command buffer, at the current position. 45660786Sps */ 45760786Sps static int 458161475Sdelphijcmd_ichar(cs, clen) 459161475Sdelphij char *cs; 460161475Sdelphij int clen; 46160786Sps{ 46260786Sps char *s; 46360786Sps 464161475Sdelphij if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 46560786Sps { 466161475Sdelphij /* No room in the command buffer for another char. */ 46760786Sps bell(); 46860786Sps return (CC_ERROR); 46960786Sps } 47060786Sps 47160786Sps /* 472161475Sdelphij * Make room for the new character (shift the tail of the buffer right). 47360786Sps */ 47460786Sps for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 475161475Sdelphij s[clen] = s[0]; 47660786Sps /* 477161475Sdelphij * Insert the character into the buffer. 478161475Sdelphij */ 479161475Sdelphij for (s = cp; s < cp + clen; s++) 480161475Sdelphij *s = *cs++; 481161475Sdelphij /* 48260786Sps * Reprint the tail of the line from the inserted char. 48360786Sps */ 484237613Sdelphij updown_match = -1; 48560786Sps cmd_repaint(cp); 48660786Sps cmd_right(); 48760786Sps return (CC_OK); 48860786Sps} 48960786Sps 49060786Sps/* 49160786Sps * Backspace in the command buffer. 49260786Sps * Delete the char to the left of the cursor. 49360786Sps */ 49460786Sps static int 49560786Spscmd_erase() 49660786Sps{ 497330571Sdelphij char *s; 498161475Sdelphij int clen; 49960786Sps 50060786Sps if (cp == cmdbuf) 50160786Sps { 50260786Sps /* 50360786Sps * Backspace past beginning of the buffer: 50460786Sps * this usually means abort the command. 50560786Sps */ 50660786Sps return (CC_QUIT); 50760786Sps } 50860786Sps /* 50960786Sps * Move cursor left (to the char being erased). 51060786Sps */ 511161475Sdelphij s = cp; 51260786Sps cmd_left(); 513294286Sdelphij clen = (int) (s - cp); 514161475Sdelphij 51560786Sps /* 51660786Sps * Remove the char from the buffer (shift the buffer left). 51760786Sps */ 518161475Sdelphij for (s = cp; ; s++) 519161475Sdelphij { 520161475Sdelphij s[0] = s[clen]; 521161475Sdelphij if (s[0] == '\0') 522161475Sdelphij break; 523161475Sdelphij } 524161475Sdelphij 52560786Sps /* 52660786Sps * Repaint the buffer after the erased char. 52760786Sps */ 528237613Sdelphij updown_match = -1; 52960786Sps cmd_repaint(cp); 53060786Sps 53160786Sps /* 53260786Sps * We say that erasing the entire command string causes us 53360786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 53460786Sps */ 53560786Sps if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 53660786Sps return (CC_QUIT); 53760786Sps return (CC_OK); 53860786Sps} 53960786Sps 54060786Sps/* 54160786Sps * Delete the char under the cursor. 54260786Sps */ 54360786Sps static int 54460786Spscmd_delete() 54560786Sps{ 54660786Sps if (*cp == '\0') 54760786Sps { 548161475Sdelphij /* At end of string; there is no char under the cursor. */ 54960786Sps return (CC_OK); 55060786Sps } 55160786Sps /* 55260786Sps * Move right, then use cmd_erase. 55360786Sps */ 55460786Sps cmd_right(); 55560786Sps cmd_erase(); 55660786Sps return (CC_OK); 55760786Sps} 55860786Sps 55960786Sps/* 56060786Sps * Delete the "word" to the left of the cursor. 56160786Sps */ 56260786Sps static int 56360786Spscmd_werase() 56460786Sps{ 56560786Sps if (cp > cmdbuf && cp[-1] == ' ') 56660786Sps { 56760786Sps /* 56860786Sps * If the char left of cursor is a space, 56960786Sps * erase all the spaces left of cursor (to the first non-space). 57060786Sps */ 57160786Sps while (cp > cmdbuf && cp[-1] == ' ') 57260786Sps (void) cmd_erase(); 57360786Sps } else 57460786Sps { 57560786Sps /* 57660786Sps * If the char left of cursor is not a space, 57760786Sps * erase all the nonspaces left of cursor (the whole "word"). 57860786Sps */ 57960786Sps while (cp > cmdbuf && cp[-1] != ' ') 58060786Sps (void) cmd_erase(); 58160786Sps } 58260786Sps return (CC_OK); 58360786Sps} 58460786Sps 58560786Sps/* 58660786Sps * Delete the "word" under the cursor. 58760786Sps */ 58860786Sps static int 58960786Spscmd_wdelete() 59060786Sps{ 59160786Sps if (*cp == ' ') 59260786Sps { 59360786Sps /* 59460786Sps * If the char under the cursor is a space, 59560786Sps * delete it and all the spaces right of cursor. 59660786Sps */ 59760786Sps while (*cp == ' ') 59860786Sps (void) cmd_delete(); 59960786Sps } else 60060786Sps { 60160786Sps /* 60260786Sps * If the char under the cursor is not a space, 60360786Sps * delete it and all nonspaces right of cursor (the whole word). 60460786Sps */ 60560786Sps while (*cp != ' ' && *cp != '\0') 60660786Sps (void) cmd_delete(); 60760786Sps } 60860786Sps return (CC_OK); 60960786Sps} 61060786Sps 61160786Sps/* 61260786Sps * Delete all chars in the command buffer. 61360786Sps */ 61460786Sps static int 61560786Spscmd_kill() 61660786Sps{ 61760786Sps if (cmdbuf[0] == '\0') 61860786Sps { 619161475Sdelphij /* Buffer is already empty; abort the current command. */ 62060786Sps return (CC_QUIT); 62160786Sps } 62260786Sps cmd_offset = 0; 62360786Sps cmd_home(); 62460786Sps *cp = '\0'; 625237613Sdelphij updown_match = -1; 62660786Sps cmd_repaint(cp); 62760786Sps 62860786Sps /* 62960786Sps * We say that erasing the entire command string causes us 63060786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 63160786Sps */ 63260786Sps if (curr_cmdflags & CF_QUIT_ON_ERASE) 63360786Sps return (CC_QUIT); 63460786Sps return (CC_OK); 63560786Sps} 63660786Sps 63760786Sps/* 63860786Sps * Select an mlist structure to be the current command history. 63960786Sps */ 64060786Sps public void 64160786Spsset_mlist(mlist, cmdflags) 64260786Sps void *mlist; 64360786Sps int cmdflags; 64460786Sps{ 645191930Sdelphij#if CMD_HISTORY 64660786Sps curr_mlist = (struct mlist *) mlist; 64760786Sps curr_cmdflags = cmdflags; 648161475Sdelphij 649161475Sdelphij /* Make sure the next up-arrow moves to the last string in the mlist. */ 650161475Sdelphij if (curr_mlist != NULL) 651161475Sdelphij curr_mlist->curr_mp = curr_mlist; 652191930Sdelphij#endif 65360786Sps} 65460786Sps 65560786Sps#if CMD_HISTORY 65660786Sps/* 65760786Sps * Move up or down in the currently selected command history list. 658237613Sdelphij * Only consider entries whose first updown_match chars are equal to 659237613Sdelphij * cmdbuf's corresponding chars. 66060786Sps */ 66160786Sps static int 66260786Spscmd_updown(action) 66360786Sps int action; 66460786Sps{ 665330571Sdelphij constant char *s; 666237613Sdelphij struct mlist *ml; 66760786Sps 66860786Sps if (curr_mlist == NULL) 66960786Sps { 67060786Sps /* 67160786Sps * The current command has no history list. 67260786Sps */ 67360786Sps bell(); 67460786Sps return (CC_OK); 67560786Sps } 676237613Sdelphij 677237613Sdelphij if (updown_match < 0) 678237613Sdelphij { 679294286Sdelphij updown_match = (int) (cp - cmdbuf); 680237613Sdelphij } 681237613Sdelphij 68260786Sps /* 683237613Sdelphij * Find the next history entry which matches. 68460786Sps */ 685237613Sdelphij for (ml = curr_mlist->curr_mp;;) 686237613Sdelphij { 687237613Sdelphij ml = (action == EC_UP) ? ml->prev : ml->next; 688237613Sdelphij if (ml == curr_mlist) 689237613Sdelphij { 690237613Sdelphij /* 691237613Sdelphij * We reached the end (or beginning) of the list. 692237613Sdelphij */ 693237613Sdelphij break; 694237613Sdelphij } 695237613Sdelphij if (strncmp(cmdbuf, ml->string, updown_match) == 0) 696237613Sdelphij { 697237613Sdelphij /* 698237613Sdelphij * This entry matches; stop here. 699237613Sdelphij * Copy the entry into cmdbuf and echo it on the screen. 700237613Sdelphij */ 701237613Sdelphij curr_mlist->curr_mp = ml; 702237613Sdelphij s = ml->string; 703237613Sdelphij if (s == NULL) 704237613Sdelphij s = ""; 705237613Sdelphij cmd_home(); 706237613Sdelphij clear_eol(); 707250592Sdelphij strcpy(cmdbuf, s); 708237613Sdelphij for (cp = cmdbuf; *cp != '\0'; ) 709237613Sdelphij cmd_right(); 710237613Sdelphij return (CC_OK); 711237613Sdelphij } 712237613Sdelphij } 71360786Sps /* 714237613Sdelphij * We didn't find a history entry that matches. 71560786Sps */ 716237613Sdelphij bell(); 71760786Sps return (CC_OK); 71860786Sps} 71960786Sps#endif 72060786Sps 72160786Sps/* 722294286Sdelphij * Add a string to an mlist. 72360786Sps */ 72460786Sps public void 725294286Sdelphijcmd_addhist(mlist, cmd, modified) 72660786Sps struct mlist *mlist; 727330571Sdelphij constant char *cmd; 728294286Sdelphij int modified; 72960786Sps{ 73060786Sps#if CMD_HISTORY 73160786Sps struct mlist *ml; 73260786Sps 73360786Sps /* 73460786Sps * Don't save a trivial command. 73560786Sps */ 73660786Sps if (strlen(cmd) == 0) 73760786Sps return; 738161475Sdelphij 73960786Sps /* 740161475Sdelphij * Save the command unless it's a duplicate of the 741161475Sdelphij * last command in the history. 74260786Sps */ 743161475Sdelphij ml = mlist->prev; 744161475Sdelphij if (ml == mlist || strcmp(ml->string, cmd) != 0) 74560786Sps { 74660786Sps /* 74760786Sps * Did not find command in history. 74860786Sps * Save the command and put it at the end of the history list. 74960786Sps */ 75060786Sps ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 75160786Sps ml->string = save(cmd); 752294286Sdelphij ml->modified = modified; 75360786Sps ml->next = mlist; 75460786Sps ml->prev = mlist->prev; 75560786Sps mlist->prev->next = ml; 75660786Sps mlist->prev = ml; 75760786Sps } 75860786Sps /* 75960786Sps * Point to the cmd just after the just-accepted command. 76060786Sps * Thus, an UPARROW will always retrieve the previous command. 76160786Sps */ 76260786Sps mlist->curr_mp = ml->next; 76360786Sps#endif 76460786Sps} 76560786Sps 76660786Sps/* 76760786Sps * Accept the command in the command buffer. 76860786Sps * Add it to the currently selected history list. 76960786Sps */ 77060786Sps public void 77160786Spscmd_accept() 77260786Sps{ 77360786Sps#if CMD_HISTORY 77460786Sps /* 77560786Sps * Nothing to do if there is no currently selected history list. 77660786Sps */ 77760786Sps if (curr_mlist == NULL) 77860786Sps return; 779294286Sdelphij cmd_addhist(curr_mlist, cmdbuf, 1); 780170256Sdelphij curr_mlist->modified = 1; 78160786Sps#endif 78260786Sps} 78360786Sps 78460786Sps/* 78560786Sps * Try to perform a line-edit function on the command buffer, 78660786Sps * using a specified char as a line-editing command. 78760786Sps * Returns: 78860786Sps * CC_PASS The char does not invoke a line edit function. 78960786Sps * CC_OK Line edit function done. 79060786Sps * CC_QUIT The char requests the current command to be aborted. 79160786Sps */ 79260786Sps static int 79360786Spscmd_edit(c) 79460786Sps int c; 79560786Sps{ 79660786Sps int action; 79760786Sps int flags; 79860786Sps 79960786Sps#if TAB_COMPLETE_FILENAME 80060786Sps#define not_in_completion() in_completion = 0 80160786Sps#else 80260786Sps#define not_in_completion() 80360786Sps#endif 80460786Sps 80560786Sps /* 80660786Sps * See if the char is indeed a line-editing command. 80760786Sps */ 80860786Sps flags = 0; 80960786Sps#if CMD_HISTORY 81060786Sps if (curr_mlist == NULL) 81160786Sps /* 81260786Sps * No current history; don't accept history manipulation cmds. 81360786Sps */ 81460786Sps flags |= EC_NOHISTORY; 81560786Sps#endif 81660786Sps#if TAB_COMPLETE_FILENAME 81760786Sps if (curr_mlist == ml_search) 81860786Sps /* 81960786Sps * In a search command; don't accept file-completion cmds. 82060786Sps */ 82160786Sps flags |= EC_NOCOMPLETE; 82260786Sps#endif 82360786Sps 82460786Sps action = editchar(c, flags); 82560786Sps 82660786Sps switch (action) 82760786Sps { 82860786Sps case EC_RIGHT: 82960786Sps not_in_completion(); 83060786Sps return (cmd_right()); 83160786Sps case EC_LEFT: 83260786Sps not_in_completion(); 83360786Sps return (cmd_left()); 83460786Sps case EC_W_RIGHT: 83560786Sps not_in_completion(); 83660786Sps while (*cp != '\0' && *cp != ' ') 83760786Sps cmd_right(); 83860786Sps while (*cp == ' ') 83960786Sps cmd_right(); 84060786Sps return (CC_OK); 84160786Sps case EC_W_LEFT: 84260786Sps not_in_completion(); 84360786Sps while (cp > cmdbuf && cp[-1] == ' ') 84460786Sps cmd_left(); 84560786Sps while (cp > cmdbuf && cp[-1] != ' ') 84660786Sps cmd_left(); 84760786Sps return (CC_OK); 84860786Sps case EC_HOME: 84960786Sps not_in_completion(); 85060786Sps cmd_offset = 0; 85160786Sps cmd_home(); 85260786Sps cmd_repaint(cp); 85360786Sps return (CC_OK); 85460786Sps case EC_END: 85560786Sps not_in_completion(); 85660786Sps while (*cp != '\0') 85760786Sps cmd_right(); 85860786Sps return (CC_OK); 85960786Sps case EC_INSERT: 86060786Sps not_in_completion(); 86160786Sps return (CC_OK); 86260786Sps case EC_BACKSPACE: 86360786Sps not_in_completion(); 86460786Sps return (cmd_erase()); 86560786Sps case EC_LINEKILL: 86660786Sps not_in_completion(); 86760786Sps return (cmd_kill()); 868221715Sdelphij case EC_ABORT: 869221715Sdelphij not_in_completion(); 870221715Sdelphij (void) cmd_kill(); 871221715Sdelphij return (CC_QUIT); 87260786Sps case EC_W_BACKSPACE: 87360786Sps not_in_completion(); 87460786Sps return (cmd_werase()); 87560786Sps case EC_DELETE: 87660786Sps not_in_completion(); 87760786Sps return (cmd_delete()); 87860786Sps case EC_W_DELETE: 87960786Sps not_in_completion(); 88060786Sps return (cmd_wdelete()); 88160786Sps case EC_LITERAL: 88260786Sps literal = 1; 88360786Sps return (CC_OK); 88460786Sps#if CMD_HISTORY 88560786Sps case EC_UP: 88660786Sps case EC_DOWN: 88760786Sps not_in_completion(); 88860786Sps return (cmd_updown(action)); 88960786Sps#endif 89060786Sps#if TAB_COMPLETE_FILENAME 89160786Sps case EC_F_COMPLETE: 89260786Sps case EC_B_COMPLETE: 89360786Sps case EC_EXPAND: 89460786Sps return (cmd_complete(action)); 89560786Sps#endif 89660786Sps case EC_NOACTION: 89760786Sps return (CC_OK); 89860786Sps default: 89960786Sps not_in_completion(); 90060786Sps return (CC_PASS); 90160786Sps } 90260786Sps} 90360786Sps 90460786Sps#if TAB_COMPLETE_FILENAME 90560786Sps/* 90660786Sps * Insert a string into the command buffer, at the current position. 90760786Sps */ 90860786Sps static int 90960786Spscmd_istr(str) 91060786Sps char *str; 91160786Sps{ 91260786Sps char *s; 91360786Sps int action; 914161475Sdelphij char *endline = str + strlen(str); 91560786Sps 916161475Sdelphij for (s = str; *s != '\0'; ) 91760786Sps { 918161475Sdelphij char *os = s; 919161475Sdelphij step_char(&s, +1, endline); 920161475Sdelphij action = cmd_ichar(os, s - os); 92160786Sps if (action != CC_OK) 92260786Sps { 92360786Sps bell(); 92460786Sps return (action); 92560786Sps } 92660786Sps } 92760786Sps return (CC_OK); 92860786Sps} 92960786Sps 93060786Sps/* 93160786Sps * Find the beginning and end of the "current" word. 93260786Sps * This is the word which the cursor (cp) is inside or at the end of. 93360786Sps * Return pointer to the beginning of the word and put the 93460786Sps * cursor at the end of the word. 93560786Sps */ 93660786Sps static char * 93760786Spsdelimit_word() 93860786Sps{ 93960786Sps char *word; 94060786Sps#if SPACES_IN_FILENAMES 94160786Sps char *p; 942128345Stjr int delim_quoted = 0; 943128345Stjr int meta_quoted = 0; 944330571Sdelphij constant char *esc = get_meta_escape(); 945294286Sdelphij int esclen = (int) strlen(esc); 94660786Sps#endif 94760786Sps 94860786Sps /* 94960786Sps * Move cursor to end of word. 95060786Sps */ 95160786Sps if (*cp != ' ' && *cp != '\0') 95260786Sps { 95360786Sps /* 95460786Sps * Cursor is on a nonspace. 95560786Sps * Move cursor right to the next space. 95660786Sps */ 95760786Sps while (*cp != ' ' && *cp != '\0') 95860786Sps cmd_right(); 95960786Sps } else if (cp > cmdbuf && cp[-1] != ' ') 96060786Sps { 96160786Sps /* 96260786Sps * Cursor is on a space, and char to the left is a nonspace. 96360786Sps * We're already at the end of the word. 96460786Sps */ 96560786Sps ; 966128345Stjr#if 0 96760786Sps } else 96860786Sps { 96960786Sps /* 97060786Sps * Cursor is on a space and char to the left is a space. 97160786Sps * Huh? There's no word here. 97260786Sps */ 97360786Sps return (NULL); 974128345Stjr#endif 97560786Sps } 97660786Sps /* 977128345Stjr * Find the beginning of the word which the cursor is in. 97860786Sps */ 97960786Sps if (cp == cmdbuf) 98060786Sps return (NULL); 98160786Sps#if SPACES_IN_FILENAMES 98260786Sps /* 98360786Sps * If we have an unbalanced quote (that is, an open quote 98460786Sps * without a corresponding close quote), we return everything 98560786Sps * from the open quote, including spaces. 98660786Sps */ 987128345Stjr for (word = cmdbuf; word < cp; word++) 988128345Stjr if (*word != ' ') 989128345Stjr break; 990128345Stjr if (word >= cp) 991128345Stjr return (cp); 99260786Sps for (p = cmdbuf; p < cp; p++) 99360786Sps { 994128345Stjr if (meta_quoted) 99560786Sps { 996128345Stjr meta_quoted = 0; 997128345Stjr } else if (esclen > 0 && p + esclen < cp && 998128345Stjr strncmp(p, esc, esclen) == 0) 99960786Sps { 1000128345Stjr meta_quoted = 1; 1001128345Stjr p += esclen - 1; 1002128345Stjr } else if (delim_quoted) 1003128345Stjr { 1004128345Stjr if (*p == closequote) 1005128345Stjr delim_quoted = 0; 1006128345Stjr } else /* (!delim_quoted) */ 1007128345Stjr { 1008128345Stjr if (*p == openquote) 1009128345Stjr delim_quoted = 1; 1010128345Stjr else if (*p == ' ') 1011128345Stjr word = p+1; 101260786Sps } 101360786Sps } 101460786Sps#endif 101560786Sps return (word); 101660786Sps} 101760786Sps 101860786Sps/* 101960786Sps * Set things up to enter completion mode. 102060786Sps * Expand the word under the cursor into a list of filenames 102160786Sps * which start with that word, and set tk_text to that list. 102260786Sps */ 102360786Sps static void 102460786Spsinit_compl() 102560786Sps{ 102660786Sps char *word; 102760786Sps char c; 102860786Sps 102960786Sps /* 103060786Sps * Get rid of any previous tk_text. 103160786Sps */ 103260786Sps if (tk_text != NULL) 103360786Sps { 103460786Sps free(tk_text); 103560786Sps tk_text = NULL; 103660786Sps } 103760786Sps /* 103860786Sps * Find the original (uncompleted) word in the command buffer. 103960786Sps */ 104060786Sps word = delimit_word(); 104160786Sps if (word == NULL) 104260786Sps return; 104360786Sps /* 104460786Sps * Set the insertion point to the point in the command buffer 104560786Sps * where the original (uncompleted) word now sits. 104660786Sps */ 104760786Sps tk_ipoint = word; 104860786Sps /* 104960786Sps * Save the original (uncompleted) word 105060786Sps */ 105160786Sps if (tk_original != NULL) 105260786Sps free(tk_original); 105360786Sps tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 105460786Sps strncpy(tk_original, word, cp-word); 105560786Sps /* 105660786Sps * Get the expanded filename. 105760786Sps * This may result in a single filename, or 105860786Sps * a blank-separated list of filenames. 105960786Sps */ 106060786Sps c = *cp; 106160786Sps *cp = '\0'; 1062128345Stjr if (*word != openquote) 1063128345Stjr { 1064128345Stjr tk_text = fcomplete(word); 1065128345Stjr } else 1066128345Stjr { 1067238730Sdelphij#if MSDOS_COMPILER 1068238730Sdelphij char *qword = NULL; 1069238730Sdelphij#else 1070128345Stjr char *qword = shell_quote(word+1); 1071238730Sdelphij#endif 1072128345Stjr if (qword == NULL) 1073128345Stjr tk_text = fcomplete(word+1); 1074128345Stjr else 1075128345Stjr { 1076128345Stjr tk_text = fcomplete(qword); 1077128345Stjr free(qword); 1078128345Stjr } 1079128345Stjr } 108060786Sps *cp = c; 108160786Sps} 108260786Sps 108360786Sps/* 108460786Sps * Return the next word in the current completion list. 108560786Sps */ 108660786Sps static char * 108760786Spsnext_compl(action, prev) 108860786Sps int action; 108960786Sps char *prev; 109060786Sps{ 109160786Sps switch (action) 109260786Sps { 109360786Sps case EC_F_COMPLETE: 109460786Sps return (forw_textlist(&tk_tlist, prev)); 109560786Sps case EC_B_COMPLETE: 109660786Sps return (back_textlist(&tk_tlist, prev)); 109760786Sps } 109860786Sps /* Cannot happen */ 109960786Sps return ("?"); 110060786Sps} 110160786Sps 110260786Sps/* 110360786Sps * Complete the filename before (or under) the cursor. 110460786Sps * cmd_complete may be called multiple times. The global in_completion 110560786Sps * remembers whether this call is the first time (create the list), 110660786Sps * or a subsequent time (step thru the list). 110760786Sps */ 110860786Sps static int 110960786Spscmd_complete(action) 111060786Sps int action; 111160786Sps{ 111260786Sps char *s; 111360786Sps 111460786Sps if (!in_completion || action == EC_EXPAND) 111560786Sps { 111660786Sps /* 111760786Sps * Expand the word under the cursor and 111860786Sps * use the first word in the expansion 111960786Sps * (or the entire expansion if we're doing EC_EXPAND). 112060786Sps */ 112160786Sps init_compl(); 112260786Sps if (tk_text == NULL) 112360786Sps { 112460786Sps bell(); 112560786Sps return (CC_OK); 112660786Sps } 112760786Sps if (action == EC_EXPAND) 112860786Sps { 112960786Sps /* 113060786Sps * Use the whole list. 113160786Sps */ 113260786Sps tk_trial = tk_text; 113360786Sps } else 113460786Sps { 113560786Sps /* 113660786Sps * Use the first filename in the list. 113760786Sps */ 113860786Sps in_completion = 1; 113960786Sps init_textlist(&tk_tlist, tk_text); 114060786Sps tk_trial = next_compl(action, (char*)NULL); 114160786Sps } 114260786Sps } else 114360786Sps { 114460786Sps /* 114560786Sps * We already have a completion list. 114660786Sps * Use the next/previous filename from the list. 114760786Sps */ 114860786Sps tk_trial = next_compl(action, tk_trial); 114960786Sps } 115060786Sps 115160786Sps /* 115260786Sps * Remove the original word, or the previous trial completion. 115360786Sps */ 115460786Sps while (cp > tk_ipoint) 115560786Sps (void) cmd_erase(); 115660786Sps 115760786Sps if (tk_trial == NULL) 115860786Sps { 115960786Sps /* 116060786Sps * There are no more trial completions. 116160786Sps * Insert the original (uncompleted) filename. 116260786Sps */ 116360786Sps in_completion = 0; 116460786Sps if (cmd_istr(tk_original) != CC_OK) 116560786Sps goto fail; 116660786Sps } else 116760786Sps { 116860786Sps /* 116960786Sps * Insert trial completion. 117060786Sps */ 117160786Sps if (cmd_istr(tk_trial) != CC_OK) 117260786Sps goto fail; 117360786Sps /* 117460786Sps * If it is a directory, append a slash. 117560786Sps */ 117660786Sps if (is_dir(tk_trial)) 117760786Sps { 117860786Sps if (cp > cmdbuf && cp[-1] == closequote) 117960786Sps (void) cmd_erase(); 118060786Sps s = lgetenv("LESSSEPARATOR"); 118160786Sps if (s == NULL) 118260786Sps s = PATHNAME_SEP; 118360786Sps if (cmd_istr(s) != CC_OK) 118460786Sps goto fail; 118560786Sps } 118660786Sps } 118760786Sps 118860786Sps return (CC_OK); 118960786Sps 119060786Spsfail: 119160786Sps in_completion = 0; 119260786Sps bell(); 119360786Sps return (CC_OK); 119460786Sps} 119560786Sps 119660786Sps#endif /* TAB_COMPLETE_FILENAME */ 119760786Sps 119860786Sps/* 119960786Sps * Process a single character of a multi-character command, such as 120060786Sps * a number, or the pattern of a search command. 120160786Sps * Returns: 120260786Sps * CC_OK The char was accepted. 120360786Sps * CC_QUIT The char requests the command to be aborted. 120460786Sps * CC_ERROR The char could not be accepted due to an error. 120560786Sps */ 120660786Sps public int 120760786Spscmd_char(c) 120860786Sps int c; 120960786Sps{ 121060786Sps int action; 1211161475Sdelphij int len; 121260786Sps 1213161475Sdelphij if (!utf_mode) 1214161475Sdelphij { 1215161475Sdelphij cmd_mbc_buf[0] = c; 1216161475Sdelphij len = 1; 1217161475Sdelphij } else 1218161475Sdelphij { 1219161475Sdelphij /* Perform strict validation in all possible cases. */ 1220161475Sdelphij if (cmd_mbc_buf_len == 0) 1221161475Sdelphij { 1222161475Sdelphij retry: 1223161475Sdelphij cmd_mbc_buf_index = 1; 1224161475Sdelphij *cmd_mbc_buf = c; 1225161475Sdelphij if (IS_ASCII_OCTET(c)) 1226161475Sdelphij cmd_mbc_buf_len = 1; 1227330571Sdelphij#if MSDOS_COMPILER || OS2 1228330571Sdelphij else if (c == (unsigned char) '\340' && IS_ASCII_OCTET(peekcc())) 1229330571Sdelphij { 1230330571Sdelphij /* Assume a special key. */ 1231330571Sdelphij cmd_mbc_buf_len = 1; 1232330571Sdelphij } 1233330571Sdelphij#endif 1234161475Sdelphij else if (IS_UTF8_LEAD(c)) 1235161475Sdelphij { 1236161475Sdelphij cmd_mbc_buf_len = utf_len(c); 1237161475Sdelphij return (CC_OK); 1238161475Sdelphij } else 1239161475Sdelphij { 1240161475Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 1241161475Sdelphij bell(); 1242161475Sdelphij return (CC_ERROR); 1243161475Sdelphij } 1244161475Sdelphij } else if (IS_UTF8_TRAIL(c)) 1245161475Sdelphij { 1246161475Sdelphij cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1247161475Sdelphij if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1248161475Sdelphij return (CC_OK); 1249294286Sdelphij if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index)) 1250161475Sdelphij { 1251161475Sdelphij /* complete, but not well formed (non-shortest form), sequence */ 1252161475Sdelphij cmd_mbc_buf_len = 0; 1253161475Sdelphij bell(); 1254161475Sdelphij return (CC_ERROR); 1255161475Sdelphij } 1256161475Sdelphij } else 1257161475Sdelphij { 1258161475Sdelphij /* Flush incomplete (truncated) sequence. */ 1259161475Sdelphij cmd_mbc_buf_len = 0; 1260161475Sdelphij bell(); 1261161475Sdelphij /* Handle new char. */ 1262161475Sdelphij goto retry; 1263161475Sdelphij } 1264161475Sdelphij 1265161475Sdelphij len = cmd_mbc_buf_len; 1266161475Sdelphij cmd_mbc_buf_len = 0; 1267161475Sdelphij } 1268161475Sdelphij 126960786Sps if (literal) 127060786Sps { 127160786Sps /* 127260786Sps * Insert the char, even if it is a line-editing char. 127360786Sps */ 127460786Sps literal = 0; 1275161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 127660786Sps } 127760786Sps 127860786Sps /* 1279161475Sdelphij * See if it is a line-editing character. 128060786Sps */ 1281161475Sdelphij if (in_mca() && len == 1) 128260786Sps { 128360786Sps action = cmd_edit(c); 128460786Sps switch (action) 128560786Sps { 128660786Sps case CC_OK: 128760786Sps case CC_QUIT: 128860786Sps return (action); 128960786Sps case CC_PASS: 129060786Sps break; 129160786Sps } 129260786Sps } 129360786Sps 129460786Sps /* 129560786Sps * Insert the char into the command buffer. 129660786Sps */ 1297161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 129860786Sps} 129960786Sps 130060786Sps/* 130160786Sps * Return the number currently in the command buffer. 130260786Sps */ 1303128345Stjr public LINENUM 1304170256Sdelphijcmd_int(frac) 1305170256Sdelphij long *frac; 130660786Sps{ 1307170256Sdelphij char *p; 1308128345Stjr LINENUM n = 0; 1309170256Sdelphij int err; 1310128345Stjr 1311170256Sdelphij for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1312170256Sdelphij n = (n * 10) + (*p - '0'); 1313170256Sdelphij *frac = 0; 1314170256Sdelphij if (*p++ == '.') 1315170256Sdelphij { 1316170256Sdelphij *frac = getfraction(&p, NULL, &err); 1317170256Sdelphij /* {{ do something if err is set? }} */ 1318170256Sdelphij } 1319128345Stjr return (n); 132060786Sps} 132160786Sps 132260786Sps/* 132360786Sps * Return a pointer to the command buffer. 132460786Sps */ 132560786Sps public char * 132660786Spsget_cmdbuf() 132760786Sps{ 132860786Sps return (cmdbuf); 132960786Sps} 1330161475Sdelphij 1331191930Sdelphij#if CMD_HISTORY 1332170256Sdelphij/* 1333170256Sdelphij * Return the last (most recent) string in the current command history. 1334170256Sdelphij */ 1335170256Sdelphij public char * 1336170256Sdelphijcmd_lastpattern() 1337170256Sdelphij{ 1338170256Sdelphij if (curr_mlist == NULL) 1339170256Sdelphij return (NULL); 1340170256Sdelphij return (curr_mlist->curr_mp->prev->string); 1341170256Sdelphij} 1342191930Sdelphij#endif 1343170256Sdelphij 1344161475Sdelphij#if CMD_HISTORY 1345161475Sdelphij/* 1346294286Sdelphij */ 1347294286Sdelphij static int 1348294286Sdelphijmlist_size(ml) 1349294286Sdelphij struct mlist *ml; 1350294286Sdelphij{ 1351294286Sdelphij int size = 0; 1352294286Sdelphij for (ml = ml->next; ml->string != NULL; ml = ml->next) 1353294286Sdelphij ++size; 1354294286Sdelphij return size; 1355294286Sdelphij} 1356294286Sdelphij 1357294286Sdelphij/* 1358161475Sdelphij * Get the name of the history file. 1359161475Sdelphij */ 1360161475Sdelphij static char * 1361161475Sdelphijhistfile_name() 1362161475Sdelphij{ 1363161475Sdelphij char *home; 1364161475Sdelphij char *name; 1365161475Sdelphij int len; 1366161475Sdelphij 1367161475Sdelphij /* See if filename is explicitly specified by $LESSHISTFILE. */ 1368161475Sdelphij name = lgetenv("LESSHISTFILE"); 1369161475Sdelphij if (name != NULL && *name != '\0') 1370161475Sdelphij { 1371170256Sdelphij if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1372161475Sdelphij /* $LESSHISTFILE == "-" means don't use a history file. */ 1373161475Sdelphij return (NULL); 1374161475Sdelphij return (save(name)); 1375161475Sdelphij } 1376161475Sdelphij 1377294286Sdelphij /* See if history file is disabled in the build. */ 1378294286Sdelphij if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0) 1379294286Sdelphij return (NULL); 1380294286Sdelphij 1381161475Sdelphij /* Otherwise, file is in $HOME. */ 1382161475Sdelphij home = lgetenv("HOME"); 1383161475Sdelphij if (home == NULL || *home == '\0') 1384161475Sdelphij { 1385161475Sdelphij#if OS2 1386161475Sdelphij home = lgetenv("INIT"); 1387161475Sdelphij if (home == NULL || *home == '\0') 1388161475Sdelphij#endif 1389161475Sdelphij return (NULL); 1390161475Sdelphij } 1391294286Sdelphij len = (int) (strlen(home) + strlen(LESSHISTFILE) + 2); 1392161475Sdelphij name = (char *) ecalloc(len, sizeof(char)); 1393161475Sdelphij SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1394161475Sdelphij return (name); 1395161475Sdelphij} 1396161475Sdelphij 1397161475Sdelphij/* 1398294286Sdelphij * Read a .lesshst file and call a callback for each line in the file. 1399161475Sdelphij */ 1400294286Sdelphij static void 1401294286Sdelphijread_cmdhist2(action, uparam, skip_search, skip_shell) 1402294286Sdelphij void (*action)(void*,struct mlist*,char*); 1403294286Sdelphij void *uparam; 1404294286Sdelphij int skip_search; 1405294286Sdelphij int skip_shell; 1406161475Sdelphij{ 1407161475Sdelphij struct mlist *ml = NULL; 1408161475Sdelphij char line[CMDBUF_SIZE]; 1409161475Sdelphij char *filename; 1410161475Sdelphij FILE *f; 1411161475Sdelphij char *p; 1412294286Sdelphij int *skip = NULL; 1413161475Sdelphij 1414161475Sdelphij filename = histfile_name(); 1415161475Sdelphij if (filename == NULL) 1416161475Sdelphij return; 1417161475Sdelphij f = fopen(filename, "r"); 1418161475Sdelphij free(filename); 1419161475Sdelphij if (f == NULL) 1420161475Sdelphij return; 1421161475Sdelphij if (fgets(line, sizeof(line), f) == NULL || 1422161475Sdelphij strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1423161475Sdelphij { 1424161475Sdelphij fclose(f); 1425161475Sdelphij return; 1426161475Sdelphij } 1427161475Sdelphij while (fgets(line, sizeof(line), f) != NULL) 1428161475Sdelphij { 1429161475Sdelphij for (p = line; *p != '\0'; p++) 1430161475Sdelphij { 1431161475Sdelphij if (*p == '\n' || *p == '\r') 1432161475Sdelphij { 1433161475Sdelphij *p = '\0'; 1434161475Sdelphij break; 1435161475Sdelphij } 1436161475Sdelphij } 1437161475Sdelphij if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1438294286Sdelphij { 1439161475Sdelphij ml = &mlist_search; 1440294286Sdelphij skip = &skip_search; 1441294286Sdelphij } else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1442170964Sdelphij { 1443161475Sdelphij#if SHELL_ESCAPE || PIPEC 1444161475Sdelphij ml = &mlist_shell; 1445294286Sdelphij skip = &skip_shell; 1446170964Sdelphij#else 1447170964Sdelphij ml = NULL; 1448294286Sdelphij skip = NULL; 1449161475Sdelphij#endif 1450170964Sdelphij } else if (*line == '"') 1451161475Sdelphij { 1452161475Sdelphij if (ml != NULL) 1453294286Sdelphij { 1454294286Sdelphij if (skip != NULL && *skip > 0) 1455294286Sdelphij --(*skip); 1456294286Sdelphij else 1457294286Sdelphij (*action)(uparam, ml, line+1); 1458294286Sdelphij } 1459161475Sdelphij } 1460161475Sdelphij } 1461161475Sdelphij fclose(f); 1462294286Sdelphij} 1463294286Sdelphij 1464294286Sdelphij static void 1465294286Sdelphijread_cmdhist(action, uparam, skip_search, skip_shell) 1466294286Sdelphij void (*action)(void*,struct mlist*,char*); 1467294286Sdelphij void *uparam; 1468294286Sdelphij int skip_search; 1469294286Sdelphij int skip_shell; 1470294286Sdelphij{ 1471294286Sdelphij read_cmdhist2(action, uparam, skip_search, skip_shell); 1472294286Sdelphij (*action)(uparam, NULL, NULL); /* signal end of file */ 1473294286Sdelphij} 1474294286Sdelphij 1475294286Sdelphij static void 1476294286Sdelphijaddhist_init(void *uparam, struct mlist *ml, char *string) 1477294286Sdelphij{ 1478294286Sdelphij if (ml == NULL || string == NULL) 1479294286Sdelphij return; 1480294286Sdelphij cmd_addhist(ml, string, 0); 1481294286Sdelphij} 1482161475Sdelphij#endif /* CMD_HISTORY */ 1483294286Sdelphij 1484294286Sdelphij/* 1485294286Sdelphij * Initialize history from a .lesshist file. 1486294286Sdelphij */ 1487294286Sdelphij public void 1488294286Sdelphijinit_cmdhist() 1489294286Sdelphij{ 1490294286Sdelphij#if CMD_HISTORY 1491294286Sdelphij read_cmdhist(&addhist_init, NULL, 0, 0); 1492294286Sdelphij#endif /* CMD_HISTORY */ 1493161475Sdelphij} 1494161475Sdelphij 1495161475Sdelphij/* 1496294286Sdelphij * Write the header for a section of the history file. 1497161475Sdelphij */ 1498161475Sdelphij#if CMD_HISTORY 1499161475Sdelphij static void 1500294286Sdelphijwrite_mlist_header(ml, f) 1501161475Sdelphij struct mlist *ml; 1502161475Sdelphij FILE *f; 1503161475Sdelphij{ 1504294286Sdelphij if (ml == &mlist_search) 1505294286Sdelphij fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1506294286Sdelphij#if SHELL_ESCAPE || PIPEC 1507294286Sdelphij else if (ml == &mlist_shell) 1508294286Sdelphij fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1509294286Sdelphij#endif 1510294286Sdelphij} 1511161475Sdelphij 1512294286Sdelphij/* 1513294286Sdelphij * Write all modified entries in an mlist to the history file. 1514294286Sdelphij */ 1515294286Sdelphij static void 1516294286Sdelphijwrite_mlist(ml, f) 1517294286Sdelphij struct mlist *ml; 1518294286Sdelphij FILE *f; 1519294286Sdelphij{ 1520294286Sdelphij for (ml = ml->next; ml->string != NULL; ml = ml->next) 1521161475Sdelphij { 1522294286Sdelphij if (!ml->modified) 1523294286Sdelphij continue; 1524294286Sdelphij fprintf(f, "\"%s\n", ml->string); 1525294286Sdelphij ml->modified = 0; 1526161475Sdelphij } 1527294286Sdelphij ml->modified = 0; /* entire mlist is now unmodified */ 1528161475Sdelphij} 1529161475Sdelphij 1530161475Sdelphij/* 1531294286Sdelphij * Make a temp name in the same directory as filename. 1532161475Sdelphij */ 1533294286Sdelphij static char * 1534294286Sdelphijmake_tempname(filename) 1535294286Sdelphij char *filename; 1536161475Sdelphij{ 1537294286Sdelphij char lastch; 1538294286Sdelphij char *tempname = ecalloc(1, strlen(filename)+1); 1539294286Sdelphij strcpy(tempname, filename); 1540294286Sdelphij lastch = tempname[strlen(tempname)-1]; 1541294286Sdelphij tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q'; 1542294286Sdelphij return tempname; 1543294286Sdelphij} 1544161475Sdelphij 1545294286Sdelphijstruct save_ctx 1546294286Sdelphij{ 1547294286Sdelphij struct mlist *mlist; 1548294286Sdelphij FILE *fout; 1549294286Sdelphij}; 1550294286Sdelphij 1551294286Sdelphij/* 1552294286Sdelphij * Copy entries from the saved history file to a new file. 1553294286Sdelphij * At the end of each mlist, append any new entries 1554294286Sdelphij * created during this session. 1555294286Sdelphij */ 1556294286Sdelphij static void 1557294286Sdelphijcopy_hist(void *uparam, struct mlist *ml, char *string) 1558294286Sdelphij{ 1559294286Sdelphij struct save_ctx *ctx = (struct save_ctx *) uparam; 1560294286Sdelphij 1561294286Sdelphij if (ml != ctx->mlist) { 1562294286Sdelphij /* We're changing mlists. */ 1563294286Sdelphij if (ctx->mlist) 1564294286Sdelphij /* Append any new entries to the end of the current mlist. */ 1565294286Sdelphij write_mlist(ctx->mlist, ctx->fout); 1566294286Sdelphij /* Write the header for the new mlist. */ 1567294286Sdelphij ctx->mlist = ml; 1568294286Sdelphij write_mlist_header(ctx->mlist, ctx->fout); 1569294286Sdelphij } 1570294286Sdelphij if (string != NULL) 1571294286Sdelphij { 1572294286Sdelphij /* Copy the entry. */ 1573294286Sdelphij fprintf(ctx->fout, "\"%s\n", string); 1574294286Sdelphij } 1575294286Sdelphij if (ml == NULL) /* End of file */ 1576294286Sdelphij { 1577294286Sdelphij /* Write any sections that were not in the original file. */ 1578294286Sdelphij if (mlist_search.modified) 1579294286Sdelphij { 1580294286Sdelphij write_mlist_header(&mlist_search, ctx->fout); 1581294286Sdelphij write_mlist(&mlist_search, ctx->fout); 1582294286Sdelphij } 1583170964Sdelphij#if SHELL_ESCAPE || PIPEC 1584294286Sdelphij if (mlist_shell.modified) 1585294286Sdelphij { 1586294286Sdelphij write_mlist_header(&mlist_shell, ctx->fout); 1587294286Sdelphij write_mlist(&mlist_shell, ctx->fout); 1588294286Sdelphij } 1589170964Sdelphij#endif 1590294286Sdelphij } 1591294286Sdelphij} 1592294286Sdelphij#endif /* CMD_HISTORY */ 1593294286Sdelphij 1594294286Sdelphij/* 1595294286Sdelphij * Make a file readable only by its owner. 1596294286Sdelphij */ 1597294286Sdelphij static void 1598294286Sdelphijmake_file_private(f) 1599294286Sdelphij FILE *f; 1600294286Sdelphij{ 1601161475Sdelphij#if HAVE_FCHMOD 1602191930Sdelphij int do_chmod = 1; 1603191930Sdelphij#if HAVE_STAT 1604191930Sdelphij struct stat statbuf; 1605191930Sdelphij int r = fstat(fileno(f), &statbuf); 1606191930Sdelphij if (r < 0 || !S_ISREG(statbuf.st_mode)) 1607191930Sdelphij /* Don't chmod if not a regular file. */ 1608191930Sdelphij do_chmod = 0; 1609161475Sdelphij#endif 1610191930Sdelphij if (do_chmod) 1611191930Sdelphij fchmod(fileno(f), 0600); 1612294286Sdelphij#endif 1613191930Sdelphij} 1614294286Sdelphij 1615294286Sdelphij/* 1616294286Sdelphij * Does the history file need to be updated? 1617294286Sdelphij */ 1618294286Sdelphij static int 1619294286Sdelphijhistfile_modified() 1620294286Sdelphij{ 1621294286Sdelphij if (mlist_search.modified) 1622294286Sdelphij return 1; 1623294286Sdelphij#if SHELL_ESCAPE || PIPEC 1624294286Sdelphij if (mlist_shell.modified) 1625294286Sdelphij return 1; 1626191930Sdelphij#endif 1627294286Sdelphij return 0; 1628294286Sdelphij} 1629161475Sdelphij 1630294286Sdelphij/* 1631294286Sdelphij * Update the .lesshst file. 1632294286Sdelphij */ 1633294286Sdelphij public void 1634294286Sdelphijsave_cmdhist() 1635294286Sdelphij{ 1636294286Sdelphij#if CMD_HISTORY 1637294286Sdelphij char *histname; 1638294286Sdelphij char *tempname; 1639294286Sdelphij int skip_search; 1640294286Sdelphij int skip_shell; 1641294286Sdelphij struct save_ctx ctx; 1642294286Sdelphij char *s; 1643294286Sdelphij FILE *fout = NULL; 1644294286Sdelphij int histsize = 0; 1645161475Sdelphij 1646294286Sdelphij if (!histfile_modified()) 1647294286Sdelphij return; 1648294286Sdelphij histname = histfile_name(); 1649294286Sdelphij if (histname == NULL) 1650294286Sdelphij return; 1651294286Sdelphij tempname = make_tempname(histname); 1652294286Sdelphij fout = fopen(tempname, "w"); 1653294286Sdelphij if (fout != NULL) 1654294286Sdelphij { 1655294286Sdelphij make_file_private(fout); 1656294286Sdelphij s = lgetenv("LESSHISTSIZE"); 1657294286Sdelphij if (s != NULL) 1658294286Sdelphij histsize = atoi(s); 1659294286Sdelphij if (histsize <= 0) 1660294286Sdelphij histsize = 100; 1661294286Sdelphij skip_search = mlist_size(&mlist_search) - histsize; 1662161475Sdelphij#if SHELL_ESCAPE || PIPEC 1663294286Sdelphij skip_shell = mlist_size(&mlist_shell) - histsize; 1664161475Sdelphij#endif 1665294286Sdelphij fprintf(fout, "%s\n", HISTFILE_FIRST_LINE); 1666294286Sdelphij ctx.fout = fout; 1667294286Sdelphij ctx.mlist = NULL; 1668294286Sdelphij read_cmdhist(copy_hist, &ctx, skip_search, skip_shell); 1669294286Sdelphij fclose(fout); 1670294286Sdelphij#if MSDOS_COMPILER==WIN32C 1671294286Sdelphij /* 1672294286Sdelphij * Windows rename doesn't remove an existing file, 1673294286Sdelphij * making it useless for atomic operations. Sigh. 1674294286Sdelphij */ 1675294286Sdelphij remove(histname); 1676294286Sdelphij#endif 1677294286Sdelphij rename(tempname, histname); 1678294286Sdelphij } 1679294286Sdelphij free(tempname); 1680294286Sdelphij free(histname); 1681161475Sdelphij#endif /* CMD_HISTORY */ 1682161475Sdelphij} 1683