cmdbuf.c revision 221715
160786Sps/* 2221715Sdelphij * Copyright (C) 1984-2011 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 * 760786Sps * For more information about less, or for information on how to 860786Sps * contact the author, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Functions which manipulate the command buffer. 1460786Sps * Used only by command() and related functions. 1560786Sps */ 1660786Sps 1760786Sps#include "less.h" 1860786Sps#include "cmd.h" 19161475Sdelphij#include "charset.h" 20161475Sdelphij#if HAVE_STAT 21161475Sdelphij#include <sys/stat.h> 22161475Sdelphij#endif 2360786Sps 2460786Spsextern int sc_width; 25161475Sdelphijextern int utf_mode; 2660786Sps 2760786Spsstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 2860786Spsstatic int cmd_col; /* Current column of the cursor */ 2960786Spsstatic int prompt_col; /* Column of cursor just after prompt */ 3060786Spsstatic char *cp; /* Pointer into cmdbuf */ 3160786Spsstatic int cmd_offset; /* Index into cmdbuf of first displayed char */ 3260786Spsstatic int literal; /* Next input char should not be interpreted */ 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; 12560786Sps} 12660786Sps 12760786Sps/* 128170256Sdelphij * Clear command line. 12960786Sps */ 13060786Sps public void 13160786Spsclear_cmd() 13260786Sps{ 13360786Sps cmd_col = prompt_col = 0; 134161475Sdelphij cmd_mbc_buf_len = 0; 13560786Sps} 13660786Sps 13760786Sps/* 13860786Sps * Display a string, usually as a prompt for input into the command buffer. 13960786Sps */ 14060786Sps public void 14160786Spscmd_putstr(s) 14260786Sps char *s; 14360786Sps{ 144161475Sdelphij LWCHAR prev_ch = 0; 145161475Sdelphij LWCHAR ch; 146161475Sdelphij char *endline = s + strlen(s); 147161475Sdelphij while (*s != '\0') 148161475Sdelphij { 149161475Sdelphij char *ns = s; 150161475Sdelphij ch = step_char(&ns, +1, endline); 151161475Sdelphij while (s < ns) 152161475Sdelphij putchr(*s++); 153161475Sdelphij if (!utf_mode) 154161475Sdelphij { 155161475Sdelphij cmd_col++; 156161475Sdelphij prompt_col++; 157161475Sdelphij } else if (!is_composing_char(ch) && 158161475Sdelphij !is_combining_char(prev_ch, ch)) 159161475Sdelphij { 160161475Sdelphij int width = is_wide_char(ch) ? 2 : 1; 161161475Sdelphij cmd_col += width; 162161475Sdelphij prompt_col += width; 163161475Sdelphij } 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(). 188161475Sdelphij */ 189161475Sdelphij static char * 190161475Sdelphijcmd_step_common(p, ch, len, pwidth, bswidth) 191161475Sdelphij char *p; 192161475Sdelphij LWCHAR ch; 193161475Sdelphij int len; 194161475Sdelphij int *pwidth; 195161475Sdelphij int *bswidth; 196161475Sdelphij{ 197161475Sdelphij char *pr; 198161475Sdelphij 199161475Sdelphij if (len == 1) 200161475Sdelphij { 201161475Sdelphij pr = prchar((int) ch); 202161475Sdelphij if (pwidth != NULL || bswidth != NULL) 203161475Sdelphij { 204161475Sdelphij int len = strlen(pr); 205161475Sdelphij if (pwidth != NULL) 206161475Sdelphij *pwidth = len; 207161475Sdelphij if (bswidth != NULL) 208161475Sdelphij *bswidth = len; 209161475Sdelphij } 210161475Sdelphij } else 211161475Sdelphij { 212161475Sdelphij pr = prutfchar(ch); 213161475Sdelphij if (pwidth != NULL || bswidth != NULL) 214161475Sdelphij { 215161475Sdelphij if (is_composing_char(ch)) 216161475Sdelphij { 217161475Sdelphij if (pwidth != NULL) 218161475Sdelphij *pwidth = 0; 219161475Sdelphij if (bswidth != NULL) 220161475Sdelphij *bswidth = 0; 221161475Sdelphij } else if (is_ubin_char(ch)) 222161475Sdelphij { 223161475Sdelphij int len = strlen(pr); 224161475Sdelphij if (pwidth != NULL) 225161475Sdelphij *pwidth = len; 226161475Sdelphij if (bswidth != NULL) 227161475Sdelphij *bswidth = len; 228161475Sdelphij } else 229161475Sdelphij { 230161475Sdelphij LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 231161475Sdelphij if (is_combining_char(prev_ch, ch)) 232161475Sdelphij { 233161475Sdelphij if (pwidth != NULL) 234161475Sdelphij *pwidth = 0; 235161475Sdelphij if (bswidth != NULL) 236161475Sdelphij *bswidth = 0; 237161475Sdelphij } else 238161475Sdelphij { 239161475Sdelphij if (pwidth != NULL) 240161475Sdelphij *pwidth = is_wide_char(ch) 241161475Sdelphij ? 2 242161475Sdelphij : 1; 243161475Sdelphij if (bswidth != NULL) 244161475Sdelphij *bswidth = 1; 245161475Sdelphij } 246161475Sdelphij } 247161475Sdelphij } 248161475Sdelphij } 249161475Sdelphij 250161475Sdelphij return (pr); 251161475Sdelphij} 252161475Sdelphij 253161475Sdelphij/* 254161475Sdelphij * Step a pointer one character right in the command buffer. 255161475Sdelphij */ 256161475Sdelphij static char * 257161475Sdelphijcmd_step_right(pp, pwidth, bswidth) 258161475Sdelphij char **pp; 259161475Sdelphij int *pwidth; 260161475Sdelphij int *bswidth; 261161475Sdelphij{ 262161475Sdelphij char *p = *pp; 263161475Sdelphij LWCHAR ch = step_char(pp, +1, p + strlen(p)); 264161475Sdelphij 265161475Sdelphij return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 266161475Sdelphij} 267161475Sdelphij 268161475Sdelphij/* 269161475Sdelphij * Step a pointer one character left in the command buffer. 270161475Sdelphij */ 271161475Sdelphij static char * 272161475Sdelphijcmd_step_left(pp, pwidth, bswidth) 273161475Sdelphij char **pp; 274161475Sdelphij int *pwidth; 275161475Sdelphij int *bswidth; 276161475Sdelphij{ 277161475Sdelphij char *p = *pp; 278161475Sdelphij LWCHAR ch = step_char(pp, -1, cmdbuf); 279161475Sdelphij 280161475Sdelphij return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 281161475Sdelphij} 282161475Sdelphij 283161475Sdelphij/* 28460786Sps * Repaint the line from cp onwards. 28560786Sps * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 28660786Sps */ 28760786Sps static void 28860786Spscmd_repaint(old_cp) 28960786Sps char *old_cp; 29060786Sps{ 29160786Sps /* 29260786Sps * Repaint the line from the current position. 29360786Sps */ 29460786Sps clear_eol(); 295161475Sdelphij while (*cp != '\0') 29660786Sps { 297161475Sdelphij char *np = cp; 298161475Sdelphij int width; 299161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 300161475Sdelphij if (cmd_col + width >= sc_width) 30160786Sps break; 302161475Sdelphij cp = np; 303161475Sdelphij putstr(pr); 304161475Sdelphij cmd_col += width; 30560786Sps } 306161475Sdelphij while (*cp != '\0') 307161475Sdelphij { 308161475Sdelphij char *np = cp; 309161475Sdelphij int width; 310161475Sdelphij char *pr = cmd_step_right(&np, &width, NULL); 311161475Sdelphij if (width > 0) 312161475Sdelphij break; 313161475Sdelphij cp = np; 314161475Sdelphij putstr(pr); 315161475Sdelphij } 31660786Sps 31760786Sps /* 31860786Sps * Back up the cursor to the correct position. 31960786Sps */ 32060786Sps while (cp > old_cp) 32160786Sps cmd_left(); 32260786Sps} 32360786Sps 32460786Sps/* 32560786Sps * Put the cursor at "home" (just after the prompt), 32660786Sps * and set cp to the corresponding char in cmdbuf. 32760786Sps */ 32860786Sps static void 32960786Spscmd_home() 33060786Sps{ 33160786Sps while (cmd_col > prompt_col) 33260786Sps { 333161475Sdelphij int width, bswidth; 334161475Sdelphij 335161475Sdelphij cmd_step_left(&cp, &width, &bswidth); 336161475Sdelphij while (bswidth-- > 0) 337161475Sdelphij putbs(); 338161475Sdelphij cmd_col -= width; 33960786Sps } 34060786Sps 34160786Sps cp = &cmdbuf[cmd_offset]; 34260786Sps} 34360786Sps 34460786Sps/* 34560786Sps * Shift the cmdbuf display left a half-screen. 34660786Sps */ 34760786Sps static void 34860786Spscmd_lshift() 34960786Sps{ 35060786Sps char *s; 35160786Sps char *save_cp; 35260786Sps int cols; 35360786Sps 35460786Sps /* 35560786Sps * Start at the first displayed char, count how far to the 35660786Sps * right we'd have to move to reach the center of the screen. 35760786Sps */ 35860786Sps s = cmdbuf + cmd_offset; 35960786Sps cols = 0; 36060786Sps while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 361161475Sdelphij { 362161475Sdelphij int width; 363161475Sdelphij cmd_step_right(&s, &width, NULL); 364161475Sdelphij cols += width; 365161475Sdelphij } 366161475Sdelphij while (*s != '\0') 367161475Sdelphij { 368161475Sdelphij int width; 369161475Sdelphij char *ns = s; 370161475Sdelphij cmd_step_right(&ns, &width, NULL); 371161475Sdelphij if (width > 0) 372161475Sdelphij break; 373161475Sdelphij s = ns; 374161475Sdelphij } 37560786Sps 37660786Sps cmd_offset = s - cmdbuf; 37760786Sps save_cp = cp; 37860786Sps cmd_home(); 37960786Sps cmd_repaint(save_cp); 38060786Sps} 38160786Sps 38260786Sps/* 38360786Sps * Shift the cmdbuf display right a half-screen. 38460786Sps */ 38560786Sps static void 38660786Spscmd_rshift() 38760786Sps{ 38860786Sps char *s; 38960786Sps char *save_cp; 39060786Sps int cols; 39160786Sps 39260786Sps /* 39360786Sps * Start at the first displayed char, count how far to the 39460786Sps * left we'd have to move to traverse a half-screen width 39560786Sps * of displayed characters. 39660786Sps */ 39760786Sps s = cmdbuf + cmd_offset; 39860786Sps cols = 0; 39960786Sps while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 40060786Sps { 401161475Sdelphij int width; 402161475Sdelphij cmd_step_left(&s, &width, NULL); 403161475Sdelphij cols += width; 40460786Sps } 40560786Sps 40660786Sps cmd_offset = s - cmdbuf; 40760786Sps save_cp = cp; 40860786Sps cmd_home(); 40960786Sps cmd_repaint(save_cp); 41060786Sps} 41160786Sps 41260786Sps/* 41360786Sps * Move cursor right one character. 41460786Sps */ 41560786Sps static int 41660786Spscmd_right() 41760786Sps{ 418161475Sdelphij char *pr; 419161475Sdelphij char *ncp; 420161475Sdelphij int width; 42160786Sps 42260786Sps if (*cp == '\0') 42360786Sps { 424161475Sdelphij /* Already at the end of the line. */ 42560786Sps return (CC_OK); 42660786Sps } 427161475Sdelphij ncp = cp; 428161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 429161475Sdelphij if (cmd_col + width >= sc_width) 43060786Sps cmd_lshift(); 431161475Sdelphij else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 43260786Sps cmd_lshift(); 433161475Sdelphij cp = ncp; 434161475Sdelphij cmd_col += width; 435161475Sdelphij putstr(pr); 436161475Sdelphij while (*cp != '\0') 437161475Sdelphij { 438161475Sdelphij pr = cmd_step_right(&ncp, &width, NULL); 439161475Sdelphij if (width > 0) 440161475Sdelphij break; 441161475Sdelphij putstr(pr); 442161475Sdelphij cp = ncp; 443161475Sdelphij } 44460786Sps return (CC_OK); 44560786Sps} 44660786Sps 44760786Sps/* 44860786Sps * Move cursor left one character. 44960786Sps */ 45060786Sps static int 45160786Spscmd_left() 45260786Sps{ 453161475Sdelphij char *ncp; 454161475Sdelphij int width, bswidth; 45560786Sps 45660786Sps if (cp <= cmdbuf) 45760786Sps { 45860786Sps /* Already at the beginning of the line */ 45960786Sps return (CC_OK); 46060786Sps } 461161475Sdelphij ncp = cp; 462161475Sdelphij while (ncp > cmdbuf) 463161475Sdelphij { 464161475Sdelphij cmd_step_left(&ncp, &width, &bswidth); 465161475Sdelphij if (width > 0) 466161475Sdelphij break; 467161475Sdelphij } 468161475Sdelphij if (cmd_col < prompt_col + width) 46960786Sps cmd_rshift(); 470161475Sdelphij cp = ncp; 471161475Sdelphij cmd_col -= width; 472161475Sdelphij while (bswidth-- > 0) 47360786Sps putbs(); 47460786Sps return (CC_OK); 47560786Sps} 47660786Sps 47760786Sps/* 47860786Sps * Insert a char into the command buffer, at the current position. 47960786Sps */ 48060786Sps static int 481161475Sdelphijcmd_ichar(cs, clen) 482161475Sdelphij char *cs; 483161475Sdelphij int clen; 48460786Sps{ 48560786Sps char *s; 48660786Sps 487161475Sdelphij if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 48860786Sps { 489161475Sdelphij /* No room in the command buffer for another char. */ 49060786Sps bell(); 49160786Sps return (CC_ERROR); 49260786Sps } 49360786Sps 49460786Sps /* 495161475Sdelphij * Make room for the new character (shift the tail of the buffer right). 49660786Sps */ 49760786Sps for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 498161475Sdelphij s[clen] = s[0]; 49960786Sps /* 500161475Sdelphij * Insert the character into the buffer. 501161475Sdelphij */ 502161475Sdelphij for (s = cp; s < cp + clen; s++) 503161475Sdelphij *s = *cs++; 504161475Sdelphij /* 50560786Sps * Reprint the tail of the line from the inserted char. 50660786Sps */ 50760786Sps cmd_repaint(cp); 50860786Sps cmd_right(); 50960786Sps return (CC_OK); 51060786Sps} 51160786Sps 51260786Sps/* 51360786Sps * Backspace in the command buffer. 51460786Sps * Delete the char to the left of the cursor. 51560786Sps */ 51660786Sps static int 51760786Spscmd_erase() 51860786Sps{ 51960786Sps register char *s; 520161475Sdelphij int clen; 52160786Sps 52260786Sps if (cp == cmdbuf) 52360786Sps { 52460786Sps /* 52560786Sps * Backspace past beginning of the buffer: 52660786Sps * this usually means abort the command. 52760786Sps */ 52860786Sps return (CC_QUIT); 52960786Sps } 53060786Sps /* 53160786Sps * Move cursor left (to the char being erased). 53260786Sps */ 533161475Sdelphij s = cp; 53460786Sps cmd_left(); 535161475Sdelphij clen = s - cp; 536161475Sdelphij 53760786Sps /* 53860786Sps * Remove the char from the buffer (shift the buffer left). 53960786Sps */ 540161475Sdelphij for (s = cp; ; s++) 541161475Sdelphij { 542161475Sdelphij s[0] = s[clen]; 543161475Sdelphij if (s[0] == '\0') 544161475Sdelphij break; 545161475Sdelphij } 546161475Sdelphij 54760786Sps /* 54860786Sps * Repaint the buffer after the erased char. 54960786Sps */ 55060786Sps cmd_repaint(cp); 55160786Sps 55260786Sps /* 55360786Sps * We say that erasing the entire command string causes us 55460786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 55560786Sps */ 55660786Sps if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 55760786Sps return (CC_QUIT); 55860786Sps return (CC_OK); 55960786Sps} 56060786Sps 56160786Sps/* 56260786Sps * Delete the char under the cursor. 56360786Sps */ 56460786Sps static int 56560786Spscmd_delete() 56660786Sps{ 56760786Sps if (*cp == '\0') 56860786Sps { 569161475Sdelphij /* At end of string; there is no char under the cursor. */ 57060786Sps return (CC_OK); 57160786Sps } 57260786Sps /* 57360786Sps * Move right, then use cmd_erase. 57460786Sps */ 57560786Sps cmd_right(); 57660786Sps cmd_erase(); 57760786Sps return (CC_OK); 57860786Sps} 57960786Sps 58060786Sps/* 58160786Sps * Delete the "word" to the left of the cursor. 58260786Sps */ 58360786Sps static int 58460786Spscmd_werase() 58560786Sps{ 58660786Sps if (cp > cmdbuf && cp[-1] == ' ') 58760786Sps { 58860786Sps /* 58960786Sps * If the char left of cursor is a space, 59060786Sps * erase all the spaces left of cursor (to the first non-space). 59160786Sps */ 59260786Sps while (cp > cmdbuf && cp[-1] == ' ') 59360786Sps (void) cmd_erase(); 59460786Sps } else 59560786Sps { 59660786Sps /* 59760786Sps * If the char left of cursor is not a space, 59860786Sps * erase all the nonspaces left of cursor (the whole "word"). 59960786Sps */ 60060786Sps while (cp > cmdbuf && cp[-1] != ' ') 60160786Sps (void) cmd_erase(); 60260786Sps } 60360786Sps return (CC_OK); 60460786Sps} 60560786Sps 60660786Sps/* 60760786Sps * Delete the "word" under the cursor. 60860786Sps */ 60960786Sps static int 61060786Spscmd_wdelete() 61160786Sps{ 61260786Sps if (*cp == ' ') 61360786Sps { 61460786Sps /* 61560786Sps * If the char under the cursor is a space, 61660786Sps * delete it and all the spaces right of cursor. 61760786Sps */ 61860786Sps while (*cp == ' ') 61960786Sps (void) cmd_delete(); 62060786Sps } else 62160786Sps { 62260786Sps /* 62360786Sps * If the char under the cursor is not a space, 62460786Sps * delete it and all nonspaces right of cursor (the whole word). 62560786Sps */ 62660786Sps while (*cp != ' ' && *cp != '\0') 62760786Sps (void) cmd_delete(); 62860786Sps } 62960786Sps return (CC_OK); 63060786Sps} 63160786Sps 63260786Sps/* 63360786Sps * Delete all chars in the command buffer. 63460786Sps */ 63560786Sps static int 63660786Spscmd_kill() 63760786Sps{ 63860786Sps if (cmdbuf[0] == '\0') 63960786Sps { 640161475Sdelphij /* Buffer is already empty; abort the current command. */ 64160786Sps return (CC_QUIT); 64260786Sps } 64360786Sps cmd_offset = 0; 64460786Sps cmd_home(); 64560786Sps *cp = '\0'; 64660786Sps cmd_repaint(cp); 64760786Sps 64860786Sps /* 64960786Sps * We say that erasing the entire command string causes us 65060786Sps * to abort the current command, if CF_QUIT_ON_ERASE is set. 65160786Sps */ 65260786Sps if (curr_cmdflags & CF_QUIT_ON_ERASE) 65360786Sps return (CC_QUIT); 65460786Sps return (CC_OK); 65560786Sps} 65660786Sps 65760786Sps/* 65860786Sps * Select an mlist structure to be the current command history. 65960786Sps */ 66060786Sps public void 66160786Spsset_mlist(mlist, cmdflags) 66260786Sps void *mlist; 66360786Sps int cmdflags; 66460786Sps{ 665191930Sdelphij#if CMD_HISTORY 66660786Sps curr_mlist = (struct mlist *) mlist; 66760786Sps curr_cmdflags = cmdflags; 668161475Sdelphij 669161475Sdelphij /* Make sure the next up-arrow moves to the last string in the mlist. */ 670161475Sdelphij if (curr_mlist != NULL) 671161475Sdelphij curr_mlist->curr_mp = curr_mlist; 672191930Sdelphij#endif 67360786Sps} 67460786Sps 67560786Sps#if CMD_HISTORY 67660786Sps/* 67760786Sps * Move up or down in the currently selected command history list. 67860786Sps */ 67960786Sps static int 68060786Spscmd_updown(action) 68160786Sps int action; 68260786Sps{ 68360786Sps char *s; 68460786Sps 68560786Sps if (curr_mlist == NULL) 68660786Sps { 68760786Sps /* 68860786Sps * The current command has no history list. 68960786Sps */ 69060786Sps bell(); 69160786Sps return (CC_OK); 69260786Sps } 69360786Sps cmd_home(); 69460786Sps clear_eol(); 69560786Sps /* 69660786Sps * Move curr_mp to the next/prev entry. 69760786Sps */ 69860786Sps if (action == EC_UP) 69960786Sps curr_mlist->curr_mp = curr_mlist->curr_mp->prev; 70060786Sps else 70160786Sps curr_mlist->curr_mp = curr_mlist->curr_mp->next; 70260786Sps /* 70360786Sps * Copy the entry into cmdbuf and echo it on the screen. 70460786Sps */ 70560786Sps s = curr_mlist->curr_mp->string; 70660786Sps if (s == NULL) 70760786Sps s = ""; 708161475Sdelphij strcpy(cmdbuf, s); 709161475Sdelphij for (cp = cmdbuf; *cp != '\0'; ) 71060786Sps cmd_right(); 71160786Sps return (CC_OK); 71260786Sps} 71360786Sps#endif 71460786Sps 71560786Sps/* 71660786Sps * Add a string to a history list. 71760786Sps */ 71860786Sps public void 71960786Spscmd_addhist(mlist, cmd) 72060786Sps struct mlist *mlist; 72160786Sps char *cmd; 72260786Sps{ 72360786Sps#if CMD_HISTORY 72460786Sps struct mlist *ml; 72560786Sps 72660786Sps /* 72760786Sps * Don't save a trivial command. 72860786Sps */ 72960786Sps if (strlen(cmd) == 0) 73060786Sps return; 731161475Sdelphij 73260786Sps /* 733161475Sdelphij * Save the command unless it's a duplicate of the 734161475Sdelphij * last command in the history. 73560786Sps */ 736161475Sdelphij ml = mlist->prev; 737161475Sdelphij if (ml == mlist || strcmp(ml->string, cmd) != 0) 73860786Sps { 73960786Sps /* 74060786Sps * Did not find command in history. 74160786Sps * Save the command and put it at the end of the history list. 74260786Sps */ 74360786Sps ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 74460786Sps ml->string = save(cmd); 74560786Sps ml->next = mlist; 74660786Sps ml->prev = mlist->prev; 74760786Sps mlist->prev->next = ml; 74860786Sps mlist->prev = ml; 74960786Sps } 75060786Sps /* 75160786Sps * Point to the cmd just after the just-accepted command. 75260786Sps * Thus, an UPARROW will always retrieve the previous command. 75360786Sps */ 75460786Sps mlist->curr_mp = ml->next; 75560786Sps#endif 75660786Sps} 75760786Sps 75860786Sps/* 75960786Sps * Accept the command in the command buffer. 76060786Sps * Add it to the currently selected history list. 76160786Sps */ 76260786Sps public void 76360786Spscmd_accept() 76460786Sps{ 76560786Sps#if CMD_HISTORY 76660786Sps /* 76760786Sps * Nothing to do if there is no currently selected history list. 76860786Sps */ 76960786Sps if (curr_mlist == NULL) 77060786Sps return; 77160786Sps cmd_addhist(curr_mlist, cmdbuf); 772170256Sdelphij curr_mlist->modified = 1; 77360786Sps#endif 77460786Sps} 77560786Sps 77660786Sps/* 77760786Sps * Try to perform a line-edit function on the command buffer, 77860786Sps * using a specified char as a line-editing command. 77960786Sps * Returns: 78060786Sps * CC_PASS The char does not invoke a line edit function. 78160786Sps * CC_OK Line edit function done. 78260786Sps * CC_QUIT The char requests the current command to be aborted. 78360786Sps */ 78460786Sps static int 78560786Spscmd_edit(c) 78660786Sps int c; 78760786Sps{ 78860786Sps int action; 78960786Sps int flags; 79060786Sps 79160786Sps#if TAB_COMPLETE_FILENAME 79260786Sps#define not_in_completion() in_completion = 0 79360786Sps#else 79460786Sps#define not_in_completion() 79560786Sps#endif 79660786Sps 79760786Sps /* 79860786Sps * See if the char is indeed a line-editing command. 79960786Sps */ 80060786Sps flags = 0; 80160786Sps#if CMD_HISTORY 80260786Sps if (curr_mlist == NULL) 80360786Sps /* 80460786Sps * No current history; don't accept history manipulation cmds. 80560786Sps */ 80660786Sps flags |= EC_NOHISTORY; 80760786Sps#endif 80860786Sps#if TAB_COMPLETE_FILENAME 80960786Sps if (curr_mlist == ml_search) 81060786Sps /* 81160786Sps * In a search command; don't accept file-completion cmds. 81260786Sps */ 81360786Sps flags |= EC_NOCOMPLETE; 81460786Sps#endif 81560786Sps 81660786Sps action = editchar(c, flags); 81760786Sps 81860786Sps switch (action) 81960786Sps { 82060786Sps case EC_RIGHT: 82160786Sps not_in_completion(); 82260786Sps return (cmd_right()); 82360786Sps case EC_LEFT: 82460786Sps not_in_completion(); 82560786Sps return (cmd_left()); 82660786Sps case EC_W_RIGHT: 82760786Sps not_in_completion(); 82860786Sps while (*cp != '\0' && *cp != ' ') 82960786Sps cmd_right(); 83060786Sps while (*cp == ' ') 83160786Sps cmd_right(); 83260786Sps return (CC_OK); 83360786Sps case EC_W_LEFT: 83460786Sps not_in_completion(); 83560786Sps while (cp > cmdbuf && cp[-1] == ' ') 83660786Sps cmd_left(); 83760786Sps while (cp > cmdbuf && cp[-1] != ' ') 83860786Sps cmd_left(); 83960786Sps return (CC_OK); 84060786Sps case EC_HOME: 84160786Sps not_in_completion(); 84260786Sps cmd_offset = 0; 84360786Sps cmd_home(); 84460786Sps cmd_repaint(cp); 84560786Sps return (CC_OK); 84660786Sps case EC_END: 84760786Sps not_in_completion(); 84860786Sps while (*cp != '\0') 84960786Sps cmd_right(); 85060786Sps return (CC_OK); 85160786Sps case EC_INSERT: 85260786Sps not_in_completion(); 85360786Sps return (CC_OK); 85460786Sps case EC_BACKSPACE: 85560786Sps not_in_completion(); 85660786Sps return (cmd_erase()); 85760786Sps case EC_LINEKILL: 85860786Sps not_in_completion(); 85960786Sps return (cmd_kill()); 860221715Sdelphij case EC_ABORT: 861221715Sdelphij not_in_completion(); 862221715Sdelphij (void) cmd_kill(); 863221715Sdelphij return (CC_QUIT); 86460786Sps case EC_W_BACKSPACE: 86560786Sps not_in_completion(); 86660786Sps return (cmd_werase()); 86760786Sps case EC_DELETE: 86860786Sps not_in_completion(); 86960786Sps return (cmd_delete()); 87060786Sps case EC_W_DELETE: 87160786Sps not_in_completion(); 87260786Sps return (cmd_wdelete()); 87360786Sps case EC_LITERAL: 87460786Sps literal = 1; 87560786Sps return (CC_OK); 87660786Sps#if CMD_HISTORY 87760786Sps case EC_UP: 87860786Sps case EC_DOWN: 87960786Sps not_in_completion(); 88060786Sps return (cmd_updown(action)); 88160786Sps#endif 88260786Sps#if TAB_COMPLETE_FILENAME 88360786Sps case EC_F_COMPLETE: 88460786Sps case EC_B_COMPLETE: 88560786Sps case EC_EXPAND: 88660786Sps return (cmd_complete(action)); 88760786Sps#endif 88860786Sps case EC_NOACTION: 88960786Sps return (CC_OK); 89060786Sps default: 89160786Sps not_in_completion(); 89260786Sps return (CC_PASS); 89360786Sps } 89460786Sps} 89560786Sps 89660786Sps#if TAB_COMPLETE_FILENAME 89760786Sps/* 89860786Sps * Insert a string into the command buffer, at the current position. 89960786Sps */ 90060786Sps static int 90160786Spscmd_istr(str) 90260786Sps char *str; 90360786Sps{ 90460786Sps char *s; 90560786Sps int action; 906161475Sdelphij char *endline = str + strlen(str); 90760786Sps 908161475Sdelphij for (s = str; *s != '\0'; ) 90960786Sps { 910161475Sdelphij char *os = s; 911161475Sdelphij step_char(&s, +1, endline); 912161475Sdelphij action = cmd_ichar(os, s - os); 91360786Sps if (action != CC_OK) 91460786Sps { 91560786Sps bell(); 91660786Sps return (action); 91760786Sps } 91860786Sps } 91960786Sps return (CC_OK); 92060786Sps} 92160786Sps 92260786Sps/* 92360786Sps * Find the beginning and end of the "current" word. 92460786Sps * This is the word which the cursor (cp) is inside or at the end of. 92560786Sps * Return pointer to the beginning of the word and put the 92660786Sps * cursor at the end of the word. 92760786Sps */ 92860786Sps static char * 92960786Spsdelimit_word() 93060786Sps{ 93160786Sps char *word; 93260786Sps#if SPACES_IN_FILENAMES 93360786Sps char *p; 934128345Stjr int delim_quoted = 0; 935128345Stjr int meta_quoted = 0; 936128345Stjr char *esc = get_meta_escape(); 937128345Stjr int esclen = strlen(esc); 93860786Sps#endif 93960786Sps 94060786Sps /* 94160786Sps * Move cursor to end of word. 94260786Sps */ 94360786Sps if (*cp != ' ' && *cp != '\0') 94460786Sps { 94560786Sps /* 94660786Sps * Cursor is on a nonspace. 94760786Sps * Move cursor right to the next space. 94860786Sps */ 94960786Sps while (*cp != ' ' && *cp != '\0') 95060786Sps cmd_right(); 95160786Sps } else if (cp > cmdbuf && cp[-1] != ' ') 95260786Sps { 95360786Sps /* 95460786Sps * Cursor is on a space, and char to the left is a nonspace. 95560786Sps * We're already at the end of the word. 95660786Sps */ 95760786Sps ; 958128345Stjr#if 0 95960786Sps } else 96060786Sps { 96160786Sps /* 96260786Sps * Cursor is on a space and char to the left is a space. 96360786Sps * Huh? There's no word here. 96460786Sps */ 96560786Sps return (NULL); 966128345Stjr#endif 96760786Sps } 96860786Sps /* 969128345Stjr * Find the beginning of the word which the cursor is in. 97060786Sps */ 97160786Sps if (cp == cmdbuf) 97260786Sps return (NULL); 97360786Sps#if SPACES_IN_FILENAMES 97460786Sps /* 97560786Sps * If we have an unbalanced quote (that is, an open quote 97660786Sps * without a corresponding close quote), we return everything 97760786Sps * from the open quote, including spaces. 97860786Sps */ 979128345Stjr for (word = cmdbuf; word < cp; word++) 980128345Stjr if (*word != ' ') 981128345Stjr break; 982128345Stjr if (word >= cp) 983128345Stjr return (cp); 98460786Sps for (p = cmdbuf; p < cp; p++) 98560786Sps { 986128345Stjr if (meta_quoted) 98760786Sps { 988128345Stjr meta_quoted = 0; 989128345Stjr } else if (esclen > 0 && p + esclen < cp && 990128345Stjr strncmp(p, esc, esclen) == 0) 99160786Sps { 992128345Stjr meta_quoted = 1; 993128345Stjr p += esclen - 1; 994128345Stjr } else if (delim_quoted) 995128345Stjr { 996128345Stjr if (*p == closequote) 997128345Stjr delim_quoted = 0; 998128345Stjr } else /* (!delim_quoted) */ 999128345Stjr { 1000128345Stjr if (*p == openquote) 1001128345Stjr delim_quoted = 1; 1002128345Stjr else if (*p == ' ') 1003128345Stjr word = p+1; 100460786Sps } 100560786Sps } 100660786Sps#endif 100760786Sps return (word); 100860786Sps} 100960786Sps 101060786Sps/* 101160786Sps * Set things up to enter completion mode. 101260786Sps * Expand the word under the cursor into a list of filenames 101360786Sps * which start with that word, and set tk_text to that list. 101460786Sps */ 101560786Sps static void 101660786Spsinit_compl() 101760786Sps{ 101860786Sps char *word; 101960786Sps char c; 102060786Sps 102160786Sps /* 102260786Sps * Get rid of any previous tk_text. 102360786Sps */ 102460786Sps if (tk_text != NULL) 102560786Sps { 102660786Sps free(tk_text); 102760786Sps tk_text = NULL; 102860786Sps } 102960786Sps /* 103060786Sps * Find the original (uncompleted) word in the command buffer. 103160786Sps */ 103260786Sps word = delimit_word(); 103360786Sps if (word == NULL) 103460786Sps return; 103560786Sps /* 103660786Sps * Set the insertion point to the point in the command buffer 103760786Sps * where the original (uncompleted) word now sits. 103860786Sps */ 103960786Sps tk_ipoint = word; 104060786Sps /* 104160786Sps * Save the original (uncompleted) word 104260786Sps */ 104360786Sps if (tk_original != NULL) 104460786Sps free(tk_original); 104560786Sps tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 104660786Sps strncpy(tk_original, word, cp-word); 104760786Sps /* 104860786Sps * Get the expanded filename. 104960786Sps * This may result in a single filename, or 105060786Sps * a blank-separated list of filenames. 105160786Sps */ 105260786Sps c = *cp; 105360786Sps *cp = '\0'; 1054128345Stjr if (*word != openquote) 1055128345Stjr { 1056128345Stjr tk_text = fcomplete(word); 1057128345Stjr } else 1058128345Stjr { 1059128345Stjr char *qword = shell_quote(word+1); 1060128345Stjr if (qword == NULL) 1061128345Stjr tk_text = fcomplete(word+1); 1062128345Stjr else 1063128345Stjr { 1064128345Stjr tk_text = fcomplete(qword); 1065128345Stjr free(qword); 1066128345Stjr } 1067128345Stjr } 106860786Sps *cp = c; 106960786Sps} 107060786Sps 107160786Sps/* 107260786Sps * Return the next word in the current completion list. 107360786Sps */ 107460786Sps static char * 107560786Spsnext_compl(action, prev) 107660786Sps int action; 107760786Sps char *prev; 107860786Sps{ 107960786Sps switch (action) 108060786Sps { 108160786Sps case EC_F_COMPLETE: 108260786Sps return (forw_textlist(&tk_tlist, prev)); 108360786Sps case EC_B_COMPLETE: 108460786Sps return (back_textlist(&tk_tlist, prev)); 108560786Sps } 108660786Sps /* Cannot happen */ 108760786Sps return ("?"); 108860786Sps} 108960786Sps 109060786Sps/* 109160786Sps * Complete the filename before (or under) the cursor. 109260786Sps * cmd_complete may be called multiple times. The global in_completion 109360786Sps * remembers whether this call is the first time (create the list), 109460786Sps * or a subsequent time (step thru the list). 109560786Sps */ 109660786Sps static int 109760786Spscmd_complete(action) 109860786Sps int action; 109960786Sps{ 110060786Sps char *s; 110160786Sps 110260786Sps if (!in_completion || action == EC_EXPAND) 110360786Sps { 110460786Sps /* 110560786Sps * Expand the word under the cursor and 110660786Sps * use the first word in the expansion 110760786Sps * (or the entire expansion if we're doing EC_EXPAND). 110860786Sps */ 110960786Sps init_compl(); 111060786Sps if (tk_text == NULL) 111160786Sps { 111260786Sps bell(); 111360786Sps return (CC_OK); 111460786Sps } 111560786Sps if (action == EC_EXPAND) 111660786Sps { 111760786Sps /* 111860786Sps * Use the whole list. 111960786Sps */ 112060786Sps tk_trial = tk_text; 112160786Sps } else 112260786Sps { 112360786Sps /* 112460786Sps * Use the first filename in the list. 112560786Sps */ 112660786Sps in_completion = 1; 112760786Sps init_textlist(&tk_tlist, tk_text); 112860786Sps tk_trial = next_compl(action, (char*)NULL); 112960786Sps } 113060786Sps } else 113160786Sps { 113260786Sps /* 113360786Sps * We already have a completion list. 113460786Sps * Use the next/previous filename from the list. 113560786Sps */ 113660786Sps tk_trial = next_compl(action, tk_trial); 113760786Sps } 113860786Sps 113960786Sps /* 114060786Sps * Remove the original word, or the previous trial completion. 114160786Sps */ 114260786Sps while (cp > tk_ipoint) 114360786Sps (void) cmd_erase(); 114460786Sps 114560786Sps if (tk_trial == NULL) 114660786Sps { 114760786Sps /* 114860786Sps * There are no more trial completions. 114960786Sps * Insert the original (uncompleted) filename. 115060786Sps */ 115160786Sps in_completion = 0; 115260786Sps if (cmd_istr(tk_original) != CC_OK) 115360786Sps goto fail; 115460786Sps } else 115560786Sps { 115660786Sps /* 115760786Sps * Insert trial completion. 115860786Sps */ 115960786Sps if (cmd_istr(tk_trial) != CC_OK) 116060786Sps goto fail; 116160786Sps /* 116260786Sps * If it is a directory, append a slash. 116360786Sps */ 116460786Sps if (is_dir(tk_trial)) 116560786Sps { 116660786Sps if (cp > cmdbuf && cp[-1] == closequote) 116760786Sps (void) cmd_erase(); 116860786Sps s = lgetenv("LESSSEPARATOR"); 116960786Sps if (s == NULL) 117060786Sps s = PATHNAME_SEP; 117160786Sps if (cmd_istr(s) != CC_OK) 117260786Sps goto fail; 117360786Sps } 117460786Sps } 117560786Sps 117660786Sps return (CC_OK); 117760786Sps 117860786Spsfail: 117960786Sps in_completion = 0; 118060786Sps bell(); 118160786Sps return (CC_OK); 118260786Sps} 118360786Sps 118460786Sps#endif /* TAB_COMPLETE_FILENAME */ 118560786Sps 118660786Sps/* 118760786Sps * Process a single character of a multi-character command, such as 118860786Sps * a number, or the pattern of a search command. 118960786Sps * Returns: 119060786Sps * CC_OK The char was accepted. 119160786Sps * CC_QUIT The char requests the command to be aborted. 119260786Sps * CC_ERROR The char could not be accepted due to an error. 119360786Sps */ 119460786Sps public int 119560786Spscmd_char(c) 119660786Sps int c; 119760786Sps{ 119860786Sps int action; 1199161475Sdelphij int len; 120060786Sps 1201161475Sdelphij if (!utf_mode) 1202161475Sdelphij { 1203161475Sdelphij cmd_mbc_buf[0] = c; 1204161475Sdelphij len = 1; 1205161475Sdelphij } else 1206161475Sdelphij { 1207161475Sdelphij /* Perform strict validation in all possible cases. */ 1208161475Sdelphij if (cmd_mbc_buf_len == 0) 1209161475Sdelphij { 1210161475Sdelphij retry: 1211161475Sdelphij cmd_mbc_buf_index = 1; 1212161475Sdelphij *cmd_mbc_buf = c; 1213161475Sdelphij if (IS_ASCII_OCTET(c)) 1214161475Sdelphij cmd_mbc_buf_len = 1; 1215161475Sdelphij else if (IS_UTF8_LEAD(c)) 1216161475Sdelphij { 1217161475Sdelphij cmd_mbc_buf_len = utf_len(c); 1218161475Sdelphij return (CC_OK); 1219161475Sdelphij } else 1220161475Sdelphij { 1221161475Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 1222161475Sdelphij bell(); 1223161475Sdelphij return (CC_ERROR); 1224161475Sdelphij } 1225161475Sdelphij } else if (IS_UTF8_TRAIL(c)) 1226161475Sdelphij { 1227161475Sdelphij cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1228161475Sdelphij if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1229161475Sdelphij return (CC_OK); 1230161475Sdelphij if (!is_utf8_well_formed(cmd_mbc_buf)) 1231161475Sdelphij { 1232161475Sdelphij /* complete, but not well formed (non-shortest form), sequence */ 1233161475Sdelphij cmd_mbc_buf_len = 0; 1234161475Sdelphij bell(); 1235161475Sdelphij return (CC_ERROR); 1236161475Sdelphij } 1237161475Sdelphij } else 1238161475Sdelphij { 1239161475Sdelphij /* Flush incomplete (truncated) sequence. */ 1240161475Sdelphij cmd_mbc_buf_len = 0; 1241161475Sdelphij bell(); 1242161475Sdelphij /* Handle new char. */ 1243161475Sdelphij goto retry; 1244161475Sdelphij } 1245161475Sdelphij 1246161475Sdelphij len = cmd_mbc_buf_len; 1247161475Sdelphij cmd_mbc_buf_len = 0; 1248161475Sdelphij } 1249161475Sdelphij 125060786Sps if (literal) 125160786Sps { 125260786Sps /* 125360786Sps * Insert the char, even if it is a line-editing char. 125460786Sps */ 125560786Sps literal = 0; 1256161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 125760786Sps } 125860786Sps 125960786Sps /* 1260161475Sdelphij * See if it is a line-editing character. 126160786Sps */ 1262161475Sdelphij if (in_mca() && len == 1) 126360786Sps { 126460786Sps action = cmd_edit(c); 126560786Sps switch (action) 126660786Sps { 126760786Sps case CC_OK: 126860786Sps case CC_QUIT: 126960786Sps return (action); 127060786Sps case CC_PASS: 127160786Sps break; 127260786Sps } 127360786Sps } 127460786Sps 127560786Sps /* 127660786Sps * Insert the char into the command buffer. 127760786Sps */ 1278161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 127960786Sps} 128060786Sps 128160786Sps/* 128260786Sps * Return the number currently in the command buffer. 128360786Sps */ 1284128345Stjr public LINENUM 1285170256Sdelphijcmd_int(frac) 1286170256Sdelphij long *frac; 128760786Sps{ 1288170256Sdelphij char *p; 1289128345Stjr LINENUM n = 0; 1290170256Sdelphij int err; 1291128345Stjr 1292170256Sdelphij for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1293170256Sdelphij n = (n * 10) + (*p - '0'); 1294170256Sdelphij *frac = 0; 1295170256Sdelphij if (*p++ == '.') 1296170256Sdelphij { 1297170256Sdelphij *frac = getfraction(&p, NULL, &err); 1298170256Sdelphij /* {{ do something if err is set? }} */ 1299170256Sdelphij } 1300128345Stjr return (n); 130160786Sps} 130260786Sps 130360786Sps/* 130460786Sps * Return a pointer to the command buffer. 130560786Sps */ 130660786Sps public char * 130760786Spsget_cmdbuf() 130860786Sps{ 130960786Sps return (cmdbuf); 131060786Sps} 1311161475Sdelphij 1312191930Sdelphij#if CMD_HISTORY 1313170256Sdelphij/* 1314170256Sdelphij * Return the last (most recent) string in the current command history. 1315170256Sdelphij */ 1316170256Sdelphij public char * 1317170256Sdelphijcmd_lastpattern() 1318170256Sdelphij{ 1319170256Sdelphij if (curr_mlist == NULL) 1320170256Sdelphij return (NULL); 1321170256Sdelphij return (curr_mlist->curr_mp->prev->string); 1322170256Sdelphij} 1323191930Sdelphij#endif 1324170256Sdelphij 1325161475Sdelphij#if CMD_HISTORY 1326161475Sdelphij/* 1327161475Sdelphij * Get the name of the history file. 1328161475Sdelphij */ 1329161475Sdelphij static char * 1330161475Sdelphijhistfile_name() 1331161475Sdelphij{ 1332161475Sdelphij char *home; 1333161475Sdelphij char *name; 1334161475Sdelphij int len; 1335161475Sdelphij 1336161475Sdelphij /* See if filename is explicitly specified by $LESSHISTFILE. */ 1337161475Sdelphij name = lgetenv("LESSHISTFILE"); 1338161475Sdelphij if (name != NULL && *name != '\0') 1339161475Sdelphij { 1340170256Sdelphij if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1341161475Sdelphij /* $LESSHISTFILE == "-" means don't use a history file. */ 1342161475Sdelphij return (NULL); 1343161475Sdelphij return (save(name)); 1344161475Sdelphij } 1345161475Sdelphij 1346161475Sdelphij /* Otherwise, file is in $HOME. */ 1347161475Sdelphij home = lgetenv("HOME"); 1348161475Sdelphij if (home == NULL || *home == '\0') 1349161475Sdelphij { 1350161475Sdelphij#if OS2 1351161475Sdelphij home = lgetenv("INIT"); 1352161475Sdelphij if (home == NULL || *home == '\0') 1353161475Sdelphij#endif 1354161475Sdelphij return (NULL); 1355161475Sdelphij } 1356161475Sdelphij len = strlen(home) + strlen(LESSHISTFILE) + 2; 1357161475Sdelphij name = (char *) ecalloc(len, sizeof(char)); 1358161475Sdelphij SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1359161475Sdelphij return (name); 1360161475Sdelphij} 1361161475Sdelphij#endif /* CMD_HISTORY */ 1362161475Sdelphij 1363161475Sdelphij/* 1364161475Sdelphij * Initialize history from a .lesshist file. 1365161475Sdelphij */ 1366161475Sdelphij public void 1367161475Sdelphijinit_cmdhist() 1368161475Sdelphij{ 1369161475Sdelphij#if CMD_HISTORY 1370161475Sdelphij struct mlist *ml = NULL; 1371161475Sdelphij char line[CMDBUF_SIZE]; 1372161475Sdelphij char *filename; 1373161475Sdelphij FILE *f; 1374161475Sdelphij char *p; 1375161475Sdelphij 1376161475Sdelphij filename = histfile_name(); 1377161475Sdelphij if (filename == NULL) 1378161475Sdelphij return; 1379161475Sdelphij f = fopen(filename, "r"); 1380161475Sdelphij free(filename); 1381161475Sdelphij if (f == NULL) 1382161475Sdelphij return; 1383161475Sdelphij if (fgets(line, sizeof(line), f) == NULL || 1384161475Sdelphij strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1385161475Sdelphij { 1386161475Sdelphij fclose(f); 1387161475Sdelphij return; 1388161475Sdelphij } 1389161475Sdelphij while (fgets(line, sizeof(line), f) != NULL) 1390161475Sdelphij { 1391161475Sdelphij for (p = line; *p != '\0'; p++) 1392161475Sdelphij { 1393161475Sdelphij if (*p == '\n' || *p == '\r') 1394161475Sdelphij { 1395161475Sdelphij *p = '\0'; 1396161475Sdelphij break; 1397161475Sdelphij } 1398161475Sdelphij } 1399161475Sdelphij if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1400161475Sdelphij ml = &mlist_search; 1401170964Sdelphij else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1402170964Sdelphij { 1403161475Sdelphij#if SHELL_ESCAPE || PIPEC 1404161475Sdelphij ml = &mlist_shell; 1405170964Sdelphij#else 1406170964Sdelphij ml = NULL; 1407161475Sdelphij#endif 1408170964Sdelphij } else if (*line == '"') 1409161475Sdelphij { 1410161475Sdelphij if (ml != NULL) 1411161475Sdelphij cmd_addhist(ml, line+1); 1412161475Sdelphij } 1413161475Sdelphij } 1414161475Sdelphij fclose(f); 1415161475Sdelphij#endif /* CMD_HISTORY */ 1416161475Sdelphij} 1417161475Sdelphij 1418161475Sdelphij/* 1419161475Sdelphij * 1420161475Sdelphij */ 1421161475Sdelphij#if CMD_HISTORY 1422161475Sdelphij static void 1423161475Sdelphijsave_mlist(ml, f) 1424161475Sdelphij struct mlist *ml; 1425161475Sdelphij FILE *f; 1426161475Sdelphij{ 1427161475Sdelphij int histsize = 0; 1428161475Sdelphij int n; 1429161475Sdelphij char *s; 1430161475Sdelphij 1431161475Sdelphij s = lgetenv("LESSHISTSIZE"); 1432161475Sdelphij if (s != NULL) 1433161475Sdelphij histsize = atoi(s); 1434161475Sdelphij if (histsize == 0) 1435161475Sdelphij histsize = 100; 1436161475Sdelphij 1437161475Sdelphij ml = ml->prev; 1438161475Sdelphij for (n = 0; n < histsize; n++) 1439161475Sdelphij { 1440161475Sdelphij if (ml->string == NULL) 1441161475Sdelphij break; 1442161475Sdelphij ml = ml->prev; 1443161475Sdelphij } 1444161475Sdelphij for (ml = ml->next; ml->string != NULL; ml = ml->next) 1445161475Sdelphij fprintf(f, "\"%s\n", ml->string); 1446161475Sdelphij} 1447161475Sdelphij#endif /* CMD_HISTORY */ 1448161475Sdelphij 1449161475Sdelphij/* 1450161475Sdelphij * 1451161475Sdelphij */ 1452161475Sdelphij public void 1453161475Sdelphijsave_cmdhist() 1454161475Sdelphij{ 1455161475Sdelphij#if CMD_HISTORY 1456161475Sdelphij char *filename; 1457161475Sdelphij FILE *f; 1458170964Sdelphij int modified = 0; 1459161475Sdelphij 1460161475Sdelphij filename = histfile_name(); 1461161475Sdelphij if (filename == NULL) 1462161475Sdelphij return; 1463170964Sdelphij if (mlist_search.modified) 1464170964Sdelphij modified = 1; 1465170964Sdelphij#if SHELL_ESCAPE || PIPEC 1466170964Sdelphij if (mlist_shell.modified) 1467170964Sdelphij modified = 1; 1468170964Sdelphij#endif 1469170964Sdelphij if (!modified) 1470170256Sdelphij return; 1471161475Sdelphij f = fopen(filename, "w"); 1472161475Sdelphij free(filename); 1473161475Sdelphij if (f == NULL) 1474161475Sdelphij return; 1475161475Sdelphij#if HAVE_FCHMOD 1476191930Sdelphij{ 1477161475Sdelphij /* Make history file readable only by owner. */ 1478191930Sdelphij int do_chmod = 1; 1479191930Sdelphij#if HAVE_STAT 1480191930Sdelphij struct stat statbuf; 1481191930Sdelphij int r = fstat(fileno(f), &statbuf); 1482191930Sdelphij if (r < 0 || !S_ISREG(statbuf.st_mode)) 1483191930Sdelphij /* Don't chmod if not a regular file. */ 1484191930Sdelphij do_chmod = 0; 1485161475Sdelphij#endif 1486191930Sdelphij if (do_chmod) 1487191930Sdelphij fchmod(fileno(f), 0600); 1488191930Sdelphij} 1489191930Sdelphij#endif 1490161475Sdelphij 1491161475Sdelphij fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1492161475Sdelphij 1493161475Sdelphij fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1494161475Sdelphij save_mlist(&mlist_search, f); 1495161475Sdelphij 1496161475Sdelphij#if SHELL_ESCAPE || PIPEC 1497161475Sdelphij fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1498161475Sdelphij save_mlist(&mlist_shell, f); 1499161475Sdelphij#endif 1500161475Sdelphij 1501161475Sdelphij fclose(f); 1502161475Sdelphij#endif /* CMD_HISTORY */ 1503161475Sdelphij} 1504