cmdbuf.c revision 191930
160786Sps/* 2191930Sdelphij * Copyright (C) 1984-2008 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()); 86060786Sps case EC_W_BACKSPACE: 86160786Sps not_in_completion(); 86260786Sps return (cmd_werase()); 86360786Sps case EC_DELETE: 86460786Sps not_in_completion(); 86560786Sps return (cmd_delete()); 86660786Sps case EC_W_DELETE: 86760786Sps not_in_completion(); 86860786Sps return (cmd_wdelete()); 86960786Sps case EC_LITERAL: 87060786Sps literal = 1; 87160786Sps return (CC_OK); 87260786Sps#if CMD_HISTORY 87360786Sps case EC_UP: 87460786Sps case EC_DOWN: 87560786Sps not_in_completion(); 87660786Sps return (cmd_updown(action)); 87760786Sps#endif 87860786Sps#if TAB_COMPLETE_FILENAME 87960786Sps case EC_F_COMPLETE: 88060786Sps case EC_B_COMPLETE: 88160786Sps case EC_EXPAND: 88260786Sps return (cmd_complete(action)); 88360786Sps#endif 88460786Sps case EC_NOACTION: 88560786Sps return (CC_OK); 88660786Sps default: 88760786Sps not_in_completion(); 88860786Sps return (CC_PASS); 88960786Sps } 89060786Sps} 89160786Sps 89260786Sps#if TAB_COMPLETE_FILENAME 89360786Sps/* 89460786Sps * Insert a string into the command buffer, at the current position. 89560786Sps */ 89660786Sps static int 89760786Spscmd_istr(str) 89860786Sps char *str; 89960786Sps{ 90060786Sps char *s; 90160786Sps int action; 902161475Sdelphij char *endline = str + strlen(str); 90360786Sps 904161475Sdelphij for (s = str; *s != '\0'; ) 90560786Sps { 906161475Sdelphij char *os = s; 907161475Sdelphij step_char(&s, +1, endline); 908161475Sdelphij action = cmd_ichar(os, s - os); 90960786Sps if (action != CC_OK) 91060786Sps { 91160786Sps bell(); 91260786Sps return (action); 91360786Sps } 91460786Sps } 91560786Sps return (CC_OK); 91660786Sps} 91760786Sps 91860786Sps/* 91960786Sps * Find the beginning and end of the "current" word. 92060786Sps * This is the word which the cursor (cp) is inside or at the end of. 92160786Sps * Return pointer to the beginning of the word and put the 92260786Sps * cursor at the end of the word. 92360786Sps */ 92460786Sps static char * 92560786Spsdelimit_word() 92660786Sps{ 92760786Sps char *word; 92860786Sps#if SPACES_IN_FILENAMES 92960786Sps char *p; 930128345Stjr int delim_quoted = 0; 931128345Stjr int meta_quoted = 0; 932128345Stjr char *esc = get_meta_escape(); 933128345Stjr int esclen = strlen(esc); 93460786Sps#endif 93560786Sps 93660786Sps /* 93760786Sps * Move cursor to end of word. 93860786Sps */ 93960786Sps if (*cp != ' ' && *cp != '\0') 94060786Sps { 94160786Sps /* 94260786Sps * Cursor is on a nonspace. 94360786Sps * Move cursor right to the next space. 94460786Sps */ 94560786Sps while (*cp != ' ' && *cp != '\0') 94660786Sps cmd_right(); 94760786Sps } else if (cp > cmdbuf && cp[-1] != ' ') 94860786Sps { 94960786Sps /* 95060786Sps * Cursor is on a space, and char to the left is a nonspace. 95160786Sps * We're already at the end of the word. 95260786Sps */ 95360786Sps ; 954128345Stjr#if 0 95560786Sps } else 95660786Sps { 95760786Sps /* 95860786Sps * Cursor is on a space and char to the left is a space. 95960786Sps * Huh? There's no word here. 96060786Sps */ 96160786Sps return (NULL); 962128345Stjr#endif 96360786Sps } 96460786Sps /* 965128345Stjr * Find the beginning of the word which the cursor is in. 96660786Sps */ 96760786Sps if (cp == cmdbuf) 96860786Sps return (NULL); 96960786Sps#if SPACES_IN_FILENAMES 97060786Sps /* 97160786Sps * If we have an unbalanced quote (that is, an open quote 97260786Sps * without a corresponding close quote), we return everything 97360786Sps * from the open quote, including spaces. 97460786Sps */ 975128345Stjr for (word = cmdbuf; word < cp; word++) 976128345Stjr if (*word != ' ') 977128345Stjr break; 978128345Stjr if (word >= cp) 979128345Stjr return (cp); 98060786Sps for (p = cmdbuf; p < cp; p++) 98160786Sps { 982128345Stjr if (meta_quoted) 98360786Sps { 984128345Stjr meta_quoted = 0; 985128345Stjr } else if (esclen > 0 && p + esclen < cp && 986128345Stjr strncmp(p, esc, esclen) == 0) 98760786Sps { 988128345Stjr meta_quoted = 1; 989128345Stjr p += esclen - 1; 990128345Stjr } else if (delim_quoted) 991128345Stjr { 992128345Stjr if (*p == closequote) 993128345Stjr delim_quoted = 0; 994128345Stjr } else /* (!delim_quoted) */ 995128345Stjr { 996128345Stjr if (*p == openquote) 997128345Stjr delim_quoted = 1; 998128345Stjr else if (*p == ' ') 999128345Stjr word = p+1; 100060786Sps } 100160786Sps } 100260786Sps#endif 100360786Sps return (word); 100460786Sps} 100560786Sps 100660786Sps/* 100760786Sps * Set things up to enter completion mode. 100860786Sps * Expand the word under the cursor into a list of filenames 100960786Sps * which start with that word, and set tk_text to that list. 101060786Sps */ 101160786Sps static void 101260786Spsinit_compl() 101360786Sps{ 101460786Sps char *word; 101560786Sps char c; 101660786Sps 101760786Sps /* 101860786Sps * Get rid of any previous tk_text. 101960786Sps */ 102060786Sps if (tk_text != NULL) 102160786Sps { 102260786Sps free(tk_text); 102360786Sps tk_text = NULL; 102460786Sps } 102560786Sps /* 102660786Sps * Find the original (uncompleted) word in the command buffer. 102760786Sps */ 102860786Sps word = delimit_word(); 102960786Sps if (word == NULL) 103060786Sps return; 103160786Sps /* 103260786Sps * Set the insertion point to the point in the command buffer 103360786Sps * where the original (uncompleted) word now sits. 103460786Sps */ 103560786Sps tk_ipoint = word; 103660786Sps /* 103760786Sps * Save the original (uncompleted) word 103860786Sps */ 103960786Sps if (tk_original != NULL) 104060786Sps free(tk_original); 104160786Sps tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 104260786Sps strncpy(tk_original, word, cp-word); 104360786Sps /* 104460786Sps * Get the expanded filename. 104560786Sps * This may result in a single filename, or 104660786Sps * a blank-separated list of filenames. 104760786Sps */ 104860786Sps c = *cp; 104960786Sps *cp = '\0'; 1050128345Stjr if (*word != openquote) 1051128345Stjr { 1052128345Stjr tk_text = fcomplete(word); 1053128345Stjr } else 1054128345Stjr { 1055128345Stjr char *qword = shell_quote(word+1); 1056128345Stjr if (qword == NULL) 1057128345Stjr tk_text = fcomplete(word+1); 1058128345Stjr else 1059128345Stjr { 1060128345Stjr tk_text = fcomplete(qword); 1061128345Stjr free(qword); 1062128345Stjr } 1063128345Stjr } 106460786Sps *cp = c; 106560786Sps} 106660786Sps 106760786Sps/* 106860786Sps * Return the next word in the current completion list. 106960786Sps */ 107060786Sps static char * 107160786Spsnext_compl(action, prev) 107260786Sps int action; 107360786Sps char *prev; 107460786Sps{ 107560786Sps switch (action) 107660786Sps { 107760786Sps case EC_F_COMPLETE: 107860786Sps return (forw_textlist(&tk_tlist, prev)); 107960786Sps case EC_B_COMPLETE: 108060786Sps return (back_textlist(&tk_tlist, prev)); 108160786Sps } 108260786Sps /* Cannot happen */ 108360786Sps return ("?"); 108460786Sps} 108560786Sps 108660786Sps/* 108760786Sps * Complete the filename before (or under) the cursor. 108860786Sps * cmd_complete may be called multiple times. The global in_completion 108960786Sps * remembers whether this call is the first time (create the list), 109060786Sps * or a subsequent time (step thru the list). 109160786Sps */ 109260786Sps static int 109360786Spscmd_complete(action) 109460786Sps int action; 109560786Sps{ 109660786Sps char *s; 109760786Sps 109860786Sps if (!in_completion || action == EC_EXPAND) 109960786Sps { 110060786Sps /* 110160786Sps * Expand the word under the cursor and 110260786Sps * use the first word in the expansion 110360786Sps * (or the entire expansion if we're doing EC_EXPAND). 110460786Sps */ 110560786Sps init_compl(); 110660786Sps if (tk_text == NULL) 110760786Sps { 110860786Sps bell(); 110960786Sps return (CC_OK); 111060786Sps } 111160786Sps if (action == EC_EXPAND) 111260786Sps { 111360786Sps /* 111460786Sps * Use the whole list. 111560786Sps */ 111660786Sps tk_trial = tk_text; 111760786Sps } else 111860786Sps { 111960786Sps /* 112060786Sps * Use the first filename in the list. 112160786Sps */ 112260786Sps in_completion = 1; 112360786Sps init_textlist(&tk_tlist, tk_text); 112460786Sps tk_trial = next_compl(action, (char*)NULL); 112560786Sps } 112660786Sps } else 112760786Sps { 112860786Sps /* 112960786Sps * We already have a completion list. 113060786Sps * Use the next/previous filename from the list. 113160786Sps */ 113260786Sps tk_trial = next_compl(action, tk_trial); 113360786Sps } 113460786Sps 113560786Sps /* 113660786Sps * Remove the original word, or the previous trial completion. 113760786Sps */ 113860786Sps while (cp > tk_ipoint) 113960786Sps (void) cmd_erase(); 114060786Sps 114160786Sps if (tk_trial == NULL) 114260786Sps { 114360786Sps /* 114460786Sps * There are no more trial completions. 114560786Sps * Insert the original (uncompleted) filename. 114660786Sps */ 114760786Sps in_completion = 0; 114860786Sps if (cmd_istr(tk_original) != CC_OK) 114960786Sps goto fail; 115060786Sps } else 115160786Sps { 115260786Sps /* 115360786Sps * Insert trial completion. 115460786Sps */ 115560786Sps if (cmd_istr(tk_trial) != CC_OK) 115660786Sps goto fail; 115760786Sps /* 115860786Sps * If it is a directory, append a slash. 115960786Sps */ 116060786Sps if (is_dir(tk_trial)) 116160786Sps { 116260786Sps if (cp > cmdbuf && cp[-1] == closequote) 116360786Sps (void) cmd_erase(); 116460786Sps s = lgetenv("LESSSEPARATOR"); 116560786Sps if (s == NULL) 116660786Sps s = PATHNAME_SEP; 116760786Sps if (cmd_istr(s) != CC_OK) 116860786Sps goto fail; 116960786Sps } 117060786Sps } 117160786Sps 117260786Sps return (CC_OK); 117360786Sps 117460786Spsfail: 117560786Sps in_completion = 0; 117660786Sps bell(); 117760786Sps return (CC_OK); 117860786Sps} 117960786Sps 118060786Sps#endif /* TAB_COMPLETE_FILENAME */ 118160786Sps 118260786Sps/* 118360786Sps * Process a single character of a multi-character command, such as 118460786Sps * a number, or the pattern of a search command. 118560786Sps * Returns: 118660786Sps * CC_OK The char was accepted. 118760786Sps * CC_QUIT The char requests the command to be aborted. 118860786Sps * CC_ERROR The char could not be accepted due to an error. 118960786Sps */ 119060786Sps public int 119160786Spscmd_char(c) 119260786Sps int c; 119360786Sps{ 119460786Sps int action; 1195161475Sdelphij int len; 119660786Sps 1197161475Sdelphij if (!utf_mode) 1198161475Sdelphij { 1199161475Sdelphij cmd_mbc_buf[0] = c; 1200161475Sdelphij len = 1; 1201161475Sdelphij } else 1202161475Sdelphij { 1203161475Sdelphij /* Perform strict validation in all possible cases. */ 1204161475Sdelphij if (cmd_mbc_buf_len == 0) 1205161475Sdelphij { 1206161475Sdelphij retry: 1207161475Sdelphij cmd_mbc_buf_index = 1; 1208161475Sdelphij *cmd_mbc_buf = c; 1209161475Sdelphij if (IS_ASCII_OCTET(c)) 1210161475Sdelphij cmd_mbc_buf_len = 1; 1211161475Sdelphij else if (IS_UTF8_LEAD(c)) 1212161475Sdelphij { 1213161475Sdelphij cmd_mbc_buf_len = utf_len(c); 1214161475Sdelphij return (CC_OK); 1215161475Sdelphij } else 1216161475Sdelphij { 1217161475Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 1218161475Sdelphij bell(); 1219161475Sdelphij return (CC_ERROR); 1220161475Sdelphij } 1221161475Sdelphij } else if (IS_UTF8_TRAIL(c)) 1222161475Sdelphij { 1223161475Sdelphij cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1224161475Sdelphij if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1225161475Sdelphij return (CC_OK); 1226161475Sdelphij if (!is_utf8_well_formed(cmd_mbc_buf)) 1227161475Sdelphij { 1228161475Sdelphij /* complete, but not well formed (non-shortest form), sequence */ 1229161475Sdelphij cmd_mbc_buf_len = 0; 1230161475Sdelphij bell(); 1231161475Sdelphij return (CC_ERROR); 1232161475Sdelphij } 1233161475Sdelphij } else 1234161475Sdelphij { 1235161475Sdelphij /* Flush incomplete (truncated) sequence. */ 1236161475Sdelphij cmd_mbc_buf_len = 0; 1237161475Sdelphij bell(); 1238161475Sdelphij /* Handle new char. */ 1239161475Sdelphij goto retry; 1240161475Sdelphij } 1241161475Sdelphij 1242161475Sdelphij len = cmd_mbc_buf_len; 1243161475Sdelphij cmd_mbc_buf_len = 0; 1244161475Sdelphij } 1245161475Sdelphij 124660786Sps if (literal) 124760786Sps { 124860786Sps /* 124960786Sps * Insert the char, even if it is a line-editing char. 125060786Sps */ 125160786Sps literal = 0; 1252161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 125360786Sps } 125460786Sps 125560786Sps /* 1256161475Sdelphij * See if it is a line-editing character. 125760786Sps */ 1258161475Sdelphij if (in_mca() && len == 1) 125960786Sps { 126060786Sps action = cmd_edit(c); 126160786Sps switch (action) 126260786Sps { 126360786Sps case CC_OK: 126460786Sps case CC_QUIT: 126560786Sps return (action); 126660786Sps case CC_PASS: 126760786Sps break; 126860786Sps } 126960786Sps } 127060786Sps 127160786Sps /* 127260786Sps * Insert the char into the command buffer. 127360786Sps */ 1274161475Sdelphij return (cmd_ichar(cmd_mbc_buf, len)); 127560786Sps} 127660786Sps 127760786Sps/* 127860786Sps * Return the number currently in the command buffer. 127960786Sps */ 1280128345Stjr public LINENUM 1281170256Sdelphijcmd_int(frac) 1282170256Sdelphij long *frac; 128360786Sps{ 1284170256Sdelphij char *p; 1285128345Stjr LINENUM n = 0; 1286170256Sdelphij int err; 1287128345Stjr 1288170256Sdelphij for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1289170256Sdelphij n = (n * 10) + (*p - '0'); 1290170256Sdelphij *frac = 0; 1291170256Sdelphij if (*p++ == '.') 1292170256Sdelphij { 1293170256Sdelphij *frac = getfraction(&p, NULL, &err); 1294170256Sdelphij /* {{ do something if err is set? }} */ 1295170256Sdelphij } 1296128345Stjr return (n); 129760786Sps} 129860786Sps 129960786Sps/* 130060786Sps * Return a pointer to the command buffer. 130160786Sps */ 130260786Sps public char * 130360786Spsget_cmdbuf() 130460786Sps{ 130560786Sps return (cmdbuf); 130660786Sps} 1307161475Sdelphij 1308191930Sdelphij#if CMD_HISTORY 1309170256Sdelphij/* 1310170256Sdelphij * Return the last (most recent) string in the current command history. 1311170256Sdelphij */ 1312170256Sdelphij public char * 1313170256Sdelphijcmd_lastpattern() 1314170256Sdelphij{ 1315170256Sdelphij if (curr_mlist == NULL) 1316170256Sdelphij return (NULL); 1317170256Sdelphij return (curr_mlist->curr_mp->prev->string); 1318170256Sdelphij} 1319191930Sdelphij#endif 1320170256Sdelphij 1321161475Sdelphij#if CMD_HISTORY 1322161475Sdelphij/* 1323161475Sdelphij * Get the name of the history file. 1324161475Sdelphij */ 1325161475Sdelphij static char * 1326161475Sdelphijhistfile_name() 1327161475Sdelphij{ 1328161475Sdelphij char *home; 1329161475Sdelphij char *name; 1330161475Sdelphij int len; 1331161475Sdelphij 1332161475Sdelphij /* See if filename is explicitly specified by $LESSHISTFILE. */ 1333161475Sdelphij name = lgetenv("LESSHISTFILE"); 1334161475Sdelphij if (name != NULL && *name != '\0') 1335161475Sdelphij { 1336170256Sdelphij if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1337161475Sdelphij /* $LESSHISTFILE == "-" means don't use a history file. */ 1338161475Sdelphij return (NULL); 1339161475Sdelphij return (save(name)); 1340161475Sdelphij } 1341161475Sdelphij 1342161475Sdelphij /* Otherwise, file is in $HOME. */ 1343161475Sdelphij home = lgetenv("HOME"); 1344161475Sdelphij if (home == NULL || *home == '\0') 1345161475Sdelphij { 1346161475Sdelphij#if OS2 1347161475Sdelphij home = lgetenv("INIT"); 1348161475Sdelphij if (home == NULL || *home == '\0') 1349161475Sdelphij#endif 1350161475Sdelphij return (NULL); 1351161475Sdelphij } 1352161475Sdelphij len = strlen(home) + strlen(LESSHISTFILE) + 2; 1353161475Sdelphij name = (char *) ecalloc(len, sizeof(char)); 1354161475Sdelphij SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1355161475Sdelphij return (name); 1356161475Sdelphij} 1357161475Sdelphij#endif /* CMD_HISTORY */ 1358161475Sdelphij 1359161475Sdelphij/* 1360161475Sdelphij * Initialize history from a .lesshist file. 1361161475Sdelphij */ 1362161475Sdelphij public void 1363161475Sdelphijinit_cmdhist() 1364161475Sdelphij{ 1365161475Sdelphij#if CMD_HISTORY 1366161475Sdelphij struct mlist *ml = NULL; 1367161475Sdelphij char line[CMDBUF_SIZE]; 1368161475Sdelphij char *filename; 1369161475Sdelphij FILE *f; 1370161475Sdelphij char *p; 1371161475Sdelphij 1372161475Sdelphij filename = histfile_name(); 1373161475Sdelphij if (filename == NULL) 1374161475Sdelphij return; 1375161475Sdelphij f = fopen(filename, "r"); 1376161475Sdelphij free(filename); 1377161475Sdelphij if (f == NULL) 1378161475Sdelphij return; 1379161475Sdelphij if (fgets(line, sizeof(line), f) == NULL || 1380161475Sdelphij strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1381161475Sdelphij { 1382161475Sdelphij fclose(f); 1383161475Sdelphij return; 1384161475Sdelphij } 1385161475Sdelphij while (fgets(line, sizeof(line), f) != NULL) 1386161475Sdelphij { 1387161475Sdelphij for (p = line; *p != '\0'; p++) 1388161475Sdelphij { 1389161475Sdelphij if (*p == '\n' || *p == '\r') 1390161475Sdelphij { 1391161475Sdelphij *p = '\0'; 1392161475Sdelphij break; 1393161475Sdelphij } 1394161475Sdelphij } 1395161475Sdelphij if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1396161475Sdelphij ml = &mlist_search; 1397170964Sdelphij else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1398170964Sdelphij { 1399161475Sdelphij#if SHELL_ESCAPE || PIPEC 1400161475Sdelphij ml = &mlist_shell; 1401170964Sdelphij#else 1402170964Sdelphij ml = NULL; 1403161475Sdelphij#endif 1404170964Sdelphij } else if (*line == '"') 1405161475Sdelphij { 1406161475Sdelphij if (ml != NULL) 1407161475Sdelphij cmd_addhist(ml, line+1); 1408161475Sdelphij } 1409161475Sdelphij } 1410161475Sdelphij fclose(f); 1411161475Sdelphij#endif /* CMD_HISTORY */ 1412161475Sdelphij} 1413161475Sdelphij 1414161475Sdelphij/* 1415161475Sdelphij * 1416161475Sdelphij */ 1417161475Sdelphij#if CMD_HISTORY 1418161475Sdelphij static void 1419161475Sdelphijsave_mlist(ml, f) 1420161475Sdelphij struct mlist *ml; 1421161475Sdelphij FILE *f; 1422161475Sdelphij{ 1423161475Sdelphij int histsize = 0; 1424161475Sdelphij int n; 1425161475Sdelphij char *s; 1426161475Sdelphij 1427161475Sdelphij s = lgetenv("LESSHISTSIZE"); 1428161475Sdelphij if (s != NULL) 1429161475Sdelphij histsize = atoi(s); 1430161475Sdelphij if (histsize == 0) 1431161475Sdelphij histsize = 100; 1432161475Sdelphij 1433161475Sdelphij ml = ml->prev; 1434161475Sdelphij for (n = 0; n < histsize; n++) 1435161475Sdelphij { 1436161475Sdelphij if (ml->string == NULL) 1437161475Sdelphij break; 1438161475Sdelphij ml = ml->prev; 1439161475Sdelphij } 1440161475Sdelphij for (ml = ml->next; ml->string != NULL; ml = ml->next) 1441161475Sdelphij fprintf(f, "\"%s\n", ml->string); 1442161475Sdelphij} 1443161475Sdelphij#endif /* CMD_HISTORY */ 1444161475Sdelphij 1445161475Sdelphij/* 1446161475Sdelphij * 1447161475Sdelphij */ 1448161475Sdelphij public void 1449161475Sdelphijsave_cmdhist() 1450161475Sdelphij{ 1451161475Sdelphij#if CMD_HISTORY 1452161475Sdelphij char *filename; 1453161475Sdelphij FILE *f; 1454170964Sdelphij int modified = 0; 1455161475Sdelphij 1456161475Sdelphij filename = histfile_name(); 1457161475Sdelphij if (filename == NULL) 1458161475Sdelphij return; 1459170964Sdelphij if (mlist_search.modified) 1460170964Sdelphij modified = 1; 1461170964Sdelphij#if SHELL_ESCAPE || PIPEC 1462170964Sdelphij if (mlist_shell.modified) 1463170964Sdelphij modified = 1; 1464170964Sdelphij#endif 1465170964Sdelphij if (!modified) 1466170256Sdelphij return; 1467161475Sdelphij f = fopen(filename, "w"); 1468161475Sdelphij free(filename); 1469161475Sdelphij if (f == NULL) 1470161475Sdelphij return; 1471161475Sdelphij#if HAVE_FCHMOD 1472191930Sdelphij{ 1473161475Sdelphij /* Make history file readable only by owner. */ 1474191930Sdelphij int do_chmod = 1; 1475191930Sdelphij#if HAVE_STAT 1476191930Sdelphij struct stat statbuf; 1477191930Sdelphij int r = fstat(fileno(f), &statbuf); 1478191930Sdelphij if (r < 0 || !S_ISREG(statbuf.st_mode)) 1479191930Sdelphij /* Don't chmod if not a regular file. */ 1480191930Sdelphij do_chmod = 0; 1481161475Sdelphij#endif 1482191930Sdelphij if (do_chmod) 1483191930Sdelphij fchmod(fileno(f), 0600); 1484191930Sdelphij} 1485191930Sdelphij#endif 1486161475Sdelphij 1487161475Sdelphij fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1488161475Sdelphij 1489161475Sdelphij fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1490161475Sdelphij save_mlist(&mlist_search, f); 1491161475Sdelphij 1492161475Sdelphij#if SHELL_ESCAPE || PIPEC 1493161475Sdelphij fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1494161475Sdelphij save_mlist(&mlist_shell, f); 1495161475Sdelphij#endif 1496161475Sdelphij 1497161475Sdelphij fclose(f); 1498161475Sdelphij#endif /* CMD_HISTORY */ 1499161475Sdelphij} 1500