home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / LESS177.ZIP / src / lesskey.c < prev    next >
C/C++ Source or Header  |  1992-07-18  |  8KB  |  361 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.     while (--argc > 0 && **(++argv) == '-')
  132.     {
  133.         switch (argv[0][1])
  134.         {
  135.         case 'o':
  136.             outfile = &argv[0][2];
  137.             if (*outfile == '\0')
  138.             {
  139.                 if (--argc <= 0)
  140.                     usage();
  141.                 outfile = *(++argv);
  142.             }
  143.             break;
  144.         default:
  145.             usage();
  146.         }
  147.     }
  148.     if (argc > 1)
  149.         usage();
  150.  
  151.  
  152.     /*
  153.      * Open the input file, or use standard input if none specified.
  154.      */
  155.     if (argc > 0)
  156.     {
  157.         if ((desc = fopen(*argv, "r")) == NULL)
  158.         {
  159.             perror(*argv);
  160.             exit(1);
  161.         }
  162.     } else
  163.         desc = stdin;
  164.  
  165.     /*
  166.      * Read the input file, one line at a time.
  167.      * Each line consists of a command string,
  168.      * followed by white space, followed by an action name.
  169.      */
  170.     linenum = 0;
  171.     errors = 0;
  172.     up = usertable;
  173.     while (fgets(line, sizeof(line), desc) != NULL)
  174.     {
  175.         ++linenum;
  176.  
  177.         /*
  178.          * Skip leading white space.
  179.          * Replace the final newline with a null byte.
  180.          * Ignore blank lines and comment lines.
  181.          */
  182.         p = line;
  183.         while (*p == ' ' || *p == '\t')
  184.             ++p;
  185.         for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
  186.             ;
  187.         p[i] = '\0';
  188.         if (*p == '#' || *p == '\0')
  189.             continue;
  190.  
  191.         /*
  192.          * Parse the command string and store it in the usertable.
  193.          */
  194.         currcmd = up;
  195.         do
  196.         {
  197.             if (up >= usertable + MAX_USERCMD)
  198.             {
  199.                 fprintf(stderr, "too many commands, line %d\n",
  200.                     linenum);
  201.                 exit(1);
  202.             }
  203.             if (up >= currcmd + MAX_CMDLEN)
  204.             {
  205.                 fprintf(stderr, "command too long on line %d\n",
  206.                     linenum);
  207.                 errors++;
  208.                 break;
  209.             }
  210.  
  211.             *up++ = tchar(&p);
  212.  
  213.         } while (*p != ' ' && *p != '\t' && *p != '\0');
  214.  
  215.         /*
  216.          * Terminate the command string with a null byte.
  217.          */
  218.         *up++ = '\0';
  219.  
  220.         /*
  221.          * Skip white space between the command string
  222.          * and the action name.
  223.          * Terminate the action name with a null byte if it 
  224.          * is followed by whitespace or a # comment.
  225.          */
  226.         if (*p == '\0')
  227.         {
  228.             fprintf(stderr, "missing whitespace on line %d\n",
  229.                 linenum);
  230.             errors++;
  231.             continue;
  232.         }
  233.         while (*p == ' ' || *p == '\t')
  234.             ++p;
  235.         for (j = 0;  p[j] != ' ' && p[j] != '\t' && 
  236.                  p[j] != '#' && p[j] != '\0';  j++)
  237.             ;
  238.         p[j] = '\0';
  239.  
  240.         /*
  241.          * Parse the action name and store it in the usertable.
  242.          */
  243.         for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
  244.             if (strcmp(cmdnames[i].cn_name, p) == 0)
  245.                 break;
  246.         if (cmdnames[i].cn_name == NULL)
  247.         {
  248.             fprintf(stderr, "unknown action <%s> on line %d\n",
  249.                 p, linenum);
  250.             errors++;
  251.             continue;
  252.         }
  253.         *up++ = cmdnames[i].cn_action;
  254.  
  255.         /*
  256.          * See if an extra string follows the action name.
  257.          */
  258.         for (j = j+1;  p[j] == ' ' || p[j] == '\t';  j++)
  259.             ;
  260.         p += j;
  261.         if (*p != '\0')
  262.         {
  263.             /*
  264.              * OR the special value A_EXTRA into the action byte.
  265.              * Put the extra string after the action byte.
  266.              */
  267.             up[-1] |= A_EXTRA;
  268.             while (*p != '\0')
  269.                 *up++ = tchar(&p);
  270.             *up++ = '\0';
  271.         }
  272.     }
  273.  
  274.     if (errors > 0)
  275.     {
  276.         fprintf(stderr, "%d errors; no output produced\n", errors);
  277.         exit(1);
  278.     }
  279.  
  280.     /*
  281.      * Write the output file.
  282.      * If no output file was specified, use "$HOME/.less"
  283.      */
  284.     if (outfile == NULL)
  285.     {
  286.         p = getenv("HOME");
  287.         if (p == NULL || *p == '\0')
  288.         {
  289.             fprintf(stderr, "cannot find $HOME - using current directory\n");
  290. #if __MSDOS__
  291.             strcpy(line, "_less");
  292. #else
  293.             strcpy(line, ".less");
  294. #endif
  295.         } else
  296.         {
  297.             strcpy(line, p);
  298. #if __MSDOS__
  299.             strcat(line, "\\_less");
  300. #else
  301.             strcat(line, "/.less");
  302. #endif
  303.         }
  304.         outfile = line;
  305.     }
  306.     if ((out = fopen(outfile, "w")) == NULL)
  307.         perror(outfile);
  308.     else
  309.         fwrite((char *)usertable, 1, up-usertable, out);
  310.     exit(0);
  311. }
  312.  
  313. /*
  314.  * Parse one character of a string.
  315.  */
  316. tchar(pp)
  317.     char **pp;
  318. {
  319.     register char *p;
  320.     register char ch;
  321.     register int i;
  322.  
  323.     p = *pp;
  324.     switch (*p)
  325.     {
  326.     case '\\':
  327.         if (*++p >= '0' && *p <= '7')
  328.         {
  329.             /*
  330.              * Parse an octal number.
  331.              */
  332.             ch = 0;
  333.             i = 0;
  334.             do
  335.                 ch = 8*ch + (*p - '0');
  336.             while (*++p >= '0' && *p <= '7' && ++i < 3);
  337.             *pp = p;
  338.             return (ch);
  339.         }
  340.         /*
  341.          * Backslash followed by a char just means that char.
  342.          */
  343.         *pp = p+1;
  344.         return (*p);
  345.     case '^':
  346.         /*
  347.          * Carat means CONTROL.
  348.          */
  349.         *pp = p+2;
  350.         return (CONTROL(p[1]));
  351.     }
  352.     *pp = p+1;
  353.     return (*p);
  354. }
  355.  
  356. usage()
  357. {
  358.     fprintf(stderr, "usage: lesskey [-o output] [input]\n");
  359.     exit(1);
  360. }
  361.