home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / less / lesskey.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  9KB  |  374 lines

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