root/trunk/txlib/txwprint.c

Revision 7, 28.6 kB (checked in by jvw, 3 years ago)

Made O_BINARY default for Linux files (CR/LF in logfiles)
Fixed Radio-repaint bug caused by a (x == TRUE) compare

Line 
1//
2//                     TxWin, Textmode Windowing Library
3//
4//   Original code Copyright (c) 1995-2005 Fsys Software and Jan van Wijk
5//
6// ==========================================================================
7//
8// This file contains Original Code and/or Modifications of Original Code as
9// defined in and that are subject to the GNU Lesser General Public License.
10// You may not use this file except in compliance with the License.
11// BY USING THIS FILE YOU AGREE TO ALL TERMS AND CONDITIONS OF THE LICENSE.
12// A copy of the License is provided with the Original Code and Modifications,
13// and is also available at http://www.dfsee.com/txwin/lgpl.htm
14//
15// This library is free software; you can redistribute it and/or modify
16// it under the terms of the GNU Lesser General Public License as published
17// by the Free Software Foundation; either version 2.1 of the License,
18// or (at your option) any later version.
19//
20// This library is distributed in the hope that it will be useful,
21// but WITHOUT ANY WARRANTY; without even the implied warranty of
22// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23// See the GNU Lesser General Public License for more details.
24//
25// You should have received a copy of the GNU Lesser General Public License
26// along with this library; (lgpl.htm) if not, write to the Free Software
27// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28//
29// Questions on TxWin licensing can be directed to: txwin@fsys.nl
30//
31// ==========================================================================
32//
33// TX Windowed text, redirect TxPrint output to scroll-buffer window
34//
35// Author: J. van Wijk
36//
37// JvW  18-05-99   Initial version
38// JvW  31-05-99   Fixed width * width bug in memory-alloc (width * length)
39// JvW  31-01-2000 Added follow_screen_toggle to allow screen on/off commands
40
41#include <txlib.h>                              // public interface
42#include <txwpriv.h>                            // private window interface
43
44
45#define TXWP_ARG_SIZE   4
46#define TXWP_NUM_ARGS  10
47#define TXWP_ANSI_ESC  27
48
49#define TXWP_MAX    0xffff                      // maximum rectangle value
50
51
52static BYTE      txwpFgColor = TXwCNW;          // default foreground color
53static BYTE      txwpBgColor = TXwCnZ;          // default background color
54
55static ULONG     txwpVirtCol = 0;               // column on virtual screen
56static ULONG     txwpVirtRow = 0;               // row    on virtual screen
57
58static TXSBDATA *txwpScrBuff = NULL;            // scroll buffer
59static char     *txwpLine    = NULL;            // intermediate line buffer
60static TXH_INFO  txwpSla;                       // TxPrint copy specification
61
62static char      txwpOriginalAnsiMode;          // remembered Ansi status
63
64// Convert and write ANSI-input string to a windowed scroll-buffer
65static void txwpHookScrollBuffer
66(
67   char               *text,                    // IN    TxPrint text to copy
68   void               *data                     // IN    user data (not used)
69);
70
71// Parse ANSI clause
72static char *txwpParseAnsi
73(
74   char               *current                  // IN    this character
75);
76
77// Parse ANSI clause without arguments
78static void txwpParseAnsiClause
79(
80   char               *current,                 // IN    this character
81   BOOL               *syntax                   // OUT   ANSI syntax error
82);
83
84// Parse ANSI clause including arguments
85static char *txwpParseAnsiArgs
86(
87   char               *current,                 // IN    this character
88   BOOL               *syntax                   // OUT   ANSI syntax error
89);
90
91// Clear virtual screen, clearing scroll-buffer and reset to default position
92static void txwpClearScreen
93(
94   void
95);
96
97// Goto specified x/y position in virtual screen
98static void txwpGotoXY
99(
100   ULONG               x,                       // IN    x coordinate (column)
101   ULONG               y                        // IN    y coordinate (row)
102);
103
104// Clear to end-of-line, on current line in scroll-buffer
105static void txwpClearToEOL
106(
107   void
108);
109
110// Write one character to the Scroll-buffer, handle newline (scroll) and wrap
111static void txwpWriteCharacter
112(
113   char                ch                       // IN    ASCII character value
114);
115
116// Scroll the scroll-buffer up on line and adjust positions
117static void txwpScrollUpOneLine
118(
119   void
120);
121
122// Automatic update of associated views for the scroll-buffer
123static void txwpAutoUpdateView
124(
125   void
126);
127
128
129/*****************************************************************************/
130// Initialize TxPrint hook to a scroll-buffer and related views
131/*****************************************************************************/
132ULONG txwInitPrintfSBHook
133(
134   TXSBDATA           *sbuf                     // IN    Scroll-buffer data
135)
136{
137   ULONG               rc = NO_ERROR;           // function return
138
139   ENTER();
140
141   if ((txwpScrBuff == NULL) && (sbuf->buf == NULL))
142   {
143      if ((sbuf->buf = TxAlloc(sbuf->width * sizeof(TXCELL),
144                                          sbuf->length)) != NULL)
145      {
146         if ((txwpLine = TxAlloc(1,  TXMAX4K)) != NULL)
147         {
148            sbuf->firstline = sbuf->length - sbuf->vsize;
149            txwpScrBuff     = sbuf;
150            txwpClearScreen();                  // clear the scroll-buffer
151
152            txwpSla.user    = NULL;
153            txwpSla.cbuf    = txwpLine;
154            txwpSla.size    = TXMAX4K;
155            txwpSla.copy    = txwpHookScrollBuffer;
156            txwpSla.active  = TRUE;
157            txwpSla.follow_screen_toggle = TRUE;
158            TxPrintHook( TXH_T_RAW, TXH_REGISTER, &txwpSla);
159
160            txwpOriginalAnsiMode = TxGetAnsiMode();
161            if (TxaExeSwitch('a') == FALSE)
162            {
163               TxSetAnsiMode( A_ON);            // allow color from now on
164            }
165         }
166         else
167         {
168            TxFreeMem( sbuf->buf);
169            rc = TX_ALLOC_ERROR;
170         }
171      }
172      else
173      {
174         rc = TX_ALLOC_ERROR;
175      }
176   }
177   else
178   {
179      rc = TX_PENDING;                          // buffer already in use
180   }
181   RETURN (rc);
182}                                               // end 'txwInitPrintfSBHook'
183/*---------------------------------------------------------------------------*/
184
185
186/*****************************************************************************/
187// Terminate TxPrint hook to a scroll-buffer and related views
188/*****************************************************************************/
189void txwTermPrintfSBHook
190(
191   void
192)
193{
194   ENTER();
195
196   TxSetAnsiMode( txwpOriginalAnsiMode);        // restore ansi mode
197
198   TxPrintHook( TXH_T_RAW, TXH_DEREGISTER, &txwpSla);
199
200   TxFreeMem( txwpLine);
201   TxFreeMem( txwpScrBuff->buf);
202
203   txwpScrBuff = NULL;
204
205   VRETURN();
206}                                               // end 'txwTermPrintfSBHook'
207/*---------------------------------------------------------------------------*/
208
209
210/*****************************************************************************/
211// Convert and write ANSI-input string to a windowed scroll-buffer
212/*****************************************************************************/
213static void txwpHookScrollBuffer
214(
215   char               *text,                    // IN    TxPrint text to copy
216   void               *data                     // IN    user data (not used)
217)
218{
219   char               *current = text;
220
221   while (*current)
222   {
223      if (*current == TXWP_ANSI_ESC)
224      {
225         current = txwpParseAnsi(current);
226      }
227      else
228      {
229         txwpWriteCharacter(*current);
230      }
231      current++;
232   }
233   txwpAutoUpdateView();
234}                                               // end 'txwpHookScrollBuffer'
235/*---------------------------------------------------------------------------*/
236
237
238/*****************************************************************************/
239// Parse ANSI clause
240/*****************************************************************************/
241static char *txwpParseAnsi
242(
243   char               *current                  // IN    this character
244)
245{
246   BOOL                error = FALSE;           // syntax error indicator
247   char               *keep  = current;
248
249   current++;
250   if (*current != '[')
251   {
252      error = TRUE;
253   }
254   else
255   {
256      current++;
257      if (!isdigit(*current))
258      {
259         txwpParseAnsiClause( current, &error);
260      }
261      else
262      {
263         current = txwpParseAnsiArgs(current, &error);
264      }
265   }
266   if (error)
267   {
268      txwpWriteCharacter(*keep);
269      return keep;
270   }
271   else
272   {
273      return current;
274   }
275}                                               // end 'txwpParseAnsi'
276/*---------------------------------------------------------------------------*/
277
278
279/*****************************************************************************/
280// Parse ANSI clause without arguments
281/*****************************************************************************/
282static void txwpParseAnsiClause
283(
284   char               *current,                 // IN    this character
285   BOOL               *syntax                   // OUT   ANSI syntax error
286)
287{
288   static ULONG x = 0;
289   static ULONG y = 0;
290
291   switch (*current)
292   {
293      case 'A': txwpGotoXY( txwpVirtCol    , txwpVirtRow + 1);     break;
294      case 'B': txwpGotoXY( txwpVirtCol    , txwpVirtRow - 1);     break;
295      case 'C': txwpGotoXY( txwpVirtCol + 1, txwpVirtRow    );     break;
296      case 'D': txwpGotoXY( txwpVirtCol - 1, txwpVirtRow    );     break;
297      case 'H': txwpGotoXY( 0              , 0              );     break;
298      case 'u': txwpGotoXY( x              , y              );     break;
299      case 's':             x = txwpVirtCol; y  = txwpVirtRow;     break;
300      case 'K': txwpClearToEOL();                                  break;
301      default : *syntax = TRUE;
302   }
303}                                               // end 'txwpParseAnsiClause'
304/*---------------------------------------------------------------------------*/
305
306
307/*****************************************************************************/
308// Parse ANSI clause including arguments
309/*****************************************************************************/
310static char *txwpParseAnsiArgs
311(
312   char               *current,                 // IN    this character
313   BOOL               *syntax                   // OUT   ANSI syntax error
314)
315{
316   int                 i;
317   int                 argument[TXWP_NUM_ARGS];
318   int                 count    = 0;
319   BYTE                intensFg = 0;
320   BYTE                intensBg = 0;
321
322   if ((*current == '2') && (*(current + 1) == 'J'))
323   {
324      ++current;
325      txwpClearScreen();
326   }
327   else
328   {
329      while (isdigit(*current) && (count <= TXWP_NUM_ARGS))
330      {
331
332         int           index = 0;
333         char          arg[TXWP_ARG_SIZE];
334
335         while (isdigit(*current) && (index < TXWP_ARG_SIZE))
336         {
337            arg[index++] = *current++;
338         }
339
340         if (index == TXWP_ARG_SIZE)
341         {
342            *syntax = TRUE;
343         }
344         else
345         {
346            arg[index] = 0;
347         }
348
349         argument[count++] = atoi(arg);
350
351         if (*current == ';')
352         {
353            ++current;
354         }
355      }
356
357      if (count == TXWP_NUM_ARGS)
358      {
359         *syntax = TRUE;
360      }
361      else
362      {
363         switch (*current)
364         {
365            case 'A':
366               if (count == 1)
367               {
368                  txwpGotoXY( txwpVirtCol, txwpVirtRow - (ULONG) argument[0]);
369               }
370               else
371               {
372                  *syntax = TRUE;
373               }
374               break;
375
376            case 'B':
377               if (count == 1)
378               {
379                  txwpGotoXY( txwpVirtCol, txwpVirtRow + (ULONG) argument[0]);
380               }
381               else
382               {
383                  *syntax = TRUE;
384               }
385               break;
386
387            case 'C':
388               if (count == 1)
389               {
390                  txwpGotoXY( txwpVirtCol + (ULONG) argument[0], txwpVirtRow);
391               }
392               else
393               {
394                  *syntax = TRUE;
395               }
396               break;
397
398            case 'D':
399               if (count == 1)
400               {
401                  txwpGotoXY( txwpVirtCol - (ULONG) argument[0], txwpVirtRow);
402               }
403               else
404               {
405                  *syntax = TRUE;
406               }
407               break;
408
409            case 'H':
410               if (count == 2)
411               {
412                  txwpGotoXY( argument[1], argument[0]);
413               }
414               else
415               {
416                  *syntax = TRUE;
417               }
418               break;
419
420            case 'm':
421               for (i = 0; i < count; i++)
422               {
423                  switch (argument[i])
424                  {
425                     case  0 :
426                        if (count == 1)         // color reset
427                        {
428                           txwpFgColor = TXwCNW; // default foreground color
429                           txwpBgColor = TXwCnZ; // default background color
430                        }
431                        else
432                        {
433                           intensFg = 0;
434                           intensBg = 0;
435                        }
436                        break;
437
438                     case 1:                    // High intensity (FG)
439                        intensFg = TXwINT;
440                        break;
441
442                     case 2:                    // DIM intensity
443                     case 4:                    // Underlined
444                     case 6:                    // Fast-blink
445                     case 7:                    // Reverse video
446                     case 8:                    // Hidden character
447                        break;
448
449                     case 5:                    // Blink (or high-intensity BG)
450                        intensBg = (TXwINT << 4);
451                        break;
452
453                     case 30 : txwpFgColor = ( TXwCNZ | intensFg ); break;
454                     case 31 : txwpFgColor = ( TXwCNR | intensFg ); break;
455                     case 32 : txwpFgColor = ( TXwCNG | intensFg ); break;
456                     case 33 : txwpFgColor = ( TXwCNY | intensFg ); break;
457                     case 34 : txwpFgColor = ( TXwCNB | intensFg ); break;
458                     case 35 : txwpFgColor = ( TXwCNM | intensFg ); break;
459                     case 36 : txwpFgColor = ( TXwCNC | intensFg ); break;
460                     case 37 : txwpFgColor = ( TXwCNW | intensFg ); break;
461                     case 40 : txwpBgColor = ( TXwCnZ | intensBg ); break;
462                     case 41 : txwpBgColor = ( TXwCnR | intensBg ); break;
463                     case 42 : txwpBgColor = ( TXwCnG | intensBg ); break;
464                     case 43 : txwpBgColor = ( TXwCnY | intensBg ); break;
465                     case 44 : txwpBgColor = ( TXwCnB | intensBg ); break;
466                     case 45 : txwpBgColor = ( TXwCnM | intensBg ); break;
467                     case 46 : txwpBgColor = ( TXwCnC | intensBg ); break;
468                     case 47 : txwpBgColor = ( TXwCnW | intensBg ); break;
469                     default : *syntax = TRUE;
470                  }
471               }
472               break;
473
474            default : *syntax = TRUE;
475         }
476      }
477   }
478   if (*syntax)
479   {
480      return NULL;
481   }
482   else
483   {
484      return current;
485   }
486}                                               // end 'txwpParseAnsiArgs'
487/*---------------------------------------------------------------------------*/
488
489
490/*****************************************************************************/
491// Clear virtual screen, clearing scroll-buffer and reset to default position
492/*****************************************************************************/
493static void txwpClearScreen
494(
495   void
496)
497{
498   size_t              lsize = (size_t) txwpScrBuff->width * sizeof(TXCELL);
499
500   memset(  txwpScrBuff->buf, 0,  ((size_t) txwpScrBuff->length * lsize));
501   txwpGotoXY( 0, 0);
502}                                               // end 'txwpClearScreen'
503/*---------------------------------------------------------------------------*/
504
505
506/*****************************************************************************/
507// Goto specified x/y position in virtual screen
508/*****************************************************************************/
509static void txwpGotoXY
510(
511   ULONG               x,                       // IN    x coordinate (column)
512   ULONG               y                        // IN    y coordinate (row)
513)
514{
515   txwpVirtCol = x;
516   txwpVirtRow = min( y, txwpScrBuff->vsize -1);
517}                                               // end 'txwpGotoXY'
518/*---------------------------------------------------------------------------*/
519
520
521/*****************************************************************************/
522// Clear to end-of-line, on current line in scroll-buffer
523/*****************************************************************************/
524static void txwpClearToEOL
525(
526   void
527)
528{
529   TXCELL              cell;                    // CELL contents to write
530   ULONG               line;                    // targetline in scroll-buffer
531   ULONG               index;                   // index in scroll-buffer
532   ULONG               col;                     // current column in line
533
534   cell.ch = ' ';                               // fill with spaces
535   cell.at = txwpFgColor | txwpBgColor;         // in the current color
536
537   line  = txwpScrBuff->length - txwpScrBuff->vsize + txwpVirtRow;
538   index = line * txwpScrBuff->width + txwpVirtCol;
539   for (col = txwpVirtCol; col < txwpScrBuff->width; col++, index++)
540   {
541      txwpScrBuff->buf[ index] = cell;
542   }
543}                                               // end 'txwpClearToEOL'
544/*---------------------------------------------------------------------------*/
545
546
547/*****************************************************************************/
548// Write one character to the Scroll-buffer, handle newline (scroll) and wrap
549/*****************************************************************************/
550static void txwpWriteCharacter
551(
552   char                ch                       // IN    ASCII character value
553)
554{
555   TXCELL              cell;                    // CELL contents to write
556   ULONG               line;                    // targetline in scroll-buffer
557
558   switch (ch)
559   {
560      case '\n':                                // newline in string
561         if (txwpVirtRow == txwpScrBuff->vsize -1) // writing at bottom line
562         {
563            txwpScrollUpOneLine();
564         }
565         else                                   // write om previous lines
566         {
567            txwpGotoXY( 0, txwpVirtRow + 1);
568         }
569         break;
570
571      default:
572         cell.ch = ch;
573         cell.at = txwpFgColor | txwpBgColor;
574         if (txwpVirtRow == txwpScrBuff->vsize -1) // writing at bottom line
575         {
576            if ((txwpScrBuff->wrap) && (txwpVirtCol >= txwpScrBuff->width))
577            {
578               txwpScrollUpOneLine();
579            }
580         }
581         if (txwpVirtCol < txwpScrBuff->width)  // within the line-width ?
582         {
583            line = txwpScrBuff->length - txwpScrBuff->vsize + txwpVirtRow;
584            txwpScrBuff->buf[ line * txwpScrBuff->width + txwpVirtCol] = cell;
585         }
586         txwpVirtCol++;
587         break;
588   }
589}                                               // end 'txwpWriteCharacter'
590/*---------------------------------------------------------------------------*/
591
592
593/*****************************************************************************/
594// Scroll the scroll-buffer up on line and adjust positions
595/*****************************************************************************/
596static void txwpScrollUpOneLine
597(
598   void
599)
600{
601   size_t              lsize = (size_t) txwpScrBuff->width * sizeof(TXCELL);
602   ULONG               lines = txwpScrBuff->length -1;
603
604   txwpVirtCol         = 0;                     // column in virtual screen
605
606   memmove( &(txwpScrBuff->buf[0]),                  //- dest, begin buf
607            &(txwpScrBuff->buf[txwpScrBuff->width]), //- source, 1 line ahead
608            lsize * ((size_t) lines));               //- bufsize minus 1 line
609
610   memset(  &(txwpScrBuff->buf[lines * txwpScrBuff->width]),
611            0, lsize);                          // clear the bottom line
612
613   if (txwpScrBuff->firstline != 0)
614   {                                            // count back, until at top
615      txwpScrBuff->firstline--;                 // of the scroll-buffer
616   }
617   if (txwpScrBuff->midscroll == FALSE)         // adjust scroll position
618   {
619      TXWINBASE   *wnd = (TXWINBASE *) txwpScrBuff->view;
620
621      if ((wnd = (TXWINBASE *) txwpScrBuff->view) != NULL)
622      {
623         if (wnd->window->sb.topline < (txwpScrBuff->length -
624                                        txwpScrBuff->vsize))
625         {
626            if (wnd->window->sb.topline != 0)
627            {                                   // count back, until at top
628               wnd->window->sb.topline--;       // of the scroll-buffer
629            }
630         }
631      }
632   }
633}                                               // end 'txwpScrollUpOneLine'
634/*---------------------------------------------------------------------------*/
635
636
637/*****************************************************************************/
638// Automatic update of invalid areas of associated views for the scroll-buffer
639/*****************************************************************************/
640static void txwpAutoUpdateView
641(
642   void
643)
644{
645   TXWINBASE          *wnd;
646
647   if ((wnd = (TXWINBASE *) txwpScrBuff->view) != NULL)
648   {
649      txwPaintSbView( wnd);
650   }
651}                                               // end 'txwpAutoUpdateView'
652/*---------------------------------------------------------------------------*/
653
654
655/*****************************************************************************/
656// ScrollBuff paint window, SBVIEW only; without any internal tracing!!!
657/*****************************************************************************/
658void txwPaintSbView
659(
660   TXWINBASE          *wnd                      // IN    current window
661)
662{
663   TXWINDOW           *win;
664   short               sy;                      // vertical size
665   short               nr;                      // line-nr while painting
666   short               size;                    // size of one line
667   ULONG               first;                   // first cell (top-left)
668   TXCELL             *cline;                   // cell line pointer
669   TXRECT              parent;                  // parent clip rectangle
670   TXRECT              clip;                    // region clip rectangle
671   TXRECT_ELEM         regs;                    // visible line regions
672   TXRECT_ELEM        *r;                       // current region
673   TXRECT_ELEM        *n;                       // next region
674   TXWINBASE          *focusShadow;             // focus related shadow window
675
676   if ((txwValidateAndClip((TXWHANDLE) wnd, &win, FALSE, &parent)) != NULL)
677   {
678      sy    = win->client.bottom - win->client.top   +1;
679      size  = (short) txwpScrBuff->width;
680      first = win->sb.topline * size + (ULONG) win->sb.leftcol;
681
682      cline = &(txwpScrBuff->buf[first]);       // top-left cell in SBview
683
684      TXSCREEN_BEGIN_UPDATE();
685      for (nr = 0; nr < (sy -1); nr++, cline += size)
686      {
687         regs.rect = win->client;               // start with full line
688         regs.next = NULL;                      // as a single element
689         regs.skip = FALSE;                     // it is a valid element
690
691         regs.rect.top   += nr;                 // actual line position
692         regs.rect.bottom = regs.rect.top;      // just 1-line vertical
693
694         txwVisibleLineRegions( wnd, &regs);    // calculate visible regions
695
696         for (r = &regs; r; r = r->next)        // Draw each line region
697         {
698            if (r->skip == FALSE)               // but only when valid
699            {
700               if (txwIntersectRect( &parent, &(r->rect), &clip))
701               {
702                  txwScrDrawCellString( win->client.top + nr,
703                                        win->client.left,
704                                        &clip, cline, size, win->sb.altcol);
705               }
706            }
707         }
708
709         for (r = regs.next; r; r = n)          // Free extra line regions
710         {
711            n = r->next;                        // next region in the list
712            free( r);
713         }
714      }
715      txwPaintWinStatus( wnd, NULL, 0);         // update SB line counters etc
716
717      focusShadow = txwa->focus;
718      if ((focusShadow != NULL) && (focusShadow->window != NULL))
719      {
720         if (focusShadow->window->style & TXWS_CAST_SHADOW)
721         {
722            txwPaintShadow( focusShadow);       // focus window border refresh
723         }
724         else                                   // retry on focus owner (dlg)
725         {
726            focusShadow = focusShadow->owner;
727            if ((focusShadow != NULL) && (focusShadow->window != NULL))
728            {
729               if (focusShadow->window->style & TXWS_CAST_SHADOW)
730               {
731                  txwPaintShadow( focusShadow);
732               }
733            }
734         }
735      }
736      TXSCREEN_ENDOF_UPDATE();
737   }
738}                                               // end 'txwPaintSbView'
739/*---------------------------------------------------------------------------*/
740
741
742/*****************************************************************************/
743// Save (part of) the scroll buffer to specified file as plain ASCII
744/*****************************************************************************/
745ULONG txwSavePrintfSB                           // RET   nr of lines written
746(
747   char               *fname,                   // IN    name of output file
748   ULONG               lines,                   // IN    lines from start/end
749   BOOL                fromEnd                  // IN    last lines (from end)
750)
751{
752   FILE               *df;                      // destination file ptr
753   ULONG               done = 0;                // nr of lines done
754
755   ENTER();
756
757   if (txwpScrBuff != NULL)
758   {
759      if ((df = fopen( fname, "w" TXFMODE)) != NULL) // create new text file
760      {
761         ULONG         used;                    // nr of lines in buffer
762         ULONG         first;                   // first line to save
763         ULONG         todo;                    // nr of lines to save
764         ULONG         l;                       // line nr in buffer
765         ULONG         i;                       // cell nr in line
766         ULONG         maxi;                    // maximum line length
767         ULONG         lstart;                  // index of 1st cell in line
768         TX1K          line;                    // ASCII copy of the line
769
770         used  = txwpScrBuff->length - txwpScrBuff->firstline;
771         if (lines >= used)
772         {
773            first = txwpScrBuff->firstline;
774            todo  = txwpScrBuff->length - first;
775         }
776         else
777         {
778            todo = lines;
779            if (fromEnd)
780            {
781               first = txwpScrBuff->length - todo;
782            }
783            else
784            {
785               first = txwpScrBuff->firstline;
786            }
787         }
788         maxi = (txwpScrBuff->width < TXMAX1K -1) ? txwpScrBuff->width
789                                    : TXMAX1K -1;
790
791         for (l = first, done = 0;