[BACK]Return to command.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Diff for /src/usr.bin/less/command.c between version 1.5 and 1.6

version 1.5, 2003/04/10 15:53:30 version 1.6, 2003/04/13 18:26:25
Line 1 
Line 1 
 /*      $OpenBSD$       */  
   
 /*  /*
  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman   * Copyright (C) 1984-2002  Mark Nudelman
  * All rights reserved.  
  *   *
  * Redistribution and use in source and binary forms, with or without   * You may distribute under the terms of either the GNU General Public
  * modification, are permitted provided that the following conditions   * License or the Less License, as specified in the README file.
  * are met:  
  * 1. Redistributions of source code must retain the above copyright  
  *    notice, this list of conditions and the following disclaimer.  
  * 2. Redistributions in binary form must reproduce the above copyright  
  *    notice in the documentation and/or other materials provided with  
  *    the distribution.  
  *   *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY   * For more information about less, or for information on how to
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   * contact the author, see the README file.
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  */   */
   
   
Line 32 
Line 14 
  */   */
   
 #include "less.h"  #include "less.h"
   #if MSDOS_COMPILER==WIN32C
   #include <windows.h>
   #endif
 #include "position.h"  #include "position.h"
 #include "option.h"  #include "option.h"
 #include "cmd.h"  #include "cmd.h"
Line 39 
Line 24 
 extern int erase_char, kill_char;  extern int erase_char, kill_char;
 extern int sigs;  extern int sigs;
 extern int quit_at_eof;  extern int quit_at_eof;
   extern int quit_if_one_screen;
   extern int squished;
 extern int hit_eof;  extern int hit_eof;
 extern int sc_width;  extern int sc_width;
 extern int sc_height;  extern int sc_height;
Line 46 
Line 33 
 extern int jump_sline;  extern int jump_sline;
 extern int quitting;  extern int quitting;
 extern int wscroll;  extern int wscroll;
 extern int nohelp;  
 extern int top_scroll;  extern int top_scroll;
 extern int ignore_eoi;  extern int ignore_eoi;
   extern int secure;
   extern int hshift;
   extern int show_attn;
 extern char *every_first_cmd;  extern char *every_first_cmd;
 extern char *curr_altfilename;  extern char *curr_altfilename;
 extern char version[];  extern char version[];
 extern struct scrpos initial_scrpos;  extern struct scrpos initial_scrpos;
 extern IFILE curr_ifile;  extern IFILE curr_ifile;
 #if CMD_HISTORY  extern void constant *ml_search;
 extern void *ml_search;  extern void constant *ml_examine;
 extern void *ml_examine;  
 #if SHELL_ESCAPE || PIPEC  #if SHELL_ESCAPE || PIPEC
 extern void *ml_shell;  extern void constant *ml_shell;
 #endif  #endif
 #else  
 /* No CMD_HISTORY */  
 #define ml_search       NULL  
 #define ml_examine      NULL  
 #define ml_shell        NULL  
 #endif  
 #if EDITOR  #if EDITOR
 extern char *editor;  extern char *editor;
 extern char *editproto;  extern char *editproto;
 #endif  #endif
 extern int screen_trashed;      /* The screen has been overwritten */  extern int screen_trashed;      /* The screen has been overwritten */
   extern int shift_count;
 extern int be_helpful;  extern int be_helpful;
   
 public int helpprompt;  static char ungot[UNGOT_SIZE];
   
 static char ungot[100];  
 static char *ungotp = NULL;  static char *ungotp = NULL;
 #if SHELL_ESCAPE  #if SHELL_ESCAPE
 static char *shellcmd = NULL;   /* For holding last shell command for "!!" */  static char *shellcmd = NULL;   /* For holding last shell command for "!!" */
 #endif  #endif
 static int mca;                 /* The multicharacter command (action) */  static int mca;                 /* The multicharacter command (action) */
 static int search_type;         /* The previous type of search */  static int search_type;         /* The previous type of search */
 static int number;              /* The number typed by the user */  static LINENUM number;          /* The number typed by the user */
 static char optchar;  static char optchar;
 static int optflag;  static int optflag;
   static int optgetname;
   static POSITION bottompos;
   static char *help_prompt;
 #if PIPEC  #if PIPEC
 static char pipec;  static char pipec;
 #endif  #endif
Line 99 
Line 83 
         static void          static void
 cmd_exec()  cmd_exec()
 {  {
           clear_attn();
         lower_left();          lower_left();
         flush();          flush();
 }  }
Line 107 
Line 92 
  * Set up the display to start a new multi-character command.   * Set up the display to start a new multi-character command.
  */   */
         static void          static void
 start_mca(action, prompt, mlist)  start_mca(action, prompt, mlist, cmdflags)
         int action;          int action;
         char *prompt;          char *prompt;
         void *mlist;          void *mlist;
           int cmdflags;
 {  {
         mca = action;          mca = action;
         clear_bot();          clear_cmd();
         cmd_putstr(prompt);          cmd_putstr(prompt);
 #if CMD_HISTORY          set_mlist(mlist, cmdflags);
         set_mlist(mlist);  
 #endif  
 }  }
   
         public int          public int
