home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_08_12 / 8n12048a < prev    next >
Text File  |  1990-10-15  |  19KB  |  607 lines

  1. /*
  2.  *  WRITE.C
  3.  *
  4.  *  routines used to write to the screen
  5.  *
  6.  */
  7.  
  8. #include    "char.h"
  9.  
  10. /*---------  external function prototypes: ---------*/
  11.  
  12.                 /* look for unused key buffer */
  13. extern char *k_alloc (void);
  14.                 /* look for key buffer */
  15. extern char *k_seek (void);
  16.                 /* write decimal integer to ring buffer */
  17. extern void r_puti (char);
  18.                 /* write byte to ring buffer */
  19. extern void r_write (char);
  20.                 /* clear selected part of the screen */
  21. extern void rawclear (void);
  22.                 /* set the video mode */
  23. extern void rawmode (void);
  24.                 /* move the cursor */
  25. extern void rawmv (void);   
  26.                 /* scroll the screen up */
  27. extern void rawscroll (void);
  28.                 /* output character as raw tty */
  29. extern void rawtty (void);
  30.                 /* output character to screen */
  31. extern void rawwrite (void);    
  32.  
  33.  
  34. /*
  35.  *  delimiters used for quoted characters as
  36.  *  parameters of escape sequences
  37.  */
  38.  
  39. #define DELIM1  '\''
  40. #define DELIM2  '"'
  41.  
  42. /*
  43.  *  characters that require special handling
  44.  */
  45.  
  46. #define BEL '\007'
  47. #define BS  '\010'
  48. #define NL  '\012'
  49. #define CR  '\015'
  50. #define ESC '\033'
  51.  
  52. /*
  53.  *  color codes
  54.  */
  55.  
  56. #define BLUE    (01)
  57. #define GREEN   (02)
  58. #define RED (04)
  59. #define CYAN    (BLUE | GREEN)
  60. #define MAGENTA (BLUE | RED)
  61. #define YELLOW  (GREEN | RED)
  62. #define WHITE   (BLUE | GREEN | RED)
  63.  
  64. /*
  65.  *  macro's for turning on and off attributes or designating
  66.  *  a color as foreground or background
  67.  */
  68.  
  69. #define ON(x)   (attrib |= (char) (x))
  70. #define OFF(x)  (attrib &= (char) (~(x)))
  71. #define FORE(x) (attrib |= (char) (x))
  72. #define BACK(x) (attrib |= (char) ((x) << 4))
  73.  
  74. /*
  75.  * we don't want to use the standard 'c' isdigit(); it's
  76.  * either implemented as a function (we don't want to use
  77.  * osmebody else's functions that might have unpleasant side
  78.  * effects) or a macro invoking an array of values that
  79.  * dictate what lexical properties a given character possesses
  80.  * (a waste of precious memory)
  81.  */
  82.  
  83. #define isdigit(x)  (((x) >= '0') && ((x) <= '9'))
  84.  
  85. /*
  86.  *  w_write()
  87.  *
  88.  *  w_write() keeps track of actually getting stuff on the screen
  89.  *  and moving the cursor around
  90.  */
  91.  
  92. void    w_write()
  93.     {
  94.     switch (outchar)
  95.         {
  96.         case    CR:
  97.  
  98.             /* just set the column to 0 for a carriage return */
  99.             
  100.             curr.loc.col = 0;
  101.             
  102.             /* and fall through to the backspace handler */
  103.             
  104.         case    BS:
  105.         
  106.             /* decrement the current column unless at the left
  107.              * margin */
  108.              
  109.             if (curr.loc.col)
  110.                 --curr.loc.col;
  111.                 
  112.             /* move the cursor and that's it... */
  113.             
  114.             rawmv();
  115.             break;
  116.             
  117.         default:
  118.         
  119.             /* first, write the character without
  120.                moving the cursor */
  121.             
  122.             rawwrite();
  123.             
  124.             /* then, if we're not on the right margin, bump the
  125.              * cursor right and that's it */
  126.              
  127.             if ((curr.loc.col + 1) <= max.loc.col)
  128.                 {
  129.                 ++curr.loc.col;
  130.                 rawmv();
  131.                 break;
  132.                 }
  133.             
  134.             /* but if we were at the right margin, check the wrap
  135.              * flag; if it's clear, just return.  if not, execute
  136.              * a carriage return (just set the column to zero -
  137.              * we'll do a rawmv() call later), set the current
  138.              * character to newline, and fall into the newline
  139.              * routine */
  140.              
  141.             if (!wrap)
  142.                 break;
  143.             curr.loc.col = 0;
  144.             outchar = NL;
  145.         case    NL:
  146.         
  147.             /* if we're not at the bottom of the screen, just bump
  148.              * the line down and call rawmv() */
  149.              
  150.             if (++curr.loc.line < 25)
  151.                 {
  152.                 rawmv();
  153.                 break;
  154.                 }
  155.             
  156.             /* but if we were at the bottom (or somehow below!),
  157.              * make sure we're on the bottom line.  If we're in
  158.              * one of the CGA 80x25 text modes, do our fancy
  159.              * assembly language scroll routine, else just let
  160.              * the BIOS handle it */
  161.              
  162.             curr.loc.line = 24;
  163.             if (video_mode == 2 || video_mode == 3)
  164.                 {
  165.                 rawscroll();
  166.                 break;
  167.                 }
  168.  
  169.         case    BEL:
  170.             
  171.             /* do a raw tty output; it handles the cursor movement
  172.              * too */
  173.  
  174.             rawtty();
  175.             break;
  176.         }
  177.     }
  178.  
  179.  
  180. /*
  181.  *  w_buffer()
  182.  *
  183.  *  w_buffer() writes a byte into the escape buffer.  silently
  184.  *  overwrites the last byte in the buffer if we get that far.  it
  185.  *  was either that or trash the new byte
  186.  */
  187.  
  188. void    w_buffer(c)
  189. char    c;
  190.     {
  191.     if (char_cnt == BUF_LEN)
  192.         esc_buf[ BUF_LEN - 1 ] = c;
  193.     else
  194.         esc_buf[ char_cnt++ ] = c;
  195.     }
  196.  
  197.  
  198.  
  199. /*
  200.  *  w_cursor()
  201.  *
  202.  *  w_cursor() handles the cursor left, right, up and down
  203.  *  functions.  bumps the value by the value of the 1st parameter
  204.  *  in the escape sequence (if there isn't one, we put a 1 in for
  205.  *  it) in the direction specified by the delta, until it hits the
  206.  *  specified limit.  then execute the cursor move...
  207.  */
  208.  
  209. void    w_cursor()
  210.     {
  211.     if (!char_cnt)
  212.         esc_buf[ 0 ] = 1;
  213.     while (*cur_val != limit)
  214.         {
  215.         *cur_val += delta;
  216.         esc_buf[ 0 ]--;
  217.         if (!esc_buf[ 0 ])
  218.             break;
  219.         }
  220.     rawmv();
  221.     }
  222.  
  223.  
  224.  
  225. /*
  226.  *  w_putc()
  227.  *
  228.  *  w_putc() updates the parameters that might have changed since
  229.  *  last time, then runs the character through the escape sequence
  230.  *  state machine
  231.  */
  232.  
  233. void    w_putc()
  234.     {
  235.         
  236.     /* update parameters */
  237.     
  238.     max.loc.col = SCREEN_WIDTH - 1;
  239.     cur_page = CURRENT_PAGE;
  240.     curr.position = (PAGE_TABLE[ cur_page ]).position;
  241.     if (curr.loc.col > max.loc.col)
  242.         curr.loc.col = max.loc.col;
  243.     if ((video_mode = CURRENT_MODE) == 7)
  244.         video_address = MONOCHROME + SCREEN_OFFSET;
  245.     else
  246.         video_address = GRAPHIC + SCREEN_OFFSET;
  247.         
  248.     /* process the escape sequence state */
  249.  
  250.     switch (state)
  251.         {
  252.             
  253.         case    HAVE_ESC:
  254.  
  255.             /* if we have an escape, we want a left bracket.
  256.              * if we get it, change the state and return,
  257.              * else reset back to the RAW state and fall
  258.              * through */
  259.          
  260.             if (outchar == '[')
  261.                 {
  262.                 state = HAVE_LBRACE;
  263.                 break;
  264.                 }
  265.             state = RAW;
  266.             
  267.         case    RAW:
  268.         
  269.             /* if it's an escape, change the stae, else output the
  270.              * character */
  271.             
  272.             if (outchar == ESC)
  273.                 state = HAVE_ESC;
  274.             else
  275.                 w_write();
  276.             break;
  277.  
  278.         case    IN_NUMBER:
  279.         
  280.             /* if it's another digit, roll it into the value. else
  281.              * the state falls back to HAVE_LBRACE, and we fall
  282.              * through */
  283.              
  284.             if (isdigit(outchar))
  285.                 {
  286.                 tmp.value *= 10;
  287.                 tmp.value += outchar - '0';
  288.                 break;
  289.                 }
  290.             else
  291.                 {
  292.                 state = HAVE_LBRACE;
  293.                 w_buffer(tmp.value);
  294.                 }
  295.  
  296.         case    HAVE_LBRACE:
  297.         
  298.             /* if we have a string delimiter, change the state and
  299.              * save the delimiter */
  300.              
  301.             if (outchar == DELIM1 || outchar == DELIM2)
  302.                 {
  303.                 state = IN_STRING;
  304.                 tmp.delim = outchar;
  305.                 break;
  306.                 }
  307.  
  308.             /* else if it's 'punctuation', ignore it */
  309.             
  310.             if (outchar == ';' || outchar == '=' ||
  311.                         outchar == '?')
  312.                 break;
  313.                 
  314.             /* else if it's a digit, start a number and
  315.                change the  state */
  316.              
  317.             if (isdigit(outchar))
  318.                 {
  319.                 state = IN_NUMBER;
  320.                 tmp.value = outchar - '0';
  321.                 break;
  322.                 }
  323.  
  324.             /* else it terminates the escape sequence, and 
  325.              * identifies its purpose */
  326.              
  327.             switch (outchar)
  328.                 {
  329.                 case    'A':            /* cursor up */
  330.                     limit = 0;
  331.                     delta = (char) -1;
  332.                     cur_val = &curr.loc.line;
  333.                     w_cursor();
  334.                     break;
  335.  
  336.                 case    'B':            /* cursor down */
  337.                     limit = 24;
  338.                     delta = 1;
  339.                     cur_val = &curr.loc.line;
  340.                     w_cursor();
  341.                     break;
  342.                     
  343.                 case    'C':            /* cursor right */
  344.                     limit = max.loc.col;
  345.                     delta = 1;
  346.                     cur_val = &curr.loc.col;
  347.                     w_cursor();
  348.                     break;
  349.                     
  350.                 case    'D':            /* cursor left */
  351.                     limit = 0;
  352.                     delta = (char) -1;
  353.                     cur_val = &curr.loc.col;
  354.                     w_cursor();
  355.                     break;
  356.                     
  357.                 case    'H':
  358.                 case    'R':
  359.                 case    'f':
  360.                 
  361.                     /* set cursor position: make sure there
  362.                      * are at least 2 parameters stored,
  363.                      * correct any out-of-range parameters,
  364.                      * and execute the move.  if the
  365.                      * character was 'R', fall through into
  366.                      * the report position sequence */
  367.                     
  368.                     switch (char_cnt)
  369.                         {
  370.                         case    0:
  371.                             w_buffer(1);
  372.                         case    1:
  373.                             w_buffer(1);
  374.                         default:
  375.                             break;
  376.                         }
  377.  
  378.                     if (!esc_buf[ 0 ])
  379.                         curr.loc.line = 1;
  380.                     else if (esc_buf[ 0 ] > 25)
  381.                         curr.loc.line = 25;
  382.                     else
  383.                         curr.loc.line = esc_buf[ 0 ];
  384.  
  385.                     if (!esc_buf[ 1 ])
  386.                         curr.loc.col = 1;
  387.                     else if (esc_buf[ 1 ] > max.loc.col + 1)
  388.                         curr.loc.col = max.loc.col + 1;
  389.                     else
  390.                         curr.loc.col = esc_buf[ 1 ];
  391.  
  392.                     curr.loc.line--;
  393.                     curr.loc.col--;
  394.                     rawmv();
  395.  
  396.                     if (outchar != 'R') 
  397.                         break;
  398.  
  399.                 case    'n':
  400.                     /* output the position; format is
  401.                      * "\033[%.2d;%.2dR\015" */
  402.                      
  403.                     r_write(ESC);
  404.                     r_write('[');
  405.                     r_puti(curr.loc.line + 1);
  406.                     r_write(';');
  407.                     r_puti(curr.loc.col + 1);
  408.                     r_write('R');
  409.                     r_write(CR);
  410.                     break;
  411.  
  412.                 case    'J':
  413.                     /* rawclear clears the screen from
  414.                      * (curr.loc.line, curr.loc.col) to
  415.                      * (max.loc.line, max.loc.col); so for
  416.                      * clear screen, set the current
  417.                      * position to the upper left hand
  418.                      * corner of the screen, and the max
  419.                      * line to the bottom of the screen */
  420.                      
  421.                     curr.loc.line = curr.loc.col = 0;
  422.                     max.loc.line = 24;
  423.                     rawclear();
  424.                     break;
  425.                     
  426.                 case    'K':
  427.                     /* and clear to end of line is even
  428.                      * simpler - just set the max line equal
  429.                      * to the same line we're on */
  430.                     
  431.                     max.loc.line = curr.loc.line;
  432.                     rawclear();
  433.                     break;
  434.                     
  435.                 case    'h':
  436.                 case    'l':
  437.                     /* set and reset mode do the same thing
  438.                      * unless the mode is 7.  easy */
  439.                      
  440.                     if (!char_cnt)
  441.                         w_buffer(2);
  442.                     if (esc_buf[ 0 ] > 7)
  443.                         break;
  444.                     if (esc_buf[ 0 ] == 7)
  445.                         wrap = (char) (outchar == 'h');
  446.                     else
  447.                         rawmode();
  448.                     break;
  449.                     
  450.                 case    'm':
  451.                     /* set graphic rendition - just do all
  452.                      * the parameters and set/reset the
  453.                      * appropriate bits in the attribute
  454.                      * byte */
  455.                     
  456.                     while (char_cnt)
  457.                         {
  458.                         switch (esc_buf[ --char_cnt ])
  459.                             {
  460.                             case    0:
  461.                                 attrib = 0007; break;
  462.                             case    1:
  463.                                 ON(010); break;
  464.                             case    4:
  465.                                 OFF(07); ON(01); break;
  466.                             case    5:
  467.                                 ON(0200); break;
  468.                             case    7:
  469.                                 OFF(07); ON(0160);  break;
  470.                             case    8:
  471.                                 OFF(0167);  break;
  472.                             case    30:
  473.                                 OFF(07);    break;
  474.                             case    31:
  475.                                 OFF(07); FORE(RED); break;
  476.                             case    32:
  477.                                 OFF(07); FORE(GREEN); break;
  478.                             case    33:
  479.                                 OFF(07); FORE(YELLOW); break;
  480.                             case    34:
  481.                                 OFF(07); FORE(BLUE); break;
  482.                             case    35:
  483.                                 OFF(07); FORE(MAGENTA); break;
  484.                             case    36:
  485.                                 OFF(07); FORE(CYAN); break;
  486.                             case    37:
  487.                                 OFF(07); FORE(WHITE); break;
  488.                             case    40:
  489.                                 OFF(0160);  break;
  490.                             case    41:
  491.                                 OFF(0160); BACK(RED); break;
  492.                             case    42:
  493.                                 OFF(0160); BACK(GREEN); break;
  494.                             case    43:
  495.                                 OFF(0160); BACK(YELLOW); break;
  496.                             case    44:
  497.                                 OFF(0160); BACK(BLUE); break;
  498.                             case    45:
  499.                                 OFF(0160); BACK(MAGENTA); break;
  500.                             case    46:
  501.                                 OFF(0160); BACK(CYAN); break;
  502.                             case    47:
  503.                                 OFF(0160); BACK(WHITE); break;
  504.                             default:
  505.                                 break;
  506.                             }
  507.                         }
  508.                     break;
  509.                 case    'p':
  510.                 
  511.                     if (esc_buf[ 0 ])
  512.                         {
  513.  
  514.     /* if the first parameter is not nul, then we're
  515.        redefining a 'normal' key.  Clear the msb of
  516.        keycheck to indicate this */
  517.  
  518.                         keycheck = (esc_buf[ 0 ]) &
  519.                             0000377;
  520.  
  521.     /* check first to see if we've already allocated
  522.        a buffer to this key; then if not, see if we have
  523.        an unused buffer to hand out */
  524.  
  525.                         if (k_seek() || k_alloc())
  526.                             {
  527.                             k = 1;
  528.                             kp->keystroke =
  529.                                 (esc_buf[ 0 ])
  530.                                 & 0000377;
  531.                             }
  532.                         else
  533.                             break;
  534.                         }
  535.                     else
  536.                         {
  537.  
  538.     /* first byte was nul - an extended key.  indicate
  539.        by setting msb of keycheck to $FF */
  540.  
  541.                         keycheck = (esc_buf[ 1 ]) |
  542.                             0177400;
  543.                         if (k_seek() || k_alloc())
  544.                             {
  545.                             k = 2;
  546.                             kp->keystroke =
  547.                                 (esc_buf[ 1 ])
  548.                                 | 0177400;
  549.                             }
  550.                         else
  551.                             break;
  552.                         }
  553.  
  554.     /* copy the parameters into the buffer, counting as we go */
  555.  
  556.                     for (*len = 0; (k < char_cnt) &&
  557.                         (k < KEY_BUFLEN); ++*len)
  558.                         *ptr++ = esc_buf[ k++ ];
  559.  
  560.                     break;
  561.                     
  562.                 case    's':
  563.                 
  564.                     /* save current position */
  565.                     
  566.                     saved.position = curr.position;
  567.                     break;
  568.  
  569.                 case    'u':
  570.                 
  571.                     /* restore current position */
  572.                     
  573.                     curr.position = saved.position;
  574.                     rawmv();
  575.                     break;
  576.  
  577.                 default:
  578.                 
  579.                     /* anything else? discard the parameters
  580.                      * and output the final character to the
  581.                      * screen */
  582.                      
  583.                     w_write();
  584.                     break;
  585.                 }
  586.                 
  587.             /* finally, clear the buffer by resetting the count
  588.                and fall back to the RAW state */
  589.             
  590.             char_cnt = 0;
  591.             state = RAW;
  592.             break;
  593.             
  594.         case    IN_STRING:
  595.         
  596.             /* finally, the IN_STRING case - if the character
  597.              * isn't the delimiter we saved, then put it into
  598.              * the buffer as it was received */
  599.             
  600.             if (outchar == tmp.delim)
  601.                 state = HAVE_LBRACE;
  602.             else
  603.                 w_buffer(outchar);
  604.             break;
  605.         }
  606.     }
  607.