home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 388_01 / ae / 93 / jul / key.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-06  |  12.3 KB  |  614 lines

  1. /*
  2.  * key.c        
  3.  *
  4.  * Anthony's Editor July 93
  5.  *
  6.  * Public Domain 1991, 1993 by Anthony Howe.  No warranty.
  7.  */
  8.  
  9. #include <ctype.h>
  10. #include <stdio.h>
  11. #include <sys\types.h>
  12. #include "header.h"
  13. #include "key.h"
  14.  
  15. /* Variable length structure. */
  16. typedef struct t_input {
  17.     struct t_input *next;
  18.     char *ptr;
  19.     char buf[1];
  20. } t_input;
  21.  
  22. static t_input *istack = NULL;
  23. static char blank[] = " \t\r\n";
  24.  
  25. static int k_default _((t_keymap *));
  26. static int k_define _((t_keymap *));
  27. static int k_erase  _((t_keymap *));
  28. static int k_itself _((t_keymap *));
  29. static int k_kill _((t_keymap *));
  30. static int k_token _((t_keymap *));
  31. static t_keymap *growkey _((t_keymap *, size_t));
  32. static int ipush _((char *));
  33. static int ipop _((void));
  34. static void iflush _((void));
  35.  
  36. t_keyinit keywords[] = {
  37.     { K_INSERT_ENTER, ".insert_enter", k_default },
  38.     { K_INSERT_EXIT, ".insert_exit", k_default },
  39.     { K_DELETE_LEFT, ".delete_left", k_default },
  40.     { K_DELETE_RIGHT, ".delete_right", k_default },
  41.     { K_FLIP_CASE, ".flip_case", k_default },
  42.     { K_BLOCK, ".block", k_default },
  43.     { K_CUT, ".cut", k_default },
  44.     { K_PASTE, ".paste", k_default },
  45.     { K_UNDO, ".undo", k_default },
  46.     { K_CURSOR_UP, ".cursor_up", k_default },
  47.     { K_CURSOR_DOWN, ".cursor_down", k_default },
  48.     { K_CURSOR_LEFT, ".cursor_left", k_default },
  49.     { K_CURSOR_RIGHT, ".cursor_right", k_default },
  50.     { K_PAGE_UP, ".page_up", k_default },
  51.     { K_PAGE_DOWN, ".page_down", k_default },
  52.     { K_WORD_LEFT, ".word_left", k_default },
  53.     { K_WORD_RIGHT, ".word_right", k_default },
  54.     { K_LINE_LEFT, ".line_left", k_default },
  55.     { K_LINE_RIGHT, ".line_right", k_default },
  56.     { K_FILE_TOP, ".file_top", k_default },
  57.     { K_FILE_BOTTOM, ".file_bottom", k_default },
  58.     { K_HELP, ".help", k_default },
  59.     { K_HELP_OFF, ".help_off", k_token },
  60.     { K_MACRO, ".macro", k_default },
  61.     { K_MACRO_DEFINE, ".macro_define", k_define },
  62.     { K_QUIT, ".quit", k_default },
  63.     { K_QUIT_ASK, ".quit_ask", k_default },
  64.     { K_FILE_READ, ".file_read", k_default },
  65.     { K_FILE_WRITE, ".file_write", k_default },
  66.     { K_STTY_ERASE, ".stty_erase", k_erase },
  67.     { K_STTY_KILL, ".stty_kill", k_kill },
  68.     { K_ITSELF, ".itself", k_itself },
  69.     { K_REDRAW, ".redraw", k_default },
  70.     { K_SHOW_VERSION, ".show_version", k_default },
  71.     { K_LITERAL, ".literal", k_default },
  72.     { K_ERROR, NULL, NULL }
  73. };
  74.  
  75. int
  76. ismsg(str)
  77. char *str;
  78. {
  79.     char *ptr;
  80.     for (ptr = str; isdigit(*ptr); ++ptr)
  81.         ;
  82.     return (str < ptr && *ptr == ':');
  83. }
  84.  
  85. /*
  86.  * Read a configuration file from either the current directory or
  87.  * the user's home directory.  Return an error status.  Pass-back
  88.  * either a pointer to a key mapping table, or NULL if an error
  89.  * occured.
  90.  */
  91. int
  92. initkey(fn, keys)
  93. char *fn;
  94. t_keymap **keys;
  95. {
  96.     FILE *fp;
  97.     int error;
  98.     t_keyinit *kp;
  99.     t_keymap *array;
  100.     size_t len, count;
  101.     char *buf, *token, *lhs, *rhs;
  102.  
  103.     *keys = NULL;
  104.     if ((fp = openrc(fn)) == NULL)
  105.         return (INITKEY_OPEN);
  106.  
  107.     /* Allocate an array big enough to hold at least one of everything. */
  108.     if ((array = growkey(NULL, len = K_MAX_CODES)) == NULL) {
  109.         error = INITKEY_ALLOC;
  110.         goto error1;
  111.     }
  112.  
  113.     count = 0;
  114.     while ((error = getblock(fp, &buf)) != GETBLOCK_EOF) {
  115.         if (error == GETBLOCK_ALLOC) {
  116.             error = INITKEY_ALLOC;
  117.             goto error1;
  118.         }
  119.     
  120.         /* Strip \r\n from end of buffer. */
  121.         if ((token = strrchr(buf, '\n')) != NULL) {
  122.             if (buf < token && token[-1] == '\r')
  123.                 --token;
  124.             *token = '\0';
  125.         }
  126.  
  127.         if (ismsg(buf)) {
  128.             long index = strtol(buf, &token, 0);
  129.             (void) encode(token);
  130.             if (0 < index) 
  131.                 message[index] = token+1;
  132.             continue;
  133.         }
  134.             
  135.         if (buf[0] != '.' 
  136.         || (token = strtok(buf, blank)) == NULL
  137.         || (kp = findikey(keywords, strlwr(token))) == NULL) {
  138.             free(buf);
  139.             continue;
  140.         }
  141.     
  142.         array[count].code = kp->code;
  143.         /* Determine lhs and rhs parameters. */
  144.         if ((token = strtok(NULL, "")) != NULL) {
  145.             /* Find start of parameters. */
  146.             token += strspn(token, blank);
  147.             /* Shuffle parameters to the start of the buffer. */
  148.             (void) strcpy(buf, token);
  149.             /* Assume shrinking buffer succeeds. */
  150.             buf = (char *) realloc(buf, strlen(buf) + 1);
  151.             /* Parse lhs, which should equal start of buffer. */
  152.             array[count].lhs = token = strtok(buf, blank);
  153.             if (encode(token) <= 0) {
  154.                 error = INITKEY_ERROR;
  155.                 goto error1;
  156.             }
  157.             /* Find rhs if present. */
  158.             if ((token = strtok(NULL, blank)) != NULL
  159.             && encode(token) <= 0) {
  160.                 error = INITKEY_ERROR;
  161.                 goto error1;
  162.             }
  163.             array[count].rhs = token;
  164.         } else {
  165.             /* No parameters for keyword. */
  166.             array[count].lhs = array[count].rhs = NULL;
  167.             free(buf);
  168.         }
  169.  
  170.         if (kp->fn != NULL && !(*kp->fn)(&array[count])) {
  171.             error = INITKEY_ERROR;
  172.             goto error1;
  173.         }
  174.         ++count;
  175.  
  176.         if (len <= count) {
  177.             t_keymap *new;
  178.             len += K_MAX_CODES;
  179.             if ((new = growkey(array, len)) == NULL) {
  180.                 error = INITKEY_ALLOC;
  181.                 goto error1;
  182.             }
  183.             array = new;
  184.         }
  185.     }
  186.     error = INITKEY_OK;
  187.     *keys = array;
  188. error1:
  189.     (void) fclose(fp);
  190.     array[count].code = K_ERROR;
  191.     if (error != INITKEY_OK)
  192.         finikey(array);
  193.     return (error);
  194. }
  195.  
  196. void
  197. finikey(keys)
  198. t_keymap *keys;
  199. {
  200.     t_keymap *kp;
  201.     if (keys != NULL) {
  202.         for (kp = keys; kp->code != K_ERROR; ++kp) {
  203.             if (kp->lhs != NULL)
  204.                 free(kp->lhs);
  205.         }
  206.         free(keys);
  207.     }
  208. }
  209.  
  210. /*
  211.  * .function string
  212.  */
  213. static int
  214. k_default(kp)
  215. t_keymap *kp;
  216. {
  217.     if (kp->lhs == NULL)
  218.         return (FALSE);
  219.     if (kp->rhs != NULL) {
  220.         free(kp->lhs);
  221.         return (FALSE);
  222.     }
  223.     return (TRUE);
  224. }
  225.  
  226. /*
  227.  * .macro_define
  228.  * .macro_define lhs rhs
  229.  *
  230.  * The first case is used as a place holder to reserve macro
  231.  * space.  The second case actual defines a macro.
  232.  */
  233. static int
  234. k_define(kp)
  235. t_keymap *kp;
  236. {
  237.     if (kp->lhs != NULL && kp->rhs == NULL) {
  238.         free(kp->lhs);
  239.         return (FALSE);
  240.     }
  241.     return (TRUE);
  242. }
  243.  
  244. /*
  245.  * .token
  246.  */
  247. static int
  248. k_token(kp)
  249. t_keymap *kp;
  250. {
  251.     if (kp->lhs != NULL) {
  252.         free(kp->lhs);
  253.         return (FALSE);
  254.     }
  255.     return (TRUE);
  256. }
  257.  
  258. /*
  259.  * .itself character
  260.  */
  261. static int
  262. k_itself(kp)
  263. t_keymap *kp;
  264. {
  265.     if (!k_default(kp))
  266.         return (FALSE);
  267.     kp->code = *(unsigned char *) kp->lhs;
  268.     kp->lhs[1] = '\0';
  269.     return (TRUE);
  270. }
  271.  
  272. /*
  273.  * .stty_erase
  274.  */
  275. static int
  276. k_erase(kp)
  277. t_keymap *kp;
  278. {
  279.     char buf[2];
  280.  
  281.     if (!k_token(kp))
  282.         return (FALSE);
  283.     buf[0] = erasechar();
  284.     buf[1] = '\0';
  285.     return ((kp->lhs = strdup(buf)) != NULL);
  286. }
  287.  
  288. /*
  289.  * .stty_kill
  290.  */
  291. static int
  292. k_kill(kp)
  293. t_keymap *kp;
  294. {
  295.     char buf[2];
  296.  
  297.     if (!k_token(kp))
  298.         return (FALSE);
  299.     buf[0] = killchar();
  300.     buf[1] = '\0';
  301.     return ((kp->lhs = strdup(buf)) != NULL);
  302. }
  303.  
  304. /*
  305.  * Find token and return corresponding table entry; else NULL.
  306.  */
  307. t_keymap *
  308. findkey(kp, token)
  309. t_keymap *kp;
  310. char *token;
  311. {
  312.     for (; kp->code != K_ERROR; ++kp)
  313.         if (kp->lhs != NULL && strcmp(token, kp->lhs) == 0)
  314.             return (kp);
  315.     return (NULL);
  316. }
  317.  
  318. t_keyinit *
  319. findikey(kp, token)
  320. t_keyinit *kp;
  321. char *token;
  322. {
  323.     for (; kp->code != K_ERROR; ++kp)
  324.         if (kp->lhs != NULL && strcmp(token, kp->lhs) == 0)
  325.             return (kp);
  326.     return (NULL);
  327. }
  328.  
  329. /*
  330.  *
  331.  */
  332. static t_keymap *
  333. growkey(array, len)
  334. t_keymap *array;
  335. size_t len;
  336. {
  337.     t_keymap *new;
  338.     if (len == 0)
  339.         return (NULL);
  340.     len *= sizeof (t_keymap);
  341.     if (array == NULL)
  342.         return ((t_keymap *) malloc(len));
  343.     return ((t_keymap *) realloc(array, len));
  344. }
  345.  
  346. int
  347. getkey(keys)
  348. t_keymap *keys;
  349. {
  350.     t_keymap *k;
  351.     int submatch;
  352.     static char buffer[K_BUFFER_LENGTH];
  353.     static char *record = buffer;
  354.  
  355.     /* If recorded bytes remain, return next recorded byte. */
  356.     if (*record != '\0')
  357.         return (*(unsigned char *)record++);
  358.     /* Reset record buffer. */
  359.     record = buffer;
  360.     do {
  361.         if (K_BUFFER_LENGTH < record - buffer) {
  362.             record = buffer;
  363.             buffer[0] = '\0';
  364.             return (K_ERROR); 
  365.         }
  366.         /* Read and record one byte. */
  367.         *record++ = getliteral();
  368.         *record = '\0';
  369.  
  370.         /* If recorded bytes match any multi-byte sequence... */
  371.         for (k = keys, submatch = FALSE; k->code != K_ERROR; ++k) {
  372.             if (k->lhs == NULL || k->code == K_DISABLED)
  373.                 continue;
  374.             if (strncmp(buffer, k->lhs, record-buffer) != 0)
  375.                 continue;
  376.             if (k->lhs[record-buffer] == '\0') {
  377.                 /* Exact match. */
  378.                 if (k->code != K_MACRO_DEFINE) {
  379.                     /* Return extended key code. */
  380.                     return (k->code);
  381.                 }
  382.                 if (k->rhs != NULL) {
  383.                     (void) ipush(k->rhs);
  384.                     record = buffer;
  385.                 }
  386.             }
  387.             /* Recorded bytes match anchored substring. */
  388.             submatch = TRUE;
  389.             break;
  390.         }
  391.         /* If recorded bytes matched an anchored substring, loop. */
  392.     } while (submatch);
  393.     /* Return first recorded byte. */
  394.     record = buffer;
  395.     return (*(unsigned char *)record++);
  396. }
  397.  
  398. int
  399. getliteral()
  400. {
  401.     int ch;
  402.  
  403.     ch = ipop();
  404.     if (ch == EOF)
  405.         return ((unsigned) getch());
  406.     return (ch);
  407. }
  408.  
  409. /*
  410.  * Return true if a new input string was pushed onto the stack,
  411.  * else false if there was no more memory for the stack.
  412.  */
  413. static int
  414. ipush(buf)
  415. char *buf;
  416. {
  417.     t_input *new;
  418.  
  419.     new = (t_input *) malloc(sizeof (t_input) + strlen (buf));
  420.     if (new == NULL)
  421.         return (FALSE);
  422.     (void) strcpy(new->buf, buf);
  423.     new->ptr = new->buf;
  424.     new->next = istack;
  425.     istack = new;
  426.     return (TRUE);
  427. }
  428.  
  429. /*
  430.  * Pop and return a character off the input stack.  Return EOF if
  431.  * the stack is empty.  If the end of an input string is reached, 
  432.  * then free the node.  This will allow clean tail recursion that 
  433.  * won't blow the stack.  
  434.  */
  435. static int
  436. ipop()
  437. {
  438.     int ch;
  439.     t_input *node;
  440.  
  441.     if (istack == NULL)
  442.         return (EOF);
  443.     ch = (unsigned) *istack->ptr++;
  444.     if (*istack->ptr == '\0') {
  445.         node = istack;
  446.         istack = istack->next;
  447.         free(node);
  448.     }
  449.     return (ch);
  450. }
  451.  
  452. /*
  453.  * Flush the entire input stack.
  454.  */
  455. static void
  456. iflush()
  457. {
  458.     t_input *node;
  459.  
  460.     while (istack != NULL) {
  461.         node = istack;
  462.         istack = istack->next;
  463.         free(node);
  464.     }
  465. }
  466.  
  467. int
  468. ismacro()
  469. {
  470.     return (istack != NULL);
  471. }
  472.  
  473. /*
  474.  * Field input.
  475.  */
  476. typedef struct t_keyfield {
  477.     int code;
  478.     int (*func) _((void));
  479. } t_keyfield;
  480.  
  481. static int fld_done _((void));
  482. static int fld_erase _((void));
  483. static int fld_kill _((void));
  484. static int fld_left _((void));
  485. static int fld_insert _((void));
  486.  
  487. #define ERASE_KEY    0
  488. #define KILL_KEY    1
  489.  
  490. static t_keyfield ktable[] = {
  491.     { K_STTY_ERASE, fld_erase },
  492.     { K_STTY_KILL, fld_kill },
  493.     { '\r', fld_done },
  494.     { '\n', fld_done },
  495.     { '\b', fld_erase },
  496.     { -1, fld_insert }
  497. };
  498.  
  499. static int fld_row;
  500. static int fld_col;
  501. static int fld_key;
  502. static int fld_echo;
  503. static int fld_index;
  504. static int fld_length;
  505. static char *fld_buffer;
  506.  
  507. #ifndef getmaxyx
  508. #define getmaxyx(w,r,c)        (r=LINES,c=COLS)
  509. #endif
  510.  
  511. int
  512. getinput(buf, len, echoing)
  513. char *buf;
  514. int len, echoing;
  515. {
  516.     int first;
  517.     t_keyfield *k;
  518.     fld_buffer = buf;
  519.     fld_index = (int) strlen(fld_buffer);
  520.     fld_length = len < 0 ? COLS : len;
  521.     if (--fld_length < 1)
  522.         return (FALSE);
  523.     ktable[ERASE_KEY].code = erasechar();
  524.     ktable[KILL_KEY].code = killchar();    
  525.     fld_echo = echoing;
  526.     getyx(stdscr, fld_row, fld_col);
  527.     addstr(fld_buffer);
  528.     move(fld_row, fld_col);
  529.     for (first = TRUE;; first = FALSE) {
  530.         refresh();
  531.         fld_key = getliteral();
  532.         for (k = ktable; k->code != -1 && k->code != fld_key; ++k)
  533.             ;
  534.         if (first && k->func == fld_insert)
  535.             fld_kill();
  536.         if (k->func != NULL && !(*k->func)()) {
  537.             fld_buffer[fld_index] = '\0';
  538.             break;
  539.         }
  540.     }
  541.     return (TRUE);
  542. }
  543.     
  544. static int
  545. fld_done()
  546. {
  547.     return (FALSE);
  548. }
  549.  
  550. static int
  551. fld_left()
  552. {
  553.     int row, col, max_row, max_col;
  554.     getyx(stdscr, row, col);
  555.     getmaxyx(stdscr, max_row, max_col);
  556.     if (0 < fld_index) {
  557.         --fld_index;
  558.         /* Assume that if 0 < fld_index then fld_row <= row 
  559.          * and fld_col < col.  So when fld_index == 0, then
  560.          * fld_row == row and fld_col == col. 
  561.          */
  562.         if (0 < col) {
  563.             --col;
  564.         } else if (0 < row) {
  565.             /* Handle reverse line wrap. */
  566.             --row;
  567.             col = max_col-1;
  568.         }
  569.         move(row, col);
  570.     }
  571.     return (TRUE);
  572. }
  573.  
  574. static int
  575. fld_erase()
  576. {
  577.     int row, col;
  578.     if (0 < fld_index) {
  579.         fld_left();
  580.         getyx(stdscr, row, col);
  581.         addch(' ');
  582.         move(row, col);
  583.         fld_buffer[fld_index] = '\0';
  584.     }
  585.     return (TRUE);
  586. }
  587.  
  588. static int
  589. fld_kill()
  590. {
  591.     move(fld_row, fld_col);
  592.     while (0 < fld_index--)
  593.         addch(' ');
  594.     move(fld_row, fld_col);
  595.     fld_buffer[0] = '\0';
  596.     fld_index = 0;
  597.     return (TRUE);
  598. }
  599.     
  600. static int
  601. fld_insert()
  602. {
  603.     if (fld_index < fld_length) {
  604.         if (!ISFUNCKEY(fld_key)) {
  605.             fld_buffer[fld_index++] = fld_key;
  606.             if (fld_echo)
  607.                 addch(fld_key);
  608.         }
  609.     }
  610.     return (fld_index < fld_length);
  611. }
  612.  
  613.  
  614.