Line 137 
Line 121 
         else          else
                 mca = A_B_SEARCH;                  mca = A_B_SEARCH;
   
         clear_bot();          clear_cmd();
   
           if (search_type & SRCH_NO_MATCH)
                   cmd_putstr("Non-match ");
         if (search_type & SRCH_FIRST_FILE)          if (search_type & SRCH_FIRST_FILE)
                 cmd_putstr("@");                  cmd_putstr("First-file ");
   
         if (search_type & SRCH_PAST_EOF)          if (search_type & SRCH_PAST_EOF)
                 cmd_putstr("*");                  cmd_putstr("EOF-ignore ");
           if (search_type & SRCH_NO_MOVE)
                   cmd_putstr("Keep-pos ");
           if (search_type & SRCH_NO_REGEX)
                   cmd_putstr("Regex-off ");
   
         if (search_type & SRCH_NOMATCH)  
                 cmd_putstr("!");  
   
         if (search_type & SRCH_FORW)          if (search_type & SRCH_FORW)
                 cmd_putstr("/");                  cmd_putstr("/");
         else          else
                 cmd_putstr("?");                  cmd_putstr("?");
 #if CMD_HISTORY          set_mlist(ml_search, 0);
         set_mlist(ml_search);  
 #endif  
 }  }
   
 /*  /*
    * Set up the display to start a new toggle-option command.
    */
           static void
   mca_opt_toggle()
   {
           int no_prompt;
           int flag;
           char *dash;
   
           no_prompt = (optflag & OPT_NO_PROMPT);
           flag = (optflag & ~OPT_NO_PROMPT);
           dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
   
           mca = A_OPT_TOGGLE;
           clear_cmd();
           cmd_putstr(dash);
           if (optgetname)
                   cmd_putstr(dash);
           if (no_prompt)
                   cmd_putstr("(P)");
           switch (flag)
           {
           case OPT_UNSET:
                   cmd_putstr("+");
                   break;
           case OPT_SET:
                   cmd_putstr("!");
                   break;
           }
           set_mlist(NULL, 0);
   }
   
   /*
  * Execute a multicharacter command.   * Execute a multicharacter command.
  */   */
         static void          static void
 exec_mca()  exec_mca()
 {  {
         char *cbuf;          register char *cbuf;
   
         cmd_exec();          cmd_exec();
         cbuf = get_cmdbuf();          cbuf = get_cmdbuf();
Line 172 
Line 189 
         {          {
         case A_F_SEARCH:          case A_F_SEARCH:
         case A_B_SEARCH:          case A_B_SEARCH:
                 multi_search(cbuf, number);                  multi_search(cbuf, (int) number);
                 break;                  break;
         case A_FIRSTCMD:          case A_FIRSTCMD:
                 /*                  /*
Line 192 
Line 209 
                 optchar = '\0';                  optchar = '\0';
                 break;                  break;
         case A_F_BRACKET:          case A_F_BRACKET:
                 match_brac(cbuf[0], cbuf[1], 1, number);                  match_brac(cbuf[0], cbuf[1], 1, (int) number);
                 break;                  break;
         case A_B_BRACKET:          case A_B_BRACKET:
                 match_brac(cbuf[1], cbuf[0], 0, number);                  match_brac(cbuf[1], cbuf[0], 0, (int) number);
                 break;                  break;
 #if EXAMINE  #if EXAMINE
         case A_EXAMINE:          case A_EXAMINE:
                   if (secure)
                           break;
                 edit_list(cbuf);                  edit_list(cbuf);
   #if TAGS
                   /* If tag structure is loaded then clean it up. */
                   cleantags();
   #endif
                 break;                  break;
 #endif  #endif
 #if SHELL_ESCAPE  #if SHELL_ESCAPE
Line 216 
Line 239 
                         shellcmd = fexpand(cbuf);                          shellcmd = fexpand(cbuf);
                 }                  }
   
                   if (secure)
                           break;
                 if (shellcmd == NULL)                  if (shellcmd == NULL)
                         lsystem("");                          lsystem("", "!done");
                 else                  else
                         lsystem(shellcmd);                          lsystem(shellcmd, "!done");
                 error("!done", NULL_PARG);  
                 break;                  break;
 #endif  #endif
 #if PIPEC  #if PIPEC
         case A_PIPE:          case A_PIPE:
                   if (secure)
                           break;
                 (void) pipe_mark(pipec, cbuf);                  (void) pipe_mark(pipec, cbuf);
                 error("|done", NULL_PARG);                  error("|done", NULL_PARG);
                 break;                  break;
Line 242 
Line 268 
         char *p;          char *p;
         int flag;          int flag;
         char buf[3];          char buf[3];
           PARG parg;
   
         switch (mca)          switch (mca)
         {          {
Line 266 
Line 293 
                  * Terminated by a non-digit.                   * Terminated by a non-digit.
                  */                   */
                 if ((c < '0' || c > '9') &&                  if ((c < '0' || c > '9') &&
                   editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)                    editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
                 {                  {
                         /*                          /*
                          * Not part of the number.                           * Not part of the number.
Line 287 
Line 314 
                  * so user doesn't have to hit RETURN.                   * so user doesn't have to hit RETURN.
                  * If the first char is + or -, this indicates                   * If the first char is + or -, this indicates
                  * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.                   * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
                    * "--" begins inputting a long option name.
                  */                   */
                 if (c == erase_char || c == kill_char)                  if (optchar == '\0' && len_cmdbuf() == 0)
                         break;                  {
                 if (optchar != '\0' && optchar != '+' && optchar != '-')                          flag = (optflag & ~OPT_NO_PROMPT);
                           if (flag == OPT_NO_TOGGLE)
                           {
                                   switch (c)
                                   {
                                   case '_':
                                           /* "__" = long option name. */
                                           optgetname = TRUE;
                                           mca_opt_toggle();
                                           return (MCA_MORE);
                                   }
                           } else
                           {
                                   switch (c)
                                   {
                                   case '+':
                                           /* "-+" = UNSET. */
                                           optflag = (flag == OPT_UNSET) ?
                                                   OPT_TOGGLE : OPT_UNSET;
                                           mca_opt_toggle();
                                           return (MCA_MORE);
                                   case '!':
                                           /* "-!" = SET */
                                           optflag = (flag == OPT_SET) ?
                                                   OPT_TOGGLE : OPT_SET;
                                           mca_opt_toggle();
                                           return (MCA_MORE);
                                   case CONTROL('P'):
                                           optflag ^= OPT_NO_PROMPT;
                                           mca_opt_toggle();
                                           return (MCA_MORE);
                                   case '-':
                                           /* "--" = long option name. */
                                           optgetname = TRUE;
                                           mca_opt_toggle();
                                           return (MCA_MORE);
                                   }
                           }
                   }
                   if (optgetname)
                   {
                         /*                          /*
                          * We already have the option letter.                           * We're getting a long option name.
                            * See if we've matched an option name yet.
                            * If so, display the complete name and stop
                            * accepting chars until user hits RETURN.
                          */                           */
                         break;                          struct loption *o;
                 switch (c)                          char *oname;
                 {                          int lc;
                 case '+':  
                         optflag = OPT_UNSET;                          if (c == '\n' || c == '\r')
                         break;  
                 case '-':  
                         optflag = OPT_SET;  
                         break;  
                 default:  
                         optchar = c;  
                         if (optflag != OPT_TOGGLE || single_char_option(c))  
                         {                          {
                                 toggle_option(c, "", optflag);                                  /*
                                 return (MCA_DONE);                                   * When the user hits RETURN, make sure
                                    * we've matched an option name, then
                                    * pretend he just entered the equivalent
                                    * option letter.
                                    */
                                   if (optchar == '\0')
                                   {
                                           parg.p_string = get_cmdbuf();
                                           error("There is no --%s option", &parg);
                                           return (MCA_DONE);
                                   }
                                   optgetname = FALSE;
                                   cmd_reset();
                                   c = optchar;
                           } else
                           {
                                   if (optchar != '\0')
                                   {
                                           /*
                                            * Already have a match for the name.
                                            * Don't accept anything but erase/kill.
                                            */
                                           if (c == erase_char || c == kill_char)
                                                   return (MCA_DONE);
                                           return (MCA_MORE);
                                   }
                                   /*
                                    * Add char to cmd buffer and try to match
                                    * the option name.
                                    */
                                   if (cmd_char(c) == CC_QUIT)
                                           return (MCA_DONE);
                                   p = get_cmdbuf();
                                   lc = islower(p[0]);
                                   o = findopt_name(&p, &oname, NULL);
                                   if (o != NULL)
                                   {
                                           /*
                                            * Got a match.
                                            * Remember the option letter and
                                            * display the full option name.
                                            */
                                           optchar = o->oletter;
                                           if (!lc && islower(optchar))
                                                   optchar = toupper(optchar);
                                           cmd_reset();
                                           mca_opt_toggle();
                                           for (p = oname;  *p != '\0';  p++)
                                           {
                                                   c = *p;
                                                   if (!lc && islower(c))
                                                           c = toupper(c);
                                                   if (cmd_char(c) != CC_OK)
                                                           return (MCA_DONE);
                                           }
                                   }
                                   return (MCA_MORE);
                         }                          }
                         break;                  } else
                   {
                           if (c == erase_char || c == kill_char)
                                   break;
                           if (optchar != '\0')
                                   /* We already have the option letter. */
                                   break;
                 }                  }
                 if (optchar == '+' || optchar == '-')  
                   optchar = c;
                   if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
                       single_char_option(c))
                 {                  {
                         optchar = c;                          toggle_option(c, "", optflag);
                         break;                          return (MCA_DONE);
                 }                  }
                 /*                  /*
                  * Display a prompt appropriate for the option letter.                   * Display a prompt appropriate for the option letter.
Line 327 
Line 456 
                         buf[2] = '\0';                          buf[2] = '\0';
                         p = buf;                          p = buf;
                 }                  }
                 start_mca(A_OPT_TOGGLE, p, (void*)NULL);                  start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
                 return (MCA_MORE);                  return (MCA_MORE);
   
         case A_F_SEARCH:          case A_F_SEARCH:
Line 336 
Line 465 
                  * Special case for search commands.                   * Special case for search commands.
                  * Certain characters as the first char of                   * Certain characters as the first char of
                  * the pattern have special meaning:                   * the pattern have special meaning:
                  *      !  Toggle the NOMATCH flag                   *      !  Toggle the NO_MATCH flag
                  *      *  Toggle the PAST_EOF flag                   *      *  Toggle the PAST_EOF flag
                  *      @  Toggle the FIRST_FILE flag                   *      @  Toggle the FIRST_FILE flag
                  */                   */
Line 349 
Line 478 
                 flag = 0;                  flag = 0;
                 switch (c)                  switch (c)
                 {                  {
                 case '!':                  case CONTROL('E'): /* ignore END of file */
                         flag = SRCH_NOMATCH;                  case '*':
                           flag = SRCH_PAST_EOF;
                         break;                          break;
                   case CONTROL('F'): /* FIRST file */
                 case '@':                  case '@':
                         flag = SRCH_FIRST_FILE;                          flag = SRCH_FIRST_FILE;
                         break;                          break;
                 case '*':                  case CONTROL('K'): /* KEEP position */
                         flag = SRCH_PAST_EOF;                          flag = SRCH_NO_MOVE;
                         break;                          break;
                   case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
                           flag = SRCH_NO_REGEX;
                           break;
                   case CONTROL('N'): /* NOT match */
                   case '!':
                           flag = SRCH_NO_MATCH;
                           break;
                 }                  }
                 if (flag != 0)                  if (flag != 0)
                 {                  {
Line 380 
Line 518 
                 exec_mca();                  exec_mca();
                 return (MCA_DONE);                  return (MCA_DONE);
         }          }
   
         /*          /*
          * Append the char to the command buffer.           * Append the char to the command buffer.
          */           */
Line 407 
Line 546 
 }  }
   
 /*  /*
  * Display the appropriate prompt.   * Make sure the screen is displayed.
  */   */
         static void          static void
 prompt()  make_display()
 {  {
         char *p;  
   
         if (ungotp != NULL && ungotp > ungot)  
         {  
                 /*  
                  * No prompt necessary if commands are from  
                  * ungotten chars rather than from the user.  
                  */  
                 return;  
         }  
   
         /*          /*
          * If nothing is displayed yet, display starting from initial_scrpos.           * If nothing is displayed yet, display starting from initial_scrpos.
          */           */
Line 446 
Line 574 
                 repaint();                  repaint();
                 top_scroll = save_top_scroll;                  top_scroll = save_top_scroll;
         }          }
   }
   
   /*
    * Display the appropriate prompt.
    */
           static void
   prompt()
   {
           register char *p;
   
           if (ungotp != NULL && ungotp > ungot)
           {
                   /*
                    * No prompt necessary if commands are from
                    * ungotten chars rather than from the user.
                    */
                   return;
           }
   
         /*          /*
            * Make sure the screen is displayed.
            */
           make_display();
           bottompos = position(BOTTOM_PLUS_ONE);
   
           /*
          * If the -E flag is set and we've hit EOF on the last file, quit.           * If the -E flag is set and we've hit EOF on the last file, quit.
          */           */
         if (quit_at_eof == OPT_ONPLUS && hit_eof &&          if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) &&
               hit_eof && !(ch_getflags() & CH_HELPFILE) &&
             next_ifile(curr_ifile) == NULL_IFILE)              next_ifile(curr_ifile) == NULL_IFILE)
                 quit(QUIT_OK);                  quit(QUIT_OK);
           quit_if_one_screen = FALSE;
   #if 0 /* This doesn't work well because some "te"s clear the screen. */
           /*
            * If the -e flag is set and we've hit EOF on the last file,
            * and the file is squished (shorter than the screen), quit.
            */
           if (quit_at_eof && squished &&
               next_ifile(curr_ifile) == NULL_IFILE)
                   quit(QUIT_OK);
   #endif
   
   #if MSDOS_COMPILER==WIN32C
           /*
            * In Win32, display the file name in the window title.
            */
           if (!(ch_getflags() & CH_HELPFILE))
                   SetConsoleTitle(pr_expand("Less?f - %f.", 0));
   #endif
         /*          /*
          * Select the proper prompt and display it.           * Select the proper prompt and display it.
          */           */
         clear_bot();          clear_cmd();
         if (helpprompt) {          p = help_prompt ? help_prompt : pr_string();
           if (p == NULL)
                   putchr(':');
           else
           {
                 so_enter();                  so_enter();
                 putstr("[Press 'h' for instructions.]");                  putstr(p);
                   if (be_helpful && !help_prompt && strlen(p) + 40 < sc_width)
                           putstr(" [Press space to continue, 'q' to quit.]");
                 so_exit();                  so_exit();
                 helpprompt = 0;  
         } else {  
                 p = pr_string();  
                 if (p == NULL)  
                         putchr(':');  
                 else  
                 {  
                         so_enter();  
                         putstr(p);  
                         if (be_helpful && strlen(p) + 40 < sc_width)  
                                 putstr(" [Press space to continue, 'q' to quit.]");  
                         so_exit();  
                 }  
         }          }
           help_prompt = NULL;
 }  }
   
   /*
    * Display the less version message.
    */
         public void          public void
 dispversion()  dispversion()
 {  {
         PARG parg;          PARG parg;
   
         parg.p_string = version;          parg.p_string = version;
         error("less  version %s", &parg);          error("less %s", &parg);
 }  }
   
 /*  /*
Line 566 
Line 733 
 ungetsc(s)  ungetsc(s)
         char *s;          char *s;
 {  {
         char *p;          register char *p;
   
         for (p = s + strlen(s) - 1;  p >= s;  p--)          for (p = s + strlen(s) - 1;  p >= s;  p--)
                 ungetcc(*p);                  ungetcc(*p);
Line 582 
Line 749 
         char *pattern;          char *pattern;
         int n;          int n;
 {  {
         int nomore;          register int nomore;
         IFILE save_ifile;          IFILE save_ifile;
         int changed_file;          int changed_file;
   
         changed_file = 0;          changed_file = 0;
         save_ifile = curr_ifile;          save_ifile = save_curr_ifile();
   
         if (search_type & SRCH_FIRST_FILE)          if (search_type & SRCH_FIRST_FILE)
         {          {
Line 600 
Line 767 
                 else                  else
                         nomore = edit_last();                          nomore = edit_last();
                 if (nomore)                  if (nomore)
                   {
                           unsave_ifile(save_ifile);
                         return;                          return;
                   }
                 changed_file = 1;                  changed_file = 1;
                 search_type &= ~SRCH_FIRST_FILE;                  search_type &= ~SRCH_FIRST_FILE;
         }          }
   
         for (;;)          for (;;)
         {          {
                 if ((n = search(search_type, pattern, n)) == 0)                  n = search(search_type, pattern, n);
                   /*
                    * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
                    * after being used once.  This allows "n" to work after
                    * using a /@@ search.
                    */
                   search_type &= ~SRCH_NO_MOVE;
                   if (n == 0)
                   {
                         /*                          /*
                          * Found it.                           * Found it.
                          */                           */
                           unsave_ifile(save_ifile);
                         return;                          return;
                   }
   
                 if (n < 0)                  if (n < 0)
                         /*                          /*
Line 650 
Line 830 
                 /*                  /*
                  * Restore the file we were originally viewing.                   * Restore the file we were originally viewing.
                  */                   */
                 if (edit_ifile(save_ifile))                  reedit_ifile(save_ifile);
                         quit(QUIT_ERROR);  
         }          }
 }  }
   
