home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / beav1402.zip / display.c < prev    next >
C/C++ Source or Header  |  1996-01-01  |  33KB  |  1,421 lines

  1. /*
  2. * The functions in this file handle redisplay. The
  3. * redisplay system knows almost nothing about the editing
  4. * process; the editing functions do, however, set some
  5. * hints to eliminate a lot of the grinding. There is more
  6. * that can be done; the "vtputc" interface is a real
  7. * pig.  The MEMMAP
  8. * changes things around for memory mapped video. With
  9. * both off, the terminal is a VT52.
  10. */
  11.  
  12. #include    <sys/types.h>
  13. #include    <sys/stat.h>
  14. #include    "def.h"
  15.  
  16. D64 get_double ();
  17. D32 get_long ();
  18. D16 get_int ();
  19. void writ_echo ();
  20. void modeline ();
  21. void bin_to_text ();
  22. uint fill_buf ();
  23. uint get_currow ();
  24. uint get_curcol ();
  25. void uline ();
  26. void ucopy ();
  27. #if MSDOS
  28. void mem_line (int row, struct vid *vvp);
  29. #endif
  30.  
  31. extern char MSG_prn_to[];
  32. extern char MSG_disp_r_n[];
  33. extern char MSG_11lX[];
  34. extern char MSG_11lo[];
  35. extern char MSG_11ld[];
  36. extern char MSG_116e[];
  37. extern char MSG_03o[];
  38. extern char MSG_06o[];
  39. extern char MSG_011lo[];
  40. extern char MSG_03u[];
  41. extern char MSG_05u[];
  42. extern char MSG_010lu[];
  43. extern char MSG_02X[];
  44. extern char MSG_04X[];
  45. extern char MSG_08lX[];
  46. extern char MSG_prog_name[];
  47. extern char MSG_disp_b_lst[];
  48. extern char MSG_file[];
  49. extern char MSG_RO[];
  50. extern char MSG_WL[];
  51. extern char MSG_RW[];
  52. extern char MSG_AU[];
  53. extern char MSG_NOT_AU[];
  54. extern char MSG_curs_asc[];
  55. extern char MSG_curs_ebc[];
  56. extern char MSG_curs_hex[];
  57. extern char MSG_curs_bin[];
  58. extern char MSG_curs_dec[];
  59. extern char MSG_curs_flt[];
  60. extern char MSG_curs_oct[];
  61. extern char MSG_siz_8[];
  62. extern char MSG_siz_16[];
  63. extern char MSG_siz_32[];
  64. extern char MSG_siz_null[];
  65. extern char MSG_int_shift[];
  66. extern char MSG_mot_shift[];
  67. extern char MSG_print1[];
  68. extern char MSG_print2[];
  69. extern char MSG_cnt_al_w[];
  70. #if RUNCHK
  71. extern char ERR_disp_1[];
  72. extern char ERR_disp_2[];
  73. extern char ERR_disp_3[];
  74. extern char ERR_disp_4[];
  75. extern char ERR_disp_5[];
  76. extern char ERR_disp_6[];
  77. #endif
  78.  
  79. extern char ebcdic_table[];
  80.  
  81. extern bool mem_map;
  82.  
  83. /*
  84. * You can change these back to the types
  85. * implied by the name if you get tight for space. If you
  86. * make both of them "int" you get better code on the VAX.
  87. * They do nothing if this is not Gosling redisplay, except
  88. * for change the size of a structure that isn't used.
  89. * A bit of a cheat.
  90. */
  91. #define XCHAR   int
  92. #define XSHORT  int
  93.  
  94. /*
  95. * A video structure always holds
  96. * an array of characters whose length is equal to
  97. * the longest line possible. Only some of this is
  98. * used if "ncol" isn't the same as "NCOL".
  99. */
  100. typedef struct vid
  101. {
  102.     short v_hash;        /* Hash code, for compares.     */
  103.     short v_flag;        /* Flag word.                   */
  104.     short v_color;        /* Color of the line.           */
  105.     char v_text[NCOL];        /* The actual characters.       */
  106. } VIDEO;
  107.  
  108. #define VFCHG   0x0001        /* Changed.                     */
  109. #define VFHBAD  0x0002        /* Hash and cost are bad.       */
  110.  
  111. /*
  112. * SCORE structures hold the optimal
  113. * trace trajectory, and the cost of redisplay, when
  114. * the dynamic programming redisplay code is used.
  115. * If no fancy redisplay, this isn't used. The trace index
  116. * fields can be "char", and the score a "short", but
  117. * this makes the code worse on the VAX.
  118. */
  119. typedef struct
  120. {
  121.     XCHAR s_itrace;        /* "i" index for track back.    */
  122.     XCHAR s_jtrace;        /* "j" index for trace back.    */
  123.     XSHORT s_cost;        /* Display cost.                */
  124. } SCORE;
  125.  
  126. int sgarbf = TRUE;        /* TRUE if screen is garbage.   */
  127. int vtrow = 0;            /* Virtual cursor row.          */
  128. int vtcol = 0;            /* Virtual cursor column.       */
  129. int tthue = CNONE;        /* Current color.               */
  130. int ttrow = HUGE;        /* Physical cursor row.         */
  131. int ttcol = HUGE;        /* Physical cursor column.      */
  132. int tttop = HUGE;        /* Top of scroll region.        */
  133. int ttbot = HUGE;        /* Bottom of scroll region.     */
  134. char file_off_bad = FALSE;    /* Have file offsets been changed */
  135.  
  136. VIDEO **vscreen;        /* Edge vector, virtual.        */
  137. VIDEO **pscreen;        /* Edge vector, physical.       */
  138. VIDEO *video;            /* Actual screen data.          */
  139. VIDEO blanks;            /* Blank line image.            */
  140.  
  141. /*
  142. * Initialize the data structures used
  143. * by the display code. The edge vectors used
  144. * to access the screens are set up. The operating
  145. * system's terminal I/O channel is set up. Fill the
  146. * "blanks" array with ASCII blanks. The rest is done
  147. * at compile time. The original window is marked
  148. * as needing full update, and the physical screen
  149. * is marked as garbage, so all the right stuff happens
  150. * on the first call to redisplay.
  151. */
  152. void
  153. vtinit ()
  154. {
  155.     register VIDEO *vp;
  156.     register int i;
  157.  
  158.     /* allocate memory for screen images */
  159.     if (((vscreen = (VIDEO **) malloc (sizeof (VIDEO *) * nrow)) == NULL) ||
  160.     ((pscreen = (VIDEO **) malloc (sizeof (VIDEO *) * nrow)) == NULL) ||
  161.     ((video = (VIDEO *) malloc (sizeof (VIDEO) * 2 * nrow)) == NULL))
  162.     {
  163.     err_echo (MSG_cnt_al_w);
  164.     exit (1);        /* can't continue */
  165.     }
  166.  
  167.     vp = &video[0];
  168.     for (i = 0; i < nrow; ++i)
  169.     {
  170.     vscreen[i] = vp;
  171.     ++vp;
  172.     pscreen[i] = vp;
  173.     ++vp;
  174.     }
  175.     blanks.v_color = CTEXT;
  176.     for (i = 0; i < NCOL; ++i)
  177.     blanks.v_text[i] = ' ';
  178. }
  179.  
  180. /*
  181. *    Free memory used by the screen buffers
  182. */
  183. void
  184. vtfree ()
  185. {
  186.     /* release old screen memory */
  187.     free (video);
  188.     free (pscreen);
  189.     free (vscreen);
  190. }
  191.  
  192. /*
  193. * Tidy up the virtual display system
  194. * in anticipation of a return back to the host
  195. * operating system. Right now all we do is position
  196. * the cursor to the last line, erase the line, and
  197. * close the terminal channel.
  198. */
  199. void
  200. vttidy ()
  201. {
  202.     ttcolor (CTEXT);
  203.     ttnowindow ();        /* No scroll window.    */
  204.     ttmove (nrow - 1, 0);    /* Echo line.           */
  205.     tteeol ();
  206.     tttidy ();
  207.     ttflush ();
  208.     ttclose ();
  209. }
  210.  
  211. /*
  212. * Move the virtual cursor to an origin
  213. * 0 spot on the virtual display screen. I could
  214. * store the column as a character pointer to the spot
  215. * on the line, which would make "vtputc" a little bit
  216. * more efficient. No checking for errors.
  217. */
  218. void
  219. vtmove (row, col)
  220.     int row, col;
  221. {
  222.     vtrow = row;
  223.     vtcol = col;
  224. }
  225.  
  226. /*
  227. * Write a character to the virtual display,
  228. * dealing with long lines and the display of unprintable
  229. * things like control characters. Also expand tabs every 8
  230. * columns. This code only puts printing characters into
  231. * the virtual display image. Special care must be taken when
  232. * expanding tabs. On a screen whose width is not a multiple
  233. * of 8, it is possible for the virtual cursor to hit the
  234. * right margin before the next tab stop is reached. This
  235. * makes the tab code loop if you are not careful.
  236. * Three guesses how we found this.
  237. */
  238. void
  239. vtputc (c)
  240.     register char c;
  241. {
  242.     register VIDEO *vp;
  243.  
  244.     vp = vscreen[vtrow];
  245.     if (vtcol >= ncol)
  246.     vp->v_text[ncol - 1] = '$';
  247.     else if (ISCTRL (c) != FALSE)
  248.     {
  249.     vtputc ('^');
  250.     vtputc ((char) (c + 0x40));
  251.     }
  252.     else
  253.     {
  254.     vp->v_text[vtcol] = c;
  255.     vtcol++;
  256.     }
  257. }
  258.  
  259. /*
  260. * Write an entire screen line in the correct format.    pvr
  261. *
  262. * This code only puts printing characters into
  263. * the virtual display image.
  264. * Return TRUE if something was printed to the line.
  265. */
  266. #define REGI  register
  267. bool
  268. vtputd (wp, row)
  269.     WINDOW *wp;
  270.     int row;            /* line # to print to v screen */
  271.  
  272. {
  273.     REGI VIDEO *vp;
  274.     REGI uchar mode;
  275.     REGI A32 row_offst;
  276.     REGI uint chrs_per_row, lin_offset, i, chrs_in_lin;
  277.     LINE *cur_line;
  278.     static char w_buf[128];    /* temp buffer for data */
  279.  
  280.     vp = vscreen[vtrow];    /* point to VIDEO structure to print into */
  281.     mode = R_TYPE (wp);        /* get type of format structure */
  282.  
  283.     /* get number of bytes per row */
  284.     chrs_per_row = R_BYTES (wp);
  285.  
  286.     /* determine the offset from begining of the buffer for this line */
  287.     row_offst = WIND_POS (wp) + (row * chrs_per_row);
  288.  
  289.     /* search for and point to first character in buffer to be printed */
  290.     cur_line = wp->w_linep;    /* start with current first window line */
  291.     while (TRUE)
  292.     {                /* find line with desired character */
  293.     if (cur_line == wp->w_bufp->b_linep)
  294.     {            /* at end of buffer? */
  295.         return (FALSE);
  296.     }
  297.     if (cur_line->l_file_offset > row_offst)
  298.     {
  299.         /* if less than current line */
  300.         cur_line = cur_line->l_bp;    /* step back */
  301.     }
  302.     else if ((cur_line->l_file_offset + cur_line->l_used) <= row_offst)
  303.     {
  304.         cur_line = cur_line->l_fp;    /* step ahead */
  305.     }
  306.     else
  307.         break;
  308.     }
  309.     lin_offset = row_offst - cur_line->l_file_offset;    /* offset into line */
  310.  
  311.     /* get index into the current line to start reading the current row's data */
  312.     /* copy line text into buffer */
  313.     chrs_in_lin = fill_buf (wp, cur_line, lin_offset, w_buf, chrs_per_row);
  314.  
  315.     /* limit line length to screen width, used in TEXT mode only */
  316.     if (chrs_in_lin > NCOL)
  317.     chrs_in_lin = NCOL;
  318.  
  319.     /* Clear the line to spaces */
  320.     for (i = 0; i < NCOL; i++)
  321.     {
  322.     vp->v_text[i] = ' ';
  323.     }
  324.     switch (mode)
  325.     {
  326.     case TEXT:
  327.     break;
  328.     case ASCII:
  329.     case EBCDIC:
  330.     case BINARY:
  331.     case HEX:
  332.     /* print the row offset from the start of the file in HEX */
  333.     sprintf (vp->v_text, MSG_11lX, row_offst);    /* to vid buf */
  334.     break;
  335.     case OCTAL:
  336.     /* print the row offset from the start of the file */
  337.     sprintf (vp->v_text, MSG_11lo, row_offst);    /* to vid buf */
  338.     break;
  339. #if    FLOAT_DISP
  340.     case FLOAT:
  341. #endif
  342.     case DECIMAL:
  343.     /* print the row offset from the start of the file */
  344.     sprintf (vp->v_text, MSG_11ld, row_offst);    /* to vid buf */
  345.     break;
  346. #if RUNCHK
  347.     default:
  348.     writ_echo (ERR_disp_1);
  349.     break;
  350. #endif
  351.     }
  352.  
  353.     /* print the binary data to the text line */
  354.     bin_to_text (w_buf, vp->v_text, chrs_in_lin, wp->w_fmt_ptr);
  355.  
  356.     vtcol = NCOL;
  357.     return (TRUE);
  358. }
  359.  
  360. /*
  361. *   Print the contents of then binary data buffer bin_buf
  362. *   into the proper mode of text into txt_buf.
  363. *   Process 'len' bytes.
  364. *
  365. *   input:
  366. *           bin_buf     pointer to buffer of binary data to process.
  367. *           txt_buf     pointer to output buffer to print text result into.
  368. *           len         length in bytes of data in bin_buf to process.
  369. *           fmt_ptr     pointer to a ROW_FMT to use to format the data
  370. *                       conversion and printing process.
  371. *   output:
  372. *           none.
  373. */
  374.  
  375. void
  376. bin_to_text (bin_buf, txt_buf, len, fmt_ptr)
  377.  
  378.     char *bin_buf, *txt_buf;
  379.     uint len;
  380.     ROW_FMT *fmt_ptr;
  381.  
  382. {
  383.     uchar i, ch, k, j, mode, size, *posn;
  384.     uint temp_int;
  385.     ulong temp_long;
  386.  
  387.     mode = fmt_ptr->r_type;    /* get type of format structure */
  388.     size = fmt_ptr->r_size;    /* get size of format structure */
  389.     posn = fmt_ptr->r_positions;/* pointer to array of display positions */
  390.  
  391.     switch (mode)
  392.     {
  393.     case TEXT:
  394.     case ASCII:
  395.     for (i = 0; i < len; i++)
  396.     {
  397.         ch = bin_buf[i];
  398.         if ((ch >= ' ') && (ch < 0x7f))
  399.         txt_buf[posn[0] + i] = ch;
  400.         else
  401.         txt_buf[posn[0] + i] = '.';
  402.     }
  403.     break;
  404.  
  405.     case EBCDIC:
  406.     for (i = 0; i < len; i++)
  407.     {
  408.         txt_buf[posn[0] + i] =
  409.         0xff & ebcdic_table[0xff & bin_buf[i]];
  410.     }
  411.     break;
  412.  
  413.     case OCTAL:
  414.     switch (size)
  415.     {
  416.     case BYTES:        /* print octal bytes */
  417.         for (i = 0; i < len; i++)
  418.         {
  419.         sprintf (&txt_buf[
  420.                      posn[i]], MSG_03o, 0xff & bin_buf[i]);
  421.         }
  422.         break;
  423.  
  424.     case WORDS:        /* print octal words */
  425.         k = 0;
  426.         for (i = 0; i < len;
  427.          i += 2)
  428.         {
  429.         temp_int = get_int (&bin_buf[i]);
  430.         sprintf (&txt_buf[posn[k++]], MSG_06o, temp_int);
  431.         }
  432.         break;
  433.  
  434.     case DWORDS:        /* print octal double words */
  435.         k = 0;
  436.         for (i = 0; i < len;
  437.          i += 4)
  438.         {
  439.         temp_long = get_long (&bin_buf[i]);
  440.         sprintf (&txt_buf[posn[k++]], MSG_011lo, temp_long);
  441.         }
  442.         break;
  443.     }
  444.     break;
  445.  
  446.     case DECIMAL:
  447.     switch (size)
  448.     {
  449.     case BYTES:        /* print decimal bytes */
  450.         for (i = 0; i < len; i++)
  451.         {
  452.         sprintf (&txt_buf[posn[i]], MSG_03u, 0xff & bin_buf[i]);
  453.         }
  454.         break;
  455.  
  456.     case WORDS:        /* print decimal words */
  457.         k = 0;
  458.         for (i = 0; i < len;
  459.          i += 2)
  460.         {
  461.         temp_int = get_int (&bin_buf[i]);
  462.         sprintf (&txt_buf[posn[k++]], MSG_05u, temp_int);
  463.         }
  464.         break;
  465.  
  466.     case DWORDS:        /* print decimal double words */
  467.         k = 0;
  468.         for (i = 0; i < len; i += 4)
  469.         {
  470.         temp_long = get_long (&bin_buf[i]);
  471.         sprintf (&txt_buf[posn[k++]], MSG_010lu, temp_long);
  472.         }
  473.         break;
  474.     }
  475.     break;
  476. #if    FLOAT_DISP
  477.     case FLOAT:
  478.     {
  479.         /*
  480.             *    The Intel floating point representation is;
  481.             *        bit 0  - 52        significand    (53 bits)
  482.             *        bit 53 - 62        biased exponent (11 bits)
  483.             *        bit    63            sign
  484.             *    maximum range;    10^-308 <= X <= 10^+308
  485.             *    obviously, not all patterns are legal floating point numbers.
  486.             *    There can be up to 16 decimal digits of significand.
  487.             *    There are only 3 decimal digits of exponent (308 max).
  488.             */
  489.         k = 0;
  490.         for (i = 0; i < len; i += sizeof (D64))
  491.         {
  492.         D64 temp_d;
  493.  
  494.         temp_d = get_double (&bin_buf[i]);
  495.         sprintf (&txt_buf[posn[k++]], MSG_116e, temp_d);
  496.         }
  497.     }
  498.     break;
  499. #endif
  500.     case HEX:
  501.     switch (size)
  502.     {
  503.     case BYTES:        /* print hex bytes and ascii chars */
  504.         for (i = 0; i < len; i++)
  505.         {
  506.         if ((bin_buf[i] >= ' ') && (bin_buf[i] < 0x7f))
  507.             txt_buf[posn[i + 16]] = 0xff & bin_buf[i];
  508.         else
  509.             txt_buf[posn[i + 16]] = '.';
  510.         sprintf (&txt_buf[posn[i]], MSG_02X, 0xff & bin_buf[i]);
  511.         }
  512.         break;
  513.  
  514.     case WORDS:        /* print hex words */
  515.         k = 0;
  516.         for (i = 0; i < len; i += 2)
  517.         {
  518.         temp_int = get_int (&bin_buf[i]);
  519.         sprintf (&txt_buf[
  520.                      posn[k++]], MSG_04X, temp_int);
  521.         }
  522.         break;
  523.  
  524.     case DWORDS:        /* print hex double words */
  525.         k = 0;
  526.         for (i = 0; i < len; i += 4)
  527.         {
  528.         temp_long = get_long (&bin_buf[i]);
  529.         sprintf (&txt_buf[
  530.                      posn[k++]], MSG_08lX, temp_long);
  531.         }
  532.         break;
  533.     }
  534.     break;
  535.  
  536.     case BINARY:
  537.     switch (size)
  538.     {
  539.     case BYTES:        /* print binary bits */
  540.         for (i = 0; i < len; i++)
  541.         {
  542.         ch = bin_buf[i];/* get char to convert */
  543.         for (k = 0; k < 8; k++)
  544.         {
  545.             if (ch & 0x80)
  546.             txt_buf[posn[i] + k] = '1';
  547.             else
  548.             txt_buf[posn[i] + k] = '0';
  549.             ch = ch << 1;    /* slide next bit into place */
  550.         }
  551.         }
  552.         break;
  553.  
  554.     case WORDS:
  555.         j = 0;
  556.         for (i = 0; i < len; i += 2)
  557.         {
  558.         temp_int = get_int (&bin_buf[i]);
  559.  
  560.         for (k = 0; k < 16; k++)
  561.         {
  562.             if (temp_int & 0x8000)
  563.             txt_buf[posn[j] + k] = '1';
  564.             else
  565.             txt_buf[posn[j] + k] = '0';
  566.             temp_int = temp_int << 1;
  567.             /* slide next bit into place */
  568.         }
  569.         j++;
  570.         }
  571.         break;
  572.  
  573.     case DWORDS:
  574.         j = 0;
  575.         for (i = 0; i < len; i += 4)
  576.         {
  577.         temp_long = get_long (&bin_buf[i]);
  578.         for (k = 0; k < 32; k++)
  579.         {
  580.             if (temp_long & 0x80000000)
  581.             txt_buf[posn[j] + k] = '1';
  582.             else
  583.             txt_buf[posn[j] + k] = '0';
  584.             temp_long = temp_long << 1;
  585.             /* slide next bit into place */
  586.         }
  587.         j++;
  588.         }
  589.         break;
  590.     }
  591.     break;
  592. #if RUNCHK
  593.     default:
  594.     writ_echo (ERR_disp_2);
  595.     break;
  596. #endif
  597.     }
  598.     len *= (fmt_ptr->r_chr_per_u + 1);
  599.     /* Clean up any garbage characters left by the sprintf's */
  600.     for (i = 0; i < NCOL; i++)
  601.     {
  602.     if (txt_buf[i] == 0)
  603.         txt_buf[i] = ' ';
  604.     }
  605. }
  606.  
  607. /*
  608. *   Get an int from the buffer.
  609. *   Perform the Intel byte shuffle if necessary
  610. */
  611.  
  612. D16
  613. get_int (w_buf)
  614.     uchar *w_buf;
  615.  
  616. {
  617.     int temp_int;
  618.  
  619.     if (curwp->w_intel_mode)
  620.     {
  621.     temp_int = 0xff & w_buf[1];
  622.     temp_int = temp_int << 8;
  623.     temp_int |= 0xff & w_buf[0];
  624.     }
  625.     else
  626.     {
  627.     temp_int = 0xff & w_buf[0];
  628.     temp_int = temp_int << 8;
  629.     temp_int |= 0xff & w_buf[1];
  630.     }
  631.     return (temp_int);
  632. }
  633.  
  634. /*
  635. *   Get an long from the buffer.
  636. *   Perform the Intel byte shuffle if necessary
  637. */
  638.  
  639. D32
  640. get_long (w_buf)
  641.     uchar *w_buf;
  642.  
  643. {
  644.     long temp_long;
  645.  
  646.     if (curwp->w_intel_mode)
  647.     {
  648.     temp_long = 0xff & w_buf[3];
  649.     temp_long = temp_long << 8;
  650.     temp_long |= 0xff & w_buf[2];
  651.     temp_long = temp_long << 8;
  652.     temp_long |= 0xff & w_buf[1];
  653.     temp_long = temp_long << 8;
  654.     temp_long |= 0xff & w_buf[0];
  655.     }
  656.     else
  657.     {
  658.     temp_long = 0xff & w_buf[0];
  659.     temp_long = temp_long << 8;
  660.     temp_long |= 0xff & w_buf[1];
  661.     temp_long = temp_long << 8;
  662.     temp_long |= 0xff & w_buf[2];
  663.     temp_long = temp_long << 8;
  664.     temp_long |= 0xff & w_buf[3];
  665.     }
  666.     return (temp_long);
  667. }
  668.  
  669. #if    FLOAT_DISP
  670. /*
  671. *   Get an double from the buffer.
  672. *   Perform the Intel byte shuffle if necessary
  673. */
  674.  
  675. D64
  676. get_double (w_buf)
  677.     uchar *w_buf;
  678. {
  679.     uchar temp_doub[sizeof (D64)];
  680.     D64 *dp;
  681.     int i, siz;
  682.  
  683.     dp = (D64 *) temp_doub;
  684.     siz = sizeof (D64);
  685.  
  686.     if (curwp->w_intel_mode)
  687.     {
  688.     for (i = 0; i <= siz - 1; i++)
  689.     {
  690.         temp_doub[i] = 0xff & w_buf[i];
  691.     }
  692.     }
  693.     else
  694.     {
  695.     for (i = 0; i <= 7; i++)
  696.     {
  697.         temp_doub[(siz - 1) - i] = 0xff & w_buf[i];
  698.     }
  699.     }
  700.     return (*dp);
  701. }
  702.  
  703. #endif
  704.  
  705. /*
  706. *   Copy a length of bytes from the buffer LINEs into the designated
  707. *   buffer.   If the current LINE does not have enough bytes then
  708. *   advance to the next.   Return the actual number of bytes copied.
  709. *   The number copied would be less than the number requested if
  710. *   end of file is reached.
  711. */
  712.  
  713. uint
  714. fill_buf (wp, lin, lin_off, w_buff, cnt)
  715.     WINDOW *wp;
  716.     LINE *lin;
  717.     uint lin_off, cnt;
  718.     char *w_buff;
  719. {
  720.     REGI uint src, dest, i;
  721.  
  722.     src = lin_off;        /* initialize source line index */
  723.     dest = 0;            /* initialize destination buffer index */
  724.  
  725.     while (TRUE)
  726.     {
  727.     while (src < lin->l_used)
  728.     {
  729.         w_buff[dest++] = lin->l_text[src++];    /* copy byte */
  730.         if (dest == cnt)
  731.         {            /* if done */
  732.         return (cnt);    /* then leave */
  733.         }
  734.     }
  735.     if (R_TYPE (wp) == TEXT)
  736.         return (dest);    /* in text mode don't advance to next line */
  737.  
  738.     lin = lin->l_fp;    /* move to the next line */
  739.     if (lin == wp->w_bufp->b_linep)
  740.     {            /* if past last line */
  741.         for (i = dest; i < cnt; ++i)
  742.         w_buff[i] = 0;    /* fill rest of buffer with zeros */
  743.         return (dest);    /* return number of chars copied */
  744.     }
  745.     src = 0;        /* start next LINE at first byte */
  746.     }
  747. }
  748.  
  749. /*
  750. * Erase from the end of the
  751. * software cursor to the end of the
  752. * line on which the software cursor is
  753. * located. The display routines will decide
  754. * if a hardware erase to end of line command
  755. * should be used to display this.
  756. */
  757. void
  758. vteeol ()
  759. {
  760.     register VIDEO *vp;
  761.  
  762.     vp = vscreen[vtrow];
  763.     while (vtcol < ncol)
  764.     vp->v_text[vtcol++] = ' ';
  765. }
  766.  
  767. /*
  768. * Make sure that the display is
  769. * right. This is a three part process. First,
  770. * scan through all of the windows looking for dirty
  771. * ones. Check the framing, and refresh the screen.
  772. * Second, make the
  773. * virtual and physical screens the same.
  774. */
  775. void
  776. update ()
  777. {
  778.     register WINDOW *wp;
  779.     register VIDEO *vp1;
  780.     register VIDEO *vp2;
  781.     register uint i;
  782.     register int hflag;
  783.  
  784.     hflag = FALSE;        /* Not hard.            */
  785.     wp = wheadp;
  786.     while (wp != NULL)
  787.     {
  788.     /* is this window to be displayed in linked mode */
  789.     if ((curbp->b_flag & BFLINK) &&
  790.         (wp->w_bufp == curbp))
  791.     {            /* move dot to current window's dot position */
  792.         wp->w_dotp = curwp->w_dotp;
  793.         wp->w_doto = curwp->w_doto;
  794.         move_ptr (wp, 0L, TRUE, TRUE, TRUE);    /* insure dot is aligned */
  795.         wind_on_dot (wp);    /* move window to new dot position */
  796.     }
  797.  
  798.     if (wp->w_flag != 0)
  799.     {            /* Need update.         */
  800.         move_ptr (wp, 0L, FALSE, TRUE, TRUE);    /* window on row boundary */
  801.         move_ptr (wp, 0L, TRUE, TRUE, TRUE);    /* dot on unit boundary */
  802.         if ((wp->w_flag & WFFORCE) == 0)
  803.         {
  804.         wind_on_dot (wp);    /* position window on dot */
  805.         }
  806.         i = get_currow (wp);/* Redo this one line, mabey.    */
  807.         if ((wp->w_flag & ~WFMODE) == WFEDIT)
  808.         {
  809.         vscreen[i]->v_color = CTEXT;
  810.         vscreen[i]->v_flag |= (VFCHG | VFHBAD);
  811.         vtmove (i, 0);
  812.         vtputd (wp, i - wp->w_toprow);    /* print line to the screen */
  813.         }
  814.         else if ((wp->w_flag & ~WFMODE) == WFMOVE)
  815.         {
  816.         while (i < wp->w_toprow + wp->w_ntrows)
  817.         {
  818.             /* paint entire window */
  819.             vscreen[i]->v_color = CTEXT;
  820.             vscreen[i]->v_flag |= (VFCHG | VFHBAD);
  821.             vtmove (i, 0);
  822.             /* print line to the screen */
  823.             if (!vtputd (wp, i - wp->w_toprow))
  824.             vteeol ();
  825.             ++i;
  826.         }
  827.         }
  828.         else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  829.         {
  830.         hflag = TRUE;
  831.         i = wp->w_toprow;
  832.         while (i < wp->w_toprow + wp->w_ntrows)
  833.         {
  834.             /* paint entire window */
  835.             vscreen[i]->v_color = CTEXT;
  836.             vscreen[i]->v_flag |= (VFCHG | VFHBAD);
  837.             vtmove (i, 0);
  838.             /* print line to the screen */
  839.             if (!vtputd (wp, i - wp->w_toprow))
  840.             vteeol ();
  841.             ++i;
  842.         }
  843.         }
  844.         if ((wp->w_flag & WFMODE) ||
  845.         (wp->w_flag & WFMOVE) ||
  846.         (wp->w_flag & WFHARD))
  847.         modeline (wp);
  848.         wp->w_flag = 0;
  849.     }
  850.     wp = wp->w_wndp;
  851.     }
  852.     if (sgarbf != FALSE)
  853.     {                /* Screen is garbage.   */
  854.     sgarbf = FALSE;        /* Erase-page clears    */
  855.     epresf = FALSE;        /* the message area.    */
  856.     tttop = HUGE;        /* Forget where you set */
  857.     ttbot = HUGE;        /* scroll region.       */
  858.     tthue = CNONE;        /* Color unknown.       */
  859.     ttmove (0, 0);
  860.     tteeop ();
  861. #if MSDOS
  862.     if (mem_map)
  863.     {
  864.         for (i = 0; i < nrow; ++i)
  865.         {
  866.         mem_line (i, vscreen[i]);
  867.         }
  868.     }
  869.     else
  870.     {
  871. #endif
  872.         for (i = 0; i < nrow; ++i)
  873.         {
  874.         uline (i, vscreen[i], &blanks);
  875.         ucopy (vscreen[i], pscreen[i]);
  876.         }
  877. #if MSDOS
  878.     }
  879. #endif
  880.     ttmove (get_currow (curwp), get_curcol (curwp));
  881.     ttflush ();
  882.     return;
  883.     }
  884.     for (i = 0; i < nrow; ++i)
  885.     {                /* Easy update.         */
  886.     vp1 = vscreen[i];
  887.     vp2 = pscreen[i];
  888.     if ((vp1->v_flag & VFCHG) != 0)
  889.     {
  890. #if MSDOS
  891.         if (mem_map)
  892.         mem_line (i, vp1);
  893.         else
  894. #endif
  895.         {
  896.         uline (i, vp1, vp2);
  897.         ucopy (vp1, vp2);
  898.         }
  899.     }
  900.     }
  901.     ttmove (get_currow (curwp), get_curcol (curwp));
  902.     ttflush ();
  903. }
  904.  
  905. /*
  906. *   Get the window relative row in which the cursor will
  907. *   appear. pvr
  908. */
  909. uint
  910. get_currow (wp)
  911.     WINDOW *wp;
  912. {
  913.     A32 row;
  914.     /* number of bytes from start of window */
  915.     row = DOT_POS (wp) - WIND_POS (wp);
  916.     /* number of rows down in window */
  917.     row /= R_BYTES (wp);
  918.     row += wp->w_toprow;
  919. #if RUNCHK
  920.     if (row < wp->w_toprow)
  921.     printf (ERR_disp_3);
  922.     if (row > (wp->w_ntrows + wp->w_toprow))
  923.     printf (ERR_disp_4);
  924. #endif
  925.     return (row & 0xffff);
  926. }
  927.  
  928. /*
  929. *   Get the window relative column in which the cursor will
  930. *   appear. pvr
  931. */
  932. uint
  933. get_curcol (wp)
  934.     WINDOW *wp;
  935. {
  936.     long offset, index;
  937.     uint b_per_u, pos;
  938.  
  939.     b_per_u = R_B_PER_U (wp);
  940.     /* dot offset from start of buffer */
  941.     offset = DOT_POS (wp);
  942.     offset -= wp->w_disp_shift;
  943.     offset &= ~(b_per_u - 1);
  944.     /* calculate mod of the current file position */
  945.     index = offset & (R_BYTES (wp) - 1);
  946.     index /= b_per_u;
  947.     /* limit to window width */
  948.     if (index >= NCOL)
  949.     index = NCOL;
  950.     pos = wp->w_fmt_ptr->r_positions[index] + wp->w_unit_offset;
  951.     return (pos);
  952. }
  953.  
  954. #if MSDOS
  955. void
  956. mem_line (row, vvp)
  957.     int row;
  958.     VIDEO *vvp;
  959. {
  960.     vvp->v_flag &= ~VFCHG;    /* Changes done.        */
  961.     ttcolor (vvp->v_color);
  962.     putline (row + 1, 1, ncol, &vvp->v_text[0]);
  963. }
  964.  
  965. #endif
  966. /*
  967. * Update a saved copy of a line,
  968. * kept in a VIDEO structure. The "vvp" is
  969. * the one in the "vscreen". The "pvp" is the one
  970. * in the "pscreen". This is called to make the
  971. * virtual and physical screens the same when
  972. * display has done an update.
  973. */
  974. void
  975. ucopy (vvp, pvp)
  976.     register VIDEO *vvp;
  977.     register VIDEO *pvp;
  978. {
  979.     register int i;
  980.  
  981.     vvp->v_flag &= ~VFCHG;    /* Changes done.        */
  982.     pvp->v_flag = vvp->v_flag;    /* Update model.        */
  983.     pvp->v_hash = vvp->v_hash;
  984.     pvp->v_color = vvp->v_color;
  985.     for (i = 0; i < ncol; ++i)
  986.     pvp->v_text[i] = vvp->v_text[i];
  987. }
  988.  
  989. /*
  990. * Update a single line. This routine only
  991. * uses basic functionality (no insert and delete character,
  992. * but erase to end of line). The "vvp" points at the VIDEO
  993. * structure for the line on the virtual screen, and the "pvp"
  994. * is the same for the physical screen. Avoid erase to end of
  995. * line when updating CMODE color lines, because of the way that
  996. * reverse video works on most terminals.
  997. */
  998. void
  999. uline (row, vvp, pvp)
  1000.     int row;
  1001.     VIDEO *vvp;
  1002.     VIDEO *pvp;
  1003. {
  1004.     register char *cp1;
  1005.     register char *cp2;
  1006.     register char *cp3;
  1007.     register char *cp4;
  1008.     register char *cp5;
  1009.     register int nbflag;
  1010.  
  1011.     if (vvp->v_color != pvp->v_color)
  1012.     {                /* Wrong color, do a    */
  1013.     ttmove (row, 0);    /* full redraw.         */
  1014.     ttcolor (vvp->v_color);
  1015.     cp1 = &vvp->v_text[0];
  1016.     cp2 = &vvp->v_text[ncol];
  1017.     while (cp1 != cp2)
  1018.     {
  1019.         ttputc (*cp1++);
  1020.         ++ttcol;
  1021.     }
  1022.     return;
  1023.     }
  1024.     cp1 = &vvp->v_text[0];    /* Compute left match.  */
  1025.     cp2 = &pvp->v_text[0];
  1026.     while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0])
  1027.     {
  1028.     ++cp1;
  1029.     ++cp2;
  1030.     }
  1031.     if (cp1 == &vvp->v_text[ncol])    /* All equal.           */
  1032.     return;
  1033.     nbflag = FALSE;
  1034.     cp3 = &vvp->v_text[ncol];    /* Compute right match. */
  1035.     cp4 = &pvp->v_text[ncol];
  1036.     while (cp3[-1] == cp4[-1])
  1037.     {
  1038.     --cp3;
  1039.     --cp4;
  1040.     if (cp3[0] != ' ')    /* Note non-blanks in   */
  1041.         nbflag = TRUE;    /* the right match.     */
  1042.     }
  1043.     cp5 = cp3;            /* Is erase good?       */
  1044.     if (nbflag == FALSE && vvp->v_color == CTEXT)
  1045.     {
  1046.     while (cp5 != cp1 && cp5[-1] == ' ')
  1047.         --cp5;
  1048.     /* Alcyon hack */
  1049.     if ((int) (cp3 - cp5) <= tceeol)
  1050.         cp5 = cp3;
  1051.     }
  1052.     /* Alcyon hack */
  1053.     ttmove (row, (int) (cp1 - &vvp->v_text[0]));
  1054.     ttcolor (vvp->v_color);
  1055.     while (cp1 != cp5)
  1056.     {
  1057.     ttputc (*cp1++);
  1058.     ++ttcol;
  1059.     }
  1060.     if (cp5 != cp3)        /* Do erase.            */
  1061.     tteeol ();
  1062. }
  1063.  
  1064. /*
  1065. * Redisplay the mode line for
  1066. * the window pointed to by the "wp".
  1067. * This is the only routine that has any idea
  1068. * of how the modeline is formatted. You can
  1069. * change the modeline format by hacking at
  1070. * this routine. Called by "update" any time
  1071. * there is a dirty window.
  1072. */
  1073.  
  1074. void
  1075. modeline (wp)
  1076.     register WINDOW *wp;
  1077. {
  1078.     register char *cp, size, u_posn, *s;
  1079.     uchar mode;
  1080.     register char c;
  1081.     register int n;
  1082.     register BUFFER *bp;
  1083.     register A32 posn;
  1084.  
  1085.     static char posn_buf[30] =
  1086.     {
  1087.     0
  1088.     };                /* krw */
  1089.  
  1090.     mode = wp->w_fmt_ptr->r_type;    /* get type of format structure */
  1091.     size = wp->w_fmt_ptr->r_size;    /* get size of format structure */
  1092.  
  1093.     n = wp->w_toprow + wp->w_ntrows;    /* Location.            */
  1094.     vscreen[n]->v_color = CMODE;/* Mode line color.     */
  1095.     vscreen[n]->v_flag |= (VFCHG | VFHBAD);    /* Recompute, display.  */
  1096.     vtmove (n, 0);        /* Seek to right line.  */
  1097.     bp = wp->w_bufp;
  1098.  
  1099.     cp = MSG_prog_name;        /* Program name.  pvr    */
  1100.     n = 5;
  1101.     while ((c = *cp++) != 0)
  1102.     {
  1103.     vtputc (c);
  1104.     ++n;
  1105.     }
  1106.  
  1107.     if ((bp->b_flag & BFBAD) != 0)    /* "?" if trashed.      */
  1108.     vtputc ('?');
  1109.     else
  1110.     vtputc (' ');
  1111.  
  1112.     if ((bp->b_flag & BFCHG) != 0)    /* "*" if changed.      */
  1113.     vtputc ('*');
  1114.     else
  1115.     vtputc (' ');
  1116.  
  1117.     if (insert_mode)        /* "I" if insert mode  */
  1118.     vtputc ('I');
  1119.     else
  1120.     vtputc ('O');
  1121.  
  1122.     if (bp == blistp)
  1123.     {                /* special list */
  1124.     cp = MSG_disp_b_lst;
  1125.     while ((c = *cp++) != 0)
  1126.     {
  1127.         vtputc (c);
  1128.         ++n;
  1129.     }
  1130.     goto pad;
  1131.     }
  1132.  
  1133.     /* Buffer name */
  1134.     vtputc (' ');
  1135.     ++n;
  1136.     cp = &bp->b_bname[0];
  1137.     while ((c = *cp++) != 0)
  1138.     {
  1139.     vtputc (c);
  1140.     ++n;
  1141.     }
  1142.     while ((int) (cp - &bp->b_bname[0]) < NBUFN)
  1143.     {
  1144.     vtputc (' ');
  1145.     n++;
  1146.     cp++;
  1147.     }
  1148.  
  1149.     /* File name.           */
  1150.     vtputc (' ');
  1151.     ++n;
  1152.     cp = MSG_file;
  1153.     while ((c = *cp++) != 0)
  1154.     {
  1155.     vtputc (c);
  1156.     ++n;
  1157.     }
  1158.     cp = &bp->b_fname[0];
  1159.     while ((c = *cp++) != 0)
  1160.     {
  1161.     vtputc (c);
  1162.     ++n;
  1163.     }
  1164.     cp--;
  1165.     while ((int) (cp - &bp->b_fname[0]) < NFILE)
  1166.     {
  1167.     vtputc (' ');
  1168.     n++;
  1169.     cp++;
  1170.     }
  1171.  
  1172.     if (bp->b_flag & BFVIEW)
  1173.     s = MSG_RO;
  1174.     else if (bp->b_flag & BFSLOCK)
  1175.     s = MSG_WL;
  1176.     else
  1177.     s = MSG_RW;
  1178.  
  1179.     while (*s)
  1180.     {                /* krw */
  1181.     vtputc (*s++);
  1182.     ++n;
  1183.     }
  1184.  
  1185.     if (auto_update && !(bp->b_flag & BFVIEW) && bp->b_bname[0])    /* jam */
  1186.     s = MSG_AU;
  1187.     else
  1188.     s = MSG_NOT_AU;
  1189.     for (; *s && n < NCOL;)
  1190.     {
  1191.     vtputc (*s++);
  1192.     ++n;
  1193.     }
  1194.  
  1195.     /* Insert current dot position into mode line. */
  1196.     posn = DOT_POS (wp);
  1197.     u_posn = R_CHR_PER_U (wp) - wp->w_unit_offset - 1;
  1198.     if (u_posn < 0)
  1199.     u_posn = 0;
  1200.     switch (mode)
  1201.     {
  1202.     case TEXT:
  1203.     case ASCII:
  1204.     sprintf (posn_buf, MSG_curs_asc, posn);
  1205.     break;
  1206.     case EBCDIC:
  1207.     sprintf (posn_buf, MSG_curs_ebc, posn);
  1208.     break;
  1209.     case HEX:
  1210.     sprintf (posn_buf, MSG_curs_hex, posn, u_posn);
  1211.     break;
  1212.     case BINARY:
  1213.     sprintf (posn_buf, MSG_curs_bin, posn, u_posn);
  1214.     break;
  1215.     case DECIMAL:
  1216.     sprintf (posn_buf, MSG_curs_dec, posn, u_posn);
  1217.     break;
  1218. #if    FLOAT_DISP
  1219.     case FLOAT:
  1220. #endif
  1221.     sprintf (posn_buf, MSG_curs_flt, posn, u_posn);
  1222.     break;
  1223.     case OCTAL:
  1224.     sprintf (posn_buf, MSG_curs_oct, posn, u_posn);
  1225.     break;
  1226. #if RUNCHK
  1227.     default:
  1228.     writ_echo (ERR_disp_5);
  1229.     break;
  1230. #endif
  1231.     }
  1232.  
  1233.     cp = posn_buf;
  1234.     while ((c = *cp++) != 0)
  1235.     {
  1236.     vtputc (c);
  1237.     ++n;
  1238.     }
  1239.  
  1240.  
  1241.     if ((mode == HEX) ||
  1242.     (mode == DECIMAL) ||
  1243.     (mode == OCTAL))
  1244.     {
  1245.     switch (size)
  1246.     {
  1247.     case BYTES:
  1248.         sprintf (posn_buf, MSG_siz_8);
  1249.         break;
  1250.     case WORDS:
  1251.         sprintf (posn_buf, MSG_siz_16);
  1252.         break;
  1253.     case DWORDS:
  1254.         sprintf (posn_buf, MSG_siz_32);
  1255.         break;
  1256. #if RUNCHK
  1257.     default:
  1258.         writ_echo (ERR_disp_6);
  1259.         break;
  1260. #endif
  1261.     }
  1262.     }
  1263.     else
  1264.     sprintf (posn_buf, MSG_siz_null);
  1265.  
  1266.     cp = posn_buf;
  1267.     while ((c = *cp++) != 0)
  1268.     {
  1269.     vtputc (c);
  1270.     ++n;
  1271.     }
  1272.  
  1273.     if (wp->w_intel_mode)
  1274.     sprintf (posn_buf, MSG_int_shift, wp->w_disp_shift);
  1275.     else
  1276.     sprintf (posn_buf, MSG_mot_shift, wp->w_disp_shift);
  1277.     cp = posn_buf;
  1278.     while ((c = *cp++) != 0)
  1279.     {
  1280.     vtputc (c);
  1281.     ++n;
  1282.     }
  1283.  
  1284.  
  1285.     /* pad out */
  1286.   pad:
  1287.     while (n < ncol)
  1288.     {
  1289.     vtputc (' ');
  1290.     ++n;
  1291.     }
  1292. }
  1293.  
  1294. /*
  1295. * write text to the echo line
  1296. */
  1297. void
  1298. writ_echo (buf)
  1299.     char *buf;
  1300. {
  1301.     int i;
  1302.     char *vpp;
  1303.     bool fill_spac;
  1304.  
  1305.     fill_spac = FALSE;
  1306.     vpp = vscreen[nrow - 1]->v_text;
  1307.     vscreen[nrow - 1]->v_color = CTEXT;
  1308.     vscreen[nrow - 1]->v_flag |= VFCHG;
  1309.     epresf = TRUE;
  1310.  
  1311.     for (i = 0; i < NCOL; i++)
  1312.     {
  1313.     if (buf[i] == 0)
  1314.         fill_spac = TRUE;
  1315.     if (fill_spac)
  1316.         vpp[i] = ' ';
  1317.     else
  1318.         vpp[i] = buf[i];
  1319.     }
  1320. #if MSDOS
  1321.     if (mem_map)
  1322.     {
  1323.     mem_line (nrow - 1, vscreen[nrow - 1]);
  1324.     }
  1325.     else
  1326. #endif
  1327.     {
  1328.     uline (nrow - 1, vscreen[nrow - 1], pscreen[nrow - 1]);
  1329.     uline (nrow - 1, vscreen[nrow - 1], &blanks);
  1330.     ucopy (vscreen[nrow - 1], pscreen[nrow - 1]);
  1331.     ttflush ();
  1332.     }
  1333. }
  1334.  
  1335. /*
  1336. * Print the current buffer from mark to dot using the
  1337. * current window's display format.
  1338. * Prompt for file name or io device to print to.
  1339. */
  1340.  
  1341. bool
  1342. print ()
  1343. {
  1344.     LINE *dot_l_sav, *mark_l_sav, *wind_l_sav;
  1345.     int dot_off_sav, mark_off_sav, wind_off_sav, i;
  1346.     char s;
  1347.     char fname[NFILEN];
  1348.     register int nline;
  1349.     char buf[NFILEN], buf1[NFILEN];
  1350.  
  1351.     /* save the original window state */
  1352.     dot_l_sav = curwp->w_dotp;
  1353.     dot_off_sav = curwp->w_doto;
  1354.     mark_l_sav = curwp->w_markp;
  1355.     mark_off_sav = curwp->w_marko;
  1356.     wind_l_sav = curwp->w_linep;
  1357.     wind_off_sav = curwp->w_loff;
  1358.  
  1359.     /* if mark is not set then set it to location zero */
  1360.     if (curwp->w_markp == NULL)
  1361.     {
  1362.     curwp->w_markp = curwp->w_bufp->b_linep->l_fp;
  1363.     curwp->w_marko = 0;
  1364.     }
  1365.  
  1366.     nline = 0;
  1367.     if ((s = ereply (MSG_prn_to, fname, NFILEN, NULL)) == ABORT)
  1368.     return (s);
  1369.     adjustcase (fname);
  1370.     if ((s = ffwopen (fname, S_IREAD | S_IWRITE)) != FIOSUC)    /* Open writes message. */
  1371.     return (FALSE);
  1372.  
  1373.     sprintf (buf, MSG_print1, fname);
  1374.     writ_echo (buf);
  1375.     /* make dot before mark */
  1376.     if (DOT_POS (curwp) > MARK_POS (curwp))
  1377.     swapmark ();        /* make mark first */
  1378.  
  1379.     while (DOT_POS (curwp) <= MARK_POS (curwp))
  1380.     {
  1381.     /* check if we should quit */
  1382.     if (ttkeyready ())
  1383.     {
  1384.         if (ttgetc () == CTL_G)    /* quit if abort was hit */
  1385.         break;
  1386.     }
  1387.     nline++;
  1388.     /* move window so that first line is on dot */
  1389.     move_ptr (curwp, DOT_POS (curwp), FALSE, TRUE, FALSE);
  1390.  
  1391.     if (vtputd (curwp, 0))    /* print line into video buffer */
  1392.     {
  1393.         for (i = NCOL; (vscreen[vtrow]->v_text[i] < '!') ||
  1394.          (vscreen[vtrow]->v_text[i] > '~'); i--)
  1395.         ;
  1396.         i++;
  1397.         if ((s = ffputline (vscreen[vtrow]->v_text, i)) != FIOSUC)
  1398.         break;
  1399.         if ((s = ffputline (MSG_disp_r_n, 2)) != FIOSUC)
  1400.         break;
  1401.     }
  1402.     else
  1403.         break;
  1404.     forwline (0, 1, KRANDOM);    /* advance to next line */
  1405.     }
  1406.     ffclose ();
  1407.     sprintf (buf1, MSG_print2, R_POS_FMT (curwp));
  1408.     sprintf (buf, buf1, (long) nline);
  1409.     writ_echo (buf);
  1410.  
  1411.     /* restore the original window state */
  1412.     curwp->w_dotp = dot_l_sav;
  1413.     curwp->w_doto = dot_off_sav;
  1414.     curwp->w_markp = mark_l_sav;
  1415.     curwp->w_marko = mark_off_sav;
  1416.     curwp->w_linep = wind_l_sav;
  1417.     curwp->w_loff = wind_off_sav;
  1418.     curwp->w_flag |= WFHARD;    /* insure that window is still presentable */
  1419.     return (TRUE);
  1420. }
  1421.