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