Line 662 
Line 841 
         public void          public void
 commands()  commands()
 {  {
         int c;          register int c;
         int action;          register int action;
         char *cbuf;          register char *cbuf;
           int newaction;
         int save_search_type;          int save_search_type;
         char *s;          char *extra;
         char tbuf[2];          char tbuf[2];
         PARG parg;          PARG parg;
           IFILE old_ifile;
           IFILE new_ifile;
           char *tagfile;
   
         search_type = SRCH_FORW;          search_type = SRCH_FORW;
         wscroll = (sc_height + 1) / 2;          wscroll = (sc_height + 1) / 2;
           newaction = A_NOACTION;
   
         for (;;)          for (;;)
         {          {
Line 689 
Line 873 
                         if (quitting)                          if (quitting)
                                 quit(QUIT_SAVED_STATUS);                                  quit(QUIT_SAVED_STATUS);
                 }                  }
   
                 /*                  /*
                    * See if window size changed, for systems that don't
                    * generate SIGWINCH.
                    */
                   check_winch();
   
                   /*
                  * Display prompt and accept a character.                   * Display prompt and accept a character.
                  */                   */
                 cmd_reset();                  cmd_reset();
                 prompt();                  prompt();
                 if (sigs)                  if (sigs)
                         continue;                          continue;
                 c = getcc();                  if (newaction == A_NOACTION)
                           c = getcc();
   
         again:          again:
                 if (sigs)                  if (sigs)
                         continue;                          continue;
   
                 /*                  if (newaction != A_NOACTION)
                  * If we are in a multicharacter command, call mca_char.                  {
                  * Otherwise we call fcmd_decode to determine the                          action = newaction;
                  * action to be performed.                          newaction = A_NOACTION;
                  */                  } else
                 if (mca)                  {
                         switch (mca_char(c))                          /*
                            * If we are in a multicharacter command, call mca_char.
                            * Otherwise we call fcmd_decode to determine the
                            * action to be performed.
                            */
                           if (mca)
                                   switch (mca_char(c))
                                   {
                                   case MCA_MORE:
                                           /*
                                            * Need another character.
                                            */
                                           c = getcc();
                                           goto again;
                                   case MCA_DONE:
                                           /*
                                            * Command has been handled by mca_char.
                                            * Start clean with a prompt.
                                            */
                                           continue;
                                   case NO_MCA:
                                           /*
                                            * Not a multi-char command
                                            * (at least, not anymore).
                                            */
                                           break;
                                   }
   
                           /*
                            * Decode the command character and decide what to do.
                            */
                           if (mca)
                         {                          {
                         case MCA_MORE:  
                                 /*                                  /*
                                  * Need another character.                                   * We're in a multichar command.
                                    * Add the character to the command buffer
                                    * and display it on the screen.
                                    * If the user backspaces past the start
                                    * of the line, abort the command.
                                  */                                   */
                                 c = getcc();                                  if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
                                 goto again;                                          continue;
                         case MCA_DONE:                                  cbuf = get_cmdbuf();
                           } else
                           {
                                 /*                                  /*
                                  * Command has been handled by mca_char.                                   * Don't use cmd_char if we're starting fresh
                                  * Start clean with a prompt.                                   * at the beginning of a command, because we
                                    * don't want to echo the command until we know
                                    * it is a multichar command.  We also don't
                                    * want erase_char/kill_char to be treated
                                    * as line editing characters.
                                  */                                   */
                                 continue;                                  tbuf[0] = c;
                         case NO_MCA:                                  tbuf[1] = '\0';
                                 /*                                  cbuf = tbuf;
                                  * Not a multi-char command  
                                  * (at least, not anymore).  
                                  */  
                                 break;  
                         }                          }
                           extra = NULL;
                 /*                          action = fcmd_decode(cbuf, &extra);
                  * Decode the command character and decide what to do.  
                  */  
                 if (mca)  
                 {  
                         /*                          /*
                          * We're in a multichar command.                           * If an "extra" string was returned,
                          * Add the character to the command buffer                           * process it as a string of command characters.
                          * and display it on the screen.  
                          * If the user backspaces past the start  
                          * of the line, abort the command.  
                          */                           */
                         if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)                          if (extra != NULL)
                                 continue;                                  ungetsc(extra);
                         cbuf = get_cmdbuf();  
                 } else  
                 {  
                         /*  
                          * Don't use cmd_char if we're starting fresh  
                          * at the beginning of a command, because we  
                          * don't want to echo the command until we know  
                          * it is a multichar command.  We also don't  
                          * want erase_char/kill_char to be treated  
                          * as line editing characters.  
                          */  
                         tbuf[0] = c;  
                         tbuf[1] = '\0';  
                         cbuf = tbuf;  
                 }                  }
                 s = NULL;  
                 action = fcmd_decode(cbuf, &s);  
                 /*                  /*
                  * If an "extra" string was returned,  
                  * process it as a string of command characters.  
                  */  
                 if (s != NULL)  
                         ungetsc(s);  
                 /*  
                  * Clear the cmdbuf string.                   * Clear the cmdbuf string.
                  * (But not if we're in the prefix of a command,                   * (But not if we're in the prefix of a command,
                  * because the partial command string is kept there.)                   * because the partial command string is kept there.)
Line 782 
Line 980 
                         /*                          /*
                          * First digit of a number.                           * First digit of a number.
                          */                           */
                         start_mca(A_DIGIT, ":", (void*)NULL);                          start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
                         goto again;                          goto again;
   
                 case A_F_WINDOW:                  case A_F_WINDOW:
