160786Sps/* 2240121Sdelphij * Copyright (C) 1984-2012 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 7240121Sdelphij * For more information, see the README file. 860786Sps */ 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 */ 32240121Sdelphijstatic 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; 4360786Spsstatic char *tk_trial; 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 }; 79128345Stjrpublic void * constant ml_search = (void *) &mlist_search; 8060786Sps 8160786Spsstruct mlist mlist_examine = 82170256Sdelphij { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; 83128345Stjrpublic void * constant ml_examine = (void *) &mlist_examine; 8460786Sps 8560786Sps#if SHELL_ESCAPE || PIPEC 8660786Spsstruct mlist mlist_shell = 87170256Sdelphij { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; 88128345Stjrpublic void * constant ml_shell = (void *) &mlist_shell; 8960786Sps#endif 9060786Sps 9160786Sps#else /* CMD_HISTORY */ 9260786Sps 9360786Sps/* If CMD_HISTORY is off, these are just flags. */ 94128345Stjrpublic void * constant ml_search = (void *)1; 95128345Stjrpublic void * constant ml_examine = (void *)2; 9660786Sps#if SHELL_ESCAPE || PIPEC 97128345Stjrpublic void * constant 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; 125240121Sdelphij 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; 136240121Sdelphij 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) 14460786Sps char *s; 14560786Sps{ 146161475Sdelphij LWCHAR prev_ch = 0; 147161475Sdelphij LWCHAR ch; 148161475Sdelphij char *endline = s + strlen(s); 149161475Sdelphij while (*s != '\0') 150161475Sdelphij { 151161475Sdelphij char *ns = s; 152161475Sdelphij ch = step_char(&ns, +1, endline); 153161475Sdelphij while (s < ns) 154161475Sdelphij putchr(*s++); 155161475Sdelphij if (!utf_mode) 156161475Sdelphij { 157161475Sdelphij cmd_col++; 158161475Sdelphij prompt_col++; 159161475Sdelphij } else if (!is_composing_char(ch) && 160161475Sdelphij !is_combining_char(prev_ch, ch)) 161161475Sdelphij { 162161475Sdelphij int width = is_wide_char(ch) ? 2 : 1; 163161475Sdelphij cmd_col += width; 164161475Sdelphij prompt_col += width; 165161475Sdelphij } 166161475Sdelphij prev_ch = ch; 167161475Sdelphij } 16860786Sps} 16960786Sps 17060786Sps/* 17160786Sps * How many characters are in the command buffer? 17260786Sps */ 17360786Sps public int 17460786Spslen_cmdbuf() 17560786Sps{ 176161475Sdelphij char *s = cmdbuf; 177161475Sdelphij char *endline = s + strlen(s); 178161475Sdelphij int len = 0; 179161475Sdelphij 180161475Sdelphij while (*s != '\0') 181161475Sdelphij { 182161475Sdelphij step_char(&s, +1, endline); 183161475Sdelphij len++; 184161475Sdelphij } 185161475Sdelphij return (len); 18660786Sps} 18760786Sps 18860786Sps/* 189161475Sdelphij * Common part of cmd_step_right() and cmd_step_left(). 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; 200161475Sdelphij 201161475Sdelphij if (len == 1) 202161475Sdelphij { 203161475Sdelphij pr = prchar((int) ch); 204161475Sdelphij if (pwidth != NULL || bswidth != NULL) 205161475Sdelphij { 206161475Sdelphij int len = strlen(pr); 207161475Sdelphij if (pwidth != NULL) 208161475Sdelphij *pwidth = len; 209161475Sdelphij if (bswidth != NULL) 210161475Sdelphij *bswidth = len; 211161475Sdelphij } 212161475Sdelphij } else 213161475Sdelphij { 214161475Sdelphij pr = prutfchar(ch); 215161475Sdelphij if (pwidth != NULL || bswidth != NULL) 216161475Sdelphij { 217161475Sdelphij if (is_composing_char(ch)) 218161475Sdelphij { 219161475Sdelphij if (pwidth != NULL) 220161475Sdelphij *pwidth = 0; 221161475Sdelphij if (bswidth != NULL) 222161475Sdelphij *bswidth = 0; 223161475Sdelphij } else if (is_ubin_char(ch)) 224161475Sdelphij { 225161475Sdelphij int len = strlen(pr); 226161475Sdelphij if (pwidth != NULL) 227161475Sdelphij *pwidth = len; 228161475Sdelphij if (bswidth != NULL) 229161475Sdelphij *bswidth = len; 230161475Sdelphij } else 231161475Sdelphij { 232161475Sdelphij LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 233161475Sdelphij if (is_combining_char(prev_ch, ch)) 234161475Sdelphij { 235161475Sdelphij if (pwidth != NULL) 236161475Sdelphij *pwidth = 0; 237161475Sdelphij if (bswidth != NULL) 238161475Sdelphij *bswidth = 0; 239161475Sdelphij } else 240161475Sdelphij { 241161475Sdelphij if (pwidth != NULL) 242161475Sdelphij *pwidth = is_wide_char(ch) 243161475Sdelphij ? 2 244161475Sdelphij : 1; 245161475Sdelphij if (bswidth != NULL) 246161475Sdelphij *bswidth = 1; 247161475Sdelphij } 248161475Sdelphij } 249161475Sdelphij } 250161475Sdelphij } 251161475Sdelphij 252161475Sdelphij return (pr); 253161475Sdelphij} 254161475Sdelphij 255161475Sdelphij/* 256161475Sdelphij * Step a pointer one character right in the command buffer. 257161475Sdelphij */ 258161475Sdelphij static char * 259161475Sdelphijcmd_step_right(pp, pwidth, bswidth) 260161475Sdelphij char **pp; 261161475Sdelphij int *pwidth; 262161475Sdelphij int *bswidth; 263161475Sdelphij{ 264161475Sdelphij char *p = *pp; 265161475Sdelphij LWCHAR ch = step_char(pp, +1, p + strlen(p)); 266161475Sdelphij 267161475Sdelphij return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 268161475Sdelphij} 269161475Sdelphij 270161475Sdelphij/* 271161475Sdelphij * Step a pointer one character left in the command buffer. 272161475Sdelphij */ 273161475Sdelphij static char * 274161475Sdelphijcmd_step_left(pp, pwidth, bswidth) 275161475Sdelphij char **pp; 276161475Sdelphij int *pwidth; 277161475Sdelphij int *bswidth; 278161475Sdelphij{ 279161475Sdelphij char *p = *pp; 280161475Sdelphij LWCHAR ch = step_char(pp, -1, cmdbuf); 281161475Sdelphij 282161475Sdelphij return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 283161475Sdelphij} 284161475Sdelphij 285161475Sdelphij/* 28660786Sps * Repaint the line from cp onwards. 28760786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 28860786Sps */ 28960786Sps static void 29060786Spscmd_repaint(old_cp) 29160786Sps char *old_cp; 29260786Sps{ 29360786Sps /* 29460786Sps * Repaint the line from the current position. 29560786Sps */ 29660786Sps clear_eol(); 297161475Sdelphij while (*cp != '\0') 29860786Sps { 299161475Sdelphij char *np = cp; 300161475Sdelphij int width; 301161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 302161475Sdelphij if (cmd_col + width >= sc_width) 30360786Sps break; 304161475Sdelphij cp = np; 305161475Sdelphij putstr(pr); 306161475Sdelphij cmd_col += width; 30760786Sps } 308161475Sdelphij while (*cp != '\0') 309161475Sdelphij { 310161475Sdelphij char *np = cp; 311161475Sdelphij int width; 312161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 313161475Sdelphij if (width > 0) 314161475Sdelphij break; 315161475Sdelphij cp = np; 316161475Sdelphij putstr(pr); 317161475Sdelphij } 31860786Sps 31960786Sps /* 32060786Sps * Back up the cursor to the correct position. 32160786Sps */ 32260786Sps while (cp > old_cp) 32360786Sps cmd_left(); 32460786Sps} 32560786Sps 32660786Sps/* 32760786Sps * Put the cursor at "home" (just after the prompt), 32860786Sps * and set cp to the corresponding char in cmdbuf. 32960786Sps */ 33060786Sps static void 33160786Spscmd_home() 33260786Sps{ 33360786Sps while (cmd_col > prompt_col) 33460786Sps { 335161475Sdelphij int width, bswidth; 336161475Sdelphij 337161475Sdelphij cmd_step_left(&cp, &width, &bswidth); 338161475Sdelphij while (bswidth-- > 0) 339161475Sdelphij putbs(); 340161475Sdelphij cmd_col -= width; 34160786Sps } 34260786Sps 34360786Sps cp = &cmdbuf[cmd_offset]; 34460786Sps} 34560786Sps 34660786Sps/* 34760786Sps * Shift the cmdbuf display left a half-screen. 34860786Sps */ 34960786Sps static void 35060786Spscmd_lshift() 35160786Sps{ 35260786Sps char *s; 35360786Sps char *save_cp; 35460786Sps int cols; 35560786Sps 35660786Sps /* 35760786Sps * Start at the first displayed char, count how far to the 35860786Sps * right we'd have to move to reach the center of the screen. 35960786Sps */ 36060786Sps s = cmdbuf + cmd_offset; 36160786Sps cols = 0; 36260786Sps while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 363161475Sdelphij { 364161475Sdelphij int width; 365161475Sdelphij cmd_step_right(&s, &width, NULL); 366161475Sdelphij cols += width; 367161475Sdelphij } 368161475Sdelphij while (*s != '\0') 369161475Sdelphij { 370161475Sdelphij int width; 371161475Sdelphij char *ns = s; 372161475Sdelphij cmd_step_right(&ns, &width, NULL); 373161475Sdelphij if (width > 0) 374161475Sdelphij break; 375161475Sdelphij s = ns; 376161475Sdelphij } 37760786Sps 37860786Sps cmd_offset = s - cmdbuf; 37960786Sps save_cp = cp; 38060786Sps cmd_home(); 38160786Sps cmd_repaint(save_cp); 38260786Sps} 38360786Sps 38460786Sps/* 38560786Sps * Shift the cmdbuf display right a half-screen. 38660786Sps */ 38760786Sps static void 38860786Spscmd_rshift() 38960786Sps{ 39060786Sps char *s; 39160786Sps char *save_cp; 39260786Sps int cols; 39360786Sps 39460786Sps /* 39560786Sps * Start at the first displayed char, count how far to the 39660786Sps * left we'd have to move to traverse a half-screen width 39760786Sps * of displayed characters. 39860786Sps */ 39960786Sps s = cmdbuf + cmd_offset; 40060786Sps cols = 0; 40160786Sps while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 40260786Sps { 403161475Sdelphij int width; 404161475Sdelphij cmd_step_left(&s, &width, NULL); 405161475Sdelphij cols += width; 40660786Sps } 40760786Sps 40860786Sps cmd_offset = s - cmdbuf; 40960786Sps save_cp = cp; 41060786Sps cmd_home(); 41160786Sps cmd_repaint(save_cp); 41260786Sps} 41360786Sps 41460786Sps/* 41560786Sps * Move cursor right one character. 41660786Sps */ 41760786Sps static int 41860786Spscmd_right() 41960786Sps{ 420161475Sdelphij char *pr; 421161475Sdelphij char *ncp; 422161475Sdelphij int width; 42360786Sps 42460786Sps if (*cp == '\0') 42560786Sps { 426161475Sdelphij /* Already at the end of the line. */ 42760786Sps return (CC_OK); 42860786Sps } 429161475Sdelphij ncp = cp; 430161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 431161475Sdelphij if (cmd_col + width >= sc_width) 43260786Sps cmd_lshift(); 433161475Sdelphij else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 43460786Sps cmd_lshift(); 435161475Sdelphij cp = ncp; 436161475Sdelphij cmd_col += width; 437161475Sdelphij putstr(pr); 438161475Sdelphij while (*cp != '\0') 439161475Sdelphij { 440161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 441161475Sdelphij if (width > 0) 442161475Sdelphij break; 443161475Sdelphij putstr(pr); 444161475Sdelphij cp = ncp; 445161475Sdelphij } 44660786Sps return (CC_OK); 44760786Sps} 44860786Sps 44960786Sps/* 45060786Sps * Move cursor left one character. 45160786Sps */ 45260786Sps static int 45360786Spscmd_left() 45460786Sps{ 455161475Sdelphij char *ncp; 456161475Sdelphij int width, bswidth; 45760786Sps 45860786Sps if (cp <= cmdbuf) 45960786Sps { 46060786Sps /* Already at the beginning of the line */ 46160786Sps return (CC_OK); 46260786Sps } 463161475Sdelphij ncp = cp; 464161475Sdelphij while (ncp > cmdbuf) 465161475Sdelphij { 466161475Sdelphij cmd_step_left(&ncp, &width, &bswidth); 467161475Sdelphij if (width > 0) 468161475Sdelphij break; 469161475Sdelphij } 470161475Sdelphij if (cmd_col < prompt_col + width) 47160786Sps cmd_rshift(); 472161475Sdelphij cp = ncp; 473161475Sdelphij cmd_col -= width; 474161475Sdelphij while (bswidth-- > 0) 47560786Sps putbs(); 47660786Sps return (CC_OK); 47760786Sps} 47860786Sps 47960786Sps/* 48060786Sps * Insert a char into the command buffer, at the current position. 48160786Sps */ 48260786Sps static int 483161475Sdelphijcmd_ichar(cs, clen) 484161475Sdelphij char *cs; 485161475Sdelphij int clen; 48660786Sps{ 48760786Sps char *s; 48860786Sps 489161475Sdelphij if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 49060786Sps { 491161475Sdelphij /* No room in the command buffer for another char. */ 49260786Sps bell(); 49360786Sps return (CC_ERROR); 49460786Sps } 49560786Sps 49660786Sps /* 497161475Sdelphij * Make room for the new character (shift the tail of the buffer right). 49860786Sps */ 49960786Sps for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 500161475Sdelphij s[clen] = s[0]; 50160786Sps /* 502161475Sdelphij * Insert the character into the buffer. 503161475Sdelphij */ 504161475Sdelphij for (s = cp; s < cp + clen; s++) 505161475Sdelphij *s = *cs++; 506161475Sdelphij /* 50760786Sps * Reprint the tail of the line from the inserted char. 50860786Sps */ 509240121Sdelphij updown_match = -1; 51060786Sps cmd_repaint(cp); 51160786Sps cmd_right(); 51260786Sps return (CC_OK); 51360786Sps} 51460786Sps 51560786Sps/* 51660786Sps * Backspace in the command buffer. 51760786Sps * Delete the char to the left of the cursor. 51860786Sps */ 51960786Sps static int 52060786Spscmd_erase() 52160786Sps{ 52260786Sps register char *s; 523161475Sdelphij int clen; 52460786Sps 52560786Sps if (cp == cmdbuf) 52660786Sps { 52760786Sps /* 52860786Sps * Backspace past beginning of the buffer: 52960786Sps * this usually means abort the command. 53060786Sps */ 53160786Sps return (CC_QUIT); 53260786Sps } 53360786Sps /* 53460786Sps * Move cursor left (to the char being erased). 53560786Sps */ 536161475Sdelphij s = cp; 53760786Sps cmd_left(); 538161475Sdelphij clen = s - cp; 539161475Sdelphij 54060786Sps /* 54160786Sps * Remove the char from the buffer (shift the buffer left). 54260786Sps */ 543161475Sdelphij for (s = cp; ; s++) 544161475Sdelphij { 545161475Sdelphij s[0] = s[clen]; 546161475Sdelphij if (s[0] == '\0') 547161475Sdelphij break; 548161475Sdelphij } 549161475Sdelphij 55060786Sps /* 55160786Sps * Repaint the buffer after the erased char. 55260786Sps */ 553240121Sdelphij updown_match = -1; 55460786Sps cmd_repaint(cp); 55560786Sps 55660786Sps /* 55760786Sps * We say that erasing the entire command string causes us 55860786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 55960786Sps */ 56060786Sps if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 56160786Sps return (CC_QUIT); 56260786Sps return (CC_OK); 56360786Sps} 56460786Sps 56560786Sps/* 56660786Sps * Delete the char under the cursor. 56760786Sps */ 56860786Sps static int 56960786Spscmd_delete() 57060786Sps{ 57160786Sps if (*cp == '\0') 57260786Sps { 573161475Sdelphij /* At end of string; there is no char under the cursor. */ 57460786Sps return (CC_OK); 57560786Sps } 57660786Sps /* 57760786Sps * Move right, then use cmd_erase. 57860786Sps */ 57960786Sps cmd_right(); 58060786Sps cmd_erase(); 58160786Sps return (CC_OK); 58260786Sps} 58360786Sps 58460786Sps/* 58560786Sps * Delete the "word" to the left of the cursor. 58660786Sps */ 58760786Sps static int 58860786Spscmd_werase() 58960786Sps{ 59060786Sps if (cp > cmdbuf && cp[-1] == ' ') 59160786Sps { 59260786Sps /* 59360786Sps * If the char left of cursor is a space, 59460786Sps * erase all the spaces left of cursor (to the first non-space). 59560786Sps */ 59660786Sps while (cp > cmdbuf && cp[-1] == ' ') 59760786Sps (void) cmd_erase(); 59860786Sps } else 59960786Sps { 60060786Sps /* 60160786Sps * If the char left of cursor is not a space, 60260786Sps * erase all the nonspaces left of cursor (the whole "word"). 60360786Sps */ 60460786Sps while (cp > cmdbuf && cp[-1] != ' ') 60560786Sps (void) cmd_erase(); 60660786Sps } 60760786Sps return (CC_OK); 60860786Sps} 60960786Sps 61060786Sps/* 61160786Sps * Delete the "word" under the cursor. 61260786Sps */ 61360786Sps static int 61460786Spscmd_wdelete() 61560786Sps{ 61660786Sps if (*cp == ' ') 61760786Sps { 61860786Sps /* 61960786Sps * If the char under the cursor is a space, 62060786Sps * delete it and all the spaces right of cursor. 62160786Sps */ 62260786Sps while (*cp == ' ') 62360786Sps (void) cmd_delete(); 62460786Sps } else 62560786Sps { 62660786Sps /* 62760786Sps * If the char under the cursor is not a space, 62860786Sps * delete it and all nonspaces right of cursor (the whole word). 62960786Sps */ 63060786Sps while (*cp != ' ' && *cp != '\0') 63160786Sps (void) cmd_delete(); 63260786Sps } 63360786Sps return (CC_OK); 63460786Sps} 63560786Sps 63660786Sps/* 63760786Sps * Delete all chars in the command buffer. 63860786Sps */ 63960786Sps static int 64060786Spscmd_kill() 64160786Sps{ 64260786Sps if (cmdbuf[0] == '\0') 64360786Sps { 644161475Sdelphij /* Buffer is already empty; abort the current command. */ 64560786Sps return (CC_QUIT); 64660786Sps } 64760786Sps cmd_offset = 0; 64860786Sps cmd_home(); 64960786Sps *cp = '\0'; 650240121Sdelphij updown_match = -1; 65160786Sps cmd_repaint(cp); 65260786Sps 65360786Sps /* 65460786Sps * We say that erasing the entire command string causes us 65560786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 65660786Sps */ 65760786Sps if (curr_cmdflags & CF_QUIT_ON_ERASE) 65860786Sps return (CC_QUIT); 65960786Sps return (CC_OK); 66060786Sps} 66160786Sps 66260786Sps/* 66360786Sps * Select an mlist structure to be the current command history. 66460786Sps */ 66560786Sps public void 66660786Spsset_mlist(mlist, cmdflags) 66760786Sps void *mlist; 66860786Sps int cmdflags; 66960786Sps{ 670191930Sdelphij#if CMD_HISTORY 67160786Sps curr_mlist = (struct mlist *) mlist; 67260786Sps curr_cmdflags = cmdflags; 673161475Sdelphij 674161475Sdelphij /* Make sure the next up-arrow moves to the last string in the mlist. */ 675161475Sdelphij if (curr_mlist != NULL) 676161475Sdelphij curr_mlist->curr_mp = curr_mlist; 677191930Sdelphij#endif 67860786Sps} 67960786Sps 68060786Sps#if CMD_HISTORY 68160786Sps/* 68260786Sps * Move up or down in the currently selected command history list. 683240121Sdelphij * Only consider entries whose first updown_match chars are equal to 684240121Sdelphij * cmdbuf's corresponding chars. 68560786Sps */ 68660786Sps static int 68760786Spscmd_updown(action) 68860786Sps int action; 68960786Sps{ 69060786Sps char *s; 691240121Sdelphij struct mlist *ml; 69260786Sps 69360786Sps if (curr_mlist == NULL) 69460786Sps { 69560786Sps /* 69660786Sps * The current command has no history list. 69760786Sps */ 69860786Sps bell(); 69960786Sps return (CC_OK); 70060786Sps } 701240121Sdelphij 702240121Sdelphij if (updown_match < 0) 703240121Sdelphij { 704240121Sdelphij updown_match = cp - cmdbuf; 705240121Sdelphij } 706240121Sdelphij 70760786Sps /* 708240121Sdelphij * Find the next history entry which matches. 70960786Sps */ 710240121Sdelphij for (ml = curr_mlist->curr_mp;;) 711240121Sdelphij { 712240121Sdelphij ml = (action == EC_UP) ? ml->prev : ml->next; 713240121Sdelphij if (ml == curr_mlist) 714240121Sdelphij { 715240121Sdelphij /* 716240121Sdelphij * We reached the end (or beginning) of the list. 717240121Sdelphij */ 718240121Sdelphij break; 719240121Sdelphij } 720240121Sdelphij if (strncmp(cmdbuf, ml->string, updown_match) == 0) 721240121Sdelphij { 722240121Sdelphij /* 723240121Sdelphij * This entry matches; stop here. 724240121Sdelphij * Copy the entry into cmdbuf and echo it on the screen. 725240121Sdelphij */ 726240121Sdelphij curr_mlist->curr_mp = ml; 727240121Sdelphij s = ml->string; 728240121Sdelphij if (s == NULL) 729240121Sdelphij s = ""; 730240121Sdelphij cmd_home(); 731240121Sdelphij clear_eol(); 732251154Sdelphij strcpy(cmdbuf, s); 733240121Sdelphij for (cp = cmdbuf; *cp != '\0'; ) 734240121Sdelphij cmd_right(); 735240121Sdelphij return (CC_OK); 736240121Sdelphij } 737240121Sdelphij } 73860786Sps /* 739240121Sdelphij * We didn't find a history entry that matches. 74060786Sps */ 741240121Sdelphij bell(); 74260786Sps return (CC_OK); 74360786Sps} 74460786Sps#endif 74560786Sps 74660786Sps/* 74760786Sps * Add a string to a history list. 74860786Sps */ 74960786Sps public void 75060786Spscmd_addhist(mlist, cmd) 75160786Sps struct mlist *mlist; 75260786Sps char *cmd; 75360786Sps{ 75460786Sps#if CMD_HISTORY 75560786Sps struct mlist *ml; 75660786Sps 75760786Sps /* 75860786Sps * Don't save a trivial command. 75960786Sps */ 76060786Sps if (strlen(cmd) == 0) 76160786Sps return; 762161475Sdelphij 76360786Sps /* 764161475Sdelphij * Save the command unless it's a duplicate of the 765161475Sdelphij * last command in the history. 76660786Sps */ 767161475Sdelphij ml = mlist->prev; 768161475Sdelphij if (ml == mlist || strcmp(ml->string, cmd) != 0) 76960786Sps { 77060786Sps /* 77160786Sps * Did not find command in history. 77260786Sps * Save the command and put it at the end of the history list. 77360786Sps */ 77460786Sps ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 77560786Sps ml->string = save(cmd); 77660786Sps ml->next = mlist; 77760786Sps ml->prev = mlist->prev; 77860786Sps mlist->prev->next = ml; 77960786Sps mlist->prev = ml; 78060786Sps } 78160786Sps /* 78260786Sps * Point to the cmd just after the just-accepted command. 78360786Sps * Thus, an UPARROW will always retrieve the previous command. 78460786Sps */ 78560786Sps mlist->curr_mp = ml->next; 78660786Sps#endif 78760786Sps} 78860786Sps 78960786Sps/* 79060786Sps * Accept the command in the command buffer. 79160786Sps * Add it to the currently selected history list. 79260786Sps */ 79360786Sps public void 79460786Spscmd_accept() 79560786Sps{ 79660786Sps#if CMD_HISTORY 79760786Sps /* 79860786Sps * Nothing to do if there is no currently selected history list. 79960786Sps */ 80060786Sps if (curr_mlist == NULL) 80160786Sps return; 80260786Sps cmd_addhist(curr_mlist, cmdbuf); 803170256Sdelphij curr_mlist->modified = 1; 80460786Sps#endif 80560786Sps} 80660786Sps 80760786Sps/* 80860786Sps * Try to perform a line-edit function on the command buffer, 80960786Sps * using a specified char as a line-editing command. 81060786Sps * Returns: 81160786Sps * CC_PASS The char does not invoke a line edit function. 81260786Sps * CC_OK Line edit function done. 81360786Sps * CC_QUIT The char requests the current command to be aborted. 81460786Sps */ 81560786Sps static int 81660786Spscmd_edit(c) 81760786Sps int c; 81860786Sps{ 81960786Sps int action; 82060786Sps int flags; 82160786Sps 82260786Sps#if TAB_COMPLETE_FILENAME 82360786Sps#define not_in_completion() in_completion = 0 82460786Sps#else 82560786Sps#define not_in_completion() 82660786Sps#endif 82760786Sps 82860786Sps /* 82960786Sps * See if the char is indeed a line-editing command. 83060786Sps */ 83160786Sps flags = 0; 83260786Sps#if CMD_HISTORY 83360786Sps if (curr_mlist == NULL) 83460786Sps /* 83560786Sps * No current history; don't accept history manipulation cmds. 83660786Sps */ 83760786Sps flags |= EC_NOHISTORY; 83860786Sps#endif 83960786Sps#if TAB_COMPLETE_FILENAME 84060786Sps if (curr_mlist == ml_search) 84160786Sps /* 84260786Sps * In a search command; don't accept file-completion cmds. 84360786Sps */ 84460786Sps flags |= EC_NOCOMPLETE; 84560786Sps#endif 84660786Sps 84760786Sps action = editchar(c, flags); 84860786Sps 84960786Sps switch (action) 85060786Sps { 85160786Sps case EC_RIGHT: 85260786Sps not_in_completion(); 85360786Sps return (cmd_right()); 85460786Sps case EC_LEFT: 85560786Sps not_in_completion(); 85660786Sps return (cmd_left()); 85760786Sps case EC_W_RIGHT: 85860786Sps not_in_completion(); 85960786Sps while (*cp != '\0' && *cp != ' ') 86060786Sps cmd_right(); 86160786Sps while (*cp == ' ') 86260786Sps cmd_right(); 86360786Sps return (CC_OK); 86460786Sps case EC_W_LEFT: 86560786Sps not_in_completion(); 86660786Sps while (cp > cmdbuf && cp[-1] == ' ') 86760786Sps cmd_left(); 86860786Sps while (cp > cmdbuf && cp[-1] != ' ') 86960786Sps cmd_left(); 87060786Sps return (CC_OK); 87160786Sps case EC_HOME: 87260786Sps not_in_completion(); 87360786Sps cmd_offset = 0; 87460786Sps cmd_home(); 87560786Sps cmd_repaint(cp); 87660786Sps return (CC_OK); 87760786Sps case EC_END: 87860786Sps not_in_completion(); 87960786Sps while (*cp != '\0') 88060786Sps cmd_right(); 88160786Sps return (CC_OK); 88260786Sps case EC_INSERT: 88360786Sps not_in_completion(); 88460786Sps return (CC_OK); 88560786Sps case EC_BACKSPACE: 88660786Sps not_in_completion(); 88760786Sps return (cmd_erase()); 88860786Sps case EC_LINEKILL: 88960786Sps not_in_completion(); 89060786Sps return (cmd_kill()); 891221715Sdelphij case EC_ABORT: 892221715Sdelphij not_in_completion(); 893221715Sdelphij (void) cmd_kill(); 894221715Sdelphij return (CC_QUIT); 89560786Sps case EC_W_BACKSPACE: 89660786Sps not_in_completion(); 89760786Sps return (cmd_werase()); 89860786Sps case EC_DELETE: 89960786Sps not_in_completion(); 90060786Sps return (cmd_delete()); 90160786Sps case EC_W_DELETE: 90260786Sps not_in_completion(); 90360786Sps return (cmd_wdelete()); 90460786Sps case EC_LITERAL: 90560786Sps literal = 1; 90660786Sps return (CC_OK); 90760786Sps#if CMD_HISTORY 90860786Sps case EC_UP: 90960786Sps case EC_DOWN: 91060786Sps not_in_completion(); 91160786Sps return (cmd_updown(action)); 91260786Sps#endif 91360786Sps#if TAB_COMPLETE_FILENAME 91460786Sps case EC_F_COMPLETE: 91560786Sps case EC_B_COMPLETE: 91660786Sps case EC_EXPAND: 91760786Sps return (cmd_complete(action)); 91860786Sps#endif 91960786Sps case EC_NOACTION: 92060786Sps return (CC_OK); 92160786Sps default: 92260786Sps not_in_completion(); 92360786Sps return (CC_PASS); 92460786Sps } 92560786Sps} 92660786Sps 92760786Sps#if TAB_COMPLETE_FILENAME 92860786Sps/* 92960786Sps * Insert a string into the command buffer, at the current position. 93060786Sps */ 93160786Sps static int 93260786Spscmd_istr(str) 93360786Sps char *str; 93460786Sps{ 93560786Sps char *s; 93660786Sps int action; 937161475Sdelphij char *endline = str + strlen(str); 93860786Sps 939161475Sdelphij for (s = str; *s != '\0'; ) 94060786Sps { 941161475Sdelphij char *os = s; 942161475Sdelphij step_char(&s, +1, endline); 943161475Sdelphij action = cmd_ichar(os, s - os); 94460786Sps if (action != CC_OK) 94560786Sps { 94660786Sps bell(); 94760786Sps return (action); 94860786Sps } 94960786Sps } 95060786Sps return (CC_OK); 95160786Sps} 95260786Sps 95360786Sps/* 95460786Sps * Find the beginning and end of the "current" word. 95560786Sps * This is the word which the cursor (cp) is inside or at the end of. 95660786Sps * Return pointer to the beginning of the word and put the 95760786Sps * cursor at the end of the word. 95860786Sps */ 95960786Sps static char * 96060786Spsdelimit_word() 96160786Sps{ 96260786Sps char *word; 96360786Sps#if SPACES_IN_FILENAMES 96460786Sps char *p; 965128345Stjr int delim_quoted = 0; 966128345Stjr int meta_quoted = 0; 967128345Stjr char *esc = get_meta_escape(); 968128345Stjr int esclen = strlen(esc); 96960786Sps#endif 97060786Sps 97160786Sps /* 97260786Sps * Move cursor to end of word. 97360786Sps */ 97460786Sps if (*cp != ' ' && *cp != '\0') 97560786Sps { 97660786Sps /* 97760786Sps * Cursor is on a nonspace. 97860786Sps * Move cursor right to the next space. 97960786Sps */ 98060786Sps while (*cp != ' ' && *cp != '\0') 98160786Sps cmd_right(); 98260786Sps } else if (cp > cmdbuf && cp[-1] != ' ') 98360786Sps { 98460786Sps /* 98560786Sps * Cursor is on a space, and char to the left is a nonspace. 98660786Sps * We're already at the end of the word. 98760786Sps */ 98860786Sps ; 989128345Stjr#if 0 99060786Sps } else 99160786Sps { 99260786Sps /* 99360786Sps * Cursor is on a space and char to the left is a space. 99460786Sps * Huh? There's no word here. 99560786Sps */ 99660786Sps return (NULL); 997128345Stjr#endif 99860786Sps } 99960786Sps /* 1000128345Stjr * Find the beginning of the word which the cursor is in. 100160786Sps */ 100260786Sps if (cp == cmdbuf) 100360786Sps return (NULL); 100460786Sps#if SPACES_IN_FILENAMES 100560786Sps /* 100660786Sps * If we have an unbalanced quote (that is, an open quote 100760786Sps * without a corresponding close quote), we return everything 100860786Sps * from the open quote, including spaces. 100960786Sps */ 1010128345Stjr for (word = cmdbuf; word < cp; word++) 1011128345Stjr if (*word != ' ') 1012128345Stjr break; 1013128345Stjr if (word >= cp) 1014128345Stjr return (cp); 101560786Sps for (p = cmdbuf; p < cp; p++) 101660786Sps { 1017128345Stjr if (meta_quoted) 101860786Sps { 1019128345Stjr meta_quoted = 0; 1020128345Stjr } else if (esclen > 0 && p + esclen < cp && 1021128345Stjr strncmp(p, esc, esclen) == 0) 102260786Sps { 1023128345Stjr meta_quoted = 1; 1024128345Stjr p += esclen - 1; 1025128345Stjr } else if (delim_quoted) 1026128345Stjr { 1027128345Stjr if (*p == closequote) 1028128345Stjr delim_quoted = 0; 1029128345Stjr } else /* (!delim_quoted) */ 1030128345Stjr { 1031128345Stjr if (*p == openquote) 1032128345Stjr delim_quoted = 1; 1033128345Stjr else if (*p == ' ') 1034128345Stjr word = p+1; 103560786Sps } 103660786Sps } 103760786Sps#endif 103860786Sps return (word); 103960786Sps} 104060786Sps 104160786Sps/* 104260786Sps * Set things up to enter completion mode. 104360786Sps * Expand the word under the cursor into a list of filenames 104460786Sps * which start with that word, and set tk_text to that list. 104560786Sps */ 104660786Sps static void 104760786Spsinit_compl() 104860786Sps{ 104960786Sps char *word; 105060786Sps char c; 105160786Sps 105260786Sps /* 105360786Sps * Get rid of any previous tk_text. 105460786Sps */ 105560786Sps if (tk_text != NULL) 105660786Sps { 105760786Sps free(tk_text); 105860786Sps tk_text = NULL; 105960786Sps } 106060786Sps /* 106160786Sps * Find the original (uncompleted) word in the command buffer. 106260786Sps */ 106360786Sps word = delimit_word(); 106460786Sps if (word == NULL) 106560786Sps return; 106660786Sps /* 106760786Sps * Set the insertion point to the point in the command buffer 106860786Sps * where the original (uncompleted) word now sits. 106960786Sps */ 107060786Sps tk_ipoint = word; 107160786Sps /* 107260786Sps * Save the original (uncompleted) word 107360786Sps */ 107460786Sps if (tk_original != NULL) 107560786Sps free(tk_original); 107660786Sps tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 107760786Sps strncpy(tk_original, word, cp-word); 107860786Sps /* 107960786Sps * Get the expanded filename. 108060786Sps * This may result in a single filename, or 108160786Sps * a blank-separated list of filenames. 108260786Sps */ 108360786Sps c = *cp; 108460786Sps *cp = '\0'; 1085128345Stjr if (*word != openquote) 1086128345Stjr { 1087128345Stjr tk_text = fcomplete(word); 1088128345Stjr } else 1089128345Stjr { 1090240121Sdelphij#if MSDOS_COMPILER 1091240121Sdelphij char *qword = NULL; 1092240121Sdelphij#else 1093128345Stjr char *qword = shell_quote(word+1); 1094240121Sdelphij#endif 1095128345Stjr if (qword == NULL) 1096128345Stjr tk_text = fcomplete(word+1); 1097128345Stjr else 1098128345Stjr { 1099128345Stjr tk_text = fcomplete(qword); 1100128345Stjr free(qword); 1101128345Stjr } 1102128345Stjr } 110360786Sps *cp = c; 110460786Sps} 110560786Sps 110660786Sps/* 110760786Sps * Return the next word in the current completion list. 110860786Sps */ 110960786Sps static char * 111060786Spsnext_compl(action, prev) 111160786Sps int action; 111260786Sps char *prev; 111360786Sps{ 111460786Sps switch (action) 111560786Sps { 111660786Sps case EC_F_COMPLETE: 111760786Sps return (forw_textlist(&tk_tlist, prev)); 111860786Sps case EC_B_COMPLETE: 111960786Sps return (back_textlist(&tk_tlist, prev)); 112060786Sps } 112160786Sps /* Cannot happen */ 112260786Sps return ("?"); 112360786Sps} 112460786Sps 112560786Sps/* 112660786Sps * Complete the filename before (or under) the cursor. 112760786Sps * cmd_complete may be called multiple times. The global in_completion 112860786Sps * remembers whether this call is the first time (create the list), 112960786Sps * or a subsequent time (step thru the list). 113060786Sps */ 113160786Sps static int 113260786Spscmd_complete(action) 113360786Sps int action; 113460786Sps{ 113560786Sps char *s; 113660786Sps 113760786Sps if (!in_completion || action == EC_EXPAND) 113860786Sps { 113960786Sps /* 114060786Sps * Expand the word under the cursor and 114160786Sps * use the first word in the expansion 114260786Sps * (or the entire expansion if we're doing EC_EXPAND). 114360786Sps */ 114460786Sps init_compl(); 114560786Sps if (tk_text == NULL) 114660786Sps { 114760786Sps bell(); 114860786Sps return (CC_OK); 114960786Sps } 115060786Sps if (action == EC_EXPAND) 115160786Sps { 115260786Sps /* 115360786Sps * Use the whole list. 115460786Sps */ 115560786Sps tk_trial = tk_text; 115660786Sps } else 115760786Sps { 115860786Sps /* 115960786Sps * Use the first filename in the list. 116060786Sps */ 116160786Sps in_completion = 1; 116260786Sps init_textlist(&tk_tlist, tk_text); 116360786Sps tk_trial = next_compl(action, (char*)NULL); 116460786Sps } 116560786Sps } else 116660786Sps { 116760786Sps /* 116860786Sps * We already have a completion list. 116960786Sps * Use the next/previous filename from the list. 117060786Sps */ 117160786Sps tk_trial = next_compl(action, tk_trial); 117260786Sps } 117360786Sps 117460786Sps /* 117560786Sps * Remove the original word, or the previous trial completion. 117660786Sps */ 117760786Sps while (cp > tk_ipoint) 117860786Sps (void) cmd_erase(); 117960786Sps 118060786Sps if (tk_trial == NULL) 118160786Sps { 118260786Sps /* 118360786Sps * There are no more trial completions. 118460786Sps * Insert the original (uncompleted) filename. 118560786Sps */ 118660786Sps in_completion = 0; 118760786Sps if (cmd_istr(tk_original) != CC_OK) 118860786Sps goto fail; 118960786Sps } else 119060786Sps { 119160786Sps /* 119260786Sps * Insert trial completion. 119360786Sps */ 119460786Sps if (cmd_istr(tk_trial) != CC_OK) 119560786Sps goto fail; 119660786Sps /* 119760786Sps * If it is a directory, append a slash. 119860786Sps */ 119960786Sps if (is_dir(tk_trial)) 120060786Sps { 120160786Sps if (cp > cmdbuf && cp[-1] == closequote) 120260786Sps (void) cmd_erase(); 120360786Sps s = lgetenv("LESSSEPARATOR"); 120460786Sps if (s == NULL) 120560786Sps s = PATHNAME_SEP; 120660786Sps if (cmd_istr(s) != CC_OK) 120760786Sps goto fail; 120860786Sps } 120960786Sps } 121060786Sps 121160786Sps return (CC_OK); 121260786Sps 121360786Spsfail: 121460786Sps in_completion = 0; 121560786Sps bell(); 121660786Sps return (CC_OK); 121760786Sps} 121860786Sps 121960786Sps#endif /* TAB_COMPLETE_FILENAME */ 122060786Sps 122160786Sps/* 122260786Sps * Process a single character of a multi-character command, such as 122360786Sps * a number, or the pattern of a search command. 122460786Sps * Returns: 122560786Sps * CC_OK The char was accepted. 122660786Sps * CC_QUIT The char requests the command to be aborted. 122760786Sps * CC_ERROR The char could not be accepted due to an error. 122860786Sps */ 122960786Sps public int 123060786Spscmd_char(c) 123160786Sps int c; 123260786Sps{ 123360786Sps int action; 1234161475Sdelphij int len; 123560786Sps 1236161475Sdelphij if (!utf_mode) 1237161475Sdelphij { 1238161475Sdelphij cmd_mbc_buf[0] = c; 1239161475Sdelphij len = 1; 1240161475Sdelphij } else 1241161475Sdelphij { 1242161475Sdelphij /* Perform strict validation in all possible cases. */ 1243161475Sdelphij if (cmd_mbc_buf_len == 0) 1244161475Sdelphij { 1245161475Sdelphij retry: 1246161475Sdelphij cmd_mbc_buf_index = 1; 1247161475Sdelphij *cmd_mbc_buf = c; 1248161475Sdelphij if (IS_ASCII_OCTET(c)) 1249161475Sdelphij cmd_mbc_buf_len = 1; 1250161475Sdelphij else if (IS_UTF8_LEAD(c)) 1251161475Sdelphij { 1252161475Sdelphij cmd_mbc_buf_len = utf_len(c); 1253161475Sdelphij return (CC_OK); 1254161475Sdelphij } else 1255161475Sdelphij { 1256161475Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 1257161475Sdelphij bell(); 1258161475Sdelphij return (CC_ERROR); 1259161475Sdelphij } 1260161475Sdelphij } else if (IS_UTF8_TRAIL(c)) 1261161475Sdelphij { 1262161475Sdelphij cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1263161475Sdelphij if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1264161475Sdelphij return (CC_OK); 1265161475Sdelphij if (!is_utf8_well_formed(cmd_mbc_buf)) 1266161475Sdelphij { 1267161475Sdelphij /* complete, but not well formed (non-shortest form), sequence */ 1268161475Sdelphij cmd_mbc_buf_len = 0; 1269161475Sdelphij bell(); 1270161475Sdelphij return (CC_ERROR); 1271161475Sdelphij } 1272161475Sdelphij } else 1273161475Sdelphij { 1274161475Sdelphij /* Flush incomplete (truncated) sequence. */ 1275161475Sdelphij cmd_mbc_buf_len = 0; 1276161475Sdelphij bell(); 1277161475Sdelphij /* Handle new char. */ 1278161475Sdelphij goto retry; 1279161475Sdelphij } 1280161475Sdelphij 1281161475Sdelphij len = cmd_mbc_buf_len; 1282161475Sdelphij cmd_mbc_buf_len = 0; 1283161475Sdelphij } 1284161475Sdelphij 128560786Sps if (literal) 128660786Sps { 128760786Sps /* 128860786Sps * Insert the char, even if it is a line-editing char. 128960786Sps */ 129060786Sps literal = 0; 1291161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 129260786Sps } 129360786Sps 129460786Sps /* 1295161475Sdelphij * See if it is a line-editing character. 129660786Sps */ 1297161475Sdelphij if (in_mca() && len == 1) 129860786Sps { 129960786Sps action = cmd_edit(c); 130060786Sps switch (action) 130160786Sps { 130260786Sps case CC_OK: 130360786Sps case CC_QUIT: 130460786Sps return (action); 130560786Sps case CC_PASS: 130660786Sps break; 130760786Sps } 130860786Sps } 130960786Sps 131060786Sps /* 131160786Sps * Insert the char into the command buffer. 131260786Sps */ 1313161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 131460786Sps} 131560786Sps 131660786Sps/* 131760786Sps * Return the number currently in the command buffer. 131860786Sps */ 1319128345Stjr public LINENUM 1320170256Sdelphijcmd_int(frac) 1321170256Sdelphij long *frac; 132260786Sps{ 1323170256Sdelphij char *p; 1324128345Stjr LINENUM n = 0; 1325170256Sdelphij int err; 1326128345Stjr 1327170256Sdelphij for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1328170256Sdelphij n = (n * 10) + (*p - '0'); 1329170256Sdelphij *frac = 0; 1330170256Sdelphij if (*p++ == '.') 1331170256Sdelphij { 1332170256Sdelphij *frac = getfraction(&p, NULL, &err); 1333170256Sdelphij /* {{ do something if err is set? }} */ 1334170256Sdelphij } 1335128345Stjr return (n); 133660786Sps} 133760786Sps 133860786Sps/* 133960786Sps * Return a pointer to the command buffer. 134060786Sps */ 134160786Sps public char * 134260786Spsget_cmdbuf() 134360786Sps{ 134460786Sps return (cmdbuf); 134560786Sps} 1346161475Sdelphij 1347191930Sdelphij#if CMD_HISTORY 1348170256Sdelphij/* 1349170256Sdelphij * Return the last (most recent) string in the current command history. 1350170256Sdelphij */ 1351170256Sdelphij public char * 1352170256Sdelphijcmd_lastpattern() 1353170256Sdelphij{ 1354170256Sdelphij if (curr_mlist == NULL) 1355170256Sdelphij return (NULL); 1356170256Sdelphij return (curr_mlist->curr_mp->prev->string); 1357170256Sdelphij} 1358191930Sdelphij#endif 1359170256Sdelphij 1360161475Sdelphij#if CMD_HISTORY 1361161475Sdelphij/* 1362161475Sdelphij * Get the name of the history file. 1363161475Sdelphij */ 1364161475Sdelphij static char * 1365161475Sdelphijhistfile_name() 1366161475Sdelphij{ 1367161475Sdelphij char *home; 1368161475Sdelphij char *name; 1369161475Sdelphij int len; 1370161475Sdelphij 1371161475Sdelphij /* See if filename is explicitly specified by $LESSHISTFILE. */ 1372161475Sdelphij name = lgetenv("LESSHISTFILE"); 1373161475Sdelphij if (name != NULL && *name != '\0') 1374161475Sdelphij { 1375170256Sdelphij if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1376161475Sdelphij /* $LESSHISTFILE == "-" means don't use a history file. */ 1377161475Sdelphij return (NULL); 1378161475Sdelphij return (save(name)); 1379161475Sdelphij } 1380161475Sdelphij 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 } 1391161475Sdelphij len = 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#endif /* CMD_HISTORY */ 1397161475Sdelphij 1398161475Sdelphij/* 1399161475Sdelphij * Initialize history from a .lesshist file. 1400161475Sdelphij */ 1401161475Sdelphij public void 1402161475Sdelphijinit_cmdhist() 1403161475Sdelphij{ 1404161475Sdelphij#if CMD_HISTORY 1405161475Sdelphij struct mlist *ml = NULL; 1406161475Sdelphij char line[CMDBUF_SIZE]; 1407161475Sdelphij char *filename; 1408161475Sdelphij FILE *f; 1409161475Sdelphij char *p; 1410161475Sdelphij 1411161475Sdelphij filename = histfile_name(); 1412161475Sdelphij if (filename == NULL) 1413161475Sdelphij return; 1414161475Sdelphij f = fopen(filename, "r"); 1415161475Sdelphij free(filename); 1416161475Sdelphij if (f == NULL) 1417161475Sdelphij return; 1418161475Sdelphij if (fgets(line, sizeof(line), f) == NULL || 1419161475Sdelphij strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1420161475Sdelphij { 1421161475Sdelphij fclose(f); 1422161475Sdelphij return; 1423161475Sdelphij } 1424161475Sdelphij while (fgets(line, sizeof(line), f) != NULL) 1425161475Sdelphij { 1426161475Sdelphij for (p = line; *p != '\0'; p++) 1427161475Sdelphij { 1428161475Sdelphij if (*p == '\n' || *p == '\r') 1429161475Sdelphij { 1430161475Sdelphij *p = '\0'; 1431161475Sdelphij break; 1432161475Sdelphij } 1433161475Sdelphij } 1434161475Sdelphij if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1435161475Sdelphij ml = &mlist_search; 1436170964Sdelphij else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1437170964Sdelphij { 1438161475Sdelphij#if SHELL_ESCAPE || PIPEC 1439161475Sdelphij ml = &mlist_shell; 1440170964Sdelphij#else 1441170964Sdelphij ml = NULL; 1442161475Sdelphij#endif 1443170964Sdelphij } else if (*line == '"') 1444161475Sdelphij { 1445161475Sdelphij if (ml != NULL) 1446161475Sdelphij cmd_addhist(ml, line+1); 1447161475Sdelphij } 1448161475Sdelphij } 1449161475Sdelphij fclose(f); 1450161475Sdelphij#endif /* CMD_HISTORY */ 1451161475Sdelphij} 1452161475Sdelphij 1453161475Sdelphij/* 1454161475Sdelphij * 1455161475Sdelphij */ 1456161475Sdelphij#if CMD_HISTORY 1457161475Sdelphij static void 1458161475Sdelphijsave_mlist(ml, f) 1459161475Sdelphij struct mlist *ml; 1460161475Sdelphij FILE *f; 1461161475Sdelphij{ 1462161475Sdelphij int histsize = 0; 1463161475Sdelphij int n; 1464161475Sdelphij char *s; 1465161475Sdelphij 1466161475Sdelphij s = lgetenv("LESSHISTSIZE"); 1467161475Sdelphij if (s != NULL) 1468161475Sdelphij histsize = atoi(s); 1469161475Sdelphij if (histsize == 0) 1470161475Sdelphij histsize = 100; 1471161475Sdelphij 1472161475Sdelphij ml = ml->prev; 1473161475Sdelphij for (n = 0; n < histsize; n++) 1474161475Sdelphij { 1475161475Sdelphij if (ml->string == NULL) 1476161475Sdelphij break; 1477161475Sdelphij ml = ml->prev; 1478161475Sdelphij } 1479161475Sdelphij for (ml = ml->next; ml->string != NULL; ml = ml->next) 1480161475Sdelphij fprintf(f, "\"%s\n", ml->string); 1481161475Sdelphij} 1482161475Sdelphij#endif /* CMD_HISTORY */ 1483161475Sdelphij 1484161475Sdelphij/* 1485161475Sdelphij * 1486161475Sdelphij */ 1487161475Sdelphij public void 1488161475Sdelphijsave_cmdhist() 1489161475Sdelphij{ 1490161475Sdelphij#if CMD_HISTORY 1491161475Sdelphij char *filename; 1492161475Sdelphij FILE *f; 1493170964Sdelphij int modified = 0; 1494161475Sdelphij 1495170964Sdelphij if (mlist_search.modified) 1496170964Sdelphij modified = 1; 1497170964Sdelphij#if SHELL_ESCAPE || PIPEC 1498170964Sdelphij if (mlist_shell.modified) 1499170964Sdelphij modified = 1; 1500170964Sdelphij#endif 1501170964Sdelphij if (!modified) 1502170256Sdelphij return; 1503240121Sdelphij filename = histfile_name(); 1504240121Sdelphij if (filename == NULL) 1505240121Sdelphij return; 1506161475Sdelphij f = fopen(filename, "w"); 1507161475Sdelphij free(filename); 1508161475Sdelphij if (f == NULL) 1509161475Sdelphij return; 1510161475Sdelphij#if HAVE_FCHMOD 1511191930Sdelphij{ 1512161475Sdelphij /* Make history file readable only by owner. */ 1513191930Sdelphij int do_chmod = 1; 1514191930Sdelphij#if HAVE_STAT 1515191930Sdelphij struct stat statbuf; 1516191930Sdelphij int r = fstat(fileno(f), &statbuf); 1517191930Sdelphij if (r < 0 || !S_ISREG(statbuf.st_mode)) 1518191930Sdelphij /* Don't chmod if not a regular file. */ 1519191930Sdelphij do_chmod = 0; 1520161475Sdelphij#endif 1521191930Sdelphij if (do_chmod) 1522191930Sdelphij fchmod(fileno(f), 0600); 1523191930Sdelphij} 1524191930Sdelphij#endif 1525161475Sdelphij 1526161475Sdelphij fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1527161475Sdelphij 1528161475Sdelphij fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1529161475Sdelphij save_mlist(&mlist_search, f); 1530161475Sdelphij 1531161475Sdelphij#if SHELL_ESCAPE || PIPEC 1532161475Sdelphij fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1533161475Sdelphij save_mlist(&mlist_shell, f); 1534161475Sdelphij#endif 1535161475Sdelphij 1536161475Sdelphij fclose(f); 1537161475Sdelphij#endif /* CMD_HISTORY */ 1538161475Sdelphij} 1539