kernel - work on dmsg disk exports
[dragonfly.git] / contrib / less / command.c
1 /*
2  * Copyright (C) 1984-2012  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 squished;
27 extern int sc_width;
28 extern int sc_height;
29 extern int swindow;
30 extern int jump_sline;
31 extern int quitting;
32 extern int wscroll;
33 extern int top_scroll;
34 extern int ignore_eoi;
35 extern int secure;
36 extern int hshift;
37 extern int show_attn;
38 extern POSITION highest_hilite;
39 extern char *every_first_cmd;
40 extern char *curr_altfilename;
41 extern char version[];
42 extern struct scrpos initial_scrpos;
43 extern IFILE curr_ifile;
44 extern void constant *ml_search;
45 extern void constant *ml_examine;
46 #if SHELL_ESCAPE || PIPEC
47 extern void constant *ml_shell;
48 #endif
49 #if EDITOR
50 extern char *editor;
51 extern char *editproto;
52 #endif
53 extern int screen_trashed;      /* The screen has been overwritten */
54 extern int shift_count;
55 extern int oldbot;
56 extern int forw_prompt;
57
58 #if SHELL_ESCAPE
59 static char *shellcmd = NULL;   /* For holding last shell command for "!!" */
60 #endif
61 static int mca;                 /* The multicharacter command (action) */
62 static int search_type;         /* The previous type of search */
63 static LINENUM number;          /* The number typed by the user */
64 static long fraction;           /* The fractional part of the number */
65 static struct loption *curropt;
66 static int opt_lower;
67 static int optflag;
68 static int optgetname;
69 static POSITION bottompos;
70 static int save_hshift;
71 #if PIPEC
72 static char pipec;
73 #endif
74
75 struct ungot {
76         struct ungot *ug_next;
77         char ug_char;
78 };
79 static struct ungot* ungot = NULL;
80 static int unget_end = 0;
81
82 static void multi_search();
83
84 /*
85  * Move the cursor to start of prompt line before executing a command.
86  * This looks nicer if the command takes a long time before
87  * updating the screen.
88  */
89         static void
90 cmd_exec()
91 {
92 #if HILITE_SEARCH
93         clear_attn();
94 #endif
95         clear_bot();
96         flush();
97 }
98
99 /*
100  * Set up the display to start a new multi-character command.
101  */
102         static void
103 start_mca(action, prompt, mlist, cmdflags)
104         int action;
105         constant char *prompt;
106         constant void *mlist;
107         int cmdflags;
108 {
109         mca = action;
110         clear_bot();
111         clear_cmd();
112         cmd_putstr(prompt);
113         set_mlist(mlist, cmdflags);
114 }
115
116         public int
117 in_mca()
118 {
119         return (mca != 0 && mca != A_PREFIX);
120 }
121
122 /*
123  * Set up the display to start a new search command.
124  */
125         static void
126 mca_search()
127 {
128 #if HILITE_SEARCH
129         if (search_type & SRCH_FILTER)
130                 mca = A_FILTER;
131         else 
132 #endif
133         if (search_type & SRCH_FORW)
134                 mca = A_F_SEARCH;
135         else
136                 mca = A_B_SEARCH;
137
138         clear_bot();
139         clear_cmd();
140
141         if (search_type & SRCH_NO_MATCH)
142                 cmd_putstr("Non-match ");
143         if (search_type & SRCH_FIRST_FILE)
144                 cmd_putstr("First-file ");
145         if (search_type & SRCH_PAST_EOF)
146                 cmd_putstr("EOF-ignore ");
147         if (search_type & SRCH_NO_MOVE)
148                 cmd_putstr("Keep-pos ");
149         if (search_type & SRCH_NO_REGEX)
150                 cmd_putstr("Regex-off ");
151
152 #if HILITE_SEARCH
153         if (search_type & SRCH_FILTER)
154                 cmd_putstr("&/");
155         else 
156 #endif
157         if (search_type & SRCH_FORW)
158                 cmd_putstr("/");
159         else
160                 cmd_putstr("?");
161         set_mlist(ml_search, 0);
162 }
163
164 /*
165  * Set up the display to start a new toggle-option command.
166  */
167         static void
168 mca_opt_toggle()
169 {
170         int no_prompt;
171         int flag;
172         char *dash;
173         
174         no_prompt = (optflag & OPT_NO_PROMPT);
175         flag = (optflag & ~OPT_NO_PROMPT);
176         dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
177
178         mca = A_OPT_TOGGLE;
179         clear_bot();
180         clear_cmd();
181         cmd_putstr(dash);
182         if (optgetname)
183                 cmd_putstr(dash);
184         if (no_prompt)
185                 cmd_putstr("(P)");
186         switch (flag)
187         {
188         case OPT_UNSET:
189                 cmd_putstr("+");
190                 break;
191         case OPT_SET:
192                 cmd_putstr("!");
193                 break;
194         }
195         set_mlist(NULL, 0);
196 }
197
198 /*
199  * Execute a multicharacter command.
200  */
201         static void
202 exec_mca()
203 {
204         register char *cbuf;
205
206         cmd_exec();
207         cbuf = get_cmdbuf();
208
209         switch (mca)
210         {
211         case A_F_SEARCH:
212         case A_B_SEARCH:
213                 multi_search(cbuf, (int) number);
214                 break;
215 #if HILITE_SEARCH
216         case A_FILTER:
217                 search_type ^= SRCH_NO_MATCH;
218                 set_filter_pattern(cbuf, search_type);
219                 break;
220 #endif
221         case A_FIRSTCMD:
222                 /*
223                  * Skip leading spaces or + signs in the string.
224                  */
225                 while (*cbuf == '+' || *cbuf == ' ')
226                         cbuf++;
227                 if (every_first_cmd != NULL)
228                         free(every_first_cmd);
229                 if (*cbuf == '\0')
230                         every_first_cmd = NULL;
231                 else
232                         every_first_cmd = save(cbuf);
233                 break;
234         case A_OPT_TOGGLE:
235                 toggle_option(curropt, opt_lower, cbuf, optflag);
236                 curropt = NULL;
237                 break;
238         case A_F_BRACKET:
239                 match_brac(cbuf[0], cbuf[1], 1, (int) number);
240                 break;
241         case A_B_BRACKET:
242                 match_brac(cbuf[1], cbuf[0], 0, (int) number);
243                 break;
244 #if EXAMINE
245         case A_EXAMINE:
246                 if (secure)
247                         break;
248                 edit_list(cbuf);
249 #if TAGS
250                 /* If tag structure is loaded then clean it up. */
251                 cleantags();
252 #endif
253                 break;
254 #endif
255 #if SHELL_ESCAPE
256         case A_SHELL:
257                 /*
258                  * !! just uses whatever is in shellcmd.
259                  * Otherwise, copy cmdbuf to shellcmd,
260                  * expanding any special characters ("%" or "#").
261                  */
262                 if (*cbuf != '!')
263                 {
264                         if (shellcmd != NULL)
265                                 free(shellcmd);
266                         shellcmd = fexpand(cbuf);
267                 }
268
269                 if (secure)
270                         break;
271                 if (shellcmd == NULL)
272                         lsystem("", "!done");
273                 else
274                         lsystem(shellcmd, "!done");
275                 break;
276 #endif
277 #if PIPEC
278         case A_PIPE:
279                 if (secure)
280                         break;
281                 (void) pipe_mark(pipec, cbuf);
282                 error("|done", NULL_PARG);
283                 break;
284 #endif
285         }
286 }
287
288 /*
289  * Is a character an erase or kill char?
290  */
291         static int
292 is_erase_char(c)
293         int c;
294 {
295         return (c == erase_char || c == erase2_char || c == kill_char);
296 }
297
298 /*
299  * Handle the first char of an option (after the initial dash).
300  */
301         static int
302 mca_opt_first_char(c)
303     int c;
304 {
305         int flag = (optflag & ~OPT_NO_PROMPT);
306         if (flag == OPT_NO_TOGGLE)
307         {
308                 switch (c)
309                 {
310                 case '_':
311                         /* "__" = long option name. */
312                         optgetname = TRUE;
313                         mca_opt_toggle();
314                         return (MCA_MORE);
315                 }
316         } else
317         {
318                 switch (c)
319                 {
320                 case '+':
321                         /* "-+" = UNSET. */
322                         optflag = (flag == OPT_UNSET) ?
323                                 OPT_TOGGLE : OPT_UNSET;
324                         mca_opt_toggle();
325                         return (MCA_MORE);
326                 case '!':
327                         /* "-!" = SET */
328                         optflag = (flag == OPT_SET) ?
329                                 OPT_TOGGLE : OPT_SET;
330                         mca_opt_toggle();
331                         return (MCA_MORE);
332                 case CONTROL('P'):
333                         optflag ^= OPT_NO_PROMPT;
334                         mca_opt_toggle();
335                         return (MCA_MORE);
336                 case '-':
337                         /* "--" = long option name. */
338                         optgetname = TRUE;
339                         mca_opt_toggle();
340                         return (MCA_MORE);
341                 }
342         }
343         /* Char was not handled here. */
344         return (NO_MCA);
345 }
346
347 /*
348  * Add a char to a long option name.
349  * See if we've got a match for an option name yet.
350  * If so, display the complete name and stop 
351  * accepting chars until user hits RETURN.
352  */
353         static int
354 mca_opt_nonfirst_char(c)
355         int c;
356 {
357         char *p;
358         char *oname;
359
360         if (curropt != NULL)
361         {
362                 /*
363                  * Already have a match for the name.
364                  * Don't accept anything but erase/kill.
365                  */
366                 if (is_erase_char(c))
367                         return (MCA_DONE);
368                 return (MCA_MORE);
369         }
370         /*
371          * Add char to cmd buffer and try to match
372          * the option name.
373          */
374         if (cmd_char(c) == CC_QUIT)
375                 return (MCA_DONE);
376         p = get_cmdbuf();
377         opt_lower = ASCII_IS_LOWER(p[0]);
378         curropt = findopt_name(&p, &oname, NULL);
379         if (curropt != NULL)
380         {
381                 /*
382                  * Got a match.
383                  * Remember the option and
384                  * display the full option name.
385                  */
386                 cmd_reset();
387                 mca_opt_toggle();
388                 for (p = oname;  *p != '\0';  p++)
389                 {
390                         c = *p;
391                         if (!opt_lower && ASCII_IS_LOWER(c))
392                                 c = ASCII_TO_UPPER(c);
393                         if (cmd_char(c) != CC_OK)
394                                 return (MCA_DONE);
395                 }
396         }
397         return (MCA_MORE);
398 }
399
400 /*
401  * Handle a char of an option toggle command.
402  */
403         static int
404 mca_opt_char(c)
405         int c;
406 {
407         PARG parg;
408
409         /*
410          * This may be a short option (single char),
411          * or one char of a long option name,
412          * or one char of the option parameter.
413          */
414         if (curropt == NULL && len_cmdbuf() == 0)
415         {
416                 int ret = mca_opt_first_char(c);
417                 if (ret != NO_MCA)
418                         return (ret);
419         }
420         if (optgetname)
421         {
422                 /* We're getting a long option name.  */
423                 if (c != '\n' && c != '\r')
424                         return (mca_opt_nonfirst_char(c));
425                 if (curropt == NULL)
426                 {
427                         parg.p_string = get_cmdbuf();
428                         error("There is no --%s option", &parg);
429                         return (MCA_DONE);
430                 }
431                 optgetname = FALSE;
432                 cmd_reset();
433         } else
434         {
435                 if (is_erase_char(c))
436                         return (NO_MCA);
437                 if (curropt != NULL)
438                         /* We're getting the option parameter. */
439                         return (NO_MCA);
440                 curropt = findopt(c);
441                 if (curropt == NULL)
442                 {
443                         parg.p_string = propt(c);
444                         error("There is no %s option", &parg);
445                         return (MCA_DONE);
446                 }
447         }
448         /*
449          * If the option which was entered does not take a 
450          * parameter, toggle the option immediately,
451          * so user doesn't have to hit RETURN.
452          */
453         if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
454             !opt_has_param(curropt))
455         {
456                 toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag);
457                 return (MCA_DONE);
458         }
459         /*
460          * Display a prompt appropriate for the option parameter.
461          */
462         start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
463         return (MCA_MORE);
464 }
465
466 /*
467  * Handle a char of a search command.
468  */
469         static int
470 mca_search_char(c)
471         int c;
472 {
473         int flag = 0;
474
475         /*
476          * Certain characters as the first char of 
477          * the pattern have special meaning:
478          *      !  Toggle the NO_MATCH flag
479          *      *  Toggle the PAST_EOF flag
480          *      @  Toggle the FIRST_FILE flag
481          */
482         if (len_cmdbuf() > 0)
483                 return (NO_MCA);
484
485         switch (c)
486         {
487         case CONTROL('E'): /* ignore END of file */
488         case '*':
489                 if (mca != A_FILTER)
490                         flag = SRCH_PAST_EOF;
491                 break;
492         case CONTROL('F'): /* FIRST file */
493         case '@':
494                 if (mca != A_FILTER)
495                         flag = SRCH_FIRST_FILE;
496                 break;
497         case CONTROL('K'): /* KEEP position */
498                 if (mca != A_FILTER)
499                         flag = SRCH_NO_MOVE;
500                 break;
501         case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
502                 flag = SRCH_NO_REGEX;
503                 break;
504         case CONTROL('N'): /* NOT match */
505         case '!':
506                 flag = SRCH_NO_MATCH;
507                 break;
508         }
509
510         if (flag != 0)
511         {
512                 search_type ^= flag;
513                 mca_search();
514                 return (MCA_MORE);
515         }
516         return (NO_MCA);
517 }
518
519 /*
520  * Handle a character of a multi-character command.
521  */
522         static int
523 mca_char(c)
524         int c;
525 {
526         int ret;
527
528         switch (mca)
529         {
530         case 0:
531                 /*
532                  * We're not in a multicharacter command.
533                  */
534                 return (NO_MCA);
535
536         case A_PREFIX:
537                 /*
538                  * In the prefix of a command.
539                  * This not considered a multichar command
540                  * (even tho it uses cmdbuf, etc.).
541                  * It is handled in the commands() switch.
542                  */
543                 return (NO_MCA);
544
545         case A_DIGIT:
546                 /*
547                  * Entering digits of a number.
548                  * Terminated by a non-digit.
549                  */
550                 if (!((c >= '0' && c <= '9') || c == '.') && 
551                   editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
552                 {
553                         /*
554                          * Not part of the number.
555                          * End the number and treat this char 
556                          * as a normal command character.
557                          */
558                         number = cmd_int(&fraction);
559                         mca = 0;
560                         cmd_accept();
561                         return (NO_MCA);
562                 }
563                 break;
564
565         case A_OPT_TOGGLE:
566                 ret = mca_opt_char(c);
567                 if (ret != NO_MCA)
568                         return (ret);
569                 break;
570
571         case A_F_SEARCH:
572         case A_B_SEARCH:
573         case A_FILTER:
574                 ret = mca_search_char(c);
575                 if (ret != NO_MCA)
576                         return (ret);
577                 break;
578
579         default:
580                 /* Other multicharacter command. */
581                 break;
582         }
583
584         /*
585          * The multichar command is terminated by a newline.
586          */
587         if (c == '\n' || c == '\r')
588         {
589                 /*
590                  * Execute the command.
591                  */
592                 exec_mca();
593                 return (MCA_DONE);
594         }
595
596         /*
597          * Append the char to the command buffer.
598          */
599         if (cmd_char(c) == CC_QUIT)
600                 /*
601                  * Abort the multi-char command.
602                  */
603                 return (MCA_DONE);
604
605         if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
606         {
607                 /*
608                  * Special case for the bracket-matching commands.
609                  * Execute the command after getting exactly two
610                  * characters from the user.
611                  */
612                 exec_mca();
613                 return (MCA_DONE);
614         }
615
616         /*
617          * Need another character.
618          */
619         return (MCA_MORE);
620 }
621
622 /*
623  * Discard any buffered file data.
624  */
625         static void
626 clear_buffers()
627 {
628         if (!(ch_getflags() & CH_CANSEEK))
629                 return;
630         ch_flush();
631         clr_linenum();
632 #if HILITE_SEARCH
633         clr_hilite();
634 #endif
635 }
636
637 /*
638  * Make sure the screen is displayed.
639  */
640         static void
641 make_display()
642 {
643         /*
644          * If nothing is displayed yet, display starting from initial_scrpos.
645          */
646         if (empty_screen())
647         {
648                 if (initial_scrpos.pos == NULL_POSITION)
649                         /*
650                          * {{ Maybe this should be:
651                          *    jump_loc(ch_zero(), jump_sline);
652                          *    but this behavior seems rather unexpected 
653                          *    on the first screen. }}
654                          */
655                         jump_loc(ch_zero(), 1);
656                 else
657                         jump_loc(initial_scrpos.pos, initial_scrpos.ln);
658         } else if (screen_trashed)
659         {
660                 int save_top_scroll = top_scroll;
661                 int save_ignore_eoi = ignore_eoi;
662                 top_scroll = 1;
663                 ignore_eoi = 0;
664                 if (screen_trashed == 2)
665                 {
666                         /* Special case used by ignore_eoi: re-open the input file
667                          * and jump to the end of the file. */
668                         reopen_curr_ifile();
669                         jump_forw();
670                 }
671                 repaint();
672                 top_scroll = save_top_scroll;
673                 ignore_eoi = save_ignore_eoi;
674         }
675 }
676
677 /*
678  * Display the appropriate prompt.
679  */
680         static void
681 prompt()
682 {
683         register constant char *p;
684
685         if (ungot != NULL)
686         {
687                 /*
688                  * No prompt necessary if commands are from 
689                  * ungotten chars rather than from the user.
690                  */
691                 return;
692         }
693
694         /*
695          * Make sure the screen is displayed.
696          */
697         make_display();
698         bottompos = position(BOTTOM_PLUS_ONE);
699
700         /*
701          * If we've hit EOF on the last file and the -E flag is set, quit.
702          */
703         if (get_quit_at_eof() == OPT_ONPLUS &&
704             eof_displayed() && !(ch_getflags() & CH_HELPFILE) && 
705             next_ifile(curr_ifile) == NULL_IFILE)
706                 quit(QUIT_OK);
707
708         /*
709          * If the entire file is displayed and the -F flag is set, quit.
710          */
711         if (quit_if_one_screen &&
712             entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && 
713             next_ifile(curr_ifile) == NULL_IFILE)
714                 quit(QUIT_OK);
715
716 #if MSDOS_COMPILER==WIN32C
717         /* 
718          * In Win32, display the file name in the window title.
719          */
720         if (!(ch_getflags() & CH_HELPFILE))
721                 SetConsoleTitle(pr_expand("Less?f - %f.", 0));
722 #endif
723         /*
724          * Select the proper prompt and display it.
725          */
726         /*
727          * If the previous action was a forward movement, 
728          * don't clear the bottom line of the display;
729          * just print the prompt since the forward movement guarantees 
730          * that we're in the right position to display the prompt.
731          * Clearing the line could cause a problem: for example, if the last
732          * line displayed ended at the right screen edge without a newline,
733          * then clearing would clear the last displayed line rather than
734          * the prompt line.
735          */
736         if (!forw_prompt)
737                 clear_bot();
738         clear_cmd();
739         forw_prompt = 0;
740         p = pr_string();
741         if (is_filtering())
742                 putstr("& ");
743         if (p == NULL || *p == '\0')
744                 putchr(':');
745         else
746         {
747                 at_enter(AT_STANDOUT);
748                 putstr(p);
749                 at_exit();
750         }
751         clear_eol();
752 }
753
754 /*
755  * Display the less version message.
756  */
757         public void
758 dispversion()
759 {
760         PARG parg;
761
762         parg.p_string = version;
763         error("less %s", &parg);
764 }
765
766 /*
767  * Get command character.
768  * The character normally comes from the keyboard,
769  * but may come from ungotten characters
770  * (characters previously given to ungetcc or ungetsc).
771  */
772         public int
773 getcc()
774 {
775         if (unget_end) 
776         {
777                 /*
778                  * We have just run out of ungotten chars.
779                  */
780                 unget_end = 0;
781                 if (len_cmdbuf() == 0 || !empty_screen())
782                         return (getchr());
783                 /*
784                  * Command is incomplete, so try to complete it.
785                  */
786                 switch (mca)
787                 {
788                 case A_DIGIT:
789                         /*
790                          * We have a number but no command.  Treat as #g.
791                          */
792                         return ('g');
793
794                 case A_F_SEARCH:
795                 case A_B_SEARCH:
796                         /*
797                          * We have "/string" but no newline.  Add the \n.
798                          */
799                         return ('\n'); 
800
801                 default:
802                         /*
803                          * Some other incomplete command.  Let user complete it.
804                          */
805                         return (getchr());
806                 }
807         }
808
809         if (ungot == NULL)
810         {
811                 /*
812                  * Normal case: no ungotten chars, so get one from the user.
813                  */
814                 return (getchr());
815         }
816
817         /*
818          * Return the next ungotten char.
819          */
820         {
821                 struct ungot *ug = ungot;
822                 char c = ug->ug_char;
823                 ungot = ug->ug_next;
824                 free(ug);
825                 unget_end = (ungot == NULL);
826                 return (c);
827         }
828 }
829
830 /*
831  * "Unget" a command character.
832  * The next getcc() will return this character.
833  */
834         public void
835 ungetcc(c)
836         int c;
837 {
838         struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
839
840         ug->ug_char = c;
841         ug->ug_next = ungot;
842         ungot = ug;
843         unget_end = 0;
844 }
845
846 /*
847  * Unget a whole string of command characters.
848  * The next sequence of getcc()'s will return this string.
849  */
850         public void
851 ungetsc(s)
852         char *s;
853 {
854         register char *p;
855
856         for (p = s + strlen(s) - 1;  p >= s;  p--)
857                 ungetcc(*p);
858 }
859
860 /*
861  * Search for a pattern, possibly in multiple files.
862  * If SRCH_FIRST_FILE is set, begin searching at the first file.
863  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
864  */
865         static void
866 multi_search(pattern, n)
867         char *pattern;
868         int n;
869 {
870         register int nomore;
871         IFILE save_ifile;
872         int changed_file;
873
874         changed_file = 0;
875         save_ifile = save_curr_ifile();
876
877         if (search_type & SRCH_FIRST_FILE)
878         {
879                 /*
880                  * Start at the first (or last) file 
881                  * in the command line list.
882                  */
883                 if (search_type & SRCH_FORW)
884                         nomore = edit_first();
885                 else
886                         nomore = edit_last();
887                 if (nomore)
888                 {
889                         unsave_ifile(save_ifile);
890                         return;
891                 }
892                 changed_file = 1;
893                 search_type &= ~SRCH_FIRST_FILE;
894         }
895
896         for (;;)
897         {
898                 n = search(search_type, pattern, n);
899                 /*
900                  * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
901                  * after being used once.  This allows "n" to work after
902                  * using a /@@ search.
903                  */
904                 search_type &= ~SRCH_NO_MOVE;
905                 if (n == 0)
906                 {
907                         /*
908                          * Found it.
909                          */
910                         unsave_ifile(save_ifile);
911                         return;
912                 }
913
914                 if (n < 0)
915                         /*
916                          * Some kind of error in the search.
917                          * Error message has been printed by search().
918                          */
919                         break;
920
921                 if ((search_type & SRCH_PAST_EOF) == 0)
922                         /*
923                          * We didn't find a match, but we're
924                          * supposed to search only one file.
925                          */
926                         break;
927                 /*
928                  * Move on to the next file.
929                  */
930                 if (search_type & SRCH_FORW)
931                         nomore = edit_next(1);
932                 else
933                         nomore = edit_prev(1);
934                 if (nomore)
935                         break;
936                 changed_file = 1;
937         }
938
939         /*
940          * Didn't find it.
941          * Print an error message if we haven't already.
942          */
943         if (n > 0)
944                 error("Pattern not found", NULL_PARG);
945
946         if (changed_file)
947         {
948                 /*
949                  * Restore the file we were originally viewing.
950                  */
951                 reedit_ifile(save_ifile);
952         } else
953         {
954                 unsave_ifile(save_ifile);
955         }
956 }
957
958 /*
959  * Forward forever, or until a highlighted line appears.
960  */
961         static int
962 forw_loop(until_hilite)
963         int until_hilite;
964 {
965         POSITION curr_len;
966
967         if (ch_getflags() & CH_HELPFILE)
968                 return (A_NOACTION);
969
970         cmd_exec();
971         jump_forw();
972         curr_len = ch_length();
973         highest_hilite = until_hilite ? curr_len : NULL_POSITION;
974         ignore_eoi = 1;
975         while (!sigs)
976         {
977                 if (until_hilite && highest_hilite > curr_len)
978                 {
979                         bell();
980                         break;
981                 }
982                 make_display();
983                 forward(1, 0, 0);
984         }
985         ignore_eoi = 0;
986         ch_set_eof();
987
988         /*
989          * This gets us back in "F mode" after processing 
990          * a non-abort signal (e.g. window-change).  
991          */
992         if (sigs && !ABORT_SIGS())
993                 return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
994
995         return (A_NOACTION);
996 }
997
998 /*
999  * Main command processor.
1000  * Accept and execute commands until a quit command.
1001  */
1002         public void
1003 commands()
1004 {
1005         register int c;
1006         register int action;
1007         register char *cbuf;
1008         int newaction;
1009         int save_search_type;
1010         char *extra;
1011         char tbuf[2];
1012         PARG parg;
1013         IFILE old_ifile;
1014         IFILE new_ifile;
1015         char *tagfile;
1016
1017         search_type = SRCH_FORW;
1018         wscroll = (sc_height + 1) / 2;
1019         newaction = A_NOACTION;
1020
1021         for (;;)
1022         {
1023                 mca = 0;
1024                 cmd_accept();
1025                 number = 0;
1026                 curropt = NULL;
1027
1028                 /*
1029                  * See if any signals need processing.
1030                  */
1031                 if (sigs)
1032                 {
1033                         psignals();
1034                         if (quitting)
1035                                 quit(QUIT_SAVED_STATUS);
1036                 }
1037
1038                 /*
1039                  * See if window size changed, for systems that don't
1040                  * generate SIGWINCH.
1041                  */
1042                 check_winch();
1043
1044                 /*
1045                  * Display prompt and accept a character.
1046                  */
1047                 cmd_reset();
1048                 prompt();
1049                 if (sigs)
1050                         continue;
1051                 if (newaction == A_NOACTION)
1052                         c = getcc();
1053
1054         again:
1055                 if (sigs)
1056                         continue;
1057
1058                 if (newaction != A_NOACTION)
1059                 {
1060                         action = newaction;
1061                         newaction = A_NOACTION;
1062                 } else
1063                 {
1064                         /*
1065                          * If we are in a multicharacter command, call mca_char.
1066                          * Otherwise we call fcmd_decode to determine the
1067                          * action to be performed.
1068                          */
1069                         if (mca)
1070                                 switch (mca_char(c))
1071                                 {
1072                                 case MCA_MORE:
1073                                         /*
1074                                          * Need another character.
1075                                          */
1076                                         c = getcc();
1077                                         goto again;
1078                                 case MCA_DONE:
1079                                         /*
1080                                          * Command has been handled by mca_char.
1081                                          * Start clean with a prompt.
1082                                          */
1083                                         continue;
1084                                 case NO_MCA:
1085                                         /*
1086                                          * Not a multi-char command
1087                                          * (at least, not anymore).
1088                                          */
1089                                         break;
1090                                 }
1091
1092                         /*
1093                          * Decode the command character and decide what to do.
1094                          */
1095                         if (mca)
1096                         {
1097                                 /*
1098                                  * We're in a multichar command.
1099                                  * Add the character to the command buffer
1100                                  * and display it on the screen.
1101                                  * If the user backspaces past the start 
1102                                  * of the line, abort the command.
1103                                  */
1104                                 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1105                                         continue;
1106                                 cbuf = get_cmdbuf();
1107                         } else
1108                         {
1109                                 /*
1110                                  * Don't use cmd_char if we're starting fresh
1111                                  * at the beginning of a command, because we
1112                                  * don't want to echo the command until we know
1113                                  * it is a multichar command.  We also don't
1114                                  * want erase_char/kill_char to be treated
1115                                  * as line editing characters.
1116                                  */
1117                                 tbuf[0] = c;
1118                                 tbuf[1] = '\0';
1119                                 cbuf = tbuf;
1120                         }
1121                         extra = NULL;
1122                         action = fcmd_decode(cbuf, &extra);
1123                         /*
1124                          * If an "extra" string was returned,
1125                          * process it as a string of command characters.
1126                          */
1127                         if (extra != NULL)
1128                                 ungetsc(extra);
1129                 }
1130                 /*
1131                  * Clear the cmdbuf string.
1132                  * (But not if we're in the prefix of a command,
1133                  * because the partial command string is kept there.)
1134                  */
1135                 if (action != A_PREFIX)
1136                         cmd_reset();
1137
1138                 switch (action)
1139                 {
1140                 case A_DIGIT:
1141                         /*
1142                          * First digit of a number.
1143                          */
1144                         start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1145                         goto again;
1146
1147                 case A_F_WINDOW:
1148                         /*
1149                          * Forward one window (and set the window size).
1150                          */
1151                         if (number > 0)
1152                                 swindow = (int) number;
1153                         /* FALLTHRU */
1154                 case A_F_SCREEN:
1155                         /*
1156                          * Forward one screen.
1157                          */
1158                         if (number <= 0)
1159                                 number = get_swindow();
1160                         cmd_exec();
1161                         if (show_attn)
1162                                 set_attnpos(bottompos);
1163                         forward((int) number, 0, 1);
1164                         break;
1165
1166                 case A_B_WINDOW:
1167                         /*
1168                          * Backward one window (and set the window size).
1169                          */
1170                         if (number > 0)
1171                                 swindow = (int) number;
1172                         /* FALLTHRU */
1173                 case A_B_SCREEN:
1174                         /*
1175                          * Backward one screen.
1176                          */
1177                         if (number <= 0)
1178                                 number = get_swindow();
1179                         cmd_exec();
1180                         backward((int) number, 0, 1);
1181                         break;
1182
1183                 case A_F_LINE:
1184                         /*
1185                          * Forward N (default 1) line.
1186                          */
1187                         if (number <= 0)
1188                                 number = 1;
1189                         cmd_exec();
1190                         if (show_attn == OPT_ONPLUS && number > 1)
1191                                 set_attnpos(bottompos);
1192                         forward((int) number, 0, 0);
1193                         break;
1194
1195                 case A_B_LINE:
1196                         /*
1197                          * Backward N (default 1) line.
1198                          */
1199                         if (number <= 0)
1200                                 number = 1;
1201                         cmd_exec();
1202                         backward((int) number, 0, 0);
1203                         break;
1204
1205                 case A_FF_LINE:
1206                         /*
1207                          * Force forward N (default 1) line.
1208                          */
1209                         if (number <= 0)
1210                                 number = 1;
1211                         cmd_exec();
1212                         if (show_attn == OPT_ONPLUS && number > 1)
1213                                 set_attnpos(bottompos);
1214                         forward((int) number, 1, 0);
1215                         break;
1216
1217                 case A_BF_LINE:
1218                         /*
1219                          * Force backward N (default 1) line.
1220                          */
1221                         if (number <= 0)
1222                                 number = 1;
1223                         cmd_exec();
1224                         backward((int) number, 1, 0);
1225                         break;
1226                 
1227                 case A_FF_SCREEN:
1228                         /*
1229                          * Force forward one screen.
1230                          */
1231                         if (number <= 0)
1232                                 number = get_swindow();
1233                         cmd_exec();
1234                         if (show_attn == OPT_ONPLUS)
1235                                 set_attnpos(bottompos);
1236                         forward((int) number, 1, 0);
1237                         break;
1238
1239                 case A_F_FOREVER:
1240                         /*
1241                          * Forward forever, ignoring EOF.
1242                          */
1243                         newaction = forw_loop(0);
1244                         break;
1245
1246                 case A_F_UNTIL_HILITE:
1247                         newaction = forw_loop(1);
1248                         break;
1249
1250                 case A_F_SCROLL:
1251                         /*
1252                          * Forward N lines 
1253                          * (default same as last 'd' or 'u' command).
1254                          */
1255                         if (number > 0)
1256                                 wscroll = (int) number;
1257                         cmd_exec();
1258                         if (show_attn == OPT_ONPLUS)
1259                                 set_attnpos(bottompos);
1260                         forward(wscroll, 0, 0);
1261                         break;
1262
1263                 case A_B_SCROLL:
1264                         /*
1265                          * Forward N lines 
1266                          * (default same as last 'd' or 'u' command).
1267                          */
1268                         if (number > 0)
1269                                 wscroll = (int) number;
1270                         cmd_exec();
1271                         backward(wscroll, 0, 0);
1272                         break;
1273
1274                 case A_FREPAINT:
1275                         /*
1276                          * Flush buffers, then repaint screen.
1277                          * Don't flush the buffers on a pipe!
1278                          */
1279                         clear_buffers();
1280                         /* FALLTHRU */
1281                 case A_REPAINT:
1282                         /*
1283                          * Repaint screen.
1284                          */
1285                         cmd_exec();
1286                         repaint();
1287                         break;
1288
1289                 case A_GOLINE:
1290                         /*
1291                          * Go to line N, default beginning of file.
1292                          */
1293                         if (number <= 0)
1294                                 number = 1;
1295                         cmd_exec();
1296                         jump_back(number);
1297                         break;
1298
1299                 case A_PERCENT:
1300                         /*
1301                          * Go to a specified percentage into the file.
1302                          */
1303                         if (number < 0)
1304                         {
1305                                 number = 0;
1306                                 fraction = 0;
1307                         }
1308                         if (number > 100)
1309                         {
1310                                 number = 100;
1311                                 fraction = 0;
1312                         }
1313                         cmd_exec();
1314                         jump_percent((int) number, fraction);
1315                         break;
1316
1317                 case A_GOEND:
1318                         /*
1319                          * Go to line N, default end of file.
1320                          */
1321                         cmd_exec();
1322                         if (number <= 0)
1323                                 jump_forw();
1324                         else
1325                                 jump_back(number);
1326                         break;
1327
1328                 case A_GOPOS:
1329                         /*
1330                          * Go to a specified byte position in the file.
1331                          */
1332                         cmd_exec();
1333                         if (number < 0)
1334                                 number = 0;
1335                         jump_line_loc((POSITION) number, jump_sline);
1336                         break;
1337
1338                 case A_STAT:
1339                         /*
1340                          * Print file name, etc.
1341                          */
1342                         if (ch_getflags() & CH_HELPFILE)
1343                                 break;
1344                         cmd_exec();
1345                         parg.p_string = eq_message();
1346                         error("%s", &parg);
1347                         break;
1348
1349                 case A_VERSION:
1350                         /*
1351                          * Print version number, without the "@(#)".
1352                          */
1353                         cmd_exec();
1354                         dispversion();
1355                         break;
1356
1357                 case A_QUIT:
1358                         /*
1359                          * Exit.
1360                          */
1361                         if (curr_ifile != NULL_IFILE && 
1362                             ch_getflags() & CH_HELPFILE)
1363                         {
1364                                 /*
1365                                  * Quit while viewing the help file
1366                                  * just means return to viewing the
1367                                  * previous file.
1368                                  */
1369                                 hshift = save_hshift;
1370                                 if (edit_prev(1) == 0)
1371                                         break;
1372                         }
1373                         if (extra != NULL)
1374                                 quit(*extra);
1375                         quit(QUIT_OK);
1376                         break;
1377
1378 /*
1379  * Define abbreviation for a commonly used sequence below.
1380  */
1381 #define DO_SEARCH() \
1382                         if (number <= 0) number = 1;    \
1383                         mca_search();                   \
1384                         cmd_exec();                     \
1385                         multi_search((char *)NULL, (int) number);
1386
1387
1388                 case A_F_SEARCH:
1389                         /*
1390                          * Search forward for a pattern.
1391                          * Get the first char of the pattern.
1392                          */
1393                         search_type = SRCH_FORW;
1394                         if (number <= 0)
1395                                 number = 1;
1396                         mca_search();
1397                         c = getcc();
1398                         goto again;
1399
1400                 case A_B_SEARCH:
1401                         /*
1402                          * Search backward for a pattern.
1403                          * Get the first char of the pattern.
1404                          */
1405                         search_type = SRCH_BACK;
1406                         if (number <= 0)
1407                                 number = 1;
1408                         mca_search();
1409                         c = getcc();
1410                         goto again;
1411
1412                 case A_FILTER:
1413 #if HILITE_SEARCH
1414                         search_type = SRCH_FORW | SRCH_FILTER;
1415                         mca_search();
1416                         c = getcc();
1417                         goto again;
1418 #else
1419                         error("Command not available", NULL_PARG);
1420                         break;
1421 #endif
1422
1423                 case A_AGAIN_SEARCH:
1424                         /*
1425                          * Repeat previous search.
1426                          */
1427                         DO_SEARCH();
1428                         break;
1429                 
1430                 case A_T_AGAIN_SEARCH:
1431                         /*
1432                          * Repeat previous search, multiple files.
1433                          */
1434                         search_type |= SRCH_PAST_EOF;
1435                         DO_SEARCH();
1436                         break;
1437
1438                 case A_REVERSE_SEARCH:
1439                         /*
1440                          * Repeat previous search, in reverse direction.
1441                          */
1442                         save_search_type = search_type;
1443                         search_type = SRCH_REVERSE(search_type);
1444                         DO_SEARCH();
1445                         search_type = save_search_type;
1446                         break;
1447
1448                 case A_T_REVERSE_SEARCH:
1449                         /* 
1450                          * Repeat previous search, 
1451                          * multiple files in reverse direction.
1452                          */
1453                         save_search_type = search_type;
1454                         search_type = SRCH_REVERSE(search_type);
1455                         search_type |= SRCH_PAST_EOF;
1456                         DO_SEARCH();
1457                         search_type = save_search_type;
1458                         break;
1459
1460                 case A_UNDO_SEARCH:
1461                         undo_search();
1462                         break;
1463
1464                 case A_HELP:
1465                         /*
1466                          * Help.
1467                          */
1468                         if (ch_getflags() & CH_HELPFILE)
1469                                 break;
1470                         cmd_exec();
1471                         save_hshift = hshift;
1472                         hshift = 0;
1473                         (void) edit(FAKE_HELPFILE);
1474                         break;
1475
1476                 case A_EXAMINE:
1477 #if EXAMINE
1478                         /*
1479                          * Edit a new file.  Get the filename.
1480                          */
1481                         if (secure)
1482                         {
1483                                 error("Command not available", NULL_PARG);
1484                                 break;
1485                         }
1486                         start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1487                         c = getcc();
1488                         goto again;
1489 #else
1490                         error("Command not available", NULL_PARG);
1491                         break;
1492 #endif
1493                         
1494                 case A_VISUAL:
1495                         /*
1496                          * Invoke an editor on the input file.
1497                          */
1498 #if EDITOR
1499                         if (secure)
1500                         {
1501                                 error("Command not available", NULL_PARG);
1502                                 break;
1503                         }
1504                         if (ch_getflags() & CH_HELPFILE)
1505                                 break;
1506                         if (strcmp(get_filename(curr_ifile), "-") == 0)
1507                         {
1508                                 error("Cannot edit standard input", NULL_PARG);
1509                                 break;
1510                         }
1511                         if (curr_altfilename != NULL)
1512                         {
1513                                 error("WARNING: This file was viewed via LESSOPEN",
1514                                         NULL_PARG);
1515                         }
1516                         start_mca(A_SHELL, "!", ml_shell, 0);
1517                         /*
1518                          * Expand the editor prototype string
1519                          * and pass it to the system to execute.
1520                          * (Make sure the screen is displayed so the
1521                          * expansion of "+%lm" works.)
1522                          */
1523                         make_display();
1524                         cmd_exec();
1525                         lsystem(pr_expand(editproto, 0), (char*)NULL);
1526                         break;
1527 #else
1528                         error("Command not available", NULL_PARG);
1529                         break;
1530 #endif
1531
1532                 case A_NEXT_FILE:
1533                         /*
1534                          * Examine next file.
1535                          */
1536 #if TAGS
1537                         if (ntags())
1538                         {
1539                                 error("No next file", NULL_PARG);
1540                                 break;
1541                         }
1542 #endif
1543                         if (number <= 0)
1544                                 number = 1;
1545                         if (edit_next((int) number))
1546                         {
1547                                 if (get_quit_at_eof() && eof_displayed() && 
1548                                     !(ch_getflags() & CH_HELPFILE))
1549                                         quit(QUIT_OK);
1550                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1551                                 error("No %snext file", &parg);
1552                         }
1553                         break;
1554
1555                 case A_PREV_FILE:
1556                         /*
1557                          * Examine previous file.
1558                          */
1559 #if TAGS
1560                         if (ntags())
1561                         {
1562                                 error("No previous file", NULL_PARG);
1563                                 break;
1564                         }
1565 #endif
1566                         if (number <= 0)
1567                                 number = 1;
1568                         if (edit_prev((int) number))
1569                         {
1570                                 parg.p_string = (number > 1) ? "(N-th) " : "";
1571                                 error("No %sprevious file", &parg);
1572                         }
1573                         break;
1574
1575                 case A_NEXT_TAG:
1576 #if TAGS
1577                         if (number <= 0)
1578                                 number = 1;
1579                         tagfile = nexttag((int) number);
1580                         if (tagfile == NULL)
1581                         {
1582                                 error("No next tag", NULL_PARG);
1583                                 break;
1584                         }
1585                         if (edit(tagfile) == 0)
1586                         {
1587                                 POSITION pos = tagsearch();
1588                                 if (pos != NULL_POSITION)
1589                                         jump_loc(pos, jump_sline);
1590                         }
1591 #else
1592                         error("Command not available", NULL_PARG);
1593 #endif
1594                         break;
1595
1596                 case A_PREV_TAG:
1597 #if TAGS
1598                         if (number <= 0)
1599                                 number = 1;
1600                         tagfile = prevtag((int) number);
1601                         if (tagfile == NULL)
1602                         {
1603                                 error("No previous tag", NULL_PARG);
1604                                 break;
1605                         }
1606                         if (edit(tagfile) == 0)
1607                         {
1608                                 POSITION pos = tagsearch();
1609                                 if (pos != NULL_POSITION)
1610                                         jump_loc(pos, jump_sline);
1611                         }
1612 #else
1613                         error("Command not available", NULL_PARG);
1614 #endif
1615                         break;
1616
1617                 case A_INDEX_FILE:
1618                         /*
1619                          * Examine a particular file.
1620                          */
1621                         if (number <= 0)
1622                                 number = 1;
1623                         if (edit_index((int) number))
1624                                 error("No such file", NULL_PARG);
1625                         break;
1626
1627                 case A_REMOVE_FILE:
1628                         if (ch_getflags() & CH_HELPFILE)
1629                                 break;
1630                         old_ifile = curr_ifile;
1631                         new_ifile = getoff_ifile(curr_ifile);
1632                         if (new_ifile == NULL_IFILE)
1633                         {
1634                                 bell();
1635                                 break;
1636                         }
1637                         if (edit_ifile(new_ifile) != 0)
1638                         {
1639                                 reedit_ifile(old_ifile);
1640                                 break;
1641                         }
1642                         del_ifile(old_ifile);
1643                         break;
1644
1645                 case A_OPT_TOGGLE:
1646                         optflag = OPT_TOGGLE;
1647                         optgetname = FALSE;
1648                         mca_opt_toggle();
1649                         c = getcc();
1650                         goto again;
1651
1652                 case A_DISP_OPTION:
1653                         /*
1654                          * Report a flag setting.
1655                          */
1656                         optflag = OPT_NO_TOGGLE;
1657                         optgetname = FALSE;
1658                         mca_opt_toggle();
1659                         c = getcc();
1660                         goto again;
1661
1662                 case A_FIRSTCMD:
1663                         /*
1664                          * Set an initial command for new files.
1665                          */
1666                         start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1667                         c = getcc();
1668                         goto again;
1669
1670                 case A_SHELL:
1671                         /*
1672                          * Shell escape.
1673                          */
1674 #if SHELL_ESCAPE
1675                         if (secure)
1676                         {
1677                                 error("Command not available", NULL_PARG);
1678                                 break;
1679                         }
1680                         start_mca(A_SHELL, "!", ml_shell, 0);
1681                         c = getcc();
1682                         goto again;
1683 #else
1684                         error("Command not available", NULL_PARG);
1685                         break;
1686 #endif
1687
1688                 case A_SETMARK:
1689                         /*
1690                          * Set a mark.
1691                          */
1692                         if (ch_getflags() & CH_HELPFILE)
1693                                 break;
1694                         start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1695                         c = getcc();
1696                         if (c == erase_char || c == erase2_char ||
1697                             c == kill_char || c == '\n' || c == '\r')
1698                                 break;
1699                         setmark(c);
1700                         break;
1701
1702                 case A_GOMARK:
1703                         /*
1704                          * Go to a mark.
1705                          */
1706                         start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1707                         c = getcc();
1708                         if (c == erase_char || c == erase2_char ||
1709                             c == kill_char || c == '\n' || c == '\r')
1710                                 break;
1711                         cmd_exec();
1712                         gomark(c);
1713                         break;
1714
1715                 case A_PIPE:
1716 #if PIPEC
1717                         if (secure)
1718                         {
1719                                 error("Command not available", NULL_PARG);
1720                                 break;
1721                         }
1722                         start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1723                         c = getcc();
1724                         if (c == erase_char || c == erase2_char || c == kill_char)
1725                                 break;
1726                         if (c == '\n' || c == '\r')
1727                                 c = '.';
1728                         if (badmark(c))
1729                                 break;
1730                         pipec = c;
1731                         start_mca(A_PIPE, "!", ml_shell, 0);
1732                         c = getcc();
1733                         goto again;
1734 #else
1735                         error("Command not available", NULL_PARG);
1736                         break;
1737 #endif
1738
1739                 case A_B_BRACKET:
1740                 case A_F_BRACKET:
1741                         start_mca(action, "Brackets: ", (void*)NULL, 0);
1742                         c = getcc();
1743                         goto again;
1744
1745                 case A_LSHIFT:
1746                         if (number > 0)
1747                                 shift_count = number;
1748                         else
1749                                 number = (shift_count > 0) ?
1750                                         shift_count : sc_width / 2;
1751                         if (number > hshift)
1752                                 number = hshift;
1753                         hshift -= number;
1754                         screen_trashed = 1;
1755                         break;
1756
1757                 case A_RSHIFT:
1758                         if (number > 0)
1759                                 shift_count = number;
1760                         else
1761                                 number = (shift_count > 0) ?
1762                                         shift_count : sc_width / 2;
1763                         hshift += number;
1764                         screen_trashed = 1;
1765                         break;
1766
1767                 case A_PREFIX:
1768                         /*
1769                          * The command is incomplete (more chars are needed).
1770                          * Display the current char, so the user knows
1771                          * what's going on, and get another character.
1772                          */
1773                         if (mca != A_PREFIX)
1774                         {
1775                                 cmd_reset();
1776                                 start_mca(A_PREFIX, " ", (void*)NULL,
1777                                         CF_QUIT_ON_ERASE);
1778                                 (void) cmd_char(c);
1779                         }
1780                         c = getcc();
1781                         goto again;
1782
1783                 case A_NOACTION:
1784                         break;
1785
1786                 default:
1787                         bell();
1788                         break;
1789                 }
1790         }
1791 }