Line 790 
Line 988 
                          * Forward one window (and set the window size).                           * Forward one window (and set the window size).
                          */                           */
                         if (number > 0)                          if (number > 0)
                                 swindow = number;                                  swindow = (int) number;
                         /* FALLTHRU */                          /* FALLTHRU */
                 case A_F_SCREEN:                  case A_F_SCREEN:
                         /*                          /*
Line 799 
Line 997 
                         if (number <= 0)                          if (number <= 0)
                                 number = get_swindow();                                  number = get_swindow();
                         cmd_exec();                          cmd_exec();
                         forward(number, 0, 1);                          if (show_attn)
                                   set_attnpos(bottompos);
                           forward((int) number, 0, 1);
                         break;                          break;
   
                 case A_B_WINDOW:                  case A_B_WINDOW:
Line 807 
Line 1007 
                          * Backward one window (and set the window size).                           * Backward one window (and set the window size).
                          */                           */
                         if (number > 0)                          if (number > 0)
                                 swindow = number;                                  swindow = (int) number;
                         /* FALLTHRU */                          /* FALLTHRU */
                 case A_B_SCREEN:                  case A_B_SCREEN:
                         /*                          /*
Line 816 
Line 1016 
                         if (number <= 0)                          if (number <= 0)
                                 number = get_swindow();                                  number = get_swindow();
                         cmd_exec();                          cmd_exec();
                         backward(number, 0, 1);                          backward((int) number, 0, 1);
                         break;                          break;
   
                 case A_F_LINE:                  case A_F_LINE:
Line 826 
Line 1026 
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         cmd_exec();                          cmd_exec();
                         forward(number, 0, 0);                          if (show_attn == OPT_ONPLUS && number > 1)
                                   set_attnpos(bottompos);
                           forward((int) number, 0, 0);
                         break;                          break;
   
                 case A_B_LINE:                  case A_B_LINE:
Line 836 
Line 1038 
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         cmd_exec();                          cmd_exec();
                         backward(number, 0, 0);                          backward((int) number, 0, 0);
                         break;                          break;
   
                 case A_FF_LINE:                  case A_FF_LINE:
Line 846 
Line 1048 
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         cmd_exec();                          cmd_exec();
                         forward(number, 1, 0);                          if (show_attn == OPT_ONPLUS && number > 1)
                                   set_attnpos(bottompos);
                           forward((int) number, 1, 0);
                         break;                          break;
   
                 case A_BF_LINE:                  case A_BF_LINE:
Line 856 
Line 1060 
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         cmd_exec();                          cmd_exec();
                         backward(number, 1, 0);                          backward((int) number, 1, 0);
                         break;                          break;
   
                   case A_FF_SCREEN:
                           /*
                            * Force forward one screen.
                            */
                           if (number <= 0)
                                   number = get_swindow();
                           cmd_exec();
                           if (show_attn == OPT_ONPLUS)
                                   set_attnpos(bottompos);
                           forward((int) number, 1, 0);
                           break;
   
                 case A_F_FOREVER:                  case A_F_FOREVER:
                         /*                          /*
                          * Forward forever, ignoring EOF.                           * Forward forever, ignoring EOF.
                          */                           */
                           if (ch_getflags() & CH_HELPFILE)
                                   break;
                         cmd_exec();                          cmd_exec();
                         jump_forw();                          jump_forw();
                         ignore_eoi = 1;                          ignore_eoi = 1;
                         hit_eof = 0;                          hit_eof = 0;
                         while (!ABORT_SIGS())                          while (!sigs)
                                 forward(1, 0, 0);                                  forward(1, 0, 0);
                         ignore_eoi = 0;                          ignore_eoi = 0;
                           /*
                            * This gets us back in "F mode" after processing
                            * a non-abort signal (e.g. window-change).
                            */
                           if (sigs && !ABORT_SIGS())
                                   newaction = A_F_FOREVER;
                         break;                          break;
   
                 case A_F_SCROLL:                  case A_F_SCROLL:
