cmdbuf.c revision 240121
1130803Smarcel/* 2130803Smarcel * Copyright (C) 1984-2012 Mark Nudelman 3130803Smarcel * 4130803Smarcel * You may distribute under the terms of either the GNU General Public 5130803Smarcel * License or the Less License, as specified in the README file. 6130803Smarcel * 7130803Smarcel * For more information, see the README file. 8130803Smarcel */ 9130803Smarcel 10130803Smarcel 11130803Smarcel/* 12130803Smarcel * Functions which manipulate the command buffer. 13130803Smarcel * Used only by command() and related functions. 14130803Smarcel */ 15130803Smarcel 16130803Smarcel#include "less.h" 17130803Smarcel#include "cmd.h" 18130803Smarcel#include "charset.h" 19130803Smarcel#if HAVE_STAT 20130803Smarcel#include <sys/stat.h> 21130803Smarcel#endif 22130803Smarcel 23130803Smarcelextern int sc_width; 24130803Smarcelextern int utf_mode; 25130803Smarcel 26130803Smarcelstatic char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ 27130803Smarcelstatic int cmd_col; /* Current column of the cursor */ 28130803Smarcelstatic int prompt_col; /* Column of cursor just after prompt */ 29130803Smarcelstatic char *cp; /* Pointer into cmdbuf */ 30130803Smarcelstatic int cmd_offset; /* Index into cmdbuf of first displayed char */ 31130803Smarcelstatic int literal; /* Next input char should not be interpreted */ 32130803Smarcelstatic int updown_match = -1; /* Prefix length in up/down movement */ 33130803Smarcel 34130803Smarcel#if TAB_COMPLETE_FILENAME 35130803Smarcelstatic int cmd_complete(); 36130803Smarcel/* 37130803Smarcel * These variables are statics used by cmd_complete. 38130803Smarcel */ 39130803Smarcelstatic int in_completion = 0; 40130803Smarcelstatic char *tk_text; 41130803Smarcelstatic char *tk_original; 42130803Smarcelstatic char *tk_ipoint; 43130803Smarcelstatic char *tk_trial; 44130803Smarcelstatic struct textlist tk_tlist; 45130803Smarcel#endif 46130803Smarcel 47130803Smarcelstatic int cmd_left(); 48130803Smarcelstatic int cmd_right(); 49130803Smarcel 50130803Smarcel#if SPACES_IN_FILENAMES 51130803Smarcelpublic char openquote = '"'; 52130803Smarcelpublic char closequote = '"'; 53130803Smarcel#endif 54130803Smarcel 55130803Smarcel#if CMD_HISTORY 56130803Smarcel 57130803Smarcel/* History file */ 58130803Smarcel#define HISTFILE_FIRST_LINE ".less-history-file:" 59130803Smarcel#define HISTFILE_SEARCH_SECTION ".search" 60130803Smarcel#define HISTFILE_SHELL_SECTION ".shell" 61130803Smarcel 62130803Smarcel/* 63130803Smarcel * A mlist structure represents a command history. 64130803Smarcel */ 65130803Smarcelstruct mlist 66130803Smarcel{ 67130803Smarcel struct mlist *next; 68130803Smarcel struct mlist *prev; 69130803Smarcel struct mlist *curr_mp; 70130803Smarcel char *string; 71130803Smarcel int modified; 72130803Smarcel}; 73130803Smarcel 74130803Smarcel/* 75130803Smarcel * These are the various command histories that exist. 76130803Smarcel */ 77130803Smarcelstruct mlist mlist_search = 78130803Smarcel { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; 79130803Smarcelpublic void * constant ml_search = (void *) &mlist_search; 80130803Smarcel 81130803Smarcelstruct mlist mlist_examine = 82130803Smarcel { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; 83130803Smarcelpublic void * constant ml_examine = (void *) &mlist_examine; 84130803Smarcel 85130803Smarcel#if SHELL_ESCAPE || PIPEC 86130803Smarcelstruct mlist mlist_shell = 87130803Smarcel { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; 88130803Smarcelpublic void * constant ml_shell = (void *) &mlist_shell; 89130803Smarcel#endif 90130803Smarcel 91130803Smarcel#else /* CMD_HISTORY */ 92130803Smarcel 93130803Smarcel/* If CMD_HISTORY is off, these are just flags. */ 94130803Smarcelpublic void * constant ml_search = (void *)1; 95130803Smarcelpublic void * constant ml_examine = (void *)2; 96130803Smarcel#if SHELL_ESCAPE || PIPEC 97130803Smarcelpublic void * constant ml_shell = (void *)3; 98130803Smarcel#endif 99130803Smarcel 100130803Smarcel#endif /* CMD_HISTORY */ 101130803Smarcel 102130803Smarcel/* 103130803Smarcel * History for the current command. 104130803Smarcel */ 105130803Smarcelstatic struct mlist *curr_mlist = NULL; 106130803Smarcelstatic int curr_cmdflags; 107130803Smarcel 108130803Smarcelstatic char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; 109130803Smarcelstatic int cmd_mbc_buf_len; 110130803Smarcelstatic int cmd_mbc_buf_index; 111130803Smarcel 112130803Smarcel 113130803Smarcel/* 114130803Smarcel * Reset command buffer (to empty). 115130803Smarcel */ 116130803Smarcel public void 117130803Smarcelcmd_reset() 118130803Smarcel{ 119130803Smarcel cp = cmdbuf; 120130803Smarcel *cp = '\0'; 121130803Smarcel cmd_col = 0; 122130803Smarcel cmd_offset = 0; 123130803Smarcel literal = 0; 124130803Smarcel cmd_mbc_buf_len = 0; 125130803Smarcel updown_match = -1; 126130803Smarcel} 127130803Smarcel 128130803Smarcel/* 129130803Smarcel * Clear command line. 130130803Smarcel */ 131130803Smarcel public void 132130803Smarcelclear_cmd() 133130803Smarcel{ 134130803Smarcel cmd_col = prompt_col = 0; 135130803Smarcel cmd_mbc_buf_len = 0; 136130803Smarcel updown_match = -1; 137130803Smarcel} 138130803Smarcel 139130803Smarcel/* 140130803Smarcel * Display a string, usually as a prompt for input into the command buffer. 141130803Smarcel */ 142130803Smarcel public void 143130803Smarcelcmd_putstr(s) 144130803Smarcel char *s; 145130803Smarcel{ 146130803Smarcel LWCHAR prev_ch = 0; 147130803Smarcel LWCHAR ch; 148130803Smarcel char *endline = s + strlen(s); 149130803Smarcel while (*s != '\0') 150130803Smarcel { 151130803Smarcel char *ns = s; 152130803Smarcel ch = step_char(&ns, +1, endline); 153130803Smarcel while (s < ns) 154130803Smarcel putchr(*s++); 155130803Smarcel if (!utf_mode) 156130803Smarcel { 157130803Smarcel cmd_col++; 158130803Smarcel prompt_col++; 159130803Smarcel } else if (!is_composing_char(ch) && 160130803Smarcel !is_combining_char(prev_ch, ch)) 161130803Smarcel { 162130803Smarcel int width = is_wide_char(ch) ? 2 : 1; 163130803Smarcel cmd_col += width; 164130803Smarcel prompt_col += width; 165130803Smarcel } 166130803Smarcel prev_ch = ch; 167130803Smarcel } 168130803Smarcel} 169130803Smarcel 170130803Smarcel/* 171130803Smarcel * How many characters are in the command buffer? 172130803Smarcel */ 173130803Smarcel public int 174130803Smarcellen_cmdbuf() 175130803Smarcel{ 176130803Smarcel char *s = cmdbuf; 177130803Smarcel char *endline = s + strlen(s); 178130803Smarcel int len = 0; 179130803Smarcel 180130803Smarcel while (*s != '\0') 181130803Smarcel { 182130803Smarcel step_char(&s, +1, endline); 183130803Smarcel len++; 184130803Smarcel } 185130803Smarcel return (len); 186130803Smarcel} 187130803Smarcel 188130803Smarcel/* 189130803Smarcel * Common part of cmd_step_right() and cmd_step_left(). 190130803Smarcel */ 191130803Smarcel static char * 192130803Smarcelcmd_step_common(p, ch, len, pwidth, bswidth) 193130803Smarcel char *p; 194130803Smarcel LWCHAR ch; 195130803Smarcel int len; 196130803Smarcel int *pwidth; 197130803Smarcel int *bswidth; 198130803Smarcel{ 199130803Smarcel char *pr; 200130803Smarcel 201130803Smarcel if (len == 1) 202130803Smarcel { 203130803Smarcel pr = prchar((int) ch); 204130803Smarcel if (pwidth != NULL || bswidth != NULL) 205130803Smarcel { 206130803Smarcel int len = strlen(pr); 207130803Smarcel if (pwidth != NULL) 208130803Smarcel *pwidth = len; 209130803Smarcel if (bswidth != NULL) 210130803Smarcel *bswidth = len; 211130803Smarcel } 212130803Smarcel } else 213130803Smarcel { 214130803Smarcel pr = prutfchar(ch); 215130803Smarcel if (pwidth != NULL || bswidth != NULL) 216130803Smarcel { 217130803Smarcel if (is_composing_char(ch)) 218130803Smarcel { 219130803Smarcel if (pwidth != NULL) 220130803Smarcel *pwidth = 0; 221130803Smarcel if (bswidth != NULL) 222130803Smarcel *bswidth = 0; 223130803Smarcel } else if (is_ubin_char(ch)) 224130803Smarcel { 225130803Smarcel int len = strlen(pr); 226130803Smarcel if (pwidth != NULL) 227130803Smarcel *pwidth = len; 228130803Smarcel if (bswidth != NULL) 229130803Smarcel *bswidth = len; 230130803Smarcel } else 231130803Smarcel { 232130803Smarcel LWCHAR prev_ch = step_char(&p, -1, cmdbuf); 233130803Smarcel if (is_combining_char(prev_ch, ch)) 234130803Smarcel { 235130803Smarcel if (pwidth != NULL) 236130803Smarcel *pwidth = 0; 237130803Smarcel if (bswidth != NULL) 238130803Smarcel *bswidth = 0; 239130803Smarcel } else 240130803Smarcel { 241130803Smarcel if (pwidth != NULL) 242130803Smarcel *pwidth = is_wide_char(ch) 243130803Smarcel ? 2 244130803Smarcel : 1; 245130803Smarcel if (bswidth != NULL) 246130803Smarcel *bswidth = 1; 247130803Smarcel } 248130803Smarcel } 249130803Smarcel } 250130803Smarcel } 251130803Smarcel 252130803Smarcel return (pr); 253130803Smarcel} 254130803Smarcel 255130803Smarcel/* 256130803Smarcel * Step a pointer one character right in the command buffer. 257130803Smarcel */ 258130803Smarcel static char * 259130803Smarcelcmd_step_right(pp, pwidth, bswidth) 260130803Smarcel char **pp; 261130803Smarcel int *pwidth; 262130803Smarcel int *bswidth; 263130803Smarcel{ 264130803Smarcel char *p = *pp; 265130803Smarcel LWCHAR ch = step_char(pp, +1, p + strlen(p)); 266130803Smarcel 267130803Smarcel return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); 268130803Smarcel} 269130803Smarcel 270130803Smarcel/* 271130803Smarcel * Step a pointer one character left in the command buffer. 272130803Smarcel */ 273130803Smarcel static char * 274130803Smarcelcmd_step_left(pp, pwidth, bswidth) 275130803Smarcel char **pp; 276130803Smarcel int *pwidth; 277130803Smarcel int *bswidth; 278130803Smarcel{ 279130803Smarcel char *p = *pp; 280130803Smarcel LWCHAR ch = step_char(pp, -1, cmdbuf); 281130803Smarcel 282130803Smarcel return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); 283130803Smarcel} 284130803Smarcel 285130803Smarcel/* 286130803Smarcel * Repaint the line from cp onwards. 287130803Smarcel * Then position the cursor just after the char old_cp (a pointer into cmdbuf). 288130803Smarcel */ 289130803Smarcel static void 290130803Smarcelcmd_repaint(old_cp) 291130803Smarcel char *old_cp; 292130803Smarcel{ 293130803Smarcel /* 294130803Smarcel * Repaint the line from the current position. 295130803Smarcel */ 296130803Smarcel clear_eol(); 297130803Smarcel while (*cp != '\0') 298130803Smarcel { 299130803Smarcel char *np = cp; 300130803Smarcel int width; 301130803Smarcel char *pr = cmd_step_right(&np, &width, NULL); 302130803Smarcel if (cmd_col + width >= sc_width) 303130803Smarcel break; 304130803Smarcel cp = np; 305130803Smarcel putstr(pr); 306130803Smarcel cmd_col += width; 307130803Smarcel } 308130803Smarcel while (*cp != '\0') 309130803Smarcel { 310130803Smarcel char *np = cp; 311130803Smarcel int width; 312130803Smarcel char *pr = cmd_step_right(&np, &width, NULL); 313130803Smarcel if (width > 0) 314130803Smarcel break; 315130803Smarcel cp = np; 316130803Smarcel putstr(pr); 317130803Smarcel } 318130803Smarcel 319130803Smarcel /* 320130803Smarcel * Back up the cursor to the correct position. 321130803Smarcel */ 322130803Smarcel while (cp > old_cp) 323130803Smarcel cmd_left(); 324130803Smarcel} 325130803Smarcel 326130803Smarcel/* 327130803Smarcel * Put the cursor at "home" (just after the prompt), 328130803Smarcel * and set cp to the corresponding char in cmdbuf. 329130803Smarcel */ 330130803Smarcel static void 331130803Smarcelcmd_home() 332130803Smarcel{ 333130803Smarcel while (cmd_col > prompt_col) 334130803Smarcel { 335130803Smarcel int width, bswidth; 336130803Smarcel 337130803Smarcel cmd_step_left(&cp, &width, &bswidth); 338130803Smarcel while (bswidth-- > 0) 339130803Smarcel putbs(); 340130803Smarcel cmd_col -= width; 341130803Smarcel } 342130803Smarcel 343130803Smarcel cp = &cmdbuf[cmd_offset]; 344130803Smarcel} 345130803Smarcel 346130803Smarcel/* 347130803Smarcel * Shift the cmdbuf display left a half-screen. 348130803Smarcel */ 349130803Smarcel static void 350130803Smarcelcmd_lshift() 351130803Smarcel{ 352130803Smarcel char *s; 353130803Smarcel char *save_cp; 354130803Smarcel int cols; 355130803Smarcel 356130803Smarcel /* 357130803Smarcel * Start at the first displayed char, count how far to the 358130803Smarcel * right we'd have to move to reach the center of the screen. 359130803Smarcel */ 360130803Smarcel s = cmdbuf + cmd_offset; 361130803Smarcel cols = 0; 362130803Smarcel while (cols < (sc_width - prompt_col) / 2 && *s != '\0') 363130803Smarcel { 364130803Smarcel int width; 365130803Smarcel cmd_step_right(&s, &width, NULL); 366130803Smarcel cols += width; 367130803Smarcel } 368130803Smarcel while (*s != '\0') 369130803Smarcel { 370130803Smarcel int width; 371130803Smarcel char *ns = s; 372130803Smarcel cmd_step_right(&ns, &width, NULL); 373130803Smarcel if (width > 0) 374130803Smarcel break; 375130803Smarcel s = ns; 376130803Smarcel } 377130803Smarcel 378130803Smarcel cmd_offset = s - cmdbuf; 379130803Smarcel save_cp = cp; 380130803Smarcel cmd_home(); 381130803Smarcel cmd_repaint(save_cp); 382130803Smarcel} 383130803Smarcel 384130803Smarcel/* 385130803Smarcel * Shift the cmdbuf display right a half-screen. 386130803Smarcel */ 387130803Smarcel static void 388130803Smarcelcmd_rshift() 389130803Smarcel{ 390130803Smarcel char *s; 391130803Smarcel char *save_cp; 392130803Smarcel int cols; 393130803Smarcel 394130803Smarcel /* 395130803Smarcel * Start at the first displayed char, count how far to the 396130803Smarcel * left we'd have to move to traverse a half-screen width 397130803Smarcel * of displayed characters. 398130803Smarcel */ 399130803Smarcel s = cmdbuf + cmd_offset; 400130803Smarcel cols = 0; 401130803Smarcel while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) 402130803Smarcel { 403130803Smarcel int width; 404130803Smarcel cmd_step_left(&s, &width, NULL); 405130803Smarcel cols += width; 406130803Smarcel } 407130803Smarcel 408130803Smarcel cmd_offset = s - cmdbuf; 409130803Smarcel save_cp = cp; 410130803Smarcel cmd_home(); 411130803Smarcel cmd_repaint(save_cp); 412130803Smarcel} 413130803Smarcel 414130803Smarcel/* 415130803Smarcel * Move cursor right one character. 416130803Smarcel */ 417130803Smarcel static int 418130803Smarcelcmd_right() 419130803Smarcel{ 420130803Smarcel char *pr; 421130803Smarcel char *ncp; 422130803Smarcel int width; 423130803Smarcel 424130803Smarcel if (*cp == '\0') 425130803Smarcel { 426130803Smarcel /* Already at the end of the line. */ 427130803Smarcel return (CC_OK); 428130803Smarcel } 429130803Smarcel ncp = cp; 430130803Smarcel pr = cmd_step_right(&ncp, &width, NULL); 431130803Smarcel if (cmd_col + width >= sc_width) 432130803Smarcel cmd_lshift(); 433130803Smarcel else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') 434130803Smarcel cmd_lshift(); 435130803Smarcel cp = ncp; 436130803Smarcel cmd_col += width; 437130803Smarcel putstr(pr); 438130803Smarcel while (*cp != '\0') 439130803Smarcel { 440130803Smarcel pr = cmd_step_right(&ncp, &width, NULL); 441130803Smarcel if (width > 0) 442130803Smarcel break; 443130803Smarcel putstr(pr); 444130803Smarcel cp = ncp; 445130803Smarcel } 446130803Smarcel return (CC_OK); 447130803Smarcel} 448130803Smarcel 449130803Smarcel/* 450130803Smarcel * Move cursor left one character. 451130803Smarcel */ 452130803Smarcel static int 453130803Smarcelcmd_left() 454130803Smarcel{ 455130803Smarcel char *ncp; 456130803Smarcel int width, bswidth; 457130803Smarcel 458130803Smarcel if (cp <= cmdbuf) 459130803Smarcel { 460130803Smarcel /* Already at the beginning of the line */ 461130803Smarcel return (CC_OK); 462130803Smarcel } 463130803Smarcel ncp = cp; 464130803Smarcel while (ncp > cmdbuf) 465130803Smarcel { 466130803Smarcel cmd_step_left(&ncp, &width, &bswidth); 467130803Smarcel if (width > 0) 468130803Smarcel break; 469130803Smarcel } 470130803Smarcel if (cmd_col < prompt_col + width) 471130803Smarcel cmd_rshift(); 472130803Smarcel cp = ncp; 473130803Smarcel cmd_col -= width; 474130803Smarcel while (bswidth-- > 0) 475130803Smarcel putbs(); 476130803Smarcel return (CC_OK); 477130803Smarcel} 478130803Smarcel 479130803Smarcel/* 480130803Smarcel * Insert a char into the command buffer, at the current position. 481130803Smarcel */ 482130803Smarcel static int 483130803Smarcelcmd_ichar(cs, clen) 484130803Smarcel char *cs; 485130803Smarcel int clen; 486130803Smarcel{ 487130803Smarcel char *s; 488130803Smarcel 489130803Smarcel if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) 490130803Smarcel { 491130803Smarcel /* No room in the command buffer for another char. */ 492130803Smarcel bell(); 493130803Smarcel return (CC_ERROR); 494130803Smarcel } 495130803Smarcel 496130803Smarcel /* 497130803Smarcel * Make room for the new character (shift the tail of the buffer right). 498130803Smarcel */ 499130803Smarcel for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) 500130803Smarcel s[clen] = s[0]; 501130803Smarcel /* 502130803Smarcel * Insert the character into the buffer. 503130803Smarcel */ 504130803Smarcel for (s = cp; s < cp + clen; s++) 505130803Smarcel *s = *cs++; 506130803Smarcel /* 507130803Smarcel * Reprint the tail of the line from the inserted char. 508130803Smarcel */ 509130803Smarcel updown_match = -1; 510130803Smarcel cmd_repaint(cp); 511130803Smarcel cmd_right(); 512130803Smarcel return (CC_OK); 513130803Smarcel} 514130803Smarcel 515130803Smarcel/* 516130803Smarcel * Backspace in the command buffer. 517130803Smarcel * Delete the char to the left of the cursor. 518130803Smarcel */ 519130803Smarcel static int 520130803Smarcelcmd_erase() 521130803Smarcel{ 522130803Smarcel register char *s; 523130803Smarcel int clen; 524130803Smarcel 525130803Smarcel if (cp == cmdbuf) 526130803Smarcel { 527130803Smarcel /* 528130803Smarcel * Backspace past beginning of the buffer: 529130803Smarcel * this usually means abort the command. 530130803Smarcel */ 531130803Smarcel return (CC_QUIT); 532130803Smarcel } 533130803Smarcel /* 534130803Smarcel * Move cursor left (to the char being erased). 535130803Smarcel */ 536130803Smarcel s = cp; 537130803Smarcel cmd_left(); 538130803Smarcel clen = s - cp; 539130803Smarcel 540130803Smarcel /* 541130803Smarcel * Remove the char from the buffer (shift the buffer left). 542130803Smarcel */ 543130803Smarcel for (s = cp; ; s++) 544130803Smarcel { 545130803Smarcel s[0] = s[clen]; 546130803Smarcel if (s[0] == '\0') 547130803Smarcel break; 548130803Smarcel } 549130803Smarcel 550130803Smarcel /* 551130803Smarcel * Repaint the buffer after the erased char. 552130803Smarcel */ 553130803Smarcel updown_match = -1; 554130803Smarcel cmd_repaint(cp); 555130803Smarcel 556130803Smarcel /* 557130803Smarcel * We say that erasing the entire command string causes us 558130803Smarcel * to abort the current command, if CF_QUIT_ON_ERASE is set. 559130803Smarcel */ 560130803Smarcel if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') 561130803Smarcel return (CC_QUIT); 562130803Smarcel return (CC_OK); 563130803Smarcel} 564130803Smarcel 565130803Smarcel/* 566130803Smarcel * Delete the char under the cursor. 567130803Smarcel */ 568130803Smarcel static int 569130803Smarcelcmd_delete() 570130803Smarcel{ 571130803Smarcel if (*cp == '\0') 572130803Smarcel { 573130803Smarcel /* At end of string; there is no char under the cursor. */ 574130803Smarcel return (CC_OK); 575130803Smarcel } 576130803Smarcel /* 577130803Smarcel * Move right, then use cmd_erase. 578130803Smarcel */ 579130803Smarcel cmd_right(); 580130803Smarcel cmd_erase(); 581130803Smarcel return (CC_OK); 582130803Smarcel} 583130803Smarcel 584130803Smarcel/* 585130803Smarcel * Delete the "word" to the left of the cursor. 586130803Smarcel */ 587130803Smarcel static int 588130803Smarcelcmd_werase() 589130803Smarcel{ 590130803Smarcel if (cp > cmdbuf && cp[-1] == ' ') 591130803Smarcel { 592130803Smarcel /* 593130803Smarcel * If the char left of cursor is a space, 594130803Smarcel * erase all the spaces left of cursor (to the first non-space). 595130803Smarcel */ 596130803Smarcel while (cp > cmdbuf && cp[-1] == ' ') 597130803Smarcel (void) cmd_erase(); 598130803Smarcel } else 599130803Smarcel { 600130803Smarcel /* 601130803Smarcel * If the char left of cursor is not a space, 602130803Smarcel * erase all the nonspaces left of cursor (the whole "word"). 603130803Smarcel */ 604130803Smarcel while (cp > cmdbuf && cp[-1] != ' ') 605130803Smarcel (void) cmd_erase(); 606130803Smarcel } 607130803Smarcel return (CC_OK); 608130803Smarcel} 609130803Smarcel 610130803Smarcel/* 611130803Smarcel * Delete the "word" under the cursor. 612130803Smarcel */ 613130803Smarcel static int 614130803Smarcelcmd_wdelete() 615130803Smarcel{ 616130803Smarcel if (*cp == ' ') 617130803Smarcel { 618130803Smarcel /* 619130803Smarcel * If the char under the cursor is a space, 620130803Smarcel * delete it and all the spaces right of cursor. 621130803Smarcel */ 622130803Smarcel while (*cp == ' ') 623130803Smarcel (void) cmd_delete(); 624130803Smarcel } else 625130803Smarcel { 626130803Smarcel /* 627130803Smarcel * If the char under the cursor is not a space, 628130803Smarcel * delete it and all nonspaces right of cursor (the whole word). 629130803Smarcel */ 630130803Smarcel while (*cp != ' ' && *cp != '\0') 631130803Smarcel (void) cmd_delete(); 632130803Smarcel } 633130803Smarcel return (CC_OK); 634130803Smarcel} 635130803Smarcel 636130803Smarcel/* 637130803Smarcel * Delete all chars in the command buffer. 638130803Smarcel */ 639130803Smarcel static int 640130803Smarcelcmd_kill() 641130803Smarcel{ 642130803Smarcel if (cmdbuf[0] == '\0') 643130803Smarcel { 644130803Smarcel /* Buffer is already empty; abort the current command. */ 645130803Smarcel return (CC_QUIT); 646130803Smarcel } 647130803Smarcel cmd_offset = 0; 648130803Smarcel cmd_home(); 649130803Smarcel *cp = '\0'; 650130803Smarcel updown_match = -1; 651130803Smarcel cmd_repaint(cp); 652130803Smarcel 653130803Smarcel /* 654130803Smarcel * We say that erasing the entire command string causes us 655130803Smarcel * to abort the current command, if CF_QUIT_ON_ERASE is set. 656130803Smarcel */ 657130803Smarcel if (curr_cmdflags & CF_QUIT_ON_ERASE) 658130803Smarcel return (CC_QUIT); 659130803Smarcel return (CC_OK); 660130803Smarcel} 661130803Smarcel 662130803Smarcel/* 663130803Smarcel * Select an mlist structure to be the current command history. 664130803Smarcel */ 665130803Smarcel public void 666130803Smarcelset_mlist(mlist, cmdflags) 667130803Smarcel void *mlist; 668130803Smarcel int cmdflags; 669130803Smarcel{ 670130803Smarcel#if CMD_HISTORY 671130803Smarcel curr_mlist = (struct mlist *) mlist; 672130803Smarcel curr_cmdflags = cmdflags; 673130803Smarcel 674130803Smarcel /* Make sure the next up-arrow moves to the last string in the mlist. */ 675130803Smarcel if (curr_mlist != NULL) 676130803Smarcel curr_mlist->curr_mp = curr_mlist; 677130803Smarcel#endif 678130803Smarcel} 679130803Smarcel 680130803Smarcel#if CMD_HISTORY 681130803Smarcel/* 682130803Smarcel * Move up or down in the currently selected command history list. 683130803Smarcel * Only consider entries whose first updown_match chars are equal to 684130803Smarcel * cmdbuf's corresponding chars. 685130803Smarcel */ 686130803Smarcel static int 687130803Smarcelcmd_updown(action) 688130803Smarcel int action; 689130803Smarcel{ 690130803Smarcel char *s; 691130803Smarcel struct mlist *ml; 692130803Smarcel 693130803Smarcel if (curr_mlist == NULL) 694130803Smarcel { 695130803Smarcel /* 696130803Smarcel * The current command has no history list. 697130803Smarcel */ 698130803Smarcel bell(); 699130803Smarcel return (CC_OK); 700130803Smarcel } 701130803Smarcel 702130803Smarcel if (updown_match < 0) 703130803Smarcel { 704130803Smarcel updown_match = cp - cmdbuf; 705130803Smarcel } 706130803Smarcel 707130803Smarcel /* 708130803Smarcel * Find the next history entry which matches. 709130803Smarcel */ 710130803Smarcel for (ml = curr_mlist->curr_mp;;) 711130803Smarcel { 712130803Smarcel ml = (action == EC_UP) ? ml->prev : ml->next; 713130803Smarcel if (ml == curr_mlist) 714130803Smarcel { 715130803Smarcel /* 716130803Smarcel * We reached the end (or beginning) of the list. 717130803Smarcel */ 718130803Smarcel break; 719130803Smarcel } 720130803Smarcel if (strncmp(cmdbuf, ml->string, updown_match) == 0) 721130803Smarcel { 722130803Smarcel /* 723130803Smarcel * This entry matches; stop here. 724130803Smarcel * Copy the entry into cmdbuf and echo it on the screen. 725130803Smarcel */ 726130803Smarcel curr_mlist->curr_mp = ml; 727130803Smarcel s = ml->string; 728130803Smarcel if (s == NULL) 729130803Smarcel s = ""; 730130803Smarcel strcpy(cmdbuf, s); 731130803Smarcel cmd_home(); 732130803Smarcel clear_eol(); 733130803Smarcel for (cp = cmdbuf; *cp != '\0'; ) 734130803Smarcel cmd_right(); 735130803Smarcel return (CC_OK); 736130803Smarcel } 737130803Smarcel } 738130803Smarcel /* 739130803Smarcel * We didn't find a history entry that matches. 740130803Smarcel */ 741130803Smarcel bell(); 742130803Smarcel return (CC_OK); 743130803Smarcel} 744130803Smarcel#endif 745130803Smarcel 746130803Smarcel/* 747130803Smarcel * Add a string to a history list. 748130803Smarcel */ 749130803Smarcel public void 750130803Smarcelcmd_addhist(mlist, cmd) 751130803Smarcel struct mlist *mlist; 752130803Smarcel char *cmd; 753130803Smarcel{ 754130803Smarcel#if CMD_HISTORY 755130803Smarcel struct mlist *ml; 756130803Smarcel 757130803Smarcel /* 758130803Smarcel * Don't save a trivial command. 759130803Smarcel */ 760130803Smarcel if (strlen(cmd) == 0) 761130803Smarcel return; 762130803Smarcel 763130803Smarcel /* 764130803Smarcel * Save the command unless it's a duplicate of the 765130803Smarcel * last command in the history. 766130803Smarcel */ 767130803Smarcel ml = mlist->prev; 768130803Smarcel if (ml == mlist || strcmp(ml->string, cmd) != 0) 769130803Smarcel { 770130803Smarcel /* 771130803Smarcel * Did not find command in history. 772130803Smarcel * Save the command and put it at the end of the history list. 773130803Smarcel */ 774130803Smarcel ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); 775130803Smarcel ml->string = save(cmd); 776130803Smarcel ml->next = mlist; 777130803Smarcel ml->prev = mlist->prev; 778130803Smarcel mlist->prev->next = ml; 779130803Smarcel mlist->prev = ml; 780130803Smarcel } 781130803Smarcel /* 782130803Smarcel * Point to the cmd just after the just-accepted command. 783130803Smarcel * Thus, an UPARROW will always retrieve the previous command. 784130803Smarcel */ 785130803Smarcel mlist->curr_mp = ml->next; 786130803Smarcel#endif 787130803Smarcel} 788130803Smarcel 789130803Smarcel/* 790130803Smarcel * Accept the command in the command buffer. 791130803Smarcel * Add it to the currently selected history list. 792130803Smarcel */ 793130803Smarcel public void 794130803Smarcelcmd_accept() 795130803Smarcel{ 796130803Smarcel#if CMD_HISTORY 797130803Smarcel /* 798130803Smarcel * Nothing to do if there is no currently selected history list. 799130803Smarcel */ 800130803Smarcel if (curr_mlist == NULL) 801130803Smarcel return; 802130803Smarcel cmd_addhist(curr_mlist, cmdbuf); 803130803Smarcel curr_mlist->modified = 1; 804130803Smarcel#endif 805130803Smarcel} 806130803Smarcel 807130803Smarcel/* 808130803Smarcel * Try to perform a line-edit function on the command buffer, 809130803Smarcel * using a specified char as a line-editing command. 810130803Smarcel * Returns: 811130803Smarcel * CC_PASS The char does not invoke a line edit function. 812130803Smarcel * CC_OK Line edit function done. 813130803Smarcel * CC_QUIT The char requests the current command to be aborted. 814130803Smarcel */ 815130803Smarcel static int 816130803Smarcelcmd_edit(c) 817130803Smarcel int c; 818130803Smarcel{ 819130803Smarcel int action; 820130803Smarcel int flags; 821130803Smarcel 822130803Smarcel#if TAB_COMPLETE_FILENAME 823130803Smarcel#define not_in_completion() in_completion = 0 824130803Smarcel#else 825130803Smarcel#define not_in_completion() 826130803Smarcel#endif 827130803Smarcel 828130803Smarcel /* 829130803Smarcel * See if the char is indeed a line-editing command. 830130803Smarcel */ 831130803Smarcel flags = 0; 832130803Smarcel#if CMD_HISTORY 833130803Smarcel if (curr_mlist == NULL) 834130803Smarcel /* 835130803Smarcel * No current history; don't accept history manipulation cmds. 836130803Smarcel */ 837130803Smarcel flags |= EC_NOHISTORY; 838130803Smarcel#endif 839130803Smarcel#if TAB_COMPLETE_FILENAME 840130803Smarcel if (curr_mlist == ml_search) 841130803Smarcel /* 842130803Smarcel * In a search command; don't accept file-completion cmds. 843130803Smarcel */ 844130803Smarcel flags |= EC_NOCOMPLETE; 845130803Smarcel#endif 846130803Smarcel 847130803Smarcel action = editchar(c, flags); 848130803Smarcel 849130803Smarcel switch (action) 850130803Smarcel { 851130803Smarcel case EC_RIGHT: 852130803Smarcel not_in_completion(); 853130803Smarcel return (cmd_right()); 854130803Smarcel case EC_LEFT: 855130803Smarcel not_in_completion(); 856130803Smarcel return (cmd_left()); 857130803Smarcel case EC_W_RIGHT: 858130803Smarcel not_in_completion(); 859130803Smarcel while (*cp != '\0' && *cp != ' ') 860130803Smarcel cmd_right(); 861130803Smarcel while (*cp == ' ') 862130803Smarcel cmd_right(); 863130803Smarcel return (CC_OK); 864130803Smarcel case EC_W_LEFT: 865130803Smarcel not_in_completion(); 866130803Smarcel while (cp > cmdbuf && cp[-1] == ' ') 867130803Smarcel cmd_left(); 868130803Smarcel while (cp > cmdbuf && cp[-1] != ' ') 869130803Smarcel cmd_left(); 870130803Smarcel return (CC_OK); 871130803Smarcel case EC_HOME: 872130803Smarcel not_in_completion(); 873130803Smarcel cmd_offset = 0; 874130803Smarcel cmd_home(); 875130803Smarcel cmd_repaint(cp); 876130803Smarcel return (CC_OK); 877130803Smarcel case EC_END: 878130803Smarcel not_in_completion(); 879130803Smarcel while (*cp != '\0') 880130803Smarcel cmd_right(); 881130803Smarcel return (CC_OK); 882130803Smarcel case EC_INSERT: 883130803Smarcel not_in_completion(); 884130803Smarcel return (CC_OK); 885130803Smarcel case EC_BACKSPACE: 886130803Smarcel not_in_completion(); 887130803Smarcel return (cmd_erase()); 888130803Smarcel case EC_LINEKILL: 889130803Smarcel not_in_completion(); 890130803Smarcel return (cmd_kill()); 891130803Smarcel case EC_ABORT: 892130803Smarcel not_in_completion(); 893130803Smarcel (void) cmd_kill(); 894130803Smarcel return (CC_QUIT); 895130803Smarcel case EC_W_BACKSPACE: 896130803Smarcel not_in_completion(); 897130803Smarcel return (cmd_werase()); 898130803Smarcel case EC_DELETE: 899130803Smarcel not_in_completion(); 900130803Smarcel return (cmd_delete()); 901130803Smarcel case EC_W_DELETE: 902130803Smarcel not_in_completion(); 903130803Smarcel return (cmd_wdelete()); 904130803Smarcel case EC_LITERAL: 905130803Smarcel literal = 1; 906130803Smarcel return (CC_OK); 907130803Smarcel#if CMD_HISTORY 908130803Smarcel case EC_UP: 909130803Smarcel case EC_DOWN: 910130803Smarcel not_in_completion(); 911130803Smarcel return (cmd_updown(action)); 912130803Smarcel#endif 913130803Smarcel#if TAB_COMPLETE_FILENAME 914130803Smarcel case EC_F_COMPLETE: 915130803Smarcel case EC_B_COMPLETE: 916130803Smarcel case EC_EXPAND: 917130803Smarcel return (cmd_complete(action)); 918130803Smarcel#endif 919130803Smarcel case EC_NOACTION: 920130803Smarcel return (CC_OK); 921130803Smarcel default: 922130803Smarcel not_in_completion(); 923130803Smarcel return (CC_PASS); 924130803Smarcel } 925130803Smarcel} 926130803Smarcel 927130803Smarcel#if TAB_COMPLETE_FILENAME 928130803Smarcel/* 929130803Smarcel * Insert a string into the command buffer, at the current position. 930130803Smarcel */ 931130803Smarcel static int 932130803Smarcelcmd_istr(str) 933130803Smarcel char *str; 934130803Smarcel{ 935130803Smarcel char *s; 936130803Smarcel int action; 937130803Smarcel char *endline = str + strlen(str); 938130803Smarcel 939130803Smarcel for (s = str; *s != '\0'; ) 940130803Smarcel { 941130803Smarcel char *os = s; 942130803Smarcel step_char(&s, +1, endline); 943130803Smarcel action = cmd_ichar(os, s - os); 944130803Smarcel if (action != CC_OK) 945130803Smarcel { 946130803Smarcel bell(); 947130803Smarcel return (action); 948130803Smarcel } 949130803Smarcel } 950130803Smarcel return (CC_OK); 951130803Smarcel} 952130803Smarcel 953130803Smarcel/* 954130803Smarcel * Find the beginning and end of the "current" word. 955130803Smarcel * This is the word which the cursor (cp) is inside or at the end of. 956130803Smarcel * Return pointer to the beginning of the word and put the 957130803Smarcel * cursor at the end of the word. 958130803Smarcel */ 959130803Smarcel static char * 960130803Smarceldelimit_word() 961130803Smarcel{ 962130803Smarcel char *word; 963130803Smarcel#if SPACES_IN_FILENAMES 964130803Smarcel char *p; 965130803Smarcel int delim_quoted = 0; 966130803Smarcel int meta_quoted = 0; 967130803Smarcel char *esc = get_meta_escape(); 968130803Smarcel int esclen = strlen(esc); 969130803Smarcel#endif 970130803Smarcel 971130803Smarcel /* 972130803Smarcel * Move cursor to end of word. 973130803Smarcel */ 974130803Smarcel if (*cp != ' ' && *cp != '\0') 975130803Smarcel { 976130803Smarcel /* 977130803Smarcel * Cursor is on a nonspace. 978130803Smarcel * Move cursor right to the next space. 979130803Smarcel */ 980130803Smarcel while (*cp != ' ' && *cp != '\0') 981130803Smarcel cmd_right(); 982130803Smarcel } else if (cp > cmdbuf && cp[-1] != ' ') 983130803Smarcel { 984130803Smarcel /* 985130803Smarcel * Cursor is on a space, and char to the left is a nonspace. 986130803Smarcel * We're already at the end of the word. 987130803Smarcel */ 988130803Smarcel ; 989130803Smarcel#if 0 990130803Smarcel } else 991130803Smarcel { 992130803Smarcel /* 993130803Smarcel * Cursor is on a space and char to the left is a space. 994130803Smarcel * Huh? There's no word here. 995130803Smarcel */ 996130803Smarcel return (NULL); 997130803Smarcel#endif 998130803Smarcel } 999130803Smarcel /* 1000130803Smarcel * Find the beginning of the word which the cursor is in. 1001130803Smarcel */ 1002130803Smarcel if (cp == cmdbuf) 1003130803Smarcel return (NULL); 1004130803Smarcel#if SPACES_IN_FILENAMES 1005130803Smarcel /* 1006130803Smarcel * If we have an unbalanced quote (that is, an open quote 1007130803Smarcel * without a corresponding close quote), we return everything 1008130803Smarcel * from the open quote, including spaces. 1009130803Smarcel */ 1010130803Smarcel for (word = cmdbuf; word < cp; word++) 1011130803Smarcel if (*word != ' ') 1012130803Smarcel break; 1013130803Smarcel if (word >= cp) 1014130803Smarcel return (cp); 1015130803Smarcel for (p = cmdbuf; p < cp; p++) 1016130803Smarcel { 1017130803Smarcel if (meta_quoted) 1018130803Smarcel { 1019130803Smarcel meta_quoted = 0; 1020130803Smarcel } else if (esclen > 0 && p + esclen < cp && 1021130803Smarcel strncmp(p, esc, esclen) == 0) 1022130803Smarcel { 1023130803Smarcel meta_quoted = 1; 1024130803Smarcel p += esclen - 1; 1025130803Smarcel } else if (delim_quoted) 1026130803Smarcel { 1027130803Smarcel if (*p == closequote) 1028130803Smarcel delim_quoted = 0; 1029130803Smarcel } else /* (!delim_quoted) */ 1030130803Smarcel { 1031130803Smarcel if (*p == openquote) 1032130803Smarcel delim_quoted = 1; 1033130803Smarcel else if (*p == ' ') 1034130803Smarcel word = p+1; 1035130803Smarcel } 1036130803Smarcel } 1037130803Smarcel#endif 1038130803Smarcel return (word); 1039130803Smarcel} 1040130803Smarcel 1041130803Smarcel/* 1042130803Smarcel * Set things up to enter completion mode. 1043130803Smarcel * Expand the word under the cursor into a list of filenames 1044130803Smarcel * which start with that word, and set tk_text to that list. 1045130803Smarcel */ 1046130803Smarcel static void 1047130803Smarcelinit_compl() 1048130803Smarcel{ 1049130803Smarcel char *word; 1050130803Smarcel char c; 1051130803Smarcel 1052130803Smarcel /* 1053130803Smarcel * Get rid of any previous tk_text. 1054130803Smarcel */ 1055130803Smarcel if (tk_text != NULL) 1056130803Smarcel { 1057130803Smarcel free(tk_text); 1058130803Smarcel tk_text = NULL; 1059130803Smarcel } 1060130803Smarcel /* 1061130803Smarcel * Find the original (uncompleted) word in the command buffer. 1062130803Smarcel */ 1063130803Smarcel word = delimit_word(); 1064130803Smarcel if (word == NULL) 1065130803Smarcel return; 1066130803Smarcel /* 1067130803Smarcel * Set the insertion point to the point in the command buffer 1068130803Smarcel * where the original (uncompleted) word now sits. 1069130803Smarcel */ 1070130803Smarcel tk_ipoint = word; 1071130803Smarcel /* 1072130803Smarcel * Save the original (uncompleted) word 1073130803Smarcel */ 1074130803Smarcel if (tk_original != NULL) 1075130803Smarcel free(tk_original); 1076130803Smarcel tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); 1077130803Smarcel strncpy(tk_original, word, cp-word); 1078130803Smarcel /* 1079130803Smarcel * Get the expanded filename. 1080130803Smarcel * This may result in a single filename, or 1081130803Smarcel * a blank-separated list of filenames. 1082130803Smarcel */ 1083130803Smarcel c = *cp; 1084130803Smarcel *cp = '\0'; 1085130803Smarcel if (*word != openquote) 1086130803Smarcel { 1087130803Smarcel tk_text = fcomplete(word); 1088130803Smarcel } else 1089130803Smarcel { 1090130803Smarcel#if MSDOS_COMPILER 1091130803Smarcel char *qword = NULL; 1092130803Smarcel#else 1093130803Smarcel char *qword = shell_quote(word+1); 1094130803Smarcel#endif 1095130803Smarcel if (qword == NULL) 1096130803Smarcel tk_text = fcomplete(word+1); 1097130803Smarcel else 1098130803Smarcel { 1099130803Smarcel tk_text = fcomplete(qword); 1100130803Smarcel free(qword); 1101130803Smarcel } 1102130803Smarcel } 1103130803Smarcel *cp = c; 1104130803Smarcel} 1105130803Smarcel 1106130803Smarcel/* 1107130803Smarcel * Return the next word in the current completion list. 1108130803Smarcel */ 1109130803Smarcel static char * 1110130803Smarcelnext_compl(action, prev) 1111130803Smarcel int action; 1112130803Smarcel char *prev; 1113130803Smarcel{ 1114130803Smarcel switch (action) 1115130803Smarcel { 1116130803Smarcel case EC_F_COMPLETE: 1117130803Smarcel return (forw_textlist(&tk_tlist, prev)); 1118130803Smarcel case EC_B_COMPLETE: 1119130803Smarcel return (back_textlist(&tk_tlist, prev)); 1120130803Smarcel } 1121130803Smarcel /* Cannot happen */ 1122130803Smarcel return ("?"); 1123130803Smarcel} 1124130803Smarcel 1125130803Smarcel/* 1126130803Smarcel * Complete the filename before (or under) the cursor. 1127130803Smarcel * cmd_complete may be called multiple times. The global in_completion 1128130803Smarcel * remembers whether this call is the first time (create the list), 1129130803Smarcel * or a subsequent time (step thru the list). 1130130803Smarcel */ 1131130803Smarcel static int 1132130803Smarcelcmd_complete(action) 1133130803Smarcel int action; 1134130803Smarcel{ 1135130803Smarcel char *s; 1136130803Smarcel 1137130803Smarcel if (!in_completion || action == EC_EXPAND) 1138130803Smarcel { 1139130803Smarcel /* 1140130803Smarcel * Expand the word under the cursor and 1141130803Smarcel * use the first word in the expansion 1142130803Smarcel * (or the entire expansion if we're doing EC_EXPAND). 1143130803Smarcel */ 1144130803Smarcel init_compl(); 1145130803Smarcel if (tk_text == NULL) 1146130803Smarcel { 1147130803Smarcel bell(); 1148130803Smarcel return (CC_OK); 1149130803Smarcel } 1150130803Smarcel if (action == EC_EXPAND) 1151130803Smarcel { 1152130803Smarcel /* 1153130803Smarcel * Use the whole list. 1154130803Smarcel */ 1155130803Smarcel tk_trial = tk_text; 1156130803Smarcel } else 1157130803Smarcel { 1158130803Smarcel /* 1159130803Smarcel * Use the first filename in the list. 1160130803Smarcel */ 1161130803Smarcel in_completion = 1; 1162130803Smarcel init_textlist(&tk_tlist, tk_text); 1163130803Smarcel tk_trial = next_compl(action, (char*)NULL); 1164130803Smarcel } 1165130803Smarcel } else 1166130803Smarcel { 1167130803Smarcel /* 1168130803Smarcel * We already have a completion list. 1169130803Smarcel * Use the next/previous filename from the list. 1170130803Smarcel */ 1171130803Smarcel tk_trial = next_compl(action, tk_trial); 1172130803Smarcel } 1173130803Smarcel 1174130803Smarcel /* 1175130803Smarcel * Remove the original word, or the previous trial completion. 1176130803Smarcel */ 1177130803Smarcel while (cp > tk_ipoint) 1178130803Smarcel (void) cmd_erase(); 1179130803Smarcel 1180130803Smarcel if (tk_trial == NULL) 1181130803Smarcel { 1182130803Smarcel /* 1183130803Smarcel * There are no more trial completions. 1184130803Smarcel * Insert the original (uncompleted) filename. 1185130803Smarcel */ 1186130803Smarcel in_completion = 0; 1187130803Smarcel if (cmd_istr(tk_original) != CC_OK) 1188130803Smarcel goto fail; 1189130803Smarcel } else 1190130803Smarcel { 1191130803Smarcel /* 1192130803Smarcel * Insert trial completion. 1193130803Smarcel */ 1194130803Smarcel if (cmd_istr(tk_trial) != CC_OK) 1195130803Smarcel goto fail; 1196130803Smarcel /* 1197130803Smarcel * If it is a directory, append a slash. 1198130803Smarcel */ 1199130803Smarcel if (is_dir(tk_trial)) 1200130803Smarcel { 1201130803Smarcel if (cp > cmdbuf && cp[-1] == closequote) 1202130803Smarcel (void) cmd_erase(); 1203130803Smarcel s = lgetenv("LESSSEPARATOR"); 1204130803Smarcel if (s == NULL) 1205130803Smarcel s = PATHNAME_SEP; 1206130803Smarcel if (cmd_istr(s) != CC_OK) 1207130803Smarcel goto fail; 1208130803Smarcel } 1209130803Smarcel } 1210130803Smarcel 1211130803Smarcel return (CC_OK); 1212130803Smarcel 1213130803Smarcelfail: 1214130803Smarcel in_completion = 0; 1215130803Smarcel bell(); 1216130803Smarcel return (CC_OK); 1217130803Smarcel} 1218130803Smarcel 1219130803Smarcel#endif /* TAB_COMPLETE_FILENAME */ 1220130803Smarcel 1221130803Smarcel/* 1222130803Smarcel * Process a single character of a multi-character command, such as 1223130803Smarcel * a number, or the pattern of a search command. 1224130803Smarcel * Returns: 1225130803Smarcel * CC_OK The char was accepted. 1226130803Smarcel * CC_QUIT The char requests the command to be aborted. 1227130803Smarcel * CC_ERROR The char could not be accepted due to an error. 1228130803Smarcel */ 1229130803Smarcel public int 1230130803Smarcelcmd_char(c) 1231130803Smarcel int c; 1232130803Smarcel{ 1233130803Smarcel int action; 1234130803Smarcel int len; 1235130803Smarcel 1236130803Smarcel if (!utf_mode) 1237130803Smarcel { 1238130803Smarcel cmd_mbc_buf[0] = c; 1239130803Smarcel len = 1; 1240130803Smarcel } else 1241130803Smarcel { 1242130803Smarcel /* Perform strict validation in all possible cases. */ 1243130803Smarcel if (cmd_mbc_buf_len == 0) 1244130803Smarcel { 1245130803Smarcel retry: 1246130803Smarcel cmd_mbc_buf_index = 1; 1247130803Smarcel *cmd_mbc_buf = c; 1248130803Smarcel if (IS_ASCII_OCTET(c)) 1249130803Smarcel cmd_mbc_buf_len = 1; 1250130803Smarcel else if (IS_UTF8_LEAD(c)) 1251130803Smarcel { 1252130803Smarcel cmd_mbc_buf_len = utf_len(c); 1253130803Smarcel return (CC_OK); 1254130803Smarcel } else 1255130803Smarcel { 1256130803Smarcel /* UTF8_INVALID or stray UTF8_TRAIL */ 1257130803Smarcel bell(); 1258130803Smarcel return (CC_ERROR); 1259130803Smarcel } 1260130803Smarcel } else if (IS_UTF8_TRAIL(c)) 1261130803Smarcel { 1262130803Smarcel cmd_mbc_buf[cmd_mbc_buf_index++] = c; 1263130803Smarcel if (cmd_mbc_buf_index < cmd_mbc_buf_len) 1264130803Smarcel return (CC_OK); 1265130803Smarcel if (!is_utf8_well_formed(cmd_mbc_buf)) 1266130803Smarcel { 1267130803Smarcel /* complete, but not well formed (non-shortest form), sequence */ 1268130803Smarcel cmd_mbc_buf_len = 0; 1269130803Smarcel bell(); 1270130803Smarcel return (CC_ERROR); 1271130803Smarcel } 1272130803Smarcel } else 1273130803Smarcel { 1274130803Smarcel /* Flush incomplete (truncated) sequence. */ 1275130803Smarcel cmd_mbc_buf_len = 0; 1276130803Smarcel bell(); 1277130803Smarcel /* Handle new char. */ 1278130803Smarcel goto retry; 1279130803Smarcel } 1280130803Smarcel 1281130803Smarcel len = cmd_mbc_buf_len; 1282130803Smarcel cmd_mbc_buf_len = 0; 1283130803Smarcel } 1284130803Smarcel 1285130803Smarcel if (literal) 1286130803Smarcel { 1287130803Smarcel /* 1288130803Smarcel * Insert the char, even if it is a line-editing char. 1289130803Smarcel */ 1290130803Smarcel literal = 0; 1291130803Smarcel return (cmd_ichar(cmd_mbc_buf, len)); 1292130803Smarcel } 1293130803Smarcel 1294130803Smarcel /* 1295130803Smarcel * See if it is a line-editing character. 1296130803Smarcel */ 1297130803Smarcel if (in_mca() && len == 1) 1298130803Smarcel { 1299130803Smarcel action = cmd_edit(c); 1300130803Smarcel switch (action) 1301130803Smarcel { 1302130803Smarcel case CC_OK: 1303130803Smarcel case CC_QUIT: 1304130803Smarcel return (action); 1305130803Smarcel case CC_PASS: 1306130803Smarcel break; 1307130803Smarcel } 1308130803Smarcel } 1309130803Smarcel 1310130803Smarcel /* 1311130803Smarcel * Insert the char into the command buffer. 1312130803Smarcel */ 1313130803Smarcel return (cmd_ichar(cmd_mbc_buf, len)); 1314130803Smarcel} 1315130803Smarcel 1316130803Smarcel/* 1317130803Smarcel * Return the number currently in the command buffer. 1318130803Smarcel */ 1319130803Smarcel public LINENUM 1320130803Smarcelcmd_int(frac) 1321130803Smarcel long *frac; 1322130803Smarcel{ 1323130803Smarcel char *p; 1324130803Smarcel LINENUM n = 0; 1325130803Smarcel int err; 1326130803Smarcel 1327130803Smarcel for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) 1328130803Smarcel n = (n * 10) + (*p - '0'); 1329130803Smarcel *frac = 0; 1330130803Smarcel if (*p++ == '.') 1331130803Smarcel { 1332130803Smarcel *frac = getfraction(&p, NULL, &err); 1333130803Smarcel /* {{ do something if err is set? }} */ 1334130803Smarcel } 1335130803Smarcel return (n); 1336130803Smarcel} 1337130803Smarcel 1338130803Smarcel/* 1339130803Smarcel * Return a pointer to the command buffer. 1340130803Smarcel */ 1341130803Smarcel public char * 1342130803Smarcelget_cmdbuf() 1343130803Smarcel{ 1344130803Smarcel return (cmdbuf); 1345130803Smarcel} 1346130803Smarcel 1347130803Smarcel#if CMD_HISTORY 1348130803Smarcel/* 1349130803Smarcel * Return the last (most recent) string in the current command history. 1350130803Smarcel */ 1351130803Smarcel public char * 1352130803Smarcelcmd_lastpattern() 1353130803Smarcel{ 1354130803Smarcel if (curr_mlist == NULL) 1355130803Smarcel return (NULL); 1356130803Smarcel return (curr_mlist->curr_mp->prev->string); 1357130803Smarcel} 1358130803Smarcel#endif 1359130803Smarcel 1360130803Smarcel#if CMD_HISTORY 1361130803Smarcel/* 1362130803Smarcel * Get the name of the history file. 1363130803Smarcel */ 1364130803Smarcel static char * 1365130803Smarcelhistfile_name() 1366130803Smarcel{ 1367130803Smarcel char *home; 1368130803Smarcel char *name; 1369130803Smarcel int len; 1370130803Smarcel 1371130803Smarcel /* See if filename is explicitly specified by $LESSHISTFILE. */ 1372130803Smarcel name = lgetenv("LESSHISTFILE"); 1373130803Smarcel if (name != NULL && *name != '\0') 1374130803Smarcel { 1375130803Smarcel if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) 1376130803Smarcel /* $LESSHISTFILE == "-" means don't use a history file. */ 1377130803Smarcel return (NULL); 1378130803Smarcel return (save(name)); 1379130803Smarcel } 1380130803Smarcel 1381130803Smarcel /* Otherwise, file is in $HOME. */ 1382130803Smarcel home = lgetenv("HOME"); 1383130803Smarcel if (home == NULL || *home == '\0') 1384130803Smarcel { 1385130803Smarcel#if OS2 1386130803Smarcel home = lgetenv("INIT"); 1387130803Smarcel if (home == NULL || *home == '\0') 1388130803Smarcel#endif 1389130803Smarcel return (NULL); 1390130803Smarcel } 1391130803Smarcel len = strlen(home) + strlen(LESSHISTFILE) + 2; 1392130803Smarcel name = (char *) ecalloc(len, sizeof(char)); 1393130803Smarcel SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); 1394130803Smarcel return (name); 1395130803Smarcel} 1396130803Smarcel#endif /* CMD_HISTORY */ 1397130803Smarcel 1398130803Smarcel/* 1399130803Smarcel * Initialize history from a .lesshist file. 1400130803Smarcel */ 1401130803Smarcel public void 1402130803Smarcelinit_cmdhist() 1403130803Smarcel{ 1404130803Smarcel#if CMD_HISTORY 1405130803Smarcel struct mlist *ml = NULL; 1406130803Smarcel char line[CMDBUF_SIZE]; 1407130803Smarcel char *filename; 1408130803Smarcel FILE *f; 1409130803Smarcel char *p; 1410130803Smarcel 1411130803Smarcel filename = histfile_name(); 1412130803Smarcel if (filename == NULL) 1413130803Smarcel return; 1414130803Smarcel f = fopen(filename, "r"); 1415130803Smarcel free(filename); 1416130803Smarcel if (f == NULL) 1417130803Smarcel return; 1418130803Smarcel if (fgets(line, sizeof(line), f) == NULL || 1419130803Smarcel strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) 1420130803Smarcel { 1421130803Smarcel fclose(f); 1422130803Smarcel return; 1423130803Smarcel } 1424130803Smarcel while (fgets(line, sizeof(line), f) != NULL) 1425130803Smarcel { 1426130803Smarcel for (p = line; *p != '\0'; p++) 1427130803Smarcel { 1428130803Smarcel if (*p == '\n' || *p == '\r') 1429130803Smarcel { 1430130803Smarcel *p = '\0'; 1431130803Smarcel break; 1432130803Smarcel } 1433130803Smarcel } 1434130803Smarcel if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) 1435130803Smarcel ml = &mlist_search; 1436130803Smarcel else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) 1437130803Smarcel { 1438130803Smarcel#if SHELL_ESCAPE || PIPEC 1439130803Smarcel ml = &mlist_shell; 1440130803Smarcel#else 1441130803Smarcel ml = NULL; 1442130803Smarcel#endif 1443130803Smarcel } else if (*line == '"') 1444130803Smarcel { 1445130803Smarcel if (ml != NULL) 1446130803Smarcel cmd_addhist(ml, line+1); 1447130803Smarcel } 1448130803Smarcel } 1449130803Smarcel fclose(f); 1450130803Smarcel#endif /* CMD_HISTORY */ 1451130803Smarcel} 1452130803Smarcel 1453130803Smarcel/* 1454130803Smarcel * 1455130803Smarcel */ 1456130803Smarcel#if CMD_HISTORY 1457130803Smarcel static void 1458130803Smarcelsave_mlist(ml, f) 1459130803Smarcel struct mlist *ml; 1460130803Smarcel FILE *f; 1461130803Smarcel{ 1462130803Smarcel int histsize = 0; 1463130803Smarcel int n; 1464130803Smarcel char *s; 1465130803Smarcel 1466130803Smarcel s = lgetenv("LESSHISTSIZE"); 1467130803Smarcel if (s != NULL) 1468130803Smarcel histsize = atoi(s); 1469130803Smarcel if (histsize == 0) 1470130803Smarcel histsize = 100; 1471130803Smarcel 1472130803Smarcel ml = ml->prev; 1473130803Smarcel for (n = 0; n < histsize; n++) 1474130803Smarcel { 1475130803Smarcel if (ml->string == NULL) 1476130803Smarcel break; 1477130803Smarcel ml = ml->prev; 1478130803Smarcel } 1479130803Smarcel for (ml = ml->next; ml->string != NULL; ml = ml->next) 1480130803Smarcel fprintf(f, "\"%s\n", ml->string); 1481130803Smarcel} 1482130803Smarcel#endif /* CMD_HISTORY */ 1483130803Smarcel 1484130803Smarcel/* 1485130803Smarcel * 1486130803Smarcel */ 1487130803Smarcel public void 1488130803Smarcelsave_cmdhist() 1489130803Smarcel{ 1490130803Smarcel#if CMD_HISTORY 1491130803Smarcel char *filename; 1492130803Smarcel FILE *f; 1493130803Smarcel int modified = 0; 1494130803Smarcel 1495130803Smarcel if (mlist_search.modified) 1496130803Smarcel modified = 1; 1497130803Smarcel#if SHELL_ESCAPE || PIPEC 1498130803Smarcel if (mlist_shell.modified) 1499130803Smarcel modified = 1; 1500130803Smarcel#endif 1501130803Smarcel if (!modified) 1502130803Smarcel return; 1503130803Smarcel filename = histfile_name(); 1504130803Smarcel if (filename == NULL) 1505130803Smarcel return; 1506130803Smarcel f = fopen(filename, "w"); 1507130803Smarcel free(filename); 1508130803Smarcel if (f == NULL) 1509130803Smarcel return; 1510130803Smarcel#if HAVE_FCHMOD 1511130803Smarcel{ 1512130803Smarcel /* Make history file readable only by owner. */ 1513130803Smarcel int do_chmod = 1; 1514130803Smarcel#if HAVE_STAT 1515130803Smarcel struct stat statbuf; 1516130803Smarcel int r = fstat(fileno(f), &statbuf); 1517130803Smarcel if (r < 0 || !S_ISREG(statbuf.st_mode)) 1518130803Smarcel /* Don't chmod if not a regular file. */ 1519130803Smarcel do_chmod = 0; 1520130803Smarcel#endif 1521130803Smarcel if (do_chmod) 1522130803Smarcel fchmod(fileno(f), 0600); 1523130803Smarcel} 1524130803Smarcel#endif 1525130803Smarcel 1526130803Smarcel fprintf(f, "%s\n", HISTFILE_FIRST_LINE); 1527130803Smarcel 1528130803Smarcel fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); 1529130803Smarcel save_mlist(&mlist_search, f); 1530130803Smarcel 1531130803Smarcel#if SHELL_ESCAPE || PIPEC 1532130803Smarcel fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); 1533130803Smarcel save_mlist(&mlist_shell, f); 1534130803Smarcel#endif 1535130803Smarcel 1536130803Smarcel fclose(f); 1537130803Smarcel#endif /* CMD_HISTORY */ 1538130803Smarcel} 1539130803Smarcel