home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / newemacs / display.c < prev    next >
Text File  |  1979-12-31  |  16KB  |  757 lines

  1. /*
  2.  * The functions in this file
  3.  * handle redisplay. There are two halves,
  4.  * the ones that update the virtual display
  5.  * screen, and the ones that make the physical
  6.  * display screen the same as the virtual
  7.  * display screen. These functions use hints
  8.  * that are left in the windows by the
  9.  * commands.
  10.  */
  11. #include    <stdio.h>
  12. #include    "ed.h"
  13.  
  14. typedef    struct    VIDEO {
  15.     short    v_flag;            /* Flags            */
  16.     char    v_text[1];        /* Screen data.            */
  17. }    VIDEO;
  18.  
  19. #define    VFCHG    0x0001            /* Changed.            */
  20.  
  21. int    sgarbf    = TRUE;            /* TRUE if screen is garbage    */
  22. int    mpresf    = FALSE;        /* TRUE if message in last line    */
  23. int    vtrow    = 0;            /* Row location of SW cursor    */
  24. int    vtcol    = 0;            /* Column location of SW cursor    */
  25. int    ttrow    = HUGE;            /* Row location of HW cursor    */
  26. int    ttcol    = HUGE;            /* Column location of HW cursor    */
  27.  
  28. VIDEO    **vscreen;            /* Virtual screen.        */
  29. VIDEO    **pscreen;            /* Physical screen.        */
  30.  
  31. /*
  32.  * Initialize the data structures used
  33.  * by the display code. The edge vectors used
  34.  * to access the screens are set up. The operating
  35.  * system's terminal I/O channel is set up. All the
  36.  * other things get initialized at compile time.
  37.  * The original window has "WFCHG" set, so that it
  38.  * will get completely redrawn on the first
  39.  * call to "update".
  40.  */
  41. vtinit()
  42. {
  43.     register int    i;
  44.     register VIDEO    *vp;
  45.  
  46.     scr_setup();
  47.     vscreen = (VIDEO **) malloc(NROW*sizeof(VIDEO *));
  48.     if (vscreen == NULL)
  49.         exit(1);
  50.     pscreen = (VIDEO **) malloc(NROW*sizeof(VIDEO *));
  51.     if (pscreen == NULL)
  52.         exit(1);
  53.     for (i=0; i<NROW; ++i) {
  54.         vp = (VIDEO *) malloc(sizeof(VIDEO)+NCOL);
  55.         if (vp == NULL)
  56.             exit(1);
  57.         vscreen[i] = vp;
  58.         vp = (VIDEO *) malloc(sizeof(VIDEO)+NCOL);
  59.         if (vp == NULL)
  60.             exit(1);
  61.         pscreen[i] = vp;
  62.     }
  63. }
  64.  
  65. /*
  66.  * Clean up the virtual terminal
  67.  * system, in anticipation for a return to the
  68.  * operating system. Move down to the last line and
  69.  * clear it out (the next system prompt will be
  70.  * written in the line). Shut down the channel
  71.  * to the terminal.
  72.  */
  73. vttidy()
  74. {
  75.     movecursor(NROW, 0);
  76.     scr_clrl();
  77. }
  78.  
  79. /*
  80.  * Set the virtual cursor to
  81.  * the specified row and column on the
  82.  * virtual screen. There is no checking for
  83.  * nonsense values; this might be a good
  84.  * idea during the early stages.
  85.  */
  86. vtmove(row, col)
  87. {
  88.     vtrow = row;
  89.     vtcol = col;
  90. }
  91.  
  92. /*
  93.  * Write a character to the
  94.  * virtual screen. The virtual row and
  95.  * column are updated. If the line is too
  96.  * long put a "$" in the last column.
  97.  * This routine only puts printing characters
  98.  * into the virtual terminal buffers.
  99.  * Only column overflow is checked.
  100.  */
  101. vtputc(c)
  102. register int    c;
  103. {
  104.     register VIDEO    *vp;
  105.  
  106.     vp = vscreen[vtrow];
  107.     if (vtcol >= NCOL)
  108.         vp->v_text[NCOL-1] = '$';
  109.     else if (c == '\t') {
  110.         do {
  111.             vtputc(' ');
  112.         } while (vtcol&0x07);
  113.     } else if (c<0x20 || c==0x7F) {
  114.         vtputc('^');
  115.         vtputc(c ^ 0x40);
  116.     } else
  117.         vp->v_text[vtcol++] = c;        
  118. }
  119.  
  120. /*
  121.  * Erase from the end of the
  122.  * software cursor to the end of the
  123.  * line on which the software cursor is
  124.  * located.
  125.  */
  126. vteeol()
  127. {
  128.     register VIDEO    *vp;
  129.  
  130.     vp = vscreen[vtrow];
  131.     while (vtcol < NCOL) vp->v_text[vtcol++] = ' ';
  132. }
  133.  
  134. /*
  135.  * Make sure that the display is
  136.  * right. This is a three part process. First,
  137.  * scan through all of the windows looking for dirty
  138.  * ones. Check the framing, and refresh the screen.
  139.  * Second, make sure that "currow" and "curcol" are
  140.  * correct for the current window. Third, make the
  141.  * virtual and physical screens the same.
  142.  */
  143. update()
  144. {
  145.     register LINE    *lp;
  146.     register WINDOW    *wp;
  147.     register VIDEO    *vp1;
  148.     register VIDEO    *vp2;
  149.     register int    i;
  150.     register int    j;
  151.     register int    c;
  152.  
  153.     for (wp = wheadp; wp; wp = wp->w_wndp) {
  154.  
  155.         /* Look at any window with update flags set on.        */
  156.  
  157.         if (wp->w_flag) {
  158.  
  159.             /* If not force reframe, check the framing.    */
  160.  
  161.             if (!(wp->w_flag&WFFORCE)) {
  162.                 lp = wp->w_linep;
  163.                 for (i=0; i<wp->w_ntrows; ++i) {
  164.                     if (lp == wp->w_dotp) goto out;
  165.                     if (lp == wp->w_bufp->b_linep) break;
  166.                     lp = lforw(lp);
  167.                 }
  168.             }
  169.  
  170.             /* Not acceptable, better compute a new value    */
  171.             /* for the line at the top of the window. Then    */
  172.             /* set the "WFHARD" flag to force full redraw.    */
  173.  
  174.             i = wp->w_force;
  175.             if (i > 0) {
  176.                 --i;
  177.                 if (i >= wp->w_ntrows) i = wp->w_ntrows-1;
  178.             } else if (i < 0) {
  179.                 i += wp->w_ntrows;
  180.                 if (i < 0) i = 0;
  181.             } else i = wp->w_ntrows/2;
  182.             lp = wp->w_dotp;
  183.             while (i && lback(lp)!=wp->w_bufp->b_linep) {
  184.                 --i;
  185.                 lp = lback(lp);
  186.             }
  187.             wp->w_linep = lp;
  188.             wp->w_flag |= WFHARD;    /* Force full.        */
  189.         out:
  190.  
  191.             /* Try to use reduced update. Mode line update    */
  192.             /* has its own special flag. The fast update is    */
  193.             /* used if the only thing to do is within the    */
  194.             /* line editing.                */
  195.  
  196.             lp = wp->w_linep;
  197.             i  = wp->w_toprow;
  198.             if ((wp->w_flag&~WFMODE) == WFEDIT) {
  199.                 while (lp != wp->w_dotp) {
  200.                     ++i;
  201.                     lp = lforw(lp);
  202.                 }
  203.                 vscreen[i]->v_flag |= VFCHG;
  204.                 vtmove(i, 0);
  205.                 for (j=0; j<llength(lp); ++j) vtputc(lgetc(lp, j));
  206.                 vteeol();
  207.             } else if (wp->w_flag&(WFEDIT|WFHARD)) {
  208.                 while (i < wp->w_toprow+wp->w_ntrows) {
  209.                     vscreen[i]->v_flag |= VFCHG;
  210.                     vtmove(i, 0);
  211.                     if (lp != wp->w_bufp->b_linep) {
  212.                         for (j=0; j<llength(lp); ++j)
  213.                             vtputc(lgetc(lp, j));
  214.                         lp = lforw(lp);
  215.                     }
  216.                     vteeol();
  217.                     ++i;
  218.                 }
  219.             }
  220.             if (wp->w_flag&(WFFORCE|WFHARD|WFMODE)) modeline(wp);
  221.             wp->w_flag = wp->w_force = 0;
  222.         }
  223.     }
  224.  
  225.     /* Always recompute the row and column number of the hardware    */
  226.     /* cursor. This is the only update for simple moves.        */
  227.  
  228.     lp = curwp->w_linep;
  229.     currow = curwp->w_toprow;
  230.     while (lp != curwp->w_dotp) {
  231.         ++currow;
  232.         lp = lforw(lp);
  233.     }
  234.     curcol = i = 0;
  235.     while (i < curwp->w_doto) {
  236.         c = lgetc(lp, i++);
  237.         if (c == '\t') curcol |= 0x07;
  238.         else if (c<0x20 || c==0x7F) ++curcol;
  239.         ++curcol;
  240.     }
  241.     if (curcol >= NCOL)        /* Long line.        */
  242.         curcol = NCOL-1;
  243.  
  244.     /* Special hacking if the screen is garbage. Clear the hardware    */
  245.     /* screen, and update your copy to agree with it. Set all the    */
  246.     /* virtual screen change bits, to force a full update.        */
  247.  
  248.     if (sgarbf) {
  249.         for (i=0; i<NROW; ++i) {
  250.             vscreen[i]->v_flag |= VFCHG;
  251.             vp1 = pscreen[i];
  252.             for (j=0; j<NCOL; ++j) vp1->v_text[j] = ' ';
  253.         }
  254.         movecursor(0, 0);        /* Erase the screen.    */
  255.         scr_cls();
  256.         mpresf = sgarbf = FALSE;    /* Erase-page clears    */
  257.                         /* the message area.    */
  258.     }
  259.  
  260.     /* Make sure that the physical and virtual displays agree.    */
  261.     /* Unlike before, the "updateline" code is only called with a    */
  262.     /* line that has been updated for sure.                */
  263.  
  264.     for (i=0; i<NROW; ++i) {
  265.         vp1 = vscreen[i];
  266.         if (vp1->v_flag&VFCHG) {
  267.             vp1->v_flag &= ~VFCHG;
  268.             vp2 = pscreen[i];
  269.             updateline(i, vp1->v_text, vp2->v_text);
  270.         }
  271.     }
  272.  
  273.     /* Finally, update the hardware cursor and flush out buffers.    */
  274.  
  275.     movecursor(currow, curcol);
  276. }
  277.  
  278. /*
  279.  * Update a single line. This
  280.  * does not know how to use insert
  281.  * or delete character sequences; we are
  282.  * using VT52 functionality. Update the physical
  283.  * row and column variables. It does try an
  284.  * exploit erase to end of line.
  285.  */
  286. updateline(row, vline, pline)
  287. char    vline[];
  288. char    pline[];
  289. {
  290.     register char    *cp1;
  291.     register char    *cp2;
  292.     register char    *cp3;
  293.     register char    *cp4;
  294.     register char    *cp5;
  295.     register int    nbflag;
  296.  
  297.     cp1 = vline;            /* Compute left match.    */
  298.     cp2 = pline;
  299.     while (cp1!=vline+NCOL && *cp1==*cp2) {
  300.         ++cp1;
  301.         ++cp2;
  302.     }
  303.  
  304.     /* This can still happen, even though we only call this routine    */
  305.     /* on changed lines. A hard update is always done when a line    */
  306.     /* splits, a massive change is done, or a buffer is displayed    */
  307.     /* twice. This optimizes out most of the excess updating. A lot    */
  308.     /* of computes are used, but these tend to be hard operations    */
  309.     /* that do a lot of update, so I don't really care.        */
  310.  
  311.     if (cp1 == vline+NCOL)        /* All equal.        */
  312.         return;
  313.     nbflag = FALSE;
  314.     cp3 = vline+NCOL;        /* Compute right match.    */
  315.     cp4 = pline+NCOL;
  316.     while (cp3[-1] == cp4[-1]) {
  317.         --cp3;
  318.         --cp4;
  319.         if (*cp3 != ' ')        /* Note if any nonblank    */
  320.             nbflag = TRUE;        /* in right match.    */
  321.     }
  322.     cp5 = cp3;
  323.     if (nbflag == FALSE) {            /* Erase to EOL ?    */
  324.         while (cp5!=cp1 && cp5[-1]==' ') --cp5;
  325.         if (cp3-cp5 <= 3)        /* Use only if erase is    */
  326.             cp5 = cp3;        /* fewer characters.    */
  327.     }
  328.     movecursor(row, cp1-vline);        /* Go to start of line.    */
  329.     while (cp1 != cp5) {            /* Ordinary.        */
  330.         scr_co(*cp1);
  331.         ++ttcol;
  332.         *cp2++ = *cp1++;
  333.     }
  334.     if (cp5 != cp3) {            /* Erase.        */
  335.         scr_clrl();
  336.         while (cp1 != cp3) *cp2++ = *cp1++;
  337.     }
  338. }
  339.  
  340. /*
  341.  * Redisplay the mode line for
  342.  * the window pointed to by the "wp".
  343.  * This is the only routine that has any idea
  344.  * of how the modeline is formatted. You can
  345.  * change the modeline format by hacking at
  346.  * this routine. Called by "update" any time
  347.  * there is a dirty window.
  348.  */
  349. modeline(wp)
  350. register WINDOW    *wp;
  351. {
  352.     register char    *cp;
  353.     register int    c;
  354.     register int    n;
  355.     register BUFFER    *bp;
  356.     register int    ratio;
  357.     static int old_ratio = -1;
  358.  
  359.     ratio = percent();
  360.     if (wp->w_flag&WFMODE==0 && ratio==old_ratio) return;
  361.     old_ratio = ratio;
  362.  
  363.     n = wp->w_toprow+wp->w_ntrows;        /* Location.        */
  364.     vscreen[n]->v_flag |= VFCHG;        /* Redraw next time.    */
  365.     vtmove(n, 0);                /* Seek to right line.    */
  366.     vtputc('-');
  367.     bp = wp->w_bufp;
  368.     if (bp->b_flag&BFCHG)        /* "*" if changed.    */
  369.         vtputc('*');
  370.     else
  371.         vtputc('-');
  372.     n  = 2;
  373.     cp = " MicroEMACS -- ";            /* Buffer name.        */
  374.     while (c = *cp++) {
  375.         vtputc(c);
  376.         ++n;
  377.     }
  378.     cp = bp->b_bname;
  379.     while (c = *cp++) {
  380.         vtputc(c);
  381.         ++n;
  382.     }
  383.     vtputc(' ');
  384.     ++n;
  385.     if (*bp->b_fname) {        /* File name.        */
  386.         cp = "-- File: ";
  387.         while (c = *cp++) {
  388.             vtputc(c);
  389.             ++n;
  390.         }
  391.         cp = bp->b_fname;
  392.         while (c = *cp++) {
  393.             vtputc(c);
  394.             ++n;
  395.         }
  396.         vtputc(' ');
  397.         ++n;
  398.     }
  399.  
  400. /*    Display the percentage position within file. */
  401.  
  402.     vtputc('('); n++;
  403.     if (ratio >= 100) {
  404.         vtputc('1');
  405.         vtputc('0');
  406.         vtputc('0');
  407.         n += 3;
  408.     }
  409.     else {
  410.         vtputc((ratio > 9) ? '0'+ratio/10 : ' ');
  411.         vtputc('0'+ratio%10);
  412.         n += 2;
  413.     }
  414.     vtputc('%');
  415.     vtputc(')');
  416.     vtputc(' ');
  417.     n += 3;
  418.  
  419.     vtputc('-');
  420.     vtputc('-');
  421.     n += 2;
  422.  
  423.     /* tell the user if he is in INSert or OVRwrite mode for the
  424.        current buffer */
  425.     cp ="INS";
  426.     if (curbp->b_flag & BFOVRWRITE)
  427.         cp = "OVR";
  428.     while( c=*cp++)
  429.         {
  430.         vtputc(c);
  431.         n++;
  432.         }    
  433.  
  434.     while (n < NCOL) {        /* Pad to full width.    */
  435.         vtputc('-');
  436.         ++n;
  437.  
  438.     }
  439.  
  440. }
  441.  
  442.  
  443. /*
  444.  * Compute percent of file we are at
  445. */
  446. percent()
  447. {
  448.     register LINE    *clp;
  449.     register long    nch;
  450.     register int    cbo;
  451.     register long    nbc;
  452.     register int    ratio;
  453.     register int    cac;
  454.  
  455.     clp = lforw(curbp->b_linep);        /* Grovel the data.    */
  456.     cbo = 0;
  457.     nch = 0;
  458.     for (;;) {
  459.         if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
  460.             nbc = nch;
  461.             if (cbo == llength(clp))
  462.                 cac = '\n';
  463.             else
  464.                 cac = lgetc(clp, cbo);
  465.         }
  466.         if (cbo == llength(clp)) {
  467.             if (clp == curbp->b_linep)
  468.                 break;
  469.             clp = lforw(clp);
  470.             cbo = 0;
  471.         } else
  472.             ++cbo;
  473.         ++nch;
  474.     }
  475.     ratio = 0;                /* Ratio before dot.    */
  476.     if (nch)
  477.         ratio = (100L*nbc) / nch;
  478.  
  479.     return ratio;
  480. }
  481.  
  482.  
  483. /*
  484.  * Send a command to the terminal
  485.  * to move the hardware cursor to row "row"
  486.  * and column "col". The row and column arguments
  487.  * are origin 0. Optimize out random calls.
  488.  * Update "ttrow" and "ttcol".
  489.  */
  490. movecursor(row, col)
  491. {
  492.     if (row!=ttrow || col!=ttcol) {
  493.         ttrow = row;
  494.         ttcol = col;
  495.         scr_rowcol(row, col);
  496.     }
  497. }
  498.  
  499. /*
  500.  * Erase the message line.
  501.  * This is a special routine because
  502.  * the message line is not considered to be
  503.  * part of the virtual screen. It always works
  504.  * immediately; the terminal buffer is flushed
  505.  * via a call to the flusher.
  506.  */
  507. mlerase()
  508. {
  509.     movecursor(NROW, 0);
  510.     scr_clrl();
  511.     mpresf = FALSE;
  512. }
  513.  
  514. /*
  515.  * Ask a yes or no question in
  516.  * the message line. Return either TRUE,
  517.  * FALSE, or ABORT. The ABORT status is returned
  518.  * if the user bumps out of the question with
  519.  * a ^G. Used any time a confirmation is
  520.  * required.
  521.  */
  522. mlyesno(prompt)
  523. char    *prompt;
  524. {
  525.     register int    s;
  526.     char        buf[64];
  527.  
  528.     for (;;) {
  529.         strcpy(buf, prompt);
  530.         strcat(buf, " [y/n]? ");
  531.         s = mlreply(buf, buf, sizeof(buf));
  532.         if (s == ABORT)
  533.             return (ABORT);
  534.         if (s) {
  535.             if (*buf=='y' || *buf=='Y')
  536.                 return (TRUE);
  537.             if (*buf=='n' || *buf=='N')
  538.                 return (FALSE);
  539.         }
  540.     }
  541. }
  542.  
  543. /*
  544.  * Write a prompt into the message
  545.  * line, then read back a response. Keep
  546.  * track of the physical position of the cursor.
  547.  * If we are in a keyboard macro throw the prompt
  548.  * away, and return the remembered response. This
  549.  * lets macros run at full speed. The reply is
  550.  * always terminated by a carriage return. Handle
  551.  * erase, kill, and abort keys.
  552.  */
  553. mlreply(prompt, buf, nbuf)
  554. char    *prompt;
  555. char    *buf;
  556. {
  557.     register int    cpos;
  558.     register int    i;
  559.     register int    c;
  560.  
  561.     cpos = 0;
  562.     if (kbdmop != NULL) {
  563.         while ((c = *kbdmop++) != '\0')
  564.             buf[cpos++] = c;
  565.         buf[cpos] = 0;
  566.         return (!!*buf);
  567.     }
  568.     mlwrite(prompt);
  569.     for (;;) {
  570.         c = scr_ci();
  571.         switch (c) {
  572.         case 0x0D:            /* Return, end of line    */
  573.             buf[cpos++] = 0;
  574.             if (kbdmip != NULL) {
  575.                 if (kbdmip+cpos > kbdm+NKBDM-3) {
  576.                     ctrlg(FALSE, 0);
  577.                     return (ABORT);
  578.                 }
  579.                 for (i=0; i<cpos; ++i)
  580.                     *kbdmip++ = buf[i];
  581.             }
  582.             scr_co('\r');
  583.             ttcol = 0;
  584.             return !!*buf;
  585.  
  586.         case 0x07:            /* Bell, abort        */
  587.             scr_co('^');
  588.             scr_co('G');
  589.             ttcol += 2;
  590.             ctrlg(FALSE, 0);
  591.             return (ABORT);
  592.  
  593.         case 0x7F:            /* Rubout, erase    */
  594.         case 0x08:            /* Backspace, erase    */
  595.             if (cpos) {
  596.                 scr_co('\b');
  597.                 scr_co(' ');
  598.                 scr_co('\b');
  599.                 --ttcol;
  600.                 if (buf[--cpos] < 0x20) {
  601.                     scr_co('\b');
  602.                     scr_co(' ');
  603.                     scr_co('\b');
  604.                     --ttcol;
  605.                 }
  606.             }
  607.             break;
  608.  
  609.         case 0x15:            /* C-U, kill        */
  610.             while (cpos) {
  611.                 scr_co('\b');
  612.                 scr_co(' ');
  613.                 scr_co('\b');
  614.                 --ttcol;
  615.                 if (buf[--cpos] < 0x20) {
  616.                     scr_co('\b');
  617.                     scr_co(' ');
  618.                     scr_co('\b');
  619.                     --ttcol;
  620.                 }
  621.             }
  622.             break;
  623.  
  624.         default:
  625.             if (cpos < nbuf-1) {
  626.                 buf[cpos++] = c;
  627.                 if (c < ' ') {
  628.                     scr_co('^');
  629.                     ++ttcol;
  630.                     c ^= 0x40;
  631.                 }
  632.                 scr_co(c);
  633.                 ++ttcol;
  634.             }
  635.         }
  636.     }
  637. }
  638.  
  639. /*
  640.  * Write a message into the message
  641.  * line. Keep track of the physical cursor
  642.  * position. A small class of printf like format
  643.  * items is handled. Assumes the stack grows
  644.  * down; this assumption is made by the "++"
  645.  * in the argument scan loop. Set the "message
  646.  * line" flag TRUE.
  647.  */
  648. mlwrite(fmt, arg)
  649. char    *fmt;
  650. {
  651.     register int    c;
  652.     register char    *ap;
  653.  
  654.     movecursor(NROW, 0);
  655.     ap = (char *) &arg;
  656.     while (c = *fmt++) {
  657.         if (c != '%') {
  658.             scr_co(c);
  659.             ++ttcol;
  660.         } else {
  661.             c = *fmt++;
  662.             switch (c) {
  663.             case 'd':
  664.                 mlputi(*(int *)ap, 10);
  665.                 ap += sizeof(int);
  666.                 break;
  667.  
  668.             case 'o':
  669.                 mlputi(*(int *)ap,  8);
  670.                 ap += sizeof(int);
  671.                 break;
  672.  
  673.             case 'x':
  674.                 mlputi(*(int *)ap, 16);
  675.                 ap += sizeof(int);
  676.                 break;
  677.  
  678.             case 'D':
  679.                 mlputli(*(long *)ap, 10);
  680.                 ap += sizeof(long);
  681.                 break;
  682.  
  683.             case 's':
  684.                 mlputs(*(char **)ap);
  685.                 ap += sizeof(char *);
  686.                 break;
  687.  
  688.             default:
  689.                 scr_co(c);
  690.                 ++ttcol;
  691.             }
  692.         }
  693.     }
  694.     scr_clrl();
  695.     mpresf = TRUE;
  696. }
  697.  
  698. /*
  699.  * Write out a string.
  700.  * Update the physical cursor position.
  701.  * This assumes that the characters in the
  702.  * string all have width "1"; if this is
  703.  * not the case things will get screwed up
  704.  * a little.
  705.  */
  706. mlputs(s)
  707. register char    *s;
  708. {
  709.     register int    c;
  710.  
  711.     while (c = *s++) {
  712.         scr_co(c);
  713.         ++ttcol;
  714.     }
  715. }
  716.  
  717. /*
  718.  * Write out an integer, in
  719.  * the specified radix. Update the physical
  720.  * cursor position. This will not handle any
  721.  * negative numbers; maybe it should.
  722.  */
  723. mlputi(i, r)
  724. {
  725.     register int    q;
  726.     static char hexdigits[] = "0123456789ABCDEF";
  727.  
  728.     if (i < 0) {
  729.         i = -i;
  730.         scr_co('-');
  731.     }
  732.     q = i/r;
  733.     if (q)
  734.         mlputi(q, r);
  735.     scr_co(hexdigits[i%r]);
  736.     ++ttcol;
  737. }
  738.  
  739. /*
  740.  * do the same except as a long integer.
  741.  */
  742. mlputli(l, r)
  743. long l;
  744. {
  745.     register long q;
  746.  
  747.     if (l < 0) {
  748.         l = -l;
  749.         scr_co('-');
  750.     }
  751.     q = l/r;
  752.     if (q)
  753.         mlputli(q, r);
  754.     scr_co((int)(l%r)+'0');
  755.     ++ttcol;
  756. }
  757.