Line 878 
Line 1102 
                          * (default same as last 'd' or 'u' command).                           * (default same as last 'd' or 'u' command).
                          */                           */
                         if (number > 0)                          if (number > 0)
                                 wscroll = number;                                  wscroll = (int) number;
                         cmd_exec();                          cmd_exec();
                           if (show_attn == OPT_ONPLUS)
                                   set_attnpos(bottompos);
                         forward(wscroll, 0, 0);                          forward(wscroll, 0, 0);
                         break;                          break;
   
Line 889 
Line 1115 
                          * (default same as last 'd' or 'u' command).                           * (default same as last 'd' or 'u' command).
                          */                           */
                         if (number > 0)                          if (number > 0)
                                 wscroll = number;                                  wscroll = (int) number;
                         cmd_exec();                          cmd_exec();
                         backward(wscroll, 0, 0);                          backward(wscroll, 0, 0);
                         break;                          break;
Line 903 
Line 1129 
                         {                          {
                                 ch_flush();                                  ch_flush();
                                 clr_linenum();                                  clr_linenum();
   #if HILITE_SEARCH
                                   clr_hilite();
   #endif
                         }                          }
                         /* FALLTHRU */                          /* FALLTHRU */
                 case A_REPAINT:                  case A_REPAINT:
Line 932 
Line 1161 
                         if (number > 100)                          if (number > 100)
                                 number = 100;                                  number = 100;
                         cmd_exec();                          cmd_exec();
                         jump_percent(number);                          jump_percent((int) number);
                         break;                          break;
   
                 case A_GOEND:                  case A_GOEND:
