Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/less/command.c
39476 views
1
/*
2
* Copyright (C) 1984-2025 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
* User-level command processor.
13
*/
14
15
#include "less.h"
16
#if MSDOS_COMPILER==WIN32C
17
#include <windows.h>
18
#endif
19
#include "position.h"
20
#include "option.h"
21
#include "cmd.h"
22
23
extern int erase_char, erase2_char, kill_char;
24
extern int sigs;
25
extern int quit_if_one_screen;
26
extern int one_screen;
27
extern int sc_width;
28
extern int sc_height;
29
extern char *kent;
30
extern int swindow;
31
extern int jump_sline;
32
extern lbool quitting;
33
extern int wscroll;
34
extern int top_scroll;
35
extern int ignore_eoi;
36
extern int hshift;
37
extern int bs_mode;
38
extern int proc_backspace;
39
extern int show_attn;
40
extern int less_is_more;
41
extern int chopline;
42
extern POSITION highest_hilite;
43
extern char *every_first_cmd;
44
extern char version[];
45
extern struct scrpos initial_scrpos;
46
extern IFILE curr_ifile;
47
extern void *ml_search;
48
extern void *ml_examine;
49
extern int wheel_lines;
50
extern int def_search_type;
51
extern lbool search_wrapped;
52
extern int no_paste;
53
extern lbool pasting;
54
extern int no_edit_warn;
55
extern POSITION soft_eof;
56
#if SHELL_ESCAPE || PIPEC
57
extern void *ml_shell;
58
#endif
59
#if EDITOR
60
extern constant char *editproto;
61
#endif
62
#if OSC8_LINK
63
extern char *osc8_uri;
64
#endif
65
extern int shift_count;
66
extern int forw_prompt;
67
extern int incr_search;
68
extern int full_screen;
69
#if MSDOS_COMPILER==WIN32C
70
extern int utf_mode;
71
extern unsigned less_acp;
72
#endif
73
74
#if SHELL_ESCAPE
75
static char *shellcmd = NULL; /* For holding last shell command for "!!" */
76
#endif
77
static int mca; /* The multicharacter command (action) */
78
static int search_type; /* The previous type of search */
79
static int last_search_type; /* Type of last executed search */
80
static LINENUM number; /* The number typed by the user */
81
static long fraction; /* The fractional part of the number */
82
static struct loption *curropt;
83
static lbool opt_lower;
84
static int optflag;
85
static lbool optgetname;
86
static POSITION bottompos;
87
static int save_hshift;
88
static int save_bs_mode;
89
static int save_proc_backspace;
90
static int screen_trashed_value = 0;
91
static lbool literal_char = FALSE;
92
static lbool ignoring_input = FALSE;
93
#if HAVE_TIME
94
static time_type ignoring_input_time;
95
#endif
96
#if PIPEC
97
static char pipec;
98
#endif
99
100
/* Stack of ungotten chars (via ungetcc) */
101
struct ungot {
102
struct ungot *ug_next;
103
char ug_char;
104
lbool ug_end_command;
105
};
106
static struct ungot* ungot = NULL;
107
108
static void multi_search(constant char *pattern, int n, int silent);
109
110
/*
111
* Move the cursor to start of prompt line before executing a command.
112
* This looks nicer if the command takes a long time before
113
* updating the screen.
114
*/
115
public void cmd_exec(void)
116
{
117
clear_attn();
118
clear_bot();
119
flush();
120
}
121
122
/*
123
* Indicate we are reading a multi-character command.
124
*/
125
static void set_mca(int action)
126
{
127
mca = action;
128
clear_bot();
129
clear_cmd();
130
}
131
132
/*
133
* Indicate we are not reading a multi-character command.
134
*/
135
static void clear_mca(void)
136
{
137
if (mca == 0)
138
return;
139
mca = 0;
140
}
141
142
/*
143
* Set up the display to start a new multi-character command.
144
*/
145
static void start_mca(int action, constant char *prompt, void *mlist, int cmdflags)
146
{
147
set_mca(action);
148
cmd_putstr(prompt);
149
set_mlist(mlist, cmdflags);
150
}
151
152
public int in_mca(void)
153
{
154
return (mca != 0 && mca != A_PREFIX);
155
}
156
157
/*
158
* Set up the display to start a new search command.
159
*/
160
static void mca_search1(void)
161
{
162
int i;
163
164
#if HILITE_SEARCH
165
if (search_type & SRCH_FILTER)
166
set_mca(A_FILTER);
167
else
168
#endif
169
if (search_type & SRCH_FORW)
170
set_mca(A_F_SEARCH);
171
else
172
set_mca(A_B_SEARCH);
173
174
if (search_type & SRCH_NO_MATCH)
175
cmd_putstr("Non-match ");
176
if (search_type & SRCH_FIRST_FILE)
177
cmd_putstr("First-file ");
178
if (search_type & SRCH_PAST_EOF)
179
cmd_putstr("EOF-ignore ");
180
if (search_type & SRCH_NO_MOVE)
181
cmd_putstr("Keep-pos ");
182
if (search_type & SRCH_NO_REGEX)
183
cmd_putstr("Regex-off ");
184
if (search_type & SRCH_WRAP)
185
cmd_putstr("Wrap ");
186
for (i = 1; i <= NUM_SEARCH_COLORS; i++)
187
{
188
if (search_type & SRCH_SUBSEARCH(i))
189
{
190
char buf[INT_STRLEN_BOUND(int)+8];
191
SNPRINTF1(buf, sizeof(buf), "Sub-%d ", i);
192
cmd_putstr(buf);
193
}
194
}
195
if (literal_char)
196
cmd_putstr("Lit ");
197
198
#if HILITE_SEARCH
199
if (search_type & SRCH_FILTER)
200
cmd_putstr("&/");
201
else
202
#endif
203
if (search_type & SRCH_FORW)
204
cmd_putstr("/");
205
else
206
cmd_putstr("?");
207
forw_prompt = 0;
208
}
209
210
static void mca_search(void)
211
{
212
mca_search1();
213
set_mlist(ml_search, 0);
214
}
215
216
/*
217
* Set up the display to start a new toggle-option command.
218
*/
219
static void mca_opt_toggle(void)
220
{
221
int no_prompt = (optflag & OPT_NO_PROMPT);
222
int flag = (optflag & ~OPT_NO_PROMPT);
223
constant char *dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
224
225
set_mca(A_OPT_TOGGLE);
226
cmd_putstr(dash);
227
if (optgetname)
228
cmd_putstr(dash);
229
if (no_prompt)
230
cmd_putstr("(P)");
231
switch (flag)
232
{
233
case OPT_UNSET:
234
cmd_putstr("+");
235
break;
236
case OPT_SET:
237
cmd_putstr("!");
238
break;
239
}
240
forw_prompt = 0;
241
set_mlist(NULL, CF_OPTION);
242
}
243
244
/*
245
* Execute a multicharacter command.
246
*/
247
static void exec_mca(void)
248
{
249
constant char *cbuf;
250
251
cmd_exec();
252
cbuf = get_cmdbuf();
253
if (cbuf == NULL)
254
return;
255
256
switch (mca)
257
{
258
case A_F_SEARCH:
259
case A_B_SEARCH:
260
multi_search(cbuf, (int) number, 0);
261
break;
262
#if HILITE_SEARCH
263
case A_FILTER:
264
search_type ^= SRCH_NO_MATCH;
265
set_filter_pattern(cbuf, search_type);
266
soft_eof = NULL_POSITION;
267
break;
268
#endif
269
case A_FIRSTCMD:
270
/*
271
* Skip leading spaces or + signs in the string.
272
*/
273
while (*cbuf == '+' || *cbuf == ' ')
274
cbuf++;
275
if (every_first_cmd != NULL)
276
free(every_first_cmd);
277
if (*cbuf == '\0')
278
every_first_cmd = NULL;
279
else
280
every_first_cmd = save(cbuf);
281
break;
282
case A_OPT_TOGGLE:
283
toggle_option(curropt, opt_lower, cbuf, optflag);
284
curropt = NULL;
285
break;
286
case A_F_BRACKET:
287
match_brac(cbuf[0], cbuf[1], 1, (int) number);
288
break;
289
case A_B_BRACKET:
290
match_brac(cbuf[1], cbuf[0], 0, (int) number);
291
break;
292
#if EXAMINE
293
case A_EXAMINE: {
294
char *p;
295
if (!secure_allow(SF_EXAMINE))
296
break;
297
p = save(cbuf);
298
edit_list(p);
299
free(p);
300
#if TAGS
301
/* If tag structure is loaded then clean it up. */
302
cleantags();
303
#endif
304
break; }
305
#endif
306
#if SHELL_ESCAPE
307
case A_SHELL: {
308
/*
309
* !! just uses whatever is in shellcmd.
310
* Otherwise, copy cmdbuf to shellcmd,
311
* expanding any special characters ("%" or "#").
312
*/
313
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "!done";
314
if (done_msg == NULL)
315
++cbuf;
316
if (*cbuf != '!')
317
{
318
if (shellcmd != NULL)
319
free(shellcmd);
320
shellcmd = fexpand(cbuf);
321
}
322
if (!secure_allow(SF_SHELL))
323
break;
324
if (shellcmd == NULL)
325
shellcmd = "";
326
lsystem(shellcmd, done_msg);
327
break; }
328
case A_PSHELL: {
329
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "#done";
330
if (done_msg == NULL)
331
++cbuf;
332
if (!secure_allow(SF_SHELL))
333
break;
334
lsystem(pr_expand(cbuf), done_msg);
335
break; }
336
#endif
337
#if PIPEC
338
case A_PIPE: {
339
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "|done";
340
if (done_msg == NULL)
341
++cbuf;
342
if (!secure_allow(SF_PIPE))
343
break;
344
(void) pipe_mark(pipec, cbuf);
345
if (done_msg != NULL)
346
error(done_msg, NULL_PARG);
347
break; }
348
#endif
349
}
350
}
351
352
/*
353
* Is a character an erase or kill char?
354
*/
355
static lbool is_erase_char(char c)
356
{
357
return (c == erase_char || c == erase2_char || c == kill_char);
358
}
359
360
/*
361
* Is a character a carriage return or newline?
362
*/
363
static lbool is_newline_char(char c)
364
{
365
return (c == '\n' || c == '\r');
366
}
367
368
/*
369
* Handle the first char of an option (after the initial dash).
370
*/
371
static int mca_opt_first_char(char c)
372
{
373
int no_prompt = (optflag & OPT_NO_PROMPT);
374
int flag = (optflag & ~OPT_NO_PROMPT);
375
if (flag == OPT_NO_TOGGLE)
376
{
377
switch (c)
378
{
379
case '_':
380
/* "__" = long option name. */
381
optgetname = TRUE;
382
mca_opt_toggle();
383
return (MCA_MORE);
384
}
385
} else
386
{
387
switch (c)
388
{
389
case '+':
390
/* "-+" = UNSET. */
391
optflag = no_prompt | ((flag == OPT_UNSET) ?
392
OPT_TOGGLE : OPT_UNSET);
393
mca_opt_toggle();
394
return (MCA_MORE);
395
case '!':
396
/* "-!" = SET */
397
optflag = no_prompt | ((flag == OPT_SET) ?
398
OPT_TOGGLE : OPT_SET);
399
mca_opt_toggle();
400
return (MCA_MORE);
401
case CONTROL('P'):
402
optflag ^= OPT_NO_PROMPT;
403
mca_opt_toggle();
404
return (MCA_MORE);
405
case '-':
406
/* "--" = long option name. */
407
optgetname = TRUE;
408
mca_opt_toggle();
409
return (MCA_MORE);
410
}
411
}
412
/* Char was not handled here. */
413
return (NO_MCA);
414
}
415
416
/*
417
* Add a char to a long option name.
418
* See if we've got a match for an option name yet.
419
* If so, display the complete name and stop
420
* accepting chars until user hits RETURN.
421
*/
422
static int mca_opt_nonfirst_char(char c)
423
{
424
constant char *p;
425
constant char *oname;
426
lbool ambig;
427
struct loption *was_curropt;
428
429
if (curropt != NULL)
430
{
431
/* Already have a match for the name. */
432
if (is_erase_char(c))
433
return (MCA_DONE);
434
/* {{ Checking for TAB here is ugly.
435
* Also doesn't extend well -- can't do BACKTAB this way
436
* because it's a multichar sequence. }} */
437
if (c != '\t')
438
return (MCA_MORE);
439
}
440
/*
441
* Add char to cmd buffer and try to match
442
* the option name.
443
*/
444
if (cmd_char(c) == CC_QUIT)
445
return (MCA_DONE);
446
p = get_cmdbuf();
447
if (p == NULL || p[0] == '\0')
448
return (MCA_MORE);
449
opt_lower = ASCII_IS_LOWER(p[0]);
450
was_curropt = curropt;
451
curropt = findopt_name(&p, &oname, &ambig);
452
if (curropt != NULL)
453
{
454
if (was_curropt == NULL)
455
{
456
/*
457
* Got a match.
458
* Remember the option and
459
* display the full option name.
460
*/
461
cmd_reset();
462
mca_opt_toggle();
463
cmd_setstring(oname, !opt_lower);
464
}
465
} else if (!ambig)
466
{
467
bell();
468
}
469
return (MCA_MORE);
470
}
471
472
/*
473
* Handle a char of an option toggle command.
474
*/
475
static int mca_opt_char(char c)
476
{
477
PARG parg;
478
479
/*
480
* This may be a short option (single char),
481
* or one char of a long option name,
482
* or one char of the option parameter.
483
*/
484
if (curropt == NULL && cmdbuf_empty())
485
{
486
int ret = mca_opt_first_char(c);
487
if (ret != NO_MCA)
488
return (ret);
489
}
490
if (optgetname)
491
{
492
/* We're getting a long option name. */
493
if (!is_newline_char(c) && c != '=')
494
return (mca_opt_nonfirst_char(c));
495
if (curropt == NULL)
496
{
497
parg.p_string = get_cmdbuf();
498
if (parg.p_string == NULL)
499
return (MCA_MORE);
500
error("There is no --%s option", &parg);
501
return (MCA_DONE);
502
}
503
optgetname = FALSE;
504
cmd_reset();
505
} else
506
{
507
if (is_erase_char(c))
508
return (NO_MCA);
509
if (curropt != NULL)
510
/* We're getting the option parameter. */
511
return (NO_MCA);
512
curropt = findopt(c);
513
if (curropt == NULL)
514
{
515
parg.p_string = propt(c);
516
error("There is no %s option", &parg);
517
return (MCA_DONE);
518
}
519
opt_lower = ASCII_IS_LOWER(c);
520
}
521
/*
522
* If the option which was entered does not take a
523
* parameter, toggle the option immediately,
524
* so user doesn't have to hit RETURN.
525
*/
526
if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
527
!opt_has_param(curropt))
528
{
529
toggle_option(curropt, opt_lower, "", optflag);
530
return (MCA_DONE);
531
}
532
/*
533
* Display a prompt appropriate for the option parameter.
534
*/
535
start_mca(A_OPT_TOGGLE, opt_prompt(curropt), NULL, CF_OPTION);
536
return (MCA_MORE);
537
}
538
539
/*
540
* Normalize search type.
541
*/
542
public int norm_search_type(int st)
543
{
544
/* WRAP and PAST_EOF are mutually exclusive. */
545
if ((st & (SRCH_PAST_EOF|SRCH_WRAP)) == (SRCH_PAST_EOF|SRCH_WRAP))
546
st ^= SRCH_PAST_EOF;
547
return st;
548
}
549
550
/*
551
* Handle a char of a search command.
552
*/
553
static int mca_search_char(char c)
554
{
555
int flag = 0;
556
557
/*
558
* Certain characters as the first char of
559
* the pattern have special meaning:
560
* ! Toggle the NO_MATCH flag
561
* * Toggle the PAST_EOF flag
562
* @ Toggle the FIRST_FILE flag
563
*/
564
if (!cmdbuf_empty() || literal_char)
565
{
566
literal_char = FALSE;
567
return (NO_MCA);
568
}
569
570
switch (c)
571
{
572
case '*':
573
if (less_is_more)
574
break;
575
case CONTROL('E'): /* ignore END of file */
576
if (mca != A_FILTER)
577
flag = SRCH_PAST_EOF;
578
search_type &= ~SRCH_WRAP;
579
break;
580
case '@':
581
if (less_is_more)
582
break;
583
case CONTROL('F'): /* FIRST file */
584
if (mca != A_FILTER)
585
flag = SRCH_FIRST_FILE;
586
break;
587
case CONTROL('K'): /* KEEP position */
588
if (mca != A_FILTER)
589
flag = SRCH_NO_MOVE;
590
break;
591
case CONTROL('S'): { /* SUBSEARCH */
592
char buf[INT_STRLEN_BOUND(int)+24];
593
SNPRINTF1(buf, sizeof(buf), "Sub-pattern (1-%d):", NUM_SEARCH_COLORS);
594
clear_bot();
595
cmd_putstr(buf);
596
flush();
597
c = getcc();
598
if (c >= '1' && c <= '0'+NUM_SEARCH_COLORS)
599
flag = SRCH_SUBSEARCH(c-'0');
600
else
601
flag = -1; /* calls mca_search() below to repaint */
602
break; }
603
case CONTROL('W'): /* WRAP around */
604
if (mca != A_FILTER)
605
flag = SRCH_WRAP;
606
break;
607
case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
608
flag = SRCH_NO_REGEX;
609
break;
610
case CONTROL('N'): /* NOT match */
611
case '!':
612
flag = SRCH_NO_MATCH;
613
break;
614
case CONTROL('L'):
615
literal_char = TRUE;
616
flag = -1;
617
break;
618
}
619
620
if (flag != 0)
621
{
622
if (flag != -1)
623
search_type = norm_search_type(search_type ^ flag);
624
mca_search();
625
return (MCA_MORE);
626
}
627
return (NO_MCA);
628
}
629
630
/*
631
* Handle a character of a multi-character command.
632
*/
633
static int mca_char(char c)
634
{
635
int ret;
636
637
switch (mca)
638
{
639
case 0:
640
/*
641
* We're not in a multicharacter command.
642
*/
643
return (NO_MCA);
644
645
case A_PREFIX:
646
/*
647
* In the prefix of a command.
648
* This not considered a multichar command
649
* (even tho it uses cmdbuf, etc.).
650
* It is handled in the commands() switch.
651
*/
652
return (NO_MCA);
653
654
case A_DIGIT:
655
/*
656
* Entering digits of a number.
657
* Terminated by a non-digit.
658
*/
659
if ((c >= '0' && c <= '9') || c == '.')
660
break;
661
switch (editchar(c, ECF_PEEK|ECF_NOHISTORY|ECF_NOCOMPLETE|ECF_NORIGHTLEFT))
662
{
663
case A_NOACTION:
664
/*
665
* Ignore this char and get another one.
666
*/
667
return (MCA_MORE);
668
case A_INVALID:
669
/*
670
* Not part of the number.
671
* End the number and treat this char
672
* as a normal command character.
673
*/
674
number = cmd_int(&fraction);
675
clear_mca();
676
cmd_accept();
677
return (NO_MCA);
678
}
679
break;
680
681
case A_OPT_TOGGLE:
682
ret = mca_opt_char(c);
683
if (ret != NO_MCA)
684
return (ret);
685
break;
686
687
case A_F_SEARCH:
688
case A_B_SEARCH:
689
case A_FILTER:
690
ret = mca_search_char(c);
691
if (ret != NO_MCA)
692
return (ret);
693
break;
694
695
default:
696
/* Other multicharacter command. */
697
break;
698
}
699
700
/*
701
* The multichar command is terminated by a newline.
702
*/
703
if (is_newline_char(c))
704
{
705
if (pasting && no_paste)
706
{
707
/* Ignore pasted input after (and including) the first newline */
708
start_ignoring_input();
709
return (MCA_MORE);
710
}
711
/* Execute the command. */
712
exec_mca();
713
return (MCA_DONE);
714
}
715
716
/*
717
* Append the char to the command buffer.
718
*/
719
if (cmd_char(c) == CC_QUIT)
720
/*
721
* Abort the multi-char command.
722
*/
723
return (MCA_DONE);
724
725
switch (mca)
726
{
727
case A_F_BRACKET:
728
case A_B_BRACKET:
729
if (len_cmdbuf() >= 2)
730
{
731
/*
732
* Special case for the bracket-matching commands.
733
* Execute the command after getting exactly two
734
* characters from the user.
735
*/
736
exec_mca();
737
return (MCA_DONE);
738
}
739
break;
740
case A_F_SEARCH:
741
case A_B_SEARCH:
742
if (incr_search)
743
{
744
/* Incremental search: do a search after every input char. */
745
int st = (search_type & (SRCH_FORW|SRCH_BACK|SRCH_NO_MATCH|SRCH_NO_REGEX|SRCH_NO_MOVE|SRCH_WRAP|SRCH_SUBSEARCH_ALL));
746
ssize_t save_updown;
747
constant char *pattern = get_cmdbuf();
748
if (pattern == NULL)
749
return (MCA_MORE);
750
/*
751
* Must save updown_match because mca_search
752
* reinits it. That breaks history scrolling.
753
* {{ This is ugly. mca_search probably shouldn't call set_mlist. }}
754
*/
755
save_updown = save_updown_match();
756
cmd_exec();
757
if (*pattern == '\0')
758
{
759
/* User has backspaced to an empty pattern. */
760
undo_search(1);
761
} else
762
{
763
if (search(st | SRCH_INCR, pattern, 1) != 0)
764
/* No match, invalid pattern, etc. */
765
undo_search(1);
766
}
767
/* Redraw the search prompt and search string. */
768
if (is_screen_trashed() || !full_screen)
769
{
770
clear();
771
repaint();
772
}
773
mca_search1();
774
restore_updown_match(save_updown);
775
cmd_repaint(NULL);
776
}
777
break;
778
}
779
780
/*
781
* Need another character.
782
*/
783
return (MCA_MORE);
784
}
785
786
/*
787
* Discard any buffered file data.
788
*/
789
static void clear_buffers(void)
790
{
791
if (!(ch_getflags() & CH_CANSEEK))
792
return;
793
ch_flush();
794
clr_linenum();
795
#if HILITE_SEARCH
796
clr_hilite();
797
#endif
798
}
799
800
public void screen_trashed_num(int trashed)
801
{
802
screen_trashed_value = trashed;
803
}
804
805
public void screen_trashed(void)
806
{
807
screen_trashed_num(1);
808
}
809
810
public int is_screen_trashed(void)
811
{
812
return screen_trashed_value;
813
}
814
815
/*
816
* Make sure the screen is displayed.
817
*/
818
static void make_display(void)
819
{
820
/*
821
* If not full_screen, we can't rely on scrolling to fill the screen.
822
* We need to clear and repaint screen before any change.
823
*/
824
if (!full_screen && !(quit_if_one_screen && one_screen))
825
clear();
826
/*
827
* If nothing is displayed yet, display starting from initial_scrpos.
828
*/
829
if (empty_screen())
830
{
831
if (initial_scrpos.pos == NULL_POSITION)
832
jump_loc(ch_zero(), 1);
833
else
834
jump_loc(initial_scrpos.pos, initial_scrpos.ln);
835
} else if (is_screen_trashed() || !full_screen)
836
{
837
int save_top_scroll = top_scroll;
838
int save_ignore_eoi = ignore_eoi;
839
top_scroll = 1;
840
ignore_eoi = 0;
841
if (is_screen_trashed() == 2)
842
{
843
/* Special case used by ignore_eoi: re-open the input file
844
* and jump to the end of the file. */
845
reopen_curr_ifile();
846
jump_forw();
847
}
848
repaint();
849
top_scroll = save_top_scroll;
850
ignore_eoi = save_ignore_eoi;
851
}
852
}
853
854
/*
855
* Display the appropriate prompt.
856
*/
857
static void prompt(void)
858
{
859
constant char *p;
860
861
if (ungot != NULL && !ungot->ug_end_command)
862
{
863
/*
864
* No prompt necessary if commands are from
865
* ungotten chars rather than from the user.
866
*/
867
return;
868
}
869
870
/*
871
* Make sure the screen is displayed.
872
*/
873
make_display();
874
bottompos = position(BOTTOM_PLUS_ONE);
875
876
/*
877
* If we've hit EOF on the last file and the -E flag is set, quit.
878
*/
879
if (get_quit_at_eof() == OPT_ONPLUS &&
880
eof_displayed(FALSE) && !(ch_getflags() & CH_HELPFILE) &&
881
next_ifile(curr_ifile) == NULL_IFILE)
882
quit(QUIT_OK);
883
884
/*
885
* If the entire file is displayed and the -F flag is set, quit.
886
*/
887
if (quit_if_one_screen &&
888
entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
889
next_ifile(curr_ifile) == NULL_IFILE)
890
quit(QUIT_OK);
891
quit_if_one_screen = FALSE; /* only get one chance at this */
892
893
#if MSDOS_COMPILER==WIN32C
894
/*
895
* In Win32, display the file name in the window title.
896
*/
897
if (!(ch_getflags() & CH_HELPFILE))
898
{
899
WCHAR w[MAX_PATH+16];
900
p = pr_expand("Less?f - %f.");
901
MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w));
902
SetConsoleTitleW(w);
903
}
904
#endif
905
906
/*
907
* Select the proper prompt and display it.
908
*/
909
/*
910
* If the previous action was a forward movement,
911
* don't clear the bottom line of the display;
912
* just print the prompt since the forward movement guarantees
913
* that we're in the right position to display the prompt.
914
* Clearing the line could cause a problem: for example, if the last
915
* line displayed ended at the right screen edge without a newline,
916
* then clearing would clear the last displayed line rather than
917
* the prompt line.
918
*/
919
if (!forw_prompt)
920
clear_bot();
921
clear_cmd();
922
forw_prompt = 0;
923
p = pr_string();
924
#if HILITE_SEARCH
925
if (is_filtering())
926
putstr("& ");
927
#endif
928
if (search_wrapped)
929
{
930
if (search_type & SRCH_BACK)
931
error("Search hit top; continuing at bottom", NULL_PARG);
932
else
933
error("Search hit bottom; continuing at top", NULL_PARG);
934
search_wrapped = FALSE;
935
}
936
#if OSC8_LINK
937
if (osc8_uri != NULL)
938
{
939
PARG parg;
940
parg.p_string = osc8_uri;
941
error("Link: %s", &parg);
942
free(osc8_uri);
943
osc8_uri = NULL;
944
}
945
#endif
946
if (p == NULL || *p == '\0')
947
{
948
at_enter(AT_NORMAL|AT_COLOR_PROMPT);
949
putchr(':');
950
at_exit();
951
} else
952
{
953
#if MSDOS_COMPILER==WIN32C
954
WCHAR w[MAX_PATH*2];
955
char a[MAX_PATH*2];
956
MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w));
957
WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(),
958
0, w, -1, a, sizeof(a), NULL, NULL);
959
p = a;
960
#endif
961
load_line(p);
962
put_line(FALSE);
963
}
964
clear_eol();
965
}
966
967
/*
968
* Display the less version message.
969
*/
970
public void dispversion(void)
971
{
972
PARG parg;
973
974
parg.p_string = version;
975
error("less %s", &parg);
976
}
977
978
/*
979
* Return a character to complete a partial command, if possible.
980
*/
981
static char getcc_end_command(void)
982
{
983
int ch;
984
switch (mca)
985
{
986
case A_DIGIT:
987
/* We have a number but no command. Treat as #g. */
988
return ('g');
989
case A_F_SEARCH:
990
case A_B_SEARCH:
991
case A_FILTER:
992
/* We have "/string" but no newline. Add the \n. */
993
return ('\n');
994
default:
995
/* Some other incomplete command. Let user complete it. */
996
if (ungot != NULL)
997
return ('\0');
998
ch = getchr();
999
if (ch < 0) ch = '\0';
1000
return (char) ch;
1001
}
1002
}
1003
1004
/*
1005
* Get a command character from the ungotten stack.
1006
*/
1007
static char get_ungot(lbool *p_end_command)
1008
{
1009
struct ungot *ug = ungot;
1010
char c = ug->ug_char;
1011
if (p_end_command != NULL)
1012
*p_end_command = ug->ug_end_command;
1013
ungot = ug->ug_next;
1014
free(ug);
1015
return c;
1016
}
1017
1018
/*
1019
* Delete all ungotten characters.
1020
*/
1021
public void getcc_clear(void)
1022
{
1023
while (ungot != NULL)
1024
(void) get_ungot(NULL);
1025
}
1026
1027
/*
1028
* Get command character.
1029
* The character normally comes from the keyboard,
1030
* but may come from ungotten characters
1031
* (characters previously given to ungetcc or ungetsc).
1032
*/
1033
static char getccu(void)
1034
{
1035
int c = 0;
1036
while (c == 0 && sigs == 0)
1037
{
1038
if (ungot == NULL)
1039
{
1040
/* Normal case: no ungotten chars.
1041
* Get char from the user. */
1042
c = getchr();
1043
if (c < 0) c = '\0';
1044
} else
1045
{
1046
/* Ungotten chars available:
1047
* Take the top of stack (most recent). */
1048
lbool end_command;
1049
c = get_ungot(&end_command);
1050
if (end_command)
1051
c = getcc_end_command();
1052
}
1053
}
1054
return ((char) c);
1055
}
1056
1057
/*
1058
* Get a command character, but if we receive the orig sequence,
1059
* convert it to the repl sequence.
1060
*/
1061
static char getcc_repl(char constant *orig, char constant *repl, char (*gr_getc)(void), void (*gr_ungetc)(char))
1062
{
1063
char c;
1064
char keys[16];
1065
size_t ki = 0;
1066
1067
c = (*gr_getc)();
1068
if (orig == NULL || orig[0] == '\0')
1069
return c;
1070
for (;;)
1071
{
1072
keys[ki] = c;
1073
if (c != orig[ki] || ki >= sizeof(keys)-1)
1074
{
1075
/* This is not orig we have been receiving.
1076
* If we have stashed chars in keys[],
1077
* unget them and return the first one. */
1078
while (ki > 0)
1079
(*gr_ungetc)(keys[ki--]);
1080
return keys[0];
1081
}
1082
if (orig[++ki] == '\0')
1083
{
1084
/* We've received the full orig sequence.
1085
* Return the repl sequence. */
1086
ki = strlen(repl)-1;
1087
while (ki > 0)
1088
(*gr_ungetc)(repl[ki--]);
1089
return repl[0];
1090
}
1091
/* We've received a partial orig sequence (ki chars of it).
1092
* Get next char and see if it continues to match orig. */
1093
c = (*gr_getc)();
1094
}
1095
}
1096
1097
/*
1098
* Get command character.
1099
*/
1100
public char getcc(void)
1101
{
1102
/* Replace kent (keypad Enter) with a newline. */
1103
return getcc_repl(kent, "\n", getccu, ungetcc);
1104
}
1105
1106
/*
1107
* "Unget" a command character.
1108
* The next getcc() will return this character.
1109
*/
1110
public void ungetcc(char c)
1111
{
1112
struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
1113
1114
ug->ug_char = c;
1115
ug->ug_next = ungot;
1116
ungot = ug;
1117
}
1118
1119
/*
1120
* "Unget" a command character.
1121
* If any other chars are already ungotten, put this one after those.
1122
*/
1123
static void ungetcc_back1(char c, lbool end_command)
1124
{
1125
struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
1126
ug->ug_char = c;
1127
ug->ug_end_command = end_command;
1128
ug->ug_next = NULL;
1129
if (ungot == NULL)
1130
ungot = ug;
1131
else
1132
{
1133
struct ungot *pu;
1134
for (pu = ungot; pu->ug_next != NULL; pu = pu->ug_next)
1135
continue;
1136
pu->ug_next = ug;
1137
}
1138
}
1139
1140
public void ungetcc_back(char c)
1141
{
1142
ungetcc_back1(c, FALSE);
1143
}
1144
1145
public void ungetcc_end_command(void)
1146
{
1147
ungetcc_back1('\0', TRUE);
1148
}
1149
1150
/*
1151
* Unget a whole string of command characters.
1152
* The next sequence of getcc()'s will return this string.
1153
*/
1154
public void ungetsc(constant char *s)
1155
{
1156
while (*s != '\0')
1157
ungetcc_back(*s++);
1158
}
1159
1160
/*
1161
* Peek the next command character, without consuming it.
1162
*/
1163
public char peekcc(void)
1164
{
1165
char c = getcc();
1166
ungetcc(c);
1167
return c;
1168
}
1169
1170
/*
1171
* Search for a pattern, possibly in multiple files.
1172
* If SRCH_FIRST_FILE is set, begin searching at the first file.
1173
* If SRCH_PAST_EOF is set, continue the search thru multiple files.
1174
*/
1175
static void multi_search(constant char *pattern, int n, int silent)
1176
{
1177
int nomore;
1178
IFILE save_ifile;
1179
lbool changed_file;
1180
1181
changed_file = FALSE;
1182
save_ifile = save_curr_ifile();
1183
1184
if ((search_type & (SRCH_FORW|SRCH_BACK)) == 0)
1185
search_type |= SRCH_FORW;
1186
if (search_type & SRCH_FIRST_FILE)
1187
{
1188
/*
1189
* Start at the first (or last) file
1190
* in the command line list.
1191
*/
1192
if (search_type & SRCH_FORW)
1193
nomore = edit_first();
1194
else
1195
nomore = edit_last();
1196
if (nomore)
1197
{
1198
unsave_ifile(save_ifile);
1199
return;
1200
}
1201
changed_file = TRUE;
1202
search_type &= ~SRCH_FIRST_FILE;
1203
}
1204
1205
for (;;)
1206
{
1207
n = search(search_type, pattern, n);
1208
/*
1209
* The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
1210
* after being used once. This allows "n" to work after
1211
* using a /@@ search.
1212
*/
1213
search_type &= ~SRCH_NO_MOVE;
1214
last_search_type = search_type;
1215
if (n == 0)
1216
{
1217
/*
1218
* Found it.
1219
*/
1220
unsave_ifile(save_ifile);
1221
return;
1222
}
1223
1224
if (n < 0)
1225
/*
1226
* Some kind of error in the search.
1227
* Error message has been printed by search().
1228
*/
1229
break;
1230
1231
if ((search_type & SRCH_PAST_EOF) == 0)
1232
/*
1233
* We didn't find a match, but we're
1234
* supposed to search only one file.
1235
*/
1236
break;
1237
/*
1238
* Move on to the next file.
1239
*/
1240
if (search_type & SRCH_FORW)
1241
nomore = edit_next(1);
1242
else
1243
nomore = edit_prev(1);
1244
if (nomore)
1245
break;
1246
changed_file = TRUE;
1247
}
1248
1249
/*
1250
* Didn't find it.
1251
* Print an error message if we haven't already.
1252
*/
1253
if (n > 0 && !silent)
1254
error("Pattern not found", NULL_PARG);
1255
1256
if (changed_file)
1257
{
1258
/*
1259
* Restore the file we were originally viewing.
1260
*/
1261
reedit_ifile(save_ifile);
1262
} else
1263
{
1264
unsave_ifile(save_ifile);
1265
}
1266
}
1267
1268
/*
1269
* Forward forever, or until a highlighted line appears.
1270
*/
1271
static int forw_loop(int until_hilite)
1272
{
1273
POSITION curr_len;
1274
1275
if (ch_getflags() & CH_HELPFILE)
1276
return (A_NOACTION);
1277
1278
cmd_exec();
1279
jump_forw_buffered();
1280
curr_len = ch_length();
1281
highest_hilite = until_hilite ? curr_len : NULL_POSITION;
1282
ignore_eoi = 1;
1283
while (!sigs)
1284
{
1285
if (until_hilite && highest_hilite > curr_len)
1286
{
1287
bell();
1288
break;
1289
}
1290
make_display();
1291
forward(1, FALSE, FALSE, FALSE);
1292
}
1293
ignore_eoi = 0;
1294
ch_set_eof();
1295
1296
/*
1297
* This gets us back in "F mode" after processing
1298
* a non-abort signal (e.g. window-change).
1299
*/
1300
if (sigs && !ABORT_SIGS())
1301
return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
1302
1303
return (A_NOACTION);
1304
}
1305
1306
/*
1307
* Ignore subsequent (pasted) input chars.
1308
*/
1309
public void start_ignoring_input()
1310
{
1311
ignoring_input = TRUE;
1312
#if HAVE_TIME
1313
ignoring_input_time = get_time();
1314
#endif
1315
}
1316
1317
/*
1318
* Stop ignoring input chars.
1319
*/
1320
public void stop_ignoring_input()
1321
{
1322
ignoring_input = FALSE;
1323
pasting = FALSE;
1324
}
1325
1326
/*
1327
* Are we ignoring input chars?
1328
*/
1329
public lbool is_ignoring_input(int action)
1330
{
1331
if (!ignoring_input)
1332
return FALSE;
1333
if (action == A_END_PASTE)
1334
stop_ignoring_input();
1335
#if HAVE_TIME
1336
if (get_time() >= ignoring_input_time + MAX_PASTE_IGNORE_SEC)
1337
stop_ignoring_input();
1338
#endif
1339
/*
1340
* Don't ignore prefix chars so we can parse a full command
1341
* (which might be A_END_PASTE).
1342
*/
1343
return (action != A_PREFIX);
1344
}
1345
1346
/*
1347
* Main command processor.
1348
* Accept and execute commands until a quit command.
1349
*/
1350
public void commands(void)
1351
{
1352
char c;
1353
int action;
1354
constant char *cbuf;
1355
constant char *msg;
1356
int newaction;
1357
int save_jump_sline;
1358
int save_search_type;
1359
constant char *extra;
1360
PARG parg;
1361
IFILE old_ifile;
1362
IFILE new_ifile;
1363
#if TAGS
1364
constant char *tagfile;
1365
#endif
1366
1367
search_type = SRCH_FORW;
1368
wscroll = (sc_height + 1) / 2;
1369
newaction = A_NOACTION;
1370
1371
for (;;)
1372
{
1373
clear_mca();
1374
cmd_accept();
1375
number = 0;
1376
curropt = NULL;
1377
1378
/*
1379
* See if any signals need processing.
1380
*/
1381
if (sigs)
1382
{
1383
psignals();
1384
if (quitting)
1385
quit(QUIT_SAVED_STATUS);
1386
}
1387
1388
/*
1389
* See if window size changed, for systems that don't
1390
* generate SIGWINCH.
1391
*/
1392
check_winch();
1393
1394
/*
1395
* Display prompt and accept a character.
1396
*/
1397
cmd_reset();
1398
prompt();
1399
if (sigs)
1400
continue;
1401
if (newaction == A_NOACTION)
1402
c = getcc();
1403
1404
again:
1405
if (sigs)
1406
continue;
1407
1408
if (newaction != A_NOACTION)
1409
{
1410
action = newaction;
1411
newaction = A_NOACTION;
1412
} else
1413
{
1414
/*
1415
* If we are in a multicharacter command, call mca_char.
1416
* Otherwise we call fcmd_decode to determine the
1417
* action to be performed.
1418
*/
1419
if (mca)
1420
switch (mca_char(c))
1421
{
1422
case MCA_MORE:
1423
/*
1424
* Need another character.
1425
*/
1426
c = getcc();
1427
goto again;
1428
case MCA_DONE:
1429
/*
1430
* Command has been handled by mca_char.
1431
* Start clean with a prompt.
1432
*/
1433
continue;
1434
case NO_MCA:
1435
/*
1436
* Not a multi-char command
1437
* (at least, not anymore).
1438
*/
1439
break;
1440
}
1441
1442
/*
1443
* Decode the command character and decide what to do.
1444
*/
1445
extra = NULL;
1446
if (mca)
1447
{
1448
/*
1449
* We're in a multichar command.
1450
* Add the character to the command buffer
1451
* and display it on the screen.
1452
* If the user backspaces past the start
1453
* of the line, abort the command.
1454
*/
1455
if (cmd_char(c) == CC_QUIT || cmdbuf_empty())
1456
continue;
1457
cbuf = get_cmdbuf();
1458
if (cbuf == NULL)
1459
{
1460
c = getcc();
1461
goto again;
1462
}
1463
action = fcmd_decode(cbuf, &extra);
1464
} else
1465
{
1466
/*
1467
* Don't use cmd_char if we're starting fresh
1468
* at the beginning of a command, because we
1469
* don't want to echo the command until we know
1470
* it is a multichar command. We also don't
1471
* want erase_char/kill_char to be treated
1472
* as line editing characters.
1473
*/
1474
constant char tbuf[2] = { c, '\0' };
1475
action = fcmd_decode(tbuf, &extra);
1476
}
1477
/*
1478
* If an "extra" string was returned,
1479
* process it as a string of command characters.
1480
*/
1481
if (extra != NULL)
1482
ungetsc(extra);
1483
}
1484
/*
1485
* Clear the cmdbuf string.
1486
* (But not if we're in the prefix of a command,
1487
* because the partial command string is kept there.)
1488
*/
1489
if (action != A_PREFIX)
1490
cmd_reset();
1491
1492
if (is_ignoring_input(action))
1493
continue;
1494
1495
switch (action)
1496
{
1497
case A_START_PASTE:
1498
if (no_paste)
1499
start_ignoring_input();
1500
break;
1501
1502
case A_DIGIT:
1503
/*
1504
* First digit of a number.
1505
*/
1506
start_mca(A_DIGIT, ":", NULL, CF_QUIT_ON_ERASE);
1507
goto again;
1508
1509
case A_F_WINDOW:
1510
/*
1511
* Forward one window (and set the window size).
1512
*/
1513
if (number > 0)
1514
swindow = (int) number;
1515
/* FALLTHRU */
1516
case A_F_SCREEN:
1517
/*
1518
* Forward one screen.
1519
*/
1520
if (number <= 0)
1521
number = get_swindow();
1522
cmd_exec();
1523
if (show_attn)
1524
set_attnpos(bottompos);
1525
forward((int) number, FALSE, TRUE, FALSE);
1526
break;
1527
1528
case A_B_WINDOW:
1529
/*
1530
* Backward one window (and set the window size).
1531
*/
1532
if (number > 0)
1533
swindow = (int) number;
1534
/* FALLTHRU */
1535
case A_B_SCREEN:
1536
/*
1537
* Backward one screen.
1538
*/
1539
if (number <= 0)
1540
number = get_swindow();
1541
cmd_exec();
1542
backward((int) number, FALSE, TRUE, FALSE);
1543
break;
1544
1545
case A_F_LINE:
1546
case A_F_NEWLINE:
1547
1548
/*
1549
* Forward N (default 1) line.
1550
*/
1551
if (number <= 0)
1552
number = 1;
1553
cmd_exec();
1554
if (show_attn == OPT_ONPLUS && number > 1)
1555
set_attnpos(bottompos);
1556
forward((int) number, FALSE, FALSE, action == A_F_NEWLINE && !chopline);
1557
break;
1558
1559
case A_B_LINE:
1560
case A_B_NEWLINE:
1561
/*
1562
* Backward N (default 1) line.
1563
*/
1564
if (number <= 0)
1565
number = 1;
1566
cmd_exec();
1567
backward((int) number, FALSE, FALSE, action == A_B_NEWLINE && !chopline);
1568
break;
1569
1570
case A_F_MOUSE:
1571
/*
1572
* Forward wheel_lines lines.
1573
*/
1574
cmd_exec();
1575
forward(wheel_lines, FALSE, FALSE, FALSE);
1576
break;
1577
1578
case A_B_MOUSE:
1579
/*
1580
* Backward wheel_lines lines.
1581
*/
1582
cmd_exec();
1583
backward(wheel_lines, FALSE, FALSE, FALSE);
1584
break;
1585
1586
case A_FF_LINE:
1587
/*
1588
* Force forward N (default 1) line.
1589
*/
1590
if (number <= 0)
1591
number = 1;
1592
cmd_exec();
1593
if (show_attn == OPT_ONPLUS && number > 1)
1594
set_attnpos(bottompos);
1595
forward((int) number, TRUE, FALSE, FALSE);
1596
break;
1597
1598
case A_BF_LINE:
1599
/*
1600
* Force backward N (default 1) line.
1601
*/
1602
if (number <= 0)
1603
number = 1;
1604
cmd_exec();
1605
backward((int) number, TRUE, FALSE, FALSE);
1606
break;
1607
1608
case A_FF_SCREEN:
1609
/*
1610
* Force forward one screen.
1611
*/
1612
if (number <= 0)
1613
number = get_swindow();
1614
cmd_exec();
1615
if (show_attn == OPT_ONPLUS)
1616
set_attnpos(bottompos);
1617
forward((int) number, TRUE, FALSE, FALSE);
1618
break;
1619
1620
case A_BF_SCREEN:
1621
/*
1622
* Force backward one screen.
1623
*/
1624
if (number <= 0)
1625
number = get_swindow();
1626
cmd_exec();
1627
backward((int) number, TRUE, FALSE, FALSE);
1628
break;
1629
1630
case A_F_FOREVER:
1631
/*
1632
* Forward forever, ignoring EOF.
1633
*/
1634
if (get_altfilename(curr_ifile) != NULL)
1635
error("Warning: command may not work correctly when file is viewed via LESSOPEN", NULL_PARG);
1636
if (show_attn)
1637
set_attnpos(bottompos);
1638
newaction = forw_loop(0);
1639
break;
1640
1641
case A_F_UNTIL_HILITE:
1642
newaction = forw_loop(1);
1643
break;
1644
1645
case A_F_SCROLL:
1646
/*
1647
* Forward N lines
1648
* (default same as last 'd' or 'u' command).
1649
*/
1650
if (number > 0)
1651
wscroll = (int) number;
1652
cmd_exec();
1653
if (show_attn == OPT_ONPLUS)
1654
set_attnpos(bottompos);
1655
forward(wscroll, FALSE, FALSE, FALSE);
1656
break;
1657
1658
case A_B_SCROLL:
1659
/*
1660
* Forward N lines
1661
* (default same as last 'd' or 'u' command).
1662
*/
1663
if (number > 0)
1664
wscroll = (int) number;
1665
cmd_exec();
1666
backward(wscroll, FALSE, FALSE, FALSE);
1667
break;
1668
1669
case A_FREPAINT:
1670
/*
1671
* Flush buffers, then repaint screen.
1672
* Don't flush the buffers on a pipe!
1673
*/
1674
clear_buffers();
1675
/* FALLTHRU */
1676
case A_REPAINT:
1677
/*
1678
* Repaint screen.
1679
*/
1680
cmd_exec();
1681
repaint();
1682
break;
1683
1684
case A_GOLINE:
1685
/*
1686
* Go to line N, default beginning of file.
1687
* If N <= 0, ignore jump_sline in order to avoid
1688
* empty lines before the beginning of the file.
1689
*/
1690
save_jump_sline = jump_sline;
1691
if (number <= 0)
1692
{
1693
number = 1;
1694
jump_sline = 0;
1695
}
1696
cmd_exec();
1697
jump_back(number);
1698
jump_sline = save_jump_sline;
1699
break;
1700
1701
case A_PERCENT:
1702
/*
1703
* Go to a specified percentage into the file.
1704
*/
1705
if (number < 0)
1706
{
1707
number = 0;
1708
fraction = 0;
1709
}
1710
if (number > 100 || (number == 100 && fraction != 0))
1711
{
1712
number = 100;
1713
fraction = 0;
1714
}
1715
cmd_exec();
1716
jump_percent((int) number, fraction);
1717
break;
1718
1719
case A_GOEND:
1720
/*
1721
* Go to line N, default end of file.
1722
*/
1723
cmd_exec();
1724
if (number <= 0)
1725
jump_forw();
1726
else
1727
jump_back(number);
1728
break;
1729
1730
case A_GOEND_BUF:
1731
/*
1732
* Go to line N, default last buffered byte.
1733
*/
1734
cmd_exec();
1735
if (number <= 0)
1736
jump_forw_buffered();
1737
else
1738
jump_back(number);
1739
break;
1740
1741
case A_GOPOS:
1742
/*
1743
* Go to a specified byte position in the file.
1744
*/
1745
cmd_exec();
1746
if (number < 0)
1747
number = 0;
1748
jump_line_loc((POSITION) number, jump_sline);
1749
break;
1750
1751
case A_STAT:
1752
/*
1753
* Print file name, etc.
1754
*/
1755
if (ch_getflags() & CH_HELPFILE)
1756
break;
1757
cmd_exec();
1758
parg.p_string = eq_message();
1759
error("%s", &parg);
1760
break;
1761
1762
case A_VERSION:
1763
/*
1764
* Print version number.
1765
*/
1766
cmd_exec();
1767
dispversion();
1768
break;
1769
1770
case A_QUIT:
1771
/*
1772
* Exit.
1773
*/
1774
if (curr_ifile != NULL_IFILE &&
1775
ch_getflags() & CH_HELPFILE)
1776
{
1777
/*
1778
* Quit while viewing the help file
1779
* just means return to viewing the
1780
* previous file.
1781
*/
1782
hshift = save_hshift;
1783
bs_mode = save_bs_mode;
1784
proc_backspace = save_proc_backspace;
1785
if (edit_prev(1) == 0)
1786
break;
1787
}
1788
if (extra != NULL)
1789
quit(*extra);
1790
quit(QUIT_OK);
1791
break;
1792
1793
/*
1794
* Define abbreviation for a commonly used sequence below.
1795
*/
1796
#define DO_SEARCH() \
1797
if (number <= 0) number = 1; \
1798
mca_search(); \
1799
cmd_exec(); \
1800
multi_search(NULL, (int) number, 0);
1801
1802
case A_F_SEARCH:
1803
/*
1804
* Search forward for a pattern.
1805
* Get the first char of the pattern.
1806
*/
1807
search_type = SRCH_FORW | def_search_type;
1808
if (number <= 0)
1809
number = 1;
1810
literal_char = FALSE;
1811
mca_search();
1812
c = getcc();
1813
goto again;
1814
1815
case A_B_SEARCH:
1816
/*
1817
* Search backward for a pattern.
1818
* Get the first char of the pattern.
1819
*/
1820
search_type = SRCH_BACK | def_search_type;
1821
if (number <= 0)
1822
number = 1;
1823
literal_char = FALSE;
1824
mca_search();
1825
c = getcc();
1826
goto again;
1827
1828
case A_OSC8_F_SEARCH:
1829
#if OSC8_LINK
1830
cmd_exec();
1831
if (number <= 0)
1832
number = 1;
1833
osc8_search(SRCH_FORW, NULL, number);
1834
#else
1835
error("Command not available", NULL_PARG);
1836
#endif
1837
break;
1838
1839
case A_OSC8_B_SEARCH:
1840
#if OSC8_LINK
1841
cmd_exec();
1842
if (number <= 0)
1843
number = 1;
1844
osc8_search(SRCH_BACK, NULL, number);
1845
#else
1846
error("Command not available", NULL_PARG);
1847
#endif
1848
break;
1849
1850
case A_OSC8_OPEN:
1851
#if OSC8_LINK
1852
if (secure_allow(SF_OSC8_OPEN))
1853
{
1854
cmd_exec();
1855
osc8_open();
1856
break;
1857
}
1858
#endif
1859
error("Command not available", NULL_PARG);
1860
break;
1861
1862
case A_OSC8_JUMP:
1863
#if OSC8_LINK
1864
cmd_exec();
1865
osc8_jump();
1866
#else
1867
error("Command not available", NULL_PARG);
1868
#endif
1869
break;
1870
1871
case A_FILTER:
1872
#if HILITE_SEARCH
1873
search_type = SRCH_FORW | SRCH_FILTER;
1874
literal_char = FALSE;
1875
mca_search();
1876
c = getcc();
1877
goto again;
1878
#else
1879
error("Command not available", NULL_PARG);
1880
break;
1881
#endif
1882
1883
case A_AGAIN_SEARCH:
1884
/*
1885
* Repeat previous search.
1886
*/
1887
search_type = last_search_type;
1888
DO_SEARCH();
1889
break;
1890
1891
case A_T_AGAIN_SEARCH:
1892
/*
1893
* Repeat previous search, multiple files.
1894
*/
1895
search_type = last_search_type | SRCH_PAST_EOF;
1896
DO_SEARCH();
1897
break;
1898
1899
case A_REVERSE_SEARCH:
1900
/*
1901
* Repeat previous search, in reverse direction.
1902
*/
1903
save_search_type = search_type = last_search_type;
1904
search_type = SRCH_REVERSE(search_type);
1905
DO_SEARCH();
1906
last_search_type = save_search_type;
1907
break;
1908
1909
case A_T_REVERSE_SEARCH:
1910
/*
1911
* Repeat previous search,
1912
* multiple files in reverse direction.
1913
*/
1914
save_search_type = search_type = last_search_type;
1915
search_type = SRCH_REVERSE(search_type) | SRCH_PAST_EOF;
1916
DO_SEARCH();
1917
last_search_type = save_search_type;
1918
break;
1919
1920
case A_UNDO_SEARCH:
1921
case A_CLR_SEARCH:
1922
/*
1923
* Clear search string highlighting.
1924
*/
1925
undo_search(action == A_CLR_SEARCH);
1926
break;
1927
1928
case A_HELP:
1929
/*
1930
* Help.
1931
*/
1932
if (ch_getflags() & CH_HELPFILE)
1933
break;
1934
cmd_exec();
1935
save_hshift = hshift;
1936
hshift = 0;
1937
save_bs_mode = bs_mode;
1938
bs_mode = BS_SPECIAL;
1939
save_proc_backspace = proc_backspace;
1940
proc_backspace = OPT_OFF;
1941
(void) edit(FAKE_HELPFILE);
1942
break;
1943
1944
case A_EXAMINE:
1945
/*
1946
* Edit a new file. Get the filename.
1947
*/
1948
#if EXAMINE
1949
if (secure_allow(SF_EXAMINE))
1950
{
1951
start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1952
c = getcc();
1953
goto again;
1954
}
1955
#endif
1956
error("Command not available", NULL_PARG);
1957
break;
1958
1959
case A_VISUAL:
1960
/*
1961
* Invoke an editor on the input file.
1962
*/
1963
#if EDITOR
1964
if (secure_allow(SF_EDIT))
1965
{
1966
if (ch_getflags() & CH_HELPFILE)
1967
break;
1968
if (strcmp(get_filename(curr_ifile), "-") == 0)
1969
{
1970
error("Cannot edit standard input", NULL_PARG);
1971
break;
1972
}
1973
if (!no_edit_warn && get_altfilename(curr_ifile) != NULL)
1974
{
1975
error("WARNING: This file was viewed via LESSOPEN", NULL_PARG);
1976
}
1977
start_mca(A_SHELL, "!", ml_shell, 0);
1978
/*
1979
* Expand the editor prototype string
1980
* and pass it to the system to execute.
1981
* (Make sure the screen is displayed so the
1982
* expansion of "+%lm" works.)
1983
*/
1984
make_display();
1985
cmd_exec();
1986
lsystem(pr_expand(editproto), NULL);
1987
break;
1988
}
1989
#endif
1990
error("Command not available", NULL_PARG);
1991
break;
1992
1993
case A_NEXT_FILE:
1994
/*
1995
* Examine next file.
1996
*/
1997
#if TAGS
1998
if (ntags())
1999
{
2000
error("No next file", NULL_PARG);
2001
break;
2002
}
2003
#endif
2004
if (number <= 0)
2005
number = 1;
2006
cmd_exec();
2007
if (edit_next((int) number))
2008
{
2009
if (get_quit_at_eof() && eof_displayed(FALSE) &&
2010
!(ch_getflags() & CH_HELPFILE))
2011
quit(QUIT_OK);
2012
parg.p_string = (number > 1) ? "(N-th) " : "";
2013
error("No %snext file", &parg);
2014
}
2015
break;
2016
2017
case A_PREV_FILE:
2018
/*
2019
* Examine previous file.
2020
*/
2021
#if TAGS
2022
if (ntags())
2023
{
2024
error("No previous file", NULL_PARG);
2025
break;
2026
}
2027
#endif
2028
if (number <= 0)
2029
number = 1;
2030
cmd_exec();
2031
if (edit_prev((int) number))
2032
{
2033
parg.p_string = (number > 1) ? "(N-th) " : "";
2034
error("No %sprevious file", &parg);
2035
}
2036
break;
2037
2038
case A_NEXT_TAG:
2039
/*
2040
* Jump to the next tag in the current tag list.
2041
*/
2042
#if TAGS
2043
if (number <= 0)
2044
number = 1;
2045
tagfile = nexttag((int) number);
2046
if (tagfile == NULL)
2047
{
2048
error("No next tag", NULL_PARG);
2049
break;
2050
}
2051
cmd_exec();
2052
if (edit(tagfile) == 0)
2053
{
2054
POSITION pos = tagsearch();
2055
if (pos != NULL_POSITION)
2056
jump_loc(pos, jump_sline);
2057
}
2058
#else
2059
error("Command not available", NULL_PARG);
2060
#endif
2061
break;
2062
2063
case A_PREV_TAG:
2064
/*
2065
* Jump to the previous tag in the current tag list.
2066
*/
2067
#if TAGS
2068
if (number <= 0)
2069
number = 1;
2070
tagfile = prevtag((int) number);
2071
if (tagfile == NULL)
2072
{
2073
error("No previous tag", NULL_PARG);
2074
break;
2075
}
2076
cmd_exec();
2077
if (edit(tagfile) == 0)
2078
{
2079
POSITION pos = tagsearch();
2080
if (pos != NULL_POSITION)
2081
jump_loc(pos, jump_sline);
2082
}
2083
#else
2084
error("Command not available", NULL_PARG);
2085
#endif
2086
break;
2087
2088
case A_INDEX_FILE:
2089
/*
2090
* Examine a particular file.
2091
*/
2092
if (number <= 0)
2093
number = 1;
2094
cmd_exec();
2095
if (edit_index((int) number))
2096
error("No such file", NULL_PARG);
2097
break;
2098
2099
case A_REMOVE_FILE:
2100
/*
2101
* Remove a file from the input file list.
2102
*/
2103
if (ch_getflags() & CH_HELPFILE)
2104
break;
2105
old_ifile = curr_ifile;
2106
new_ifile = getoff_ifile(curr_ifile);
2107
cmd_exec();
2108
if (new_ifile == NULL_IFILE)
2109
{
2110
bell();
2111
break;
2112
}
2113
if (edit_ifile(new_ifile) != 0)
2114
{
2115
reedit_ifile(old_ifile);
2116
break;
2117
}
2118
del_ifile(old_ifile);
2119
break;
2120
2121
case A_OPT_TOGGLE:
2122
/*
2123
* Change the setting of an option.
2124
*/
2125
optflag = OPT_TOGGLE;
2126
optgetname = FALSE;
2127
mca_opt_toggle();
2128
c = getcc();
2129
msg = opt_toggle_disallowed(c);
2130
if (msg != NULL)
2131
{
2132
error(msg, NULL_PARG);
2133
break;
2134
}
2135
goto again;
2136
2137
case A_DISP_OPTION:
2138
/*
2139
* Report the setting of an option.
2140
*/
2141
optflag = OPT_NO_TOGGLE;
2142
optgetname = FALSE;
2143
mca_opt_toggle();
2144
c = getcc();
2145
goto again;
2146
2147
case A_FIRSTCMD:
2148
/*
2149
* Set an initial command for new files.
2150
*/
2151
start_mca(A_FIRSTCMD, "+", NULL, 0);
2152
c = getcc();
2153
goto again;
2154
2155
case A_SHELL:
2156
case A_PSHELL:
2157
/*
2158
* Shell escape.
2159
*/
2160
#if SHELL_ESCAPE
2161
if (secure_allow(SF_SHELL))
2162
{
2163
start_mca(action, (action == A_SHELL) ? "!" : "#", ml_shell, 0);
2164
c = getcc();
2165
goto again;
2166
}
2167
#endif
2168
error("Command not available", NULL_PARG);
2169
break;
2170
2171
case A_SETMARK:
2172
case A_SETMARKBOT:
2173
/*
2174
* Set a mark.
2175
*/
2176
if (ch_getflags() & CH_HELPFILE)
2177
{
2178
if (ungot != NULL)
2179
{
2180
/*
2181
* Probably from a lesskey file, in which case there
2182
* is probably an ungotten letter from the "extra" string.
2183
* Eat it so it is not interpreted as a command.
2184
*/
2185
(void) getcc();
2186
}
2187
break;
2188
}
2189
start_mca(A_SETMARK, "set mark: ", NULL, 0);
2190
c = getcc();
2191
if (is_erase_char(c) || is_newline_char(c))
2192
break;
2193
setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP);
2194
repaint();
2195
break;
2196
2197
case A_CLRMARK:
2198
/*
2199
* Clear a mark.
2200
*/
2201
start_mca(A_CLRMARK, "clear mark: ", NULL, 0);
2202
c = getcc();
2203
if (is_erase_char(c) || is_newline_char(c))
2204
break;
2205
clrmark(c);
2206
repaint();
2207
break;
2208
2209
case A_GOMARK:
2210
/*
2211
* Jump to a marked position.
2212
*/
2213
start_mca(A_GOMARK, "goto mark: ", NULL, 0);
2214
c = getcc();
2215
if (is_erase_char(c) || is_newline_char(c))
2216
break;
2217
cmd_exec();
2218
gomark(c);
2219
break;
2220
2221
case A_PIPE:
2222
/*
2223
* Write part of the input to a pipe to a shell command.
2224
*/
2225
#if PIPEC
2226
if (secure_allow(SF_PIPE))
2227
{
2228
start_mca(A_PIPE, "|mark: ", NULL, 0);
2229
c = getcc();
2230
if (is_erase_char(c))
2231
break;
2232
if (is_newline_char(c))
2233
c = '.';
2234
if (badmark(c))
2235
break;
2236
pipec = c;
2237
start_mca(A_PIPE, "!", ml_shell, 0);
2238
c = getcc();
2239
goto again;
2240
}
2241
#endif
2242
error("Command not available", NULL_PARG);
2243
break;
2244
2245
case A_B_BRACKET:
2246
case A_F_BRACKET:
2247
start_mca(action, "Brackets: ", NULL, 0);
2248
c = getcc();
2249
goto again;
2250
2251
case A_LSHIFT:
2252
/*
2253
* Shift view left.
2254
*/
2255
if (number > 0)
2256
shift_count = (int) number;
2257
else
2258
number = (shift_count > 0) ? shift_count : sc_width / 2;
2259
if (number > hshift)
2260
number = hshift;
2261
pos_rehead();
2262
hshift -= (int) number;
2263
screen_trashed();
2264
break;
2265
2266
case A_RSHIFT:
2267
/*
2268
* Shift view right.
2269
*/
2270
if (number > 0)
2271
shift_count = (int) number;
2272
else
2273
number = (shift_count > 0) ? shift_count : sc_width / 2;
2274
pos_rehead();
2275
hshift += (int) number;
2276
screen_trashed();
2277
break;
2278
2279
case A_LLSHIFT:
2280
/*
2281
* Shift view left to margin.
2282
*/
2283
pos_rehead();
2284
hshift = 0;
2285
screen_trashed();
2286
break;
2287
2288
case A_RRSHIFT:
2289
/*
2290
* Shift view right to view rightmost char on screen.
2291
*/
2292
pos_rehead();
2293
hshift = rrshift();
2294
screen_trashed();
2295
break;
2296
2297
case A_PREFIX:
2298
/*
2299
* The command is incomplete (more chars are needed).
2300
* Display the current char, so the user knows
2301
* what's going on, and get another character.
2302
*/
2303
if (mca != A_PREFIX)
2304
{
2305
cmd_reset();
2306
start_mca(A_PREFIX, " ", NULL, CF_QUIT_ON_ERASE);
2307
(void) cmd_char(c);
2308
}
2309
c = getcc();
2310
goto again;
2311
2312
case A_NOACTION:
2313
break;
2314
2315
default:
2316
bell();
2317
break;
2318
}
2319
}
2320
}
2321
2322