home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / LESSKEY.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  10KB  |  419 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  *    lesskey [-o output] [input]
  30.  *
  31.  *    Make a .less file.
  32.  *    If no input file is specified, standard input is used.
  33.  *    If no output file is specified, $HOME/.less is used.
  34.  *
  35.  *    The .less file is used to specify (to "less") user-defined
  36.  *    key bindings.  Basically any sequence of 1 to MAX_CMDLEN
  37.  *    keystrokes may be bound to an existing less function.
  38.  *
  39.  *    The input file is an ascii file consisting of a 
  40.  *    sequence of lines of the form:
  41.  *        string <whitespace> action [chars] <newline>
  42.  *
  43.  *    "string" is a sequence of command characters which form
  44.  *        the new user-defined command.  The command
  45.  *        characters may be:
  46.  *        1. The actual character itself.
  47.  *        2. A character preceded by ^ to specify a
  48.  *           control character (e.g. ^X means control-X).
  49.  *        3. A backslash followed by one to three octal digits
  50.  *           to specify a character by its octal value.
  51.  *        4. A backslash followed by b, e, n, r or t
  52.  *           to specify \b, ESC, \n, \r or \t, respectively.
  53.  *        5. Any character (other than those mentioned above) preceded 
  54.  *           by a \ to specify the character itself (characters which
  55.  *           must be preceded by \ include ^, \, and whitespace.
  56.  *    "action" is the name of a "less" action, from the table below.
  57.  *    "chars" is an optional sequence of characters which is treated
  58.  *        as keyboard input after the command is executed.
  59.  *
  60.  *    Blank lines and lines which start with # are ignored.
  61.  *
  62.  *
  63.  *    The output file is a non-ascii file, consisting of
  64.  *    zero or more byte sequences of the form:
  65.  *        string <0> <action>
  66.  *    or
  67.  *        string <0> <action|A_EXTRA> chars <0>
  68.  *
  69.  *    "string" is the command string.
  70.  *    "<0>" is one null byte.
  71.  *    "<action>" is one byte containing the action code (the A_xxx value).
  72.  *    If action is ORed with A_EXTRA, the action byte is followed
  73.  *        by the null-terminated "chars" string.
  74.  */
  75.  
  76. #include "less.h"
  77. #include "cmd.h"
  78.  
  79. char usertable[MAX_USERCMD];
  80.  
  81. struct cmdname
  82. {
  83.     char *cn_name;
  84.     int cn_action;
  85. } cmdnames[] = 
  86. {
  87.     "back-bracket",        A_B_BRACKET,
  88.     "back-line",        A_B_LINE,
  89.     "back-line-force",    A_BF_LINE,
  90.     "back-screen",        A_B_SCREEN,
  91.     "back-scroll",        A_B_SCROLL,
  92.     "back-search",        A_B_SEARCH,
  93.     "back-window",        A_B_WINDOW,
  94.     "debug",        A_DEBUG,
  95.     "display-flag",        A_DISP_OPTION,
  96.     "display-option",    A_DISP_OPTION,
  97.     "end",            A_GOEND,
  98.     "examine",        A_EXAMINE,
  99.     "first-cmd",        A_FIRSTCMD,
  100.     "firstcmd",        A_FIRSTCMD,
  101.     "flush-repaint",    A_FREPAINT,
  102.     "forw-bracket",        A_F_BRACKET,
  103.     "forw-forever",        A_F_FOREVER,
  104.     "forw-line",        A_F_LINE,
  105.     "forw-line-force",    A_FF_LINE,
  106.     "forw-screen",        A_F_SCREEN,
  107.     "forw-scroll",        A_F_SCROLL,
  108.     "forw-search",        A_F_SEARCH,
  109.     "forw-window",        A_F_WINDOW,
  110.     "goto-end",        A_GOEND,
  111.     "goto-line",        A_GOLINE,
  112.     "goto-mark",        A_GOMARK,
  113.     "help",            A_HELP,
  114.     "index-file",        A_INDEX_FILE,
  115.     "invalid",        A_UINVALID,
  116.     "next-file",        A_NEXT_FILE,
  117.     "noaction",        A_NOACTION,
  118.     "percent",        A_PERCENT,
  119.     "pipe",            A_PIPE,
  120.     "prev-file",        A_PREV_FILE,
  121.     "quit",            A_QUIT,
  122.     "repaint",        A_REPAINT,
  123.     "repaint-flush",    A_FREPAINT,
  124.     "repeat-search",    A_AGAIN_SEARCH,
  125.     "repeat-search-all",    A_T_AGAIN_SEARCH,
  126.     "reverse-search",    A_REVERSE_SEARCH,
  127.     "reverse-search-all",    A_T_REVERSE_SEARCH,
  128.     "set-mark",        A_SETMARK,
  129.     "shell",        A_SHELL,
  130.     "status",        A_STAT,
  131.     "toggle-flag",        A_OPT_TOGGLE,
  132.     "toggle-option",    A_OPT_TOGGLE,
  133.     "undo-hilite",        A_UNDO_SEARCH,
  134.     "version",        A_VERSION,
  135.     "visual",        A_VISUAL,
  136.     NULL,            0
  137. };
  138.  
  139. /*
  140.  * Parse one character of a string.
  141.  */
  142. int
  143. tchar(pp)
  144.     char **pp;
  145. {
  146.     register char *p;
  147.     register char ch;
  148.     register int i;
  149.  
  150.     p = *pp;
  151.     switch (*p)
  152.     {
  153.     case '\\':
  154.         ++p;
  155.         switch (*p)
  156.         {
  157.         case '0': case '1': case '2': case '3':
  158.         case '4': case '5': case '6': case '7':
  159.             /*
  160.              * Parse an octal number.
  161.              */
  162.             ch = 0;
  163.             i = 0;
  164.             do
  165.                 ch = 8*ch + (*p - '0');
  166.             while (*++p >= '0' && *p <= '7' && ++i < 3);
  167.             *pp = p;
  168.             return (ch);
  169.         case 'b':
  170.             *pp = p+1;
  171.             return ('\r');
  172.         case 'e':
  173.             *pp = p+1;
  174.             return (ESC);
  175.         case 'n':
  176.             *pp = p+1;
  177.             return ('\n');
  178.         case 'r':
  179.             *pp = p+1;
  180.             return ('\r');
  181.         case 't':
  182.             *pp = p+1;
  183.             return ('\t');
  184.         default:
  185.             /*
  186.              * Backslash followed by any other char 
  187.              * just means that char.
  188.              */
  189.             *pp = p+1;
  190.             return (*p);
  191.         }
  192.     case '^':
  193.         /*
  194.          * Carat means CONTROL.
  195.          */
  196.         *pp = p+2;
  197.         return (CONTROL(p[1]));
  198.     }
  199.     *pp = p+1;
  200.     return (*p);
  201. }
  202.  
  203. usage()
  204. {
  205.     fprintf(stderr, "\nUsage: lesskey [-o output] [input]\n");
  206.     exit(1);
  207. }
  208.  
  209. main(argc, argv)
  210.     int argc;
  211.     char *argv[];
  212. {
  213.     char *p;        /* {{ Can't be register since we use &p }} */
  214.     register char *up;    /* Pointer into usertable */
  215.     FILE *desc;        /* Description file (input) */
  216.     FILE *out;        /* Output file */
  217.     int linenum;        /* Line number in input file */
  218.     char *currcmd;        /* Start of current command string */
  219.     int errors;
  220.     int i, j;
  221.     char line[200];
  222.     char *outfile;
  223.  
  224.     /*
  225.      * Process command line arguments.
  226.      */
  227.     outfile = NULL;
  228.     while (--argc > 0 && **(++argv) == '-')
  229.     {
  230.         switch (argv[0][1])
  231.         {
  232.         case 'o':
  233.             outfile = &argv[0][2];
  234.             if (*outfile == '\0')
  235.             {
  236.                 if (--argc <= 0)
  237.                     usage();
  238.                 outfile = *(++argv);
  239.             }
  240.             break;
  241.         default:
  242.             usage();
  243.         }
  244.     }
  245.     if (argc > 1)
  246.         usage();
  247.  
  248.  
  249.     /*
  250.      * Open the input file, or use standard input if none specified.
  251.      */
  252.     if (argc > 0)
  253.     {
  254.         if ((desc = fopen(*argv, "r")) == NULL)
  255.         {
  256.             perror(*argv);
  257.             exit(1);
  258.         }
  259.     } else
  260.             if ( isatty(fileno(stdin)) )
  261.                 usage();
  262.             else
  263.         desc = stdin;
  264.  
  265.     /*
  266.      * Read the input file, one line at a time.
  267.      * Each line consists of a command string,
  268.      * followed by white space, followed by an action name.
  269.      */
  270.     linenum = 0;
  271.     errors = 0;
  272.     up = usertable;
  273.     while (fgets(line, sizeof(line), desc) != NULL)
  274.     {
  275.         ++linenum;
  276.  
  277.         /*
  278.          * Skip leading white space.
  279.          * Replace the final newline with a null byte.
  280.          * Ignore blank lines and comment lines.
  281.          */
  282.         p = line;
  283.         while (*p == ' ' || *p == '\t')
  284.             ++p;
  285.         for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
  286.             ;
  287.         p[i] = '\0';
  288.         if (*p == '#' || *p == '\0')
  289.             continue;
  290.  
  291.         /*
  292.          * Parse the command string and store it in the usertable.
  293.          */
  294.         currcmd = up;
  295.         do
  296.         {
  297.             if (up >= usertable + MAX_USERCMD)
  298.             {
  299.                 fprintf(stderr, "too many commands, line %d\n",
  300.                     linenum);
  301.                 exit(1);
  302.             }
  303.             if (up >= currcmd + MAX_CMDLEN)
  304.             {
  305.                 fprintf(stderr, "command too long on line %d\n",
  306.                     linenum);
  307.                 errors++;
  308.                 break;
  309.             }
  310.  
  311.             *up++ = tchar(&p);
  312.  
  313.         } while (*p != ' ' && *p != '\t' && *p != '\0');
  314.  
  315.         /*
  316.          * Terminate the command string with a null byte.
  317.          */
  318.         *up++ = '\0';
  319.  
  320.         /*
  321.          * Skip white space between the command string
  322.          * and the action name.
  323.          * Terminate the action name with a null byte if it 
  324.          * is followed by whitespace or a # comment.
  325.          */
  326.         if (*p == '\0')
  327.         {
  328.             fprintf(stderr, "missing whitespace on line %d\n",
  329.                 linenum);
  330.             errors++;
  331.             continue;
  332.         }
  333.         while (*p == ' ' || *p == '\t')
  334.             ++p;
  335.         for (j = 0;  p[j] != ' ' && p[j] != '\t' && 
  336.                  p[j] != '#' && p[j] != '\0';  j++)
  337.             ;
  338.         p[j] = '\0';
  339.  
  340.         /*
  341.          * Parse the action name and store it in the usertable.
  342.          */
  343.         for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
  344.             if (strcmp(cmdnames[i].cn_name, p) == 0)
  345.                 break;
  346.         if (cmdnames[i].cn_name == NULL)
  347.         {
  348.             fprintf(stderr, "unknown action <%s> on line %d\n",
  349.                 p, linenum);
  350.             errors++;
  351.             continue;
  352.         }
  353.         *up++ = cmdnames[i].cn_action;
  354.  
  355.         /*
  356.          * See if an extra string follows the action name.
  357.          */
  358.         for (j = j+1;  p[j] == ' ' || p[j] == '\t';  j++)
  359.             ;
  360.         p += j;
  361.         if (*p != '\0')
  362.         {
  363.             /*
  364.              * OR the special value A_EXTRA into the action byte.
  365.              * Put the extra string after the action byte.
  366.              */
  367.             up[-1] |= A_EXTRA;
  368.             while (*p != '\0')
  369.                 *up++ = tchar(&p);
  370.             *up++ = '\0';
  371.         }
  372.     }
  373.  
  374.     if (errors > 0)
  375.     {
  376.         fprintf(stderr, "%d errors; no output produced\n", errors);
  377.         exit(1);
  378.     }
  379.  
  380.     /*
  381.      * Write the output file.
  382.      * If no output file was specified, use "$HOME/.less"
  383.      */
  384.     if (outfile == NULL)
  385.     {
  386. #if OS2
  387.                 if ( p = getenv("INIT") )
  388.                   strcpy(line, p);
  389.                 else
  390.                   strcpy(line, ".");
  391.  
  392.                 strcat(line, "\\less.ini");
  393. #else
  394.         p = getenv("HOME");
  395.         if (p == NULL || *p == '\0')
  396.         {
  397.             fprintf(stderr, "cannot find $HOME - using current directory\n");
  398.             strcpy(line, LESSKEYFILE);
  399.         } else
  400.         {
  401.             strcpy(line, p);
  402. #if MSOFTC
  403.             strcat(line, "\\");
  404. #else
  405.             strcat(line, "/");
  406. #endif
  407.             strcat(line, LESSKEYFILE);
  408.         }
  409.         outfile = line;
  410. #endif
  411.     }
  412.     if ((out = fopen(outfile, "w")) == NULL)
  413.         perror(outfile);
  414.     else
  415.         fwrite((char *)usertable, 1, up-usertable, out);
  416.     exit(0);
  417. }
  418.  
  419.