Line 953 
Line 1182 
                         cmd_exec();                          cmd_exec();
                         if (number < 0)                          if (number < 0)
                                 number = 0;                                  number = 0;
                         jump_line_loc((POSITION)number, jump_sline);                          jump_line_loc((POSITION) number, jump_sline);
                         break;                          break;
   
                 case A_STAT:                  case A_STAT:
                         /*                          /*
                          * Print file name, etc.                           * Print file name, etc.
                          */                           */
                           if (ch_getflags() & CH_HELPFILE)
                                   break;
                         cmd_exec();                          cmd_exec();
                         parg.p_string = eq_message();                          parg.p_string = eq_message();
                         error("%s", &parg);                          error("%s", &parg);
                         break;                          break;
   
                 case A_VERSION:                  case A_VERSION:
                         /*                          /*
                          * Print version number, without the "@(#)".                           * Print version number, without the "@(#)".
Line 977 
Line 1208 
                         /*                          /*
                          * Exit.                           * Exit.
                          */                           */
                           if (curr_ifile != NULL_IFILE &&
                               ch_getflags() & CH_HELPFILE)
                           {
                                   /*
                                    * Quit while viewing the help file
                                    * just means return to viewing the
                                    * previous file.
                                    */
                                   if (edit_prev(1) == 0)
                                           break;
                           }
                           if (extra != NULL)
                                   quit(*extra);
                         quit(QUIT_OK);                          quit(QUIT_OK);
                           break;
   
 /*  /*
  * Define abbreviation for a commonly used sequence below.   * Define abbreviation for a commonly used sequence below.
Line 985 
Line 1230 
 #define DO_SEARCH()     if (number <= 0) number = 1;    \  #define DO_SEARCH()     if (number <= 0) number = 1;    \
                         mca_search();                   \                          mca_search();                   \
                         cmd_exec();                     \                          cmd_exec();                     \
                         multi_search((char *)NULL, number);                          multi_search((char *)NULL, (int) number);
   
   
                 case A_F_SEARCH:                  case A_F_SEARCH:
Line 1057 
Line 1302 
                         /*                          /*
                          * Help.                           * Help.
                          */                           */
                         if (nohelp)                          if (ch_getflags() & CH_HELPFILE)
                         {  
                                 bell();  
                                 break;                                  break;
                         }  
                         clear_bot();  
                         putstr(" help");  
                         cmd_exec();                          cmd_exec();
                         help(0);                          (void) edit(FAKE_HELPFILE);
                         break;                          break;
   
                 case A_EXAMINE:                  case A_EXAMINE:
