Imported Upstream version 487
[platform/upstream/less.git] / cmdbuf.c
1 /*
2  * Copyright (C) 1984-2016  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9
10
11 /*
12  * Functions which manipulate the command buffer.
13  * Used only by command() and related functions.
14  */
15
16 #include "less.h"
17 #include "cmd.h"
18 #include "charset.h"
19 #if HAVE_STAT
20 #include <sys/stat.h>
21 #endif
22
23 extern int sc_width;
24 extern int utf_mode;
25
26 static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
27 static int cmd_col;             /* Current column of the cursor */
28 static int prompt_col;          /* Column of cursor just after prompt */
29 static char *cp;                /* Pointer into cmdbuf */
30 static int cmd_offset;          /* Index into cmdbuf of first displayed char */
31 static int literal;             /* Next input char should not be interpreted */
32 static int updown_match = -1;   /* Prefix length in up/down movement */
33
34 #if TAB_COMPLETE_FILENAME
35 static int cmd_complete();
36 /*
37  * These variables are statics used by cmd_complete.
38  */
39 static int in_completion = 0;
40 static char *tk_text;
41 static char *tk_original;
42 static char *tk_ipoint;
43 static char *tk_trial;
44 static struct textlist tk_tlist;
45 #endif
46
47 static int cmd_left();
48 static int cmd_right();
49
50 #if SPACES_IN_FILENAMES
51 public char openquote = '"';
52 public char closequote = '"';
53 #endif
54
55 #if CMD_HISTORY
56
57 /* History file */
58 #define HISTFILE_FIRST_LINE      ".less-history-file:"
59 #define HISTFILE_SEARCH_SECTION  ".search"
60 #define HISTFILE_SHELL_SECTION   ".shell"
61
62 /*
63  * A mlist structure represents a command history.
64  */
65 struct mlist
66 {
67         struct mlist *next;
68         struct mlist *prev;
69         struct mlist *curr_mp;
70         char *string;
71         int modified;
72 };
73
74 /*
75  * These are the various command histories that exist.
76  */
77 struct mlist mlist_search =  
78         { &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
79 public void * constant ml_search = (void *) &mlist_search;
80
81 struct mlist mlist_examine = 
82         { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
83 public void * constant ml_examine = (void *) &mlist_examine;
84
85 #if SHELL_ESCAPE || PIPEC
86 struct mlist mlist_shell =   
87         { &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
88 public void * constant ml_shell = (void *) &mlist_shell;
89 #endif
90
91 #else /* CMD_HISTORY */
92
93 /* If CMD_HISTORY is off, these are just flags. */
94 public void * constant ml_search = (void *)1;
95 public void * constant ml_examine = (void *)2;
96 #if SHELL_ESCAPE || PIPEC
97 public void * constant ml_shell = (void *)3;
98 #endif
99
100 #endif /* CMD_HISTORY */
101
102 /*
103  * History for the current command.
104  */
105 static struct mlist *curr_mlist = NULL;
106 static int curr_cmdflags;
107
108 static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
109 static int cmd_mbc_buf_len;
110 static int cmd_mbc_buf_index;
111
112
113 /*
114  * Reset command buffer (to empty).
115  */
116         public void
117 cmd_reset()
118 {
119         cp = cmdbuf;
120         *cp = '\0';
121         cmd_col = 0;
122         cmd_offset = 0;
123         literal = 0;
124         cmd_mbc_buf_len = 0;
125         updown_match = -1;
126 }
127
128 /*
129  * Clear command line.
130  */
131         public void
132 clear_cmd()
133 {
134         cmd_col = prompt_col = 0;
135         cmd_mbc_buf_len = 0;
136         updown_match = -1;
137 }
138
139 /*
140  * Display a string, usually as a prompt for input into the command buffer.
141  */
142         public void
143 cmd_putstr(s)
144         char *s;
145 {
146         LWCHAR prev_ch = 0;
147         LWCHAR ch;
148         char *endline = s + strlen(s);
149         while (*s != '\0')
150         {
151                 char *ns = s;
152                 int width;
153                 ch = step_char(&ns, +1, endline);
154                 while (s < ns)
155                         putchr(*s++);
156                 if (!utf_mode)
157                         width = 1;
158                 else if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
159                         width = 0;
160                 else
161                         width = is_wide_char(ch) ? 2 : 1;
162                 cmd_col += width;
163                 prompt_col += width;
164                 prev_ch = ch;
165         }
166 }
167
168 /*
169  * How many characters are in the command buffer?
170  */
171         public int
172 len_cmdbuf()
173 {
174         char *s = cmdbuf;
175         char *endline = s + strlen(s);
176         int len = 0;
177
178         while (*s != '\0')
179         {
180                 step_char(&s, +1, endline);
181                 len++;
182         }
183         return (len);
184 }
185
186 /*
187  * Common part of cmd_step_right() and cmd_step_left().
188  * {{ Returning pwidth and bswidth separately is a historical artifact
189  *    since they're always the same. Maybe clean this up someday. }}
190  */
191         static char *
192 cmd_step_common(p, ch, len, pwidth, bswidth)
193         char *p;
194         LWCHAR ch;
195         int len;
196         int *pwidth;
197         int *bswidth;
198 {
199         char *pr;
200         int width;
201
202         if (len == 1)
203         {
204                 pr = prchar((int) ch);
205                 width = (int) strlen(pr);
206         } else
207         {
208                 pr = prutfchar(ch);
209                 if (is_composing_char(ch))
210                         width = 0;
211                 else if (is_ubin_char(ch))
212                         width = (int) strlen(pr);
213                 else
214                 {
215                         LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
216                         if (is_combining_char(prev_ch, ch))
217                                 width = 0;
218                         else
219                                 width = is_wide_char(ch) ? 2 : 1;
220                 }
221         }
222         if (pwidth != NULL)
223                 *pwidth = width;
224         if (bswidth != NULL)
225                 *bswidth = width;
226         return (pr);
227 }
228
229 /*
230  * Step a pointer one character right in the command buffer.
231  */
232         static char *
233 cmd_step_right(pp, pwidth, bswidth)
234         char **pp;
235         int *pwidth;
236         int *bswidth;
237 {
238         char *p = *pp;
239         LWCHAR ch = step_char(pp, +1, p + strlen(p));
240
241         return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
242 }
243
244 /*
245  * Step a pointer one character left in the command buffer.
246  */
247         static char *
248 cmd_step_left(pp, pwidth, bswidth)
249         char **pp;
250         int *pwidth;
251         int *bswidth;
252 {
253         char *p = *pp;
254         LWCHAR ch = step_char(pp, -1, cmdbuf);
255
256         return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
257 }
258
259 /*
260  * Repaint the line from cp onwards.
261  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
262  */
263         static void
264 cmd_repaint(old_cp)
265         char *old_cp;
266 {
267         /*
268          * Repaint the line from the current position.
269          */
270         clear_eol();
271         while (*cp != '\0')
272         {
273                 char *np = cp;
274                 int width;
275                 char *pr = cmd_step_right(&np, &width, NULL);
276                 if (cmd_col + width >= sc_width)
277                         break;
278                 cp = np;
279                 putstr(pr);
280                 cmd_col += width;
281         }
282         while (*cp != '\0')
283         {
284                 char *np = cp;
285                 int width;
286                 char *pr = cmd_step_right(&np, &width, NULL);
287                 if (width > 0)
288                         break;
289                 cp = np;
290                 putstr(pr);
291         }
292
293         /*
294          * Back up the cursor to the correct position.
295          */
296         while (cp > old_cp)
297                 cmd_left();
298 }
299
300 /*
301  * Put the cursor at "home" (just after the prompt),
302  * and set cp to the corresponding char in cmdbuf.
303  */
304         static void
305 cmd_home()
306 {
307         while (cmd_col > prompt_col)
308         {
309                 int width, bswidth;
310
311                 cmd_step_left(&cp, &width, &bswidth);
312                 while (bswidth-- > 0)
313                         putbs();
314                 cmd_col -= width;
315         }
316
317         cp = &cmdbuf[cmd_offset];
318 }
319
320 /*
321  * Shift the cmdbuf display left a half-screen.
322  */
323         static void
324 cmd_lshift()
325 {
326         char *s;
327         char *save_cp;
328         int cols;
329
330         /*
331          * Start at the first displayed char, count how far to the
332          * right we'd have to move to reach the center of the screen.
333          */
334         s = cmdbuf + cmd_offset;
335         cols = 0;
336         while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
337         {
338                 int width;
339                 cmd_step_right(&s, &width, NULL);
340                 cols += width;
341         }
342         while (*s != '\0')
343         {
344                 int width;
345                 char *ns = s;
346                 cmd_step_right(&ns, &width, NULL);
347                 if (width > 0)
348                         break;
349                 s = ns;
350         }
351
352         cmd_offset = (int) (s - cmdbuf);
353         save_cp = cp;
354         cmd_home();
355         cmd_repaint(save_cp);
356 }
357
358 /*
359  * Shift the cmdbuf display right a half-screen.
360  */
361         static void
362 cmd_rshift()
363 {
364         char *s;
365         char *save_cp;
366         int cols;
367
368         /*
369          * Start at the first displayed char, count how far to the
370          * left we'd have to move to traverse a half-screen width
371          * of displayed characters.
372          */
373         s = cmdbuf + cmd_offset;
374         cols = 0;
375         while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
376         {
377                 int width;
378                 cmd_step_left(&s, &width, NULL);
379                 cols += width;
380         }
381
382         cmd_offset = (int) (s - cmdbuf);
383         save_cp = cp;
384         cmd_home();
385         cmd_repaint(save_cp);
386 }
387
388 /*
389  * Move cursor right one character.
390  */
391         static int
392 cmd_right()
393 {
394         char *pr;
395         char *ncp;
396         int width;
397         
398         if (*cp == '\0')
399         {
400                 /* Already at the end of the line. */
401                 return (CC_OK);
402         }
403         ncp = cp;
404         pr = cmd_step_right(&ncp, &width, NULL);
405         if (cmd_col + width >= sc_width)
406                 cmd_lshift();
407         else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
408                 cmd_lshift();
409         cp = ncp;
410         cmd_col += width;
411         putstr(pr);
412         while (*cp != '\0')
413         {
414                 pr = cmd_step_right(&ncp, &width, NULL);
415                 if (width > 0)
416                         break;
417                 putstr(pr);
418                 cp = ncp;
419         }
420         return (CC_OK);
421 }
422
423 /*
424  * Move cursor left one character.
425  */
426         static int
427 cmd_left()
428 {
429         char *ncp;
430         int width, bswidth;
431         
432         if (cp <= cmdbuf)
433         {
434                 /* Already at the beginning of the line */
435                 return (CC_OK);
436         }
437         ncp = cp;
438         while (ncp > cmdbuf)
439         {
440                 cmd_step_left(&ncp, &width, &bswidth);
441                 if (width > 0)
442                         break;
443         }
444         if (cmd_col < prompt_col + width)
445                 cmd_rshift();
446         cp = ncp;
447         cmd_col -= width;
448         while (bswidth-- > 0)
449                 putbs();
450         return (CC_OK);
451 }
452
453 /*
454  * Insert a char into the command buffer, at the current position.
455  */
456         static int
457 cmd_ichar(cs, clen)
458         char *cs;
459         int clen;
460 {
461         char *s;
462         
463         if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1)
464         {
465                 /* No room in the command buffer for another char. */
466                 bell();
467                 return (CC_ERROR);
468         }
469                 
470         /*
471          * Make room for the new character (shift the tail of the buffer right).
472          */
473         for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
474                 s[clen] = s[0];
475         /*
476          * Insert the character into the buffer.
477          */
478         for (s = cp;  s < cp + clen;  s++)
479                 *s = *cs++;
480         /*
481          * Reprint the tail of the line from the inserted char.
482          */
483         updown_match = -1;
484         cmd_repaint(cp);
485         cmd_right();
486         return (CC_OK);
487 }
488
489 /*
490  * Backspace in the command buffer.
491  * Delete the char to the left of the cursor.
492  */
493         static int
494 cmd_erase()
495 {
496         register char *s;
497         int clen;
498
499         if (cp == cmdbuf)
500         {
501                 /*
502                  * Backspace past beginning of the buffer:
503                  * this usually means abort the command.
504                  */
505                 return (CC_QUIT);
506         }
507         /*
508          * Move cursor left (to the char being erased).
509          */
510         s = cp;
511         cmd_left();
512         clen = (int) (s - cp);
513
514         /*
515          * Remove the char from the buffer (shift the buffer left).
516          */
517         for (s = cp;  ;  s++)
518         {
519                 s[0] = s[clen];
520                 if (s[0] == '\0')
521                         break;
522         }
523
524         /*
525          * Repaint the buffer after the erased char.
526          */
527         updown_match = -1;
528         cmd_repaint(cp);
529         
530         /*
531          * We say that erasing the entire command string causes us
532          * to abort the current command, if CF_QUIT_ON_ERASE is set.
533          */
534         if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
535                 return (CC_QUIT);
536         return (CC_OK);
537 }
538
539 /*
540  * Delete the char under the cursor.
541  */
542         static int
543 cmd_delete()
544 {
545         if (*cp == '\0')
546         {
547                 /* At end of string; there is no char under the cursor. */
548                 return (CC_OK);
549         }
550         /*
551          * Move right, then use cmd_erase.
552          */
553         cmd_right();
554         cmd_erase();
555         return (CC_OK);
556 }
557
558 /*
559  * Delete the "word" to the left of the cursor.
560  */
561         static int
562 cmd_werase()
563 {
564         if (cp > cmdbuf && cp[-1] == ' ')
565         {
566                 /*
567                  * If the char left of cursor is a space,
568                  * erase all the spaces left of cursor (to the first non-space).
569                  */
570                 while (cp > cmdbuf && cp[-1] == ' ')
571                         (void) cmd_erase();
572         } else
573         {
574                 /*
575                  * If the char left of cursor is not a space,
576                  * erase all the nonspaces left of cursor (the whole "word").
577                  */
578                 while (cp > cmdbuf && cp[-1] != ' ')
579                         (void) cmd_erase();
580         }
581         return (CC_OK);
582 }
583
584 /*
585  * Delete the "word" under the cursor.
586  */
587         static int
588 cmd_wdelete()
589 {
590         if (*cp == ' ')
591         {
592                 /*
593                  * If the char under the cursor is a space,
594                  * delete it and all the spaces right of cursor.
595                  */
596                 while (*cp == ' ')
597                         (void) cmd_delete();
598         } else
599         {
600                 /*
601                  * If the char under the cursor is not a space,
602                  * delete it and all nonspaces right of cursor (the whole word).
603                  */
604                 while (*cp != ' ' && *cp != '\0')
605                         (void) cmd_delete();
606         }
607         return (CC_OK);
608 }
609
610 /*
611  * Delete all chars in the command buffer.
612  */
613         static int
614 cmd_kill()
615 {
616         if (cmdbuf[0] == '\0')
617         {
618                 /* Buffer is already empty; abort the current command. */
619                 return (CC_QUIT);
620         }
621         cmd_offset = 0;
622         cmd_home();
623         *cp = '\0';
624         updown_match = -1;
625         cmd_repaint(cp);
626
627         /*
628          * We say that erasing the entire command string causes us
629          * to abort the current command, if CF_QUIT_ON_ERASE is set.
630          */
631         if (curr_cmdflags & CF_QUIT_ON_ERASE)
632                 return (CC_QUIT);
633         return (CC_OK);
634 }
635
636 /*
637  * Select an mlist structure to be the current command history.
638  */
639         public void
640 set_mlist(mlist, cmdflags)
641         void *mlist;
642         int cmdflags;
643 {
644 #if CMD_HISTORY
645         curr_mlist = (struct mlist *) mlist;
646         curr_cmdflags = cmdflags;
647
648         /* Make sure the next up-arrow moves to the last string in the mlist. */
649         if (curr_mlist != NULL)
650                 curr_mlist->curr_mp = curr_mlist;
651 #endif
652 }
653
654 #if CMD_HISTORY
655 /*
656  * Move up or down in the currently selected command history list.
657  * Only consider entries whose first updown_match chars are equal to
658  * cmdbuf's corresponding chars.
659  */
660         static int
661 cmd_updown(action)
662         int action;
663 {
664         char *s;
665         struct mlist *ml;
666         
667         if (curr_mlist == NULL)
668         {
669                 /*
670                  * The current command has no history list.
671                  */
672                 bell();
673                 return (CC_OK);
674         }
675
676         if (updown_match < 0)
677         {
678                 updown_match = (int) (cp - cmdbuf);
679         }
680
681         /*
682          * Find the next history entry which matches.
683          */
684         for (ml = curr_mlist->curr_mp;;)
685         {
686                 ml = (action == EC_UP) ? ml->prev : ml->next;
687                 if (ml == curr_mlist)
688                 {
689                         /*
690                          * We reached the end (or beginning) of the list.
691                          */
692                         break;
693                 }
694                 if (strncmp(cmdbuf, ml->string, updown_match) == 0)
695                 {
696                         /*
697                          * This entry matches; stop here.
698                          * Copy the entry into cmdbuf and echo it on the screen.
699                          */
700                         curr_mlist->curr_mp = ml;
701                         s = ml->string;
702                         if (s == NULL)
703                                 s = "";
704                         cmd_home();
705                         clear_eol();
706                         strcpy(cmdbuf, s);
707                         for (cp = cmdbuf;  *cp != '\0';  )
708                                 cmd_right();
709                         return (CC_OK);
710                 }
711         }
712         /*
713          * We didn't find a history entry that matches.
714          */
715         bell();
716         return (CC_OK);
717 }
718 #endif
719
720 /*
721  * Add a string to an mlist.
722  */
723         public void
724 cmd_addhist(mlist, cmd, modified)
725         struct mlist *mlist;
726         char *cmd;
727         int modified;
728 {
729 #if CMD_HISTORY
730         struct mlist *ml;
731         
732         /*
733          * Don't save a trivial command.
734          */
735         if (strlen(cmd) == 0)
736                 return;
737
738         /*
739          * Save the command unless it's a duplicate of the
740          * last command in the history.
741          */
742         ml = mlist->prev;
743         if (ml == mlist || strcmp(ml->string, cmd) != 0)
744         {
745                 /*
746                  * Did not find command in history.
747                  * Save the command and put it at the end of the history list.
748                  */
749                 ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
750                 ml->string = save(cmd);
751                 ml->modified = modified;
752                 ml->next = mlist;
753                 ml->prev = mlist->prev;
754                 mlist->prev->next = ml;
755                 mlist->prev = ml;
756         }
757         /*
758          * Point to the cmd just after the just-accepted command.
759          * Thus, an UPARROW will always retrieve the previous command.
760          */
761         mlist->curr_mp = ml->next;
762 #endif
763 }
764
765 /*
766  * Accept the command in the command buffer.
767  * Add it to the currently selected history list.
768  */
769         public void
770 cmd_accept()
771 {
772 #if CMD_HISTORY
773         /*
774          * Nothing to do if there is no currently selected history list.
775          */
776         if (curr_mlist == NULL)
777                 return;
778         cmd_addhist(curr_mlist, cmdbuf, 1);
779         curr_mlist->modified = 1;
780 #endif
781 }
782
783 /*
784  * Try to perform a line-edit function on the command buffer,
785  * using a specified char as a line-editing command.
786  * Returns:
787  *      CC_PASS The char does not invoke a line edit function.
788  *      CC_OK   Line edit function done.
789  *      CC_QUIT The char requests the current command to be aborted.
790  */
791         static int
792 cmd_edit(c)
793         int c;
794 {
795         int action;
796         int flags;
797
798 #if TAB_COMPLETE_FILENAME
799 #define not_in_completion()     in_completion = 0
800 #else
801 #define not_in_completion()
802 #endif
803         
804         /*
805          * See if the char is indeed a line-editing command.
806          */
807         flags = 0;
808 #if CMD_HISTORY
809         if (curr_mlist == NULL)
810                 /*
811                  * No current history; don't accept history manipulation cmds.
812                  */
813                 flags |= EC_NOHISTORY;
814 #endif
815 #if TAB_COMPLETE_FILENAME
816         if (curr_mlist == ml_search)
817                 /*
818                  * In a search command; don't accept file-completion cmds.
819                  */
820                 flags |= EC_NOCOMPLETE;
821 #endif
822
823         action = editchar(c, flags);
824
825         switch (action)
826         {
827         case EC_RIGHT:
828                 not_in_completion();
829                 return (cmd_right());
830         case EC_LEFT:
831                 not_in_completion();
832                 return (cmd_left());
833         case EC_W_RIGHT:
834                 not_in_completion();
835                 while (*cp != '\0' && *cp != ' ')
836                         cmd_right();
837                 while (*cp == ' ')
838                         cmd_right();
839                 return (CC_OK);
840         case EC_W_LEFT:
841                 not_in_completion();
842                 while (cp > cmdbuf && cp[-1] == ' ')
843                         cmd_left();
844                 while (cp > cmdbuf && cp[-1] != ' ')
845                         cmd_left();
846                 return (CC_OK);
847         case EC_HOME:
848                 not_in_completion();
849                 cmd_offset = 0;
850                 cmd_home();
851                 cmd_repaint(cp);
852                 return (CC_OK);
853         case EC_END:
854                 not_in_completion();
855                 while (*cp != '\0')
856                         cmd_right();
857                 return (CC_OK);
858         case EC_INSERT:
859                 not_in_completion();
860                 return (CC_OK);
861         case EC_BACKSPACE:
862                 not_in_completion();
863                 return (cmd_erase());
864         case EC_LINEKILL:
865                 not_in_completion();
866                 return (cmd_kill());
867         case EC_ABORT:
868                 not_in_completion();
869                 (void) cmd_kill();
870                 return (CC_QUIT);
871         case EC_W_BACKSPACE:
872                 not_in_completion();
873                 return (cmd_werase());
874         case EC_DELETE:
875                 not_in_completion();
876                 return (cmd_delete());
877         case EC_W_DELETE:
878                 not_in_completion();
879                 return (cmd_wdelete());
880         case EC_LITERAL:
881                 literal = 1;
882                 return (CC_OK);
883 #if CMD_HISTORY
884         case EC_UP:
885         case EC_DOWN:
886                 not_in_completion();
887                 return (cmd_updown(action));
888 #endif
889 #if TAB_COMPLETE_FILENAME
890         case EC_F_COMPLETE:
891         case EC_B_COMPLETE:
892         case EC_EXPAND:
893                 return (cmd_complete(action));
894 #endif
895         case EC_NOACTION:
896                 return (CC_OK);
897         default:
898                 not_in_completion();
899                 return (CC_PASS);
900         }
901 }
902
903 #if TAB_COMPLETE_FILENAME
904 /*
905  * Insert a string into the command buffer, at the current position.
906  */
907         static int
908 cmd_istr(str)
909         char *str;
910 {
911         char *s;
912         int action;
913         char *endline = str + strlen(str);
914         
915         for (s = str;  *s != '\0';  )
916         {
917                 char *os = s;
918                 step_char(&s, +1, endline);
919                 action = cmd_ichar(os, s - os);
920                 if (action != CC_OK)
921                 {
922                         bell();
923                         return (action);
924                 }
925         }
926         return (CC_OK);
927 }
928
929 /*
930  * Find the beginning and end of the "current" word.
931  * This is the word which the cursor (cp) is inside or at the end of.
932  * Return pointer to the beginning of the word and put the
933  * cursor at the end of the word.
934  */
935         static char *
936 delimit_word()
937 {
938         char *word;
939 #if SPACES_IN_FILENAMES
940         char *p;
941         int delim_quoted = 0;
942         int meta_quoted = 0;
943         char *esc = get_meta_escape();
944         int esclen = (int) strlen(esc);
945 #endif
946         
947         /*
948          * Move cursor to end of word.
949          */
950         if (*cp != ' ' && *cp != '\0')
951         {
952                 /*
953                  * Cursor is on a nonspace.
954                  * Move cursor right to the next space.
955                  */
956                 while (*cp != ' ' && *cp != '\0')
957                         cmd_right();
958         } else if (cp > cmdbuf && cp[-1] != ' ')
959         {
960                 /*
961                  * Cursor is on a space, and char to the left is a nonspace.
962                  * We're already at the end of the word.
963                  */
964                 ;
965 #if 0
966         } else
967         {
968                 /*
969                  * Cursor is on a space and char to the left is a space.
970                  * Huh? There's no word here.
971                  */
972                 return (NULL);
973 #endif
974         }
975         /*
976          * Find the beginning of the word which the cursor is in.
977          */
978         if (cp == cmdbuf)
979                 return (NULL);
980 #if SPACES_IN_FILENAMES
981         /*
982          * If we have an unbalanced quote (that is, an open quote
983          * without a corresponding close quote), we return everything
984          * from the open quote, including spaces.
985          */
986         for (word = cmdbuf;  word < cp;  word++)
987                 if (*word != ' ')
988                         break;
989         if (word >= cp)
990                 return (cp);
991         for (p = cmdbuf;  p < cp;  p++)
992         {
993                 if (meta_quoted)
994                 {
995                         meta_quoted = 0;
996                 } else if (esclen > 0 && p + esclen < cp &&
997                            strncmp(p, esc, esclen) == 0)
998                 {
999                         meta_quoted = 1;
1000                         p += esclen - 1;
1001                 } else if (delim_quoted)
1002                 {
1003                         if (*p == closequote)
1004                                 delim_quoted = 0;
1005                 } else /* (!delim_quoted) */
1006                 {
1007                         if (*p == openquote)
1008                                 delim_quoted = 1;
1009                         else if (*p == ' ')
1010                                 word = p+1;
1011                 }
1012         }
1013 #endif
1014         return (word);
1015 }
1016
1017 /*
1018  * Set things up to enter completion mode.
1019  * Expand the word under the cursor into a list of filenames 
1020  * which start with that word, and set tk_text to that list.
1021  */
1022         static void
1023 init_compl()
1024 {
1025         char *word;
1026         char c;
1027         
1028         /*
1029          * Get rid of any previous tk_text.
1030          */
1031         if (tk_text != NULL)
1032         {
1033                 free(tk_text);
1034                 tk_text = NULL;
1035         }
1036         /*
1037          * Find the original (uncompleted) word in the command buffer.
1038          */
1039         word = delimit_word();
1040         if (word == NULL)
1041                 return;
1042         /*
1043          * Set the insertion point to the point in the command buffer
1044          * where the original (uncompleted) word now sits.
1045          */
1046         tk_ipoint = word;
1047         /*
1048          * Save the original (uncompleted) word
1049          */
1050         if (tk_original != NULL)
1051                 free(tk_original);
1052         tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
1053         strncpy(tk_original, word, cp-word);
1054         /*
1055          * Get the expanded filename.
1056          * This may result in a single filename, or
1057          * a blank-separated list of filenames.
1058          */
1059         c = *cp;
1060         *cp = '\0';
1061         if (*word != openquote)
1062         {
1063                 tk_text = fcomplete(word);
1064         } else
1065         {
1066 #if MSDOS_COMPILER
1067                 char *qword = NULL;
1068 #else
1069                 char *qword = shell_quote(word+1);
1070 #endif
1071                 if (qword == NULL)
1072                         tk_text = fcomplete(word+1);
1073                 else
1074                 {
1075                         tk_text = fcomplete(qword);
1076                         free(qword);
1077                 }
1078         }
1079         *cp = c;
1080 }
1081
1082 /*
1083  * Return the next word in the current completion list.
1084  */
1085         static char *
1086 next_compl(action, prev)
1087         int action;
1088         char *prev;
1089 {
1090         switch (action)
1091         {
1092         case EC_F_COMPLETE:
1093                 return (forw_textlist(&tk_tlist, prev));
1094         case EC_B_COMPLETE:
1095                 return (back_textlist(&tk_tlist, prev));
1096         }
1097         /* Cannot happen */
1098         return ("?");
1099 }
1100
1101 /*
1102  * Complete the filename before (or under) the cursor.
1103  * cmd_complete may be called multiple times.  The global in_completion
1104  * remembers whether this call is the first time (create the list),
1105  * or a subsequent time (step thru the list).
1106  */
1107         static int
1108 cmd_complete(action)
1109         int action;
1110 {
1111         char *s;
1112
1113         if (!in_completion || action == EC_EXPAND)
1114         {
1115                 /*
1116                  * Expand the word under the cursor and 
1117                  * use the first word in the expansion 
1118                  * (or the entire expansion if we're doing EC_EXPAND).
1119                  */
1120                 init_compl();
1121                 if (tk_text == NULL)
1122                 {
1123                         bell();
1124                         return (CC_OK);
1125                 }
1126                 if (action == EC_EXPAND)
1127                 {
1128                         /*
1129                          * Use the whole list.
1130                          */
1131                         tk_trial = tk_text;
1132                 } else
1133                 {
1134                         /*
1135                          * Use the first filename in the list.
1136                          */
1137                         in_completion = 1;
1138                         init_textlist(&tk_tlist, tk_text);
1139                         tk_trial = next_compl(action, (char*)NULL);
1140                 }
1141         } else
1142         {
1143                 /*
1144                  * We already have a completion list.
1145                  * Use the next/previous filename from the list.
1146                  */
1147                 tk_trial = next_compl(action, tk_trial);
1148         }
1149         
1150         /*
1151          * Remove the original word, or the previous trial completion.
1152          */
1153         while (cp > tk_ipoint)
1154                 (void) cmd_erase();
1155         
1156         if (tk_trial == NULL)
1157         {
1158                 /*
1159                  * There are no more trial completions.
1160                  * Insert the original (uncompleted) filename.
1161                  */
1162                 in_completion = 0;
1163                 if (cmd_istr(tk_original) != CC_OK)
1164                         goto fail;
1165         } else
1166         {
1167                 /*
1168                  * Insert trial completion.
1169                  */
1170                 if (cmd_istr(tk_trial) != CC_OK)
1171                         goto fail;
1172                 /*
1173                  * If it is a directory, append a slash.
1174                  */
1175                 if (is_dir(tk_trial))
1176                 {
1177                         if (cp > cmdbuf && cp[-1] == closequote)
1178                                 (void) cmd_erase();
1179                         s = lgetenv("LESSSEPARATOR");
1180                         if (s == NULL)
1181                                 s = PATHNAME_SEP;
1182                         if (cmd_istr(s) != CC_OK)
1183                                 goto fail;
1184                 }
1185         }
1186         
1187         return (CC_OK);
1188         
1189 fail:
1190         in_completion = 0;
1191         bell();
1192         return (CC_OK);
1193 }
1194
1195 #endif /* TAB_COMPLETE_FILENAME */
1196
1197 /*
1198  * Process a single character of a multi-character command, such as
1199  * a number, or the pattern of a search command.
1200  * Returns:
1201  *      CC_OK           The char was accepted.
1202  *      CC_QUIT         The char requests the command to be aborted.
1203  *      CC_ERROR        The char could not be accepted due to an error.
1204  */
1205         public int
1206 cmd_char(c)
1207         int c;
1208 {
1209         int action;
1210         int len;
1211
1212         if (!utf_mode)
1213         {
1214                 cmd_mbc_buf[0] = c;
1215                 len = 1;
1216         } else
1217         {
1218                 /* Perform strict validation in all possible cases.  */
1219                 if (cmd_mbc_buf_len == 0)
1220                 {
1221                  retry:
1222                         cmd_mbc_buf_index = 1;
1223                         *cmd_mbc_buf = c;
1224                         if (IS_ASCII_OCTET(c))
1225                                 cmd_mbc_buf_len = 1;
1226                         else if (IS_UTF8_LEAD(c))
1227                         {
1228                                 cmd_mbc_buf_len = utf_len(c);
1229                                 return (CC_OK);
1230                         } else
1231                         {
1232                                 /* UTF8_INVALID or stray UTF8_TRAIL */
1233                                 bell();
1234                                 return (CC_ERROR);
1235                         }
1236                 } else if (IS_UTF8_TRAIL(c))
1237                 {
1238                         cmd_mbc_buf[cmd_mbc_buf_index++] = c;
1239                         if (cmd_mbc_buf_index < cmd_mbc_buf_len)
1240                                 return (CC_OK);
1241                         if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index))
1242                         {
1243                                 /* complete, but not well formed (non-shortest form), sequence */
1244                                 cmd_mbc_buf_len = 0;
1245                                 bell();
1246                                 return (CC_ERROR);
1247                         }
1248                 } else
1249                 {
1250                         /* Flush incomplete (truncated) sequence.  */
1251                         cmd_mbc_buf_len = 0;
1252                         bell();
1253                         /* Handle new char.  */
1254                         goto retry;
1255                 }
1256
1257                 len = cmd_mbc_buf_len;
1258                 cmd_mbc_buf_len = 0;
1259         }
1260
1261         if (literal)
1262         {
1263                 /*
1264                  * Insert the char, even if it is a line-editing char.
1265                  */
1266                 literal = 0;
1267                 return (cmd_ichar(cmd_mbc_buf, len));
1268         }
1269                 
1270         /*
1271          * See if it is a line-editing character.
1272          */
1273         if (in_mca() && len == 1)
1274         {
1275                 action = cmd_edit(c);
1276                 switch (action)
1277                 {
1278                 case CC_OK:
1279                 case CC_QUIT:
1280                         return (action);
1281                 case CC_PASS:
1282                         break;
1283                 }
1284         }
1285         
1286         /*
1287          * Insert the char into the command buffer.
1288          */
1289         return (cmd_ichar(cmd_mbc_buf, len));
1290 }
1291
1292 /*
1293  * Return the number currently in the command buffer.
1294  */
1295         public LINENUM
1296 cmd_int(frac)
1297         long *frac;
1298 {
1299         char *p;
1300         LINENUM n = 0;
1301         int err;
1302
1303         for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
1304                 n = (n * 10) + (*p - '0');
1305         *frac = 0;
1306         if (*p++ == '.')
1307         {
1308                 *frac = getfraction(&p, NULL, &err);
1309                 /* {{ do something if err is set? }} */
1310         }
1311         return (n);
1312 }
1313
1314 /*
1315  * Return a pointer to the command buffer.
1316  */
1317         public char *
1318 get_cmdbuf()
1319 {
1320         return (cmdbuf);
1321 }
1322
1323 #if CMD_HISTORY
1324 /*
1325  * Return the last (most recent) string in the current command history.
1326  */
1327         public char *
1328 cmd_lastpattern()
1329 {
1330         if (curr_mlist == NULL)
1331                 return (NULL);
1332         return (curr_mlist->curr_mp->prev->string);
1333 }
1334 #endif
1335
1336 #if CMD_HISTORY
1337 /*
1338  */
1339         static int
1340 mlist_size(ml)
1341         struct mlist *ml;
1342 {
1343         int size = 0;
1344         for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1345                 ++size;
1346         return size;
1347 }
1348
1349 /*
1350  * Get the name of the history file.
1351  */
1352         static char *
1353 histfile_name()
1354 {
1355         char *home;
1356         char *name;
1357         int len;
1358         
1359         /* See if filename is explicitly specified by $LESSHISTFILE. */
1360         name = lgetenv("LESSHISTFILE");
1361         if (name != NULL && *name != '\0')
1362         {
1363                 if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1364                         /* $LESSHISTFILE == "-" means don't use a history file. */
1365                         return (NULL);
1366                 return (save(name));
1367         }
1368
1369         /* See if history file is disabled in the build. */
1370         if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0)
1371                 return (NULL);
1372
1373         /* Otherwise, file is in $HOME. */
1374         home = lgetenv("HOME");
1375         if (home == NULL || *home == '\0')
1376         {
1377 #if OS2
1378                 home = lgetenv("INIT");
1379                 if (home == NULL || *home == '\0')
1380 #endif
1381                         return (NULL);
1382         }
1383         len = (int) (strlen(home) + strlen(LESSHISTFILE) + 2);
1384         name = (char *) ecalloc(len, sizeof(char));
1385         SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE);
1386         return (name);
1387 }
1388
1389 /*
1390  * Read a .lesshst file and call a callback for each line in the file.
1391  */
1392         static void
1393 read_cmdhist2(action, uparam, skip_search, skip_shell)
1394         void (*action)(void*,struct mlist*,char*);
1395         void *uparam;
1396         int skip_search;
1397         int skip_shell;
1398 {
1399         struct mlist *ml = NULL;
1400         char line[CMDBUF_SIZE];
1401         char *filename;
1402         FILE *f;
1403         char *p;
1404         int *skip = NULL;
1405
1406         filename = histfile_name();
1407         if (filename == NULL)
1408                 return;
1409         f = fopen(filename, "r");
1410         free(filename);
1411         if (f == NULL)
1412                 return;
1413         if (fgets(line, sizeof(line), f) == NULL ||
1414             strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0)
1415         {
1416                 fclose(f);
1417                 return;
1418         }
1419         while (fgets(line, sizeof(line), f) != NULL)
1420         {
1421                 for (p = line;  *p != '\0';  p++)
1422                 {
1423                         if (*p == '\n' || *p == '\r')
1424                         {
1425                                 *p = '\0';
1426                                 break;
1427                         }
1428                 }
1429                 if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
1430                 {
1431                         ml = &mlist_search;
1432                         skip = &skip_search;
1433                 } else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0)
1434                 {
1435 #if SHELL_ESCAPE || PIPEC
1436                         ml = &mlist_shell;
1437                         skip = &skip_shell;
1438 #else
1439                         ml = NULL;
1440                         skip = NULL;
1441 #endif
1442                 } else if (*line == '"')
1443                 {
1444                         if (ml != NULL)
1445                         {
1446                                 if (skip != NULL && *skip > 0)
1447                                         --(*skip);
1448                                 else
1449                                         (*action)(uparam, ml, line+1);
1450                         }
1451                 }
1452         }
1453         fclose(f);
1454 }
1455
1456         static void
1457 read_cmdhist(action, uparam, skip_search, skip_shell)
1458         void (*action)(void*,struct mlist*,char*);
1459         void *uparam;
1460         int skip_search;
1461         int skip_shell;
1462 {
1463         read_cmdhist2(action, uparam, skip_search, skip_shell);
1464         (*action)(uparam, NULL, NULL); /* signal end of file */
1465 }
1466
1467         static void
1468 addhist_init(void *uparam, struct mlist *ml, char *string)
1469 {
1470         if (ml == NULL || string == NULL)
1471                 return;
1472         cmd_addhist(ml, string, 0);
1473 }
1474 #endif /* CMD_HISTORY */
1475
1476 /*
1477  * Initialize history from a .lesshist file.
1478  */
1479         public void
1480 init_cmdhist()
1481 {
1482 #if CMD_HISTORY
1483         read_cmdhist(&addhist_init, NULL, 0, 0);
1484 #endif /* CMD_HISTORY */
1485 }
1486
1487 /*
1488  * Write the header for a section of the history file.
1489  */
1490 #if CMD_HISTORY
1491         static void
1492 write_mlist_header(ml, f)
1493         struct mlist *ml;
1494         FILE *f;
1495 {
1496         if (ml == &mlist_search)
1497                 fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1498 #if SHELL_ESCAPE || PIPEC
1499         else if (ml == &mlist_shell)
1500                 fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1501 #endif
1502 }
1503
1504 /*
1505  * Write all modified entries in an mlist to the history file.
1506  */
1507         static void
1508 write_mlist(ml, f)
1509         struct mlist *ml;
1510         FILE *f;
1511 {
1512         for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1513         {
1514                 if (!ml->modified)
1515                         continue;
1516                 fprintf(f, "\"%s\n", ml->string);
1517                 ml->modified = 0;
1518         }
1519         ml->modified = 0; /* entire mlist is now unmodified */
1520 }
1521
1522 /*
1523  * Make a temp name in the same directory as filename.
1524  */
1525         static char *
1526 make_tempname(filename)
1527         char *filename;
1528 {
1529         char lastch;
1530         char *tempname = ecalloc(1, strlen(filename)+1);
1531         strcpy(tempname, filename);
1532         lastch = tempname[strlen(tempname)-1];
1533         tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q';
1534         return tempname;
1535 }
1536
1537 struct save_ctx
1538 {
1539         struct mlist *mlist;
1540         FILE *fout;
1541 };
1542
1543 /*
1544  * Copy entries from the saved history file to a new file.
1545  * At the end of each mlist, append any new entries
1546  * created during this session.
1547  */
1548         static void
1549 copy_hist(void *uparam, struct mlist *ml, char *string)
1550 {
1551         struct save_ctx *ctx = (struct save_ctx *) uparam;
1552
1553         if (ml != ctx->mlist) {
1554                 /* We're changing mlists. */
1555                 if (ctx->mlist)
1556                         /* Append any new entries to the end of the current mlist. */
1557                         write_mlist(ctx->mlist, ctx->fout);
1558                 /* Write the header for the new mlist. */
1559                 ctx->mlist = ml;
1560                 write_mlist_header(ctx->mlist, ctx->fout);
1561         }
1562         if (string != NULL)
1563         {
1564                 /* Copy the entry. */
1565                 fprintf(ctx->fout, "\"%s\n", string);
1566         }
1567         if (ml == NULL) /* End of file */
1568         {
1569                 /* Write any sections that were not in the original file. */
1570                 if (mlist_search.modified)
1571                 {
1572                         write_mlist_header(&mlist_search, ctx->fout);
1573                         write_mlist(&mlist_search, ctx->fout);
1574                 }
1575 #if SHELL_ESCAPE || PIPEC
1576                 if (mlist_shell.modified)
1577                 {
1578                         write_mlist_header(&mlist_shell, ctx->fout);
1579                         write_mlist(&mlist_shell, ctx->fout);
1580                 }
1581 #endif
1582         }
1583 }
1584 #endif /* CMD_HISTORY */
1585
1586 /*
1587  * Make a file readable only by its owner.
1588  */
1589         static void
1590 make_file_private(f)
1591         FILE *f;
1592 {
1593 #if HAVE_FCHMOD
1594         int do_chmod = 1;
1595 #if HAVE_STAT
1596         struct stat statbuf;
1597         int r = fstat(fileno(f), &statbuf);
1598         if (r < 0 || !S_ISREG(statbuf.st_mode))
1599                 /* Don't chmod if not a regular file. */
1600                 do_chmod = 0;
1601 #endif
1602         if (do_chmod)
1603                 fchmod(fileno(f), 0600);
1604 #endif
1605 }
1606
1607 /*
1608  * Does the history file need to be updated?
1609  */
1610         static int
1611 histfile_modified()
1612 {
1613         if (mlist_search.modified)
1614                 return 1;
1615 #if SHELL_ESCAPE || PIPEC
1616         if (mlist_shell.modified)
1617                 return 1;
1618 #endif
1619         return 0;
1620 }
1621
1622 /*
1623  * Update the .lesshst file.
1624  */
1625         public void
1626 save_cmdhist()
1627 {
1628 #if CMD_HISTORY
1629         char *histname;
1630         char *tempname;
1631         int skip_search;
1632         int skip_shell;
1633         struct save_ctx ctx;
1634         char *s;
1635         FILE *fout = NULL;
1636         int histsize = 0;
1637
1638         if (!histfile_modified())
1639                 return;
1640         histname = histfile_name();
1641         if (histname == NULL)
1642                 return;
1643         tempname = make_tempname(histname);
1644         fout = fopen(tempname, "w");
1645         if (fout != NULL)
1646         {
1647                 make_file_private(fout);
1648                 s = lgetenv("LESSHISTSIZE");
1649                 if (s != NULL)
1650                         histsize = atoi(s);
1651                 if (histsize <= 0)
1652                         histsize = 100;
1653                 skip_search = mlist_size(&mlist_search) - histsize;
1654 #if SHELL_ESCAPE || PIPEC
1655                 skip_shell = mlist_size(&mlist_shell) - histsize;
1656 #endif
1657                 fprintf(fout, "%s\n", HISTFILE_FIRST_LINE);
1658                 ctx.fout = fout;
1659                 ctx.mlist = NULL;
1660                 read_cmdhist(copy_hist, &ctx, skip_search, skip_shell);
1661                 fclose(fout);
1662 #if MSDOS_COMPILER==WIN32C
1663                 /*
1664                  * Windows rename doesn't remove an existing file,
1665                  * making it useless for atomic operations. Sigh.
1666                  */
1667                 remove(histname);
1668 #endif
1669                 rename(tempname, histname);
1670         }
1671         free(tempname);
1672         free(histname);
1673 #endif /* CMD_HISTORY */
1674 }