Line 1073 
Line 1313 
                         /*                          /*
                          * Edit a new file.  Get the filename.                           * Edit a new file.  Get the filename.
                          */                           */
                         start_mca(A_EXAMINE, "Examine: ", ml_examine);                          if (secure)
                           {
                                   error("Command not available", NULL_PARG);
                                   break;
                           }
                           start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
 #else  #else
Line 1086 
Line 1331 
                          * Invoke an editor on the input file.                           * Invoke an editor on the input file.
                          */                           */
 #if EDITOR  #if EDITOR
                           if (secure)
                           {
                                   error("Command not available", NULL_PARG);
                                   break;
                           }
                           if (ch_getflags() & CH_HELPFILE)
                                   break;
                         if (strcmp(get_filename(curr_ifile), "-") == 0)                          if (strcmp(get_filename(curr_ifile), "-") == 0)
                         {                          {
                                 error("Cannot edit standard input", NULL_PARG);                                  error("Cannot edit standard input", NULL_PARG);
Line 1097 
Line 1349 
                                         NULL_PARG);                                          NULL_PARG);
                                 break;                                  break;
                         }                          }
                           start_mca(A_SHELL, "!", ml_shell, 0);
                         /*                          /*
                          * Expand the editor prototype string                           * Expand the editor prototype string
                          * and pass it to the system to execute.                           * and pass it to the system to execute.
                            * (Make sure the screen is displayed so the
                            * expansion of "+%lm" works.)
                          */                           */
                           make_display();
                         cmd_exec();                          cmd_exec();
                         lsystem(pr_expand(editproto, 0));                          lsystem(pr_expand(editproto, 0), (char*)NULL);
                         /*  
                          * Re-edit the file, since data may have changed.  
                          * Some editors even recreate the file, so flushing  
                          * buffers is not sufficient.  
                          */  
                         if (edit_ifile(curr_ifile))  
                                 quit(QUIT_ERROR);  
                         break;                          break;
 #else  #else
                         error("Command not available", NULL_PARG);                          error("Command not available", NULL_PARG);
Line 1120 
Line 1369 
                         /*                          /*
                          * Examine next file.                           * Examine next file.
                          */                           */
   #if TAGS
                           if (ntags())
                           {
                                   error("No next file", NULL_PARG);
                                   break;
                           }
   #endif
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         if (edit_next(number))                          if (edit_next((int) number))
                         {                          {
                                 if (quit_at_eof && hit_eof)                                  if (quit_at_eof && hit_eof &&
                                       !(ch_getflags() & CH_HELPFILE))
                                         quit(QUIT_OK);                                          quit(QUIT_OK);
                                 parg.p_string = (number > 1) ? "(N-th) " : "";                                  parg.p_string = (number > 1) ? "(N-th) " : "";
                                 error("No %snext file", &parg);                                  error("No %snext file", &parg);
Line 1135 
Line 1392 
                         /*                          /*
                          * Examine previous file.                           * Examine previous file.
                          */                           */
   #if TAGS
                           if (ntags())
                           {
                                   error("No previous file", NULL_PARG);
                                   break;
                           }
   #endif
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         if (edit_prev(number))                          if (edit_prev((int) number))
                         {                          {
                                 parg.p_string = (number > 1) ? "(N-th) " : "";                                  parg.p_string = (number > 1) ? "(N-th) " : "";
                                 error("No %sprevious file", &parg);                                  error("No %sprevious file", &parg);
                         }                          }
                         break;                          break;
   
                   case A_NEXT_TAG:
   #if TAGS
                           if (number <= 0)
                                   number = 1;
                           tagfile = nexttag((int) number);
                           if (tagfile == NULL)
                           {
                                   error("No next tag", NULL_PARG);
                                   break;
                           }
                           if (edit(tagfile) == 0)
                           {
                                   POSITION pos = tagsearch();
                                   if (pos != NULL_POSITION)
                                           jump_loc(pos, jump_sline);
                           }
   #else
                           error("Command not available", NULL_PARG);
   #endif
                           break;
   
                   case A_PREV_TAG:
   #if TAGS
                           if (number <= 0)
                                   number = 1;
                           tagfile = prevtag((int) number);
                           if (tagfile == NULL)
                           {
                                   error("No previous tag", NULL_PARG);
                                   break;
                           }
                           if (edit(tagfile) == 0)
                           {
                                   POSITION pos = tagsearch();
                                   if (pos != NULL_POSITION)
                                           jump_loc(pos, jump_sline);
                           }
   #else
                           error("Command not available", NULL_PARG);
   #endif
                           break;
   
                 case A_INDEX_FILE:                  case A_INDEX_FILE:
                         /*                          /*
                          * Examine a particular file.                           * Examine a particular file.
                          */                           */
                         if (number <= 0)                          if (number <= 0)
                                 number = 1;                                  number = 1;
                         if (edit_index(number))                          if (edit_index((int) number))
                                 error("No such file", NULL_PARG);                                  error("No such file", NULL_PARG);
                         break;                          break;
   
                   case A_REMOVE_FILE:
                           if (ch_getflags() & CH_HELPFILE)
                                   break;
                           old_ifile = curr_ifile;
                           new_ifile = getoff_ifile(curr_ifile);
                           if (new_ifile == NULL_IFILE)
                           {
                                   bell();
                                   break;
                           }
                           if (edit_ifile(new_ifile) != 0)
                           {
                                   reedit_ifile(old_ifile);
                                   break;
                           }
                           del_ifile(old_ifile);
                           break;
   
                 case A_OPT_TOGGLE:                  case A_OPT_TOGGLE:
                         start_mca(A_OPT_TOGGLE, "-", (void*)NULL);  
                         optflag = OPT_TOGGLE;                          optflag = OPT_TOGGLE;
                           optgetname = FALSE;
                           mca_opt_toggle();
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
   
Line 1164 
Line 1489 
                         /*                          /*
                          * Report a flag setting.                           * Report a flag setting.
                          */                           */
                         start_mca(A_DISP_OPTION, "_", (void*)NULL);                          optflag = OPT_NO_TOGGLE;
                           optgetname = FALSE;
                           mca_opt_toggle();
                         c = getcc();                          c = getcc();
                         if (c == erase_char || c == kill_char)                          goto again;
                                 break;  
                         toggle_option(c, "", OPT_NO_TOGGLE);  
                         break;  
   
                 case A_FIRSTCMD:                  case A_FIRSTCMD:
                         /*                          /*
                          * Set an initial command for new files.                           * Set an initial command for new files.
                          */                           */
                         start_mca(A_FIRSTCMD, "+", (void*)NULL);                          start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
   
Line 1184 
Line 1508 
                          * Shell escape.                           * Shell escape.
                          */                           */
 #if SHELL_ESCAPE  #if SHELL_ESCAPE
                         start_mca(A_SHELL, "!", ml_shell);                          if (secure)
                           {
                                   error("Command not available", NULL_PARG);
                                   break;
                           }
                           start_mca(A_SHELL, "!", ml_shell, 0);
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
 #else  #else
Line 1196 
Line 1525 
                         /*                          /*
                          * Set a mark.                           * Set a mark.
                          */                           */
                         start_mca(A_SETMARK, "mark: ", (void*)NULL);                          if (ch_getflags() & CH_HELPFILE)
                                   break;
                           start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
                         c = getcc();                          c = getcc();
                         if (c == erase_char || c == kill_char ||                          if (c == erase_char || c == kill_char ||
                             c == '\n' || c == '\r')                              c == '\n' || c == '\r')
Line 1208 
Line 1539 
                         /*                          /*
                          * Go to a mark.                           * Go to a mark.
                          */                           */
                         start_mca(A_GOMARK, "goto mark: ", (void*)NULL);                          start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
                         c = getcc();                          c = getcc();
                         if (c == erase_char || c == kill_char ||                          if (c == erase_char || c == kill_char ||
                             c == '\n' || c == '\r')                              c == '\n' || c == '\r')
Line 1218 
Line 1549 
   
                 case A_PIPE:                  case A_PIPE:
 #if PIPEC  #if PIPEC
                         start_mca(A_PIPE, "|mark: ", (void*)NULL);                          if (secure)
                           {
                                   error("Command not available", NULL_PARG);
                                   break;
                           }
                           start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
                         c = getcc();                          c = getcc();
                         if (c == erase_char || c == kill_char)                          if (c == erase_char || c == kill_char)
                                 break;                                  break;
Line 1227 
Line 1563 
                         if (badmark(c))                          if (badmark(c))
                                 break;                                  break;
                         pipec = c;                          pipec = c;
                         start_mca(A_PIPE, "!", ml_shell);                          start_mca(A_PIPE, "!", ml_shell, 0);
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
 #else  #else
Line 1237 
Line 1573 
   
                 case A_B_BRACKET:                  case A_B_BRACKET:
                 case A_F_BRACKET:                  case A_F_BRACKET:
                         start_mca(action, "Brackets: ", (void*)NULL);                          start_mca(action, "Brackets: ", (void*)NULL, 0);
                         c = getcc();                          c = getcc();
                         goto again;                          goto again;
   
                   case A_LSHIFT:
                           if (number > 0)
                                   shift_count = number;
                           else
                                   number = (shift_count > 0) ?
                                           shift_count : sc_width / 2;
                           if (number > hshift)
                                   number = hshift;
                           hshift -= number;
                           screen_trashed = 1;
                           break;
   
                   case A_RSHIFT:
                           if (number > 0)
                                   shift_count = number;
                           else
                                   number = (shift_count > 0) ?
                                           shift_count : sc_width / 2;
                           hshift += number;
                           screen_trashed = 1;
                           break;
   
                 case A_PREFIX:                  case A_PREFIX:
                         /*                          /*
                          * The command is incomplete (more chars are needed).                           * The command is incomplete (more chars are needed).
Line 1249 
Line 1607 
                          */                           */
                         if (mca != A_PREFIX)                          if (mca != A_PREFIX)
                         {                          {
                                 start_mca(A_PREFIX, " ", (void*)NULL);  
                                 cmd_reset();                                  cmd_reset();
                                   start_mca(A_PREFIX, " ", (void*)NULL,
                                           CF_QUIT_ON_ERASE);
                                 (void) cmd_char(c);                                  (void) cmd_char(c);
                         }                          }
                         c = getcc();                          c = getcc();
Line 1261 
Line 1620 
   
                 default:                  default:
                         if (be_helpful)                          if (be_helpful)
                                 helpprompt = 1;                                  help_prompt = "[Press 'h' for instructions.]";
                         else                          else
                                 bell();                                  bell();
                         break;                          break;

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.6