Remove NOTDONE code
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimBuffer.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*                                                                      *
24  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company               *
25  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
26  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.                *
27  * (c) Copyright 1993, 1994, 1996 Novell, Inc.                          *
28  * (c) Copyright 1996 Digital Equipment Corporation.                    *
29  * (c) Copyright 1996 FUJITSU LIMITED.                                  *
30  * (c) Copyright 1996 Hitachi.                                          *
31  */
32
33 #define USE_MEMCPY      /* use memcpy for line movement... */
34
35 #include <stdlib.h>
36 #include <wchar.h>
37 #include <Xm/Xm.h>
38 #include "TermHeader.h"       /* for MIN/MAX */
39 #include "TermPrim.h"
40 #include "TermPrimBuffer.h"
41 #include "TermPrimBufferP.h"
42 #include "TermPrimSelect.h"
43 #include "TermPrimDebug.h"
44
45 /*
46 ** Allocate and initialize a new terminal buffer.
47 */
48 TermBuffer
49 _DtTermPrimBufferCreateBuffer
50 (
51     const Widget  w,
52     const short   rows,
53     const short   cols,
54     const short   sizeOfBuffer,
55     const short   sizeOfLine,
56     const short   sizeOfEnh
57 )
58 {
59     int             i;
60     TermLine       *newTL;
61     TermBuffer      newTB;
62     Boolean        *tabs;
63     int             sizeOfChar;
64     
65     /*
66     ** malloc new a TermBuffer and an array of TermLine pointers
67     */
68     newTB = (TermBuffer) malloc(sizeOfBuffer);
69     /* the following MIN ensures that on systems where malloc of 0 returns
70      * NULL, we won't confuse a 0 size buffer with a malloc failure and an
71      * invalid buffer...
72      */
73     newTL  = (TermLine *) malloc((unsigned) MAX(rows, 1) * sizeof(TermLine));
74
75     if (!newTB || !newTL)
76     {
77         /*
78         ** clean up and leave if either of the previous malloc's failed.
79         */
80         if (newTB)
81         {
82             free(newTB);
83         }
84         if (newTL)
85         {
86             free(newTL);
87         }
88         return((TermBuffer)NULL);
89     }
90
91     /*
92     ** malloc a tabs array...
93     */
94     tabs = (Boolean *) malloc((unsigned) cols * sizeof(Boolean));
95     if (!tabs)
96     {
97         free(newTB);
98         free(newTL);
99         /*
100         ** clean up and leave if either of the previous malloc's failed.
101         */
102         return((TermBuffer)NULL);
103     }
104     /*
105     ** initialize the tabs...
106     */
107     for (i = 0; i < cols; i++) {
108         tabs[i] = 0 == i % 8;
109     }
110
111     /* 
112     ** decide how many bytes to allocate per character...
113     */
114     BYTES_PER_CHAR(newTB) = MB_CUR_MAX == 1 ? 1 : sizeof(wchar_t);
115
116     /* 
117     ** setting debug flag m:1 forces us into wide char mode...
118     */
119     DebugF('m', 1, BYTES_PER_CHAR(newTB) = sizeof(wchar_t));
120
121     /*
122     ** now malloc the individual lines...
123     */
124     for (i = 0; i < rows; i++)
125     {
126         newTL[i] = (TermLine) malloc(sizeOfLine);
127         if (newTL[i])
128         {
129             BUFFER(newTL[i]) = (termChar *) 
130                                       malloc((unsigned) 
131                                              cols * BYTES_PER_CHAR(newTB));
132
133             if (BUFFER(newTL[i]) == NULL)
134             {
135                 /*
136                 ** we couldn't malloc a line buffer
137                 */
138                 break;
139             }
140             LENGTH(newTL[i])  = 0;
141             WIDTH(newTL[i])   = 0;
142             WRAPPED(newTL[i]) = False;
143             START_SELECTION(newTL[i]) = NON_SELECTION_COL;
144             END_SELECTION(newTL[i]) = NON_SELECTION_COL;
145         }
146         else
147         {
148             /*
149             ** we couldn't malloc a line
150             */
151             break;
152         }
153     }
154
155     /*
156     ** If we were unable to malloc a full set of lines (i != rows),
157     ** then cleanup and leave.
158     */
159     if (i != rows)
160     {
161         int j;
162
163         for (j = 0; j < i; j++)
164         {
165             free(BUFFER(newTL[j]));
166             free(newTL[j]);
167         }
168         free(newTL);
169         free(newTB);
170         free(tabs);
171         return((TermBuffer)NULL);
172     }
173
174     /*
175     ** Initialize the new TermBuffer.
176     */
177     LINES(newTB)                = newTL;
178     TABS(newTB)                 = tabs;
179     ROWS(newTB)                 = rows;
180     COLS(newTB)                 = cols;
181     MAX_ROWS(newTB)             = rows;
182     MAX_COLS(newTB)             = cols;
183     SIZE_OF_BUFFER(newTB)       = sizeOfBuffer;
184     SIZE_OF_LINE(newTB)         = sizeOfLine;
185     SIZE_OF_ENH(newTB)          = sizeOfEnh;
186     BUFFER_CREATE(newTB)        = _DtTermPrimBufferCreateBuffer;
187     BUFFER_FREE(newTB)          = NULL;
188     BUFFER_RESIZE(newTB)        = NULL;
189     ENH_PROC(newTB)             = NULL;
190     CLEAR_ENH(newTB)            = NULL;
191     INSERT_ENH(newTB)           = NULL;
192     DELETE_ENH(newTB)           = NULL;
193     SET_ENH(newTB)              = NULL;
194     GET_ENH(newTB)              = NULL;
195     SET_LINE_WIDTH(newTB)       = NULL;
196     CLEAR_LINE(newTB)           = NULL;
197     NEXT_BUFFER(newTB)          = NULL;
198     PREV_BUFFER(newTB)          = NULL;
199     BUFFER_SELECT_INFO(newTB)   = NULL;
200     WIDGET(newTB)               = w;
201     return (newTB);
202 }
203
204 void
205 _DtTermPrimBufferFreeBuffer
206 (
207     const TermBuffer tb
208 )
209 {
210     int i;
211
212     /* check for null buffer... */
213     if (!tb) {
214         return;
215     }
216
217     /*
218     ** Free any buffer-specific info first...
219     */
220     if (BUFFER_FREE(tb))
221     {
222         (*BUFFER_FREE(tb))(tb);
223     }
224         
225     /*
226     ** free the old buffer...
227     */
228     for (i = 0; i < MAX_ROWS(tb); i++)
229     {
230         (void) free(BUFFER(LINES(tb)[i]));
231         (void) free(LINES(tb)[i]);
232     }
233     (void) free(TABS(tb));
234     (void) free(LINES(tb));
235     (void) free(tb);
236 }
237
238 /*
239 ** Resize the terminal buffer, and try to be smart about it.  If the buffer
240 ** shrinks, then simply adjust the size, no reallocs necessary (that way if
241 ** the user returns to the previous size, no data is lost).
242 **
243 ** NOTE:
244 **    Given this implementation, the memory allocated to the buffer will 
245 **    never decrease it simply increases to accommodate the largest size
246 **    that has ever been requested.
247 */
248 void
249 _DtTermPrimBufferResizeBuffer
250 (
251     TermBuffer *oldTB,
252     short      *newRows,
253     short      *newCols
254 )
255 {
256     int         i;
257     short       thisTabWidth;
258     short       tabWidth;
259     TermLine   *newTL;
260     Boolean    *tabs;
261     
262     /*
263     ** enforce some minimum size constraints...
264     */
265     *newRows = MAX(*newRows, 1);
266     *newCols = MAX(*newCols, 1);
267
268     /*
269     ** the number of cols is increasing, start small and adjust the tab
270     ** stops first...
271     */
272     if (*newCols > MAX_COLS(*oldTB)) 
273     {
274         tabs = (Boolean *) malloc((unsigned) *newCols * sizeof(Boolean));
275         if (tabs)
276         {
277             /* copy over the tab stops... */
278             (void) memcpy(tabs, TABS(*oldTB), COLS(*oldTB) * sizeof(Boolean));
279             free(TABS(*oldTB));
280             TABS(*oldTB) = tabs;
281             
282             /*
283             ** we need to extend the tab stops...
284             **
285             ** when tabWidth == 0, it has not been initialized.  When
286             ** it is < 0, we have encountered unequal tab stops...
287             */
288             tabWidth = 0;
289             for (i = 0, thisTabWidth = 0; i < COLS(*oldTB); i++, thisTabWidth++)
290             {
291                 if (TABS(*oldTB)[i])
292                 {
293                     if (tabWidth == 0)
294                     {
295                         /* first tab stop, set tabWidth... */
296                         tabWidth = thisTabWidth;
297                     } 
298                     else if (tabWidth != thisTabWidth)
299                     {
300                         /* tab stops differ, set tabWidth to -1... */
301                         tabWidth = -1;
302                     }
303                     /* reset for next tab stop... */
304                     thisTabWidth = 0;
305                 }
306             }
307             
308             if (tabWidth > 0)
309             {
310                 /*
311                 ** we have consistent tab stops.  Extend the buffer...
312                 */
313                 for (i = COLS(*oldTB); i < *newCols; i++)
314                 {
315                     TABS(*oldTB)[i] = (0 == (i % tabWidth));
316                 }
317             }
318             else
319             {
320                 /*
321                 ** we don't have consistent tab stops, so clear the rest...
322                 */
323                 (void) memset(&(TABS(*oldTB)[COLS(*oldTB)]), '\0',
324                               (*newCols - COLS(*oldTB)) * sizeof(Boolean));
325             }
326         }
327         else 
328         {
329             /*
330             ** the malloc failed, adjust newCols and newRows and leave...
331             */
332             *newCols     = MAX_COLS(*oldTB);
333             *newRows     = MIN(*newRows, MAX_ROWS(*oldTB));
334             COLS(*oldTB) = *newCols;
335             ROWS(*oldTB) = *newRows;
336             return;
337         }
338     }
339     else
340     {
341         /*
342         ** take care of any shrinkage...
343         */
344         COLS(*oldTB) = *newCols;
345     }
346
347     /*
348     ** Now give the resize helper a shot at resizing the buffer
349     */
350     if (BUFFER_RESIZE(*oldTB))
351     {
352         /* 
353         ** Call the resize helper function if it exists...
354         */
355         (*BUFFER_RESIZE(*oldTB))(*oldTB, newRows, newCols);
356     }
357     else
358     {
359         /*
360         ** There is no helper function, do it ourselves...
361         ** NOTE:
362         **     This might cause some duplication of code, but due
363         **     to time constraints, it is the most expeditious route.
364         */
365         /*
366         ** make any necessary width adjustments first...
367         **
368         ** NOTE:
369         **    We do not take any action if the new column width is less
370         **    than the current column width.  It is the responsibility of
371         **    the rendering code to make sure that two column characters
372         **    are handled properly if the second column falls past the last
373         **    column in the window.
374         */
375         if (*newCols > MAX_COLS(*oldTB)) 
376         {
377             termChar *newLineBuffer;
378             
379             /*
380             ** now extend the line buffers for all lines, (even lines that
381             ** are not being used at the moment (ROWS < MAX_ROWS))...
382             */
383             newTL = LINES(*oldTB);
384             for (i = 0; i < MAX_ROWS(*oldTB); i++)
385             {
386                 newLineBuffer = (termChar *) malloc((unsigned) 
387                                                     *newCols * BYTES_PER_CHAR(*oldTB));
388                 
389                 if (newLineBuffer == NULL)
390                 {
391                     /*
392                     ** line buffer malloc failed, we can only increase the
393                     ** width to the current maximum...
394                     */
395                     *newCols = MAX_COLS(*oldTB);
396                     *newRows = MIN(*newRows, MAX_ROWS(*oldTB));
397                     break;
398                 }
399                 memcpy(newLineBuffer, BUFFER(newTL[i]), LENGTH(newTL[i]));
400                 free(BUFFER(newTL[i]));
401                 BUFFER(newTL[i])  = newLineBuffer;
402                 WRAPPED(newTL[i]) = False;
403             }
404             MAX_COLS(*oldTB) = *newCols;
405         }
406         COLS(*oldTB) = *newCols;
407         
408         /*
409         ** now adjust the length of the buffer as necessary...
410         */
411         if (*newRows > MAX_ROWS(*oldTB)) 
412         {
413             /*
414             ** the number of rows is increasing
415             */
416             newTL = (TermLine *) malloc((unsigned) *newRows * 
417                                         sizeof(TermLine));
418             if (newTL != NULL)
419             {
420                 /*
421                 ** the malloc succeeded, copy the old information, and
422                 ** then free it...
423                 */
424                 memcpy(newTL, LINES(*oldTB), sizeof(TermLine) * 
425                        ROWS(*oldTB));
426                 free(LINES(*oldTB));
427                 LINES(*oldTB) = newTL;
428                 
429                 /*
430                 ** now initialize the new lines...
431                 */
432                 for (i = ROWS(*oldTB); i < *newRows; i++)
433                 {
434                     newTL[i] = (TermLine) malloc(SIZE_OF_LINE(*oldTB));
435                     if (newTL[i])
436                     {
437                         BUFFER(newTL[i]) = (termChar *) 
438                             malloc((unsigned) COLS(*oldTB) * 
439                                    BYTES_PER_CHAR(*oldTB));
440                         if (BUFFER(newTL[i]) == NULL)
441                         {
442                             /*
443                             ** line buffer malloc failed...
444                             */
445                             *newRows = i;
446                             break;
447                         }
448                         LENGTH(newTL[i])  = 0;
449                         WIDTH(newTL[i])   = 0;
450                         WRAPPED(newTL[i]) = False;
451                         START_SELECTION(newTL[i]) = NON_SELECTION_COL;
452                         END_SELECTION(newTL[i]) = NON_SELECTION_COL;
453                     }
454                     else
455                     {
456                         /*
457                         ** line malloc failed, lets limit the
458                         ** number of rows
459                         */
460                         *newRows = i;
461                         break;
462                     }
463                 }
464                 /*
465                 ** its conceivable that MAX_ROWS could actually decrease if
466                 ** we are low on memory...
467                 */
468                 MAX_ROWS(*oldTB) = MIN(MAX_ROWS(*oldTB), *newRows);
469             }
470             else
471             {
472                 /*
473                 ** the row buffer malloc failed, revert back to MAX_ROWS
474                 */
475                 *newRows = MAX_ROWS(*oldTB);
476             }
477         }
478         ROWS(*oldTB) = *newRows;
479     }
480 }
481
482 #ifdef NOCODE
483
484 /*
485 ** Create an enhancement block at the specified row and column.
486 **
487 ** NOTE:  For the time being, we simply allocate an entire row's worth
488 **        of enhancement blocks if it doesn't exist already.  We may
489 **        get smarter later on.
490 */
491 /*ARGSUSED*/
492 Boolean
493 _DtTermPrimBufferCreateEnhancement
494 (
495     const TermBuffer  tb,
496     const short       row,
497     const short       col
498 )
499 {
500     return(True);
501 }
502
503 /*
504 ** Clear the enhancement block at the specified row and column.
505 */
506 static
507 void
508 clearEnhancement
509 (
510     const TermBuffer  tb,
511     const short       row,
512     const short       col
513 )
514 {
515
516 }
517 #endif /* NOCODE */
518
519 /* 
520 ** Call the emulations specific function to insert the desired number of
521 ** enhancements at the specified position...
522 **
523 ** Assumptions:
524 **   - insertCount has been properly clipped to insure that we don't go
525 **     out of bounds
526 **
527 ** Notes:
528 **   - results are undefined if this function is called when the specified
529 **     column is at the end of the line.
530 */
531 static void
532 insertEnhancements
533 (
534     const TermBuffer  tb, 
535     const short       row,
536     const short       col,
537     const short       insertCount,
538     const Boolean     insertFlag
539 )
540 {
541     /* 
542     ** call the insert function only when it exists and we're in 
543     ** insert mode...
544     */
545     if (INSERT_ENH(tb))
546     {
547         (*INSERT_ENH(tb))(tb, row, col, insertCount, insertFlag);
548     }
549 }
550
551 /*
552 ** Free the enhancement block at the specified row and column.
553 **
554 ** NOTE: We may get smarter later on.
555 */
556 Boolean
557 _DtTermPrimBufferFreeEnhancement
558 (
559     const TermBuffer  tb,
560     const short       row,
561     const short       col
562 )
563 {
564     return(True);
565 }
566
567 #ifdef  BBA
568 #pragma BBA_IGNORE
569 #endif  /*BBA*/
570 static void
571 RememberTermBuffer
572 (
573     const TermBuffer  tb,
574     TermLine **referenceLines,
575     int *referenceLineCount
576 )
577 {
578     int i1;
579     int i2;
580
581     *referenceLineCount = ROWS(tb);
582     *referenceLines = (TermLine *) XtMalloc(ROWS(tb) * sizeof(TermLine));
583
584     /* copy over the lines...
585      */
586     for (i1 = 0; i1 < ROWS(tb); i1++) {
587         (*referenceLines)[i1] = LINE_OF_TBUF(tb, i1);
588     }
589
590     /* verify that they are all unique...
591      */
592
593     /* do a brute force check... */
594     for (i1 = 0; i1 < ROWS(tb) - 1; i1++) {
595         for (i2 = i1 + 1; i2 < ROWS(tb); i2++) {
596             if ((*referenceLines)[i1] == (*referenceLines)[i2]) {
597                 (void) fprintf(stderr,
598                         ">>RememberTermBuffer: dup ptrs lines %d and %d\n",
599                         i1, i2);
600             }
601         }
602     }
603 }
604
605 #ifdef  BBA
606 #pragma BBA_IGNORE
607 #endif  /*BBA*/
608 static void
609 CheckTermBuffer
610 (
611     const TermBuffer  tb, 
612     TermLine *referenceLines,
613     int referenceLineCount
614 )
615 {
616     int i1;
617     int i2;
618     int matchedLine;
619
620     if (referenceLineCount != ROWS(tb)) {
621         for (i1 = 0; i1 < ROWS(tb); i1++) {
622             matchedLine = -1;
623             for (i2 = 0; i2 < ROWS(tb); i2++) {
624                 if (referenceLines[i1] == LINE_OF_TBUF(tb, i2)) {
625                     if (matchedLine == -1) {
626                         matchedLine = i2;
627                     } else {
628                         /* duplicate pointer... */
629                         fprintf(stderr,
630                                 ">>CheckTermBuffer: duplicate pointer src=%d, dest=%d and %d\n",
631                                 i1, matchedLine, i2);
632                     }
633                 }
634             }
635             if (matchedLine == -1) {
636                 fprintf(stderr,
637                         ">>CheckTermBuffer: dropped pointer src=%d\n", i1);
638             }
639         }
640     }
641     
642     if (referenceLines) {
643       XtFree((char *) referenceLines);
644     }
645 }
646
647 short
648 _DtTermPrimBufferGetText
649 (
650     const TermBuffer      tb,
651     const short           row,
652     const short           col,
653     const short           width,
654           char           *buffer,
655     const Boolean         needWideChar
656 )
657 {
658     short           retLen;
659     short           colWidth;
660     short           mbLen;
661     char           *pChar;
662     wchar_t        *pwc;
663     TermCharInfoRec charInfoFirst;
664     TermCharInfoRec charInfoLast;
665     
666     if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
667     {
668         return(0);
669     }
670     
671     colWidth = MIN(width, WIDTH(LINE_OF_TBUF(tb, row)) - col);
672     if (colWidth > 0)
673     {
674         _DtTermPrimGetCharacterInfo(tb, row, col, &charInfoFirst);
675         _DtTermPrimGetCharacterInfo(tb, row, col + colWidth - 1, &charInfoLast);
676         
677         if ((BYTES_PER_CHAR(tb) > 1) && !needWideChar)
678         {
679             /*
680             ** our caller wants multi-byte characters...
681             */
682             retLen = 0;
683             pChar  = buffer;
684             
685             for (pwc = charInfoFirst.u.pwc; pwc <= charInfoLast.u.pwc; pwc++)
686             {
687                 mbLen = wctomb(pChar, *pwc);
688                 if (mbLen > 0)
689                 {
690                     pChar  += mbLen;
691                     retLen += mbLen;
692                 }
693                 else
694                 {
695                     fprintf(stderr, "_DtTermPrimBufferGetText: invalid wide char.\n");
696                 }
697             }
698         }
699         else
700         {
701             retLen = charInfoLast.idx - charInfoFirst.idx + 1;
702             memcpy(buffer, charInfoFirst.u.pc, retLen);
703         }
704     }
705     else
706     {
707         /*
708         ** colWidth == 0, there is nothing to return
709         */
710         retLen = 0;
711     }
712     
713     return(retLen);
714 }
715
716 short
717 _DtTermPrimBufferGetRows
718 (
719     const TermBuffer  tb
720 )
721 {
722     return(ROWS(tb));
723 }
724
725 short
726 _DtTermPrimBufferGetCols
727 (
728     const TermBuffer  tb
729 )
730 {
731     return(COLS(tb));
732 }
733
734 void
735 _DtTermPrimBufferSetLinks
736 (
737     const TermBuffer  tb,
738     const TermBuffer  prev,
739     const TermBuffer  next
740 )
741 {
742     PREV_BUFFER(tb) = prev;
743     NEXT_BUFFER(tb) = next;
744 }    
745
746 void
747 _DtTermPrimBufferSetSelectInfo
748 (
749     const TermBuffer        tb,
750     const TermSelectInfo    si
751 )
752 {
753     BUFFER_SELECT_INFO(tb) = si;
754 }
755
756 /*
757 ** Set the desired enhancement; simply call the emulator specific
758 ** function.
759 */
760 int
761 _DtTermPrimBufferSetEnhancement
762 (
763     const TermBuffer      tb,
764     const short           row,
765     const short           col,
766     const unsigned char   id,
767     const enhValue        value
768 )
769 {
770     if (SET_ENH(tb))
771     {
772         return((*SET_ENH(tb))(tb, row, col, id, value));
773     }
774     else
775     {
776         return(0);
777     }
778 }
779
780 /*
781 ** Get the enhancements for the desired row and column by calling the
782 ** emulator specific routine.
783 */
784 Boolean
785 _DtTermPrimBufferGetEnhancement
786 (
787     const TermBuffer      tb,
788     const short           row,
789     const short           col,
790           enhValue      **values,
791           short          *count,
792     const countSpec       countWhich
793 )
794 {
795     if (GET_ENH(tb))
796     {
797         return((*GET_ENH(tb))(tb, row, col, values, count, countWhich));
798     }
799     else
800     {
801         *count = WIDTH(LINE_OF_TBUF(tb, row));
802         return(True);
803     }
804 }
805
806 /*
807 ** Return a pointer to the character at the specified row,col.
808 */
809 termChar *
810 _DtTermPrimBufferGetCharacterPointer
811 (
812     const TermBuffer  tb,
813     const short       row,
814     const short       col
815 )
816 {
817     TermCharInfoRec    charInfo;
818     
819     if (VALID_ROW(tb, row) && VALID_COL(tb, col))
820     {
821         _DtTermPrimGetCharacterInfo(tb, row, col, &charInfo);
822         return((termChar *)charInfo.u.pc);
823     }
824     else
825     {
826         return(NULL);
827     }
828 }
829
830 /* 
831 ** Get the character info for the specified character.  If the
832 ** specified column exceeds the width of the line, then make up
833 ** some reasonable information, allowing the calling routine to
834 ** continue in a reasonable fashion.
835 **
836 ** XXX: this doesn't get the emulator specific information yet (like
837 **      enhancements
838 */
839 Boolean
840 _DtTermPrimGetCharacterInfo
841 (
842     TermBuffer      tb,
843     short           row,
844     short           col,
845     TermCharInfo    charInfo
846 )
847 {
848     wchar_t    *pwc;
849     short       charWidth;  
850     TermLine    line;
851     short       totalWidth;
852
853     if (!VALID_ROW(tb, row))
854     {
855         return(False);
856     }    
857
858     line = LINE_OF_TBUF(tb, row);
859
860     /*
861     ** handle the easy cases first...
862     */
863     if (col >= WIDTH(line))
864     {
865         /* 
866         ** make up some reasonable values when col equals or 
867         ** exceeds the width of the line
868         */
869         charInfo->u.ptc    = NULL;
870         charInfo->idx      = LENGTH(line);
871         charInfo->len      = BYTES_PER_CHAR(tb);
872         charInfo->startCol = col;
873         charInfo->width    = 1;
874         charInfo->enh      = 0;
875         charInfo->enhLen   = 0;
876     }
877     else if (BYTES_PER_CHAR(tb) == 1)
878     {
879         /* 
880         ** this is easy in a 1 byte locale...
881         */
882         charInfo->u.ptc    = BUFFER(line) + col;
883         charInfo->idx      = col;
884         charInfo->len      = BYTES_PER_CHAR(tb);
885         charInfo->startCol = col;
886         charInfo->width    = 1;
887         charInfo->enh      = 0;
888         charInfo->enhLen   = 0;
889     }
890     else if (col == 0)
891     {
892         charInfo->u.pwc    = (wchar_t *)BUFFER(line);
893         charInfo->idx      = 0;
894         charInfo->len      = BYTES_PER_CHAR(tb);
895         charInfo->startCol = 0;
896         charInfo->width    = MAX(1, wcwidth(*((wchar_t *)BUFFER(line))));
897         charInfo->enh      = 0;
898         charInfo->enhLen   = 0;
899     }
900     else
901     {
902         /* 
903         ** not so easy when we're dealing with wchars...
904         */
905         if (col <= WIDTH(line) / 2)
906         {
907             /*
908             ** work from left to right
909             */
910             totalWidth = 0;
911             for (pwc = (wchar_t *)BUFFER(line);
912                  pwc < (wchar_t *)BUFFER(line) + LENGTH(line);
913                  pwc++)
914             {
915                 charWidth = MAX(1, wcwidth(*pwc));
916                 totalWidth += charWidth;
917                 if (col < totalWidth)
918                 {
919                     /* 
920                     ** we've found the character, now fill in the info...
921                     */
922                     charInfo->u.pwc    = pwc;
923                     charInfo->idx      = pwc - (wchar_t *)BUFFER(line);
924                     charInfo->startCol = totalWidth - charWidth;
925                     charInfo->width    = charWidth;
926                     break;
927                 }
928             }
929         }
930         else
931         {
932             /*
933             ** work from right to left
934             */
935             totalWidth = WIDTH(line);
936             for (pwc = (wchar_t *)BUFFER(line) + (LENGTH(line) - 1);
937                  pwc >= (wchar_t *)BUFFER(line);
938                  pwc--)
939             {
940                 charWidth = MAX(1, wcwidth(*pwc));
941                 totalWidth -= charWidth;
942                 if (totalWidth <= col)
943                 {
944                     /* 
945                     ** we've found the character, now fill in the info...
946                     */
947                     charInfo->u.pwc = pwc;
948                     charInfo->idx   = pwc - (wchar_t *)BUFFER(line);
949                     if (totalWidth == col)
950                     {
951                         charInfo->startCol = col;
952                     }
953                     else
954                     {
955                         charInfo->startCol = col - 1;
956                     }
957                     charInfo->width = charWidth;
958                     break;
959                 }
960             }
961         }
962         charInfo->len    = BYTES_PER_CHAR(tb);
963         charInfo->enh    = 0;
964         charInfo->enhLen = 0;
965     }
966     return(True);
967 }
968
969 /*
970 ** Insert as many characters as possible at the specified row,col
971 ** return a count of the number of characters bumped off the end of the line
972 ** in 'returnCount' and a pointer to a buffer containing those characters
973 ** 'returnChars'.
974 ** 
975 ** The the new width of the line is returned as the value of the function.
976 */
977 short
978 _DtTermPrimBufferInsert
979 (
980     const TermBuffer  tb,
981     const short       row,
982     const short       col,
983     const termChar   *newChars,
984           short       numChars,
985           Boolean     insertFlag,   /* if TRUE, insert, else overwrite        */
986           termChar  **returnChars,  /* pointer to overflow buffer             */
987           short      *returnCount   /* count of characters in overflow buffer */
988 )   
989 {
990            short        insertCount;
991            short        newWidth;
992            short        insertOverflow; /* # of newChars that would overflow */
993            TermLine     line;
994            termChar    *start;
995            short        copyCount;   /* number of bytes to copy */
996     
997     if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
998     {
999         *returnCount = 0;
1000         return(0);
1001     }
1002     
1003     line = LINE_OF_TBUF(tb, row);
1004
1005 #ifdef  OLD_CODE
1006     /* before we modify the buffer, disown the selection... */
1007     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1008 #endif  /* OLD_CODE */
1009
1010     /* if this line is part of the selection, disown the selection... */
1011     if (IS_IN_SELECTION(line, col, MAX_SELECTION_COL)) {
1012         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1013     }
1014
1015     if (BYTES_PER_CHAR(tb) > 1)
1016     {
1017         /*
1018         ** we do not handle multibyte characters here...
1019         */
1020         _DtTermPrimBufferInsertWc(tb, row, col, (wchar_t *)newChars,
1021                                   numChars, insertFlag,
1022                                   returnChars, returnCount);
1023         return 0;
1024     }
1025
1026     if (WIDTH(line) < col)
1027     {
1028         /*
1029         ** We're adding characters past the current end of line,
1030         ** pad it out.
1031         */
1032         _DtTermPrimBufferPadLine(tb, row, col);
1033     }
1034     
1035     insertCount  = MIN(numChars, COLS(tb) - col);
1036     copyCount    = insertCount * BYTES_PER_CHAR(tb);
1037     start        = BUFFER(line) + col;
1038     
1039     /*
1040     ** It doesn't matter if we're overwriting, or inserting at the end 
1041     ** of the line, the result is the same...
1042     */
1043     if (col == WIDTH(line))
1044     {
1045         insertFlag = False;
1046     }
1047
1048     if (insertFlag == False)
1049     {
1050         /* 
1051         ** We're overwriting:
1052         **      - determine the new line width
1053         **      - put any extra new characters into the overflow buffer
1054         ** 
1055         ** The following will be done later:
1056         **      - copy the newChars into the line buffer 
1057         **      - adjust line width (_DtTermPrimBufferSetLineWidth won't
1058         **        let the line get shorter)...
1059         */
1060         newWidth     = MAX(WIDTH(line), col + insertCount);
1061         *returnCount = numChars - insertCount;
1062         if (*returnCount > 0)
1063         {
1064             memcpy(*returnChars, newChars + insertCount,
1065                    *returnCount * BYTES_PER_CHAR(tb));
1066         }
1067     }
1068     else
1069     {
1070         /* 
1071         ** we're inserting text in the middle of the line...
1072         */
1073         *returnCount = MAX(0, (WIDTH(line) + numChars) - COLS(tb));
1074         if (*returnCount > 0)
1075         {
1076             /* 
1077             ** we'll have some overflow, figure out how many (if any)
1078             ** of the new characters will overflow and put them into
1079             ** the overflow buffer...
1080             */
1081             insertOverflow = numChars - insertCount;
1082             memcpy(*returnChars, newChars + insertCount,
1083                    insertOverflow * BYTES_PER_CHAR(tb));
1084
1085             /* 
1086             ** copy the displaced characters from the line to the 
1087             ** overflow buffer as well...
1088             */
1089             memcpy(*returnChars + insertOverflow, BUFFER(line) + 
1090                    (WIDTH(line) - MAX(0, *returnCount - insertOverflow)),
1091                    MAX(0, *returnCount - insertOverflow) 
1092                    * BYTES_PER_CHAR(tb));
1093         }
1094         /* 
1095         ** Any overflow has been taken care of, now it's time to make
1096         ** room for the new characters...
1097         */
1098         memmove(start + insertCount, start,
1099                 MAX(0, WIDTH(line) - col - *returnCount));
1100
1101         /* 
1102         ** Compute the new line width...
1103         */
1104         newWidth = WIDTH(line) + insertCount;
1105     }
1106
1107     /* 
1108     ** Everything's ready:
1109     **     - put the characters into the line
1110     **     - adjust the line width (_DtTermPrimBufferSetLineWidth won't
1111     **       let the line get shorter)...
1112     **     - update the enhancements
1113     */
1114     memcpy(start, newChars, copyCount);
1115     _DtTermPrimBufferSetLineWidth(tb, row, newWidth);
1116     insertEnhancements(tb, row, col, insertCount, insertFlag);
1117     return(WIDTH(line));
1118 }
1119
1120 /* 
1121 ** Delete enough characters from the buffer to exceed width.
1122 **
1123 ** If returnChars != NULL then the deleted characters are returned
1124 ** in a buffer pointed to by returnChars.  It is the responsibility
1125 ** of the calling function to XtFree the buffer containing returned
1126 ** characters when they are no longer needed.
1127 */
1128 void
1129 _DtTermPrimBufferDelete
1130 (
1131     TermBuffer  tb,
1132     short      *row,
1133     short      *col,
1134     short      *width,
1135     termChar  **returnChars,  /* pointer to delete buffer        */
1136     short      *returnCount   /* count of bytes in delete buffer */
1137 )
1138 {
1139     int      copyCount;
1140     TermLine line;
1141     short    localRow;
1142     short    localCol;    
1143     TermCharInfoRec charInfo;
1144     
1145
1146     if (!VALID_ROW(tb, *row) || !VALID_COL(tb, *col))
1147     {
1148         if (returnChars)
1149         {
1150             *returnChars = NULL;
1151             *returnCount = 0;
1152         }
1153         return; 
1154     }
1155
1156     line     = LINE_OF_TBUF(tb, *row);
1157
1158 #ifdef  OLD_CODE
1159     /* before we modify the buffer, disown the selection... */
1160     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1161 #endif  /* OLD_CODE */
1162
1163     /* if this line is part of the selection, disown the selection... */
1164     if (IS_IN_SELECTION(line, *col, MAX_SELECTION_COL)) {
1165         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1166     }
1167
1168     if (BYTES_PER_CHAR(tb) > 1)
1169     {
1170         /*
1171         ** we do not handle multibyte characters here...
1172         */
1173         _DtTermPrimBufferDeleteWc(tb, row, col, width, returnChars,
1174                                   returnCount);
1175         return;
1176     }
1177     localRow = *row;
1178     localCol = *col;
1179     *width   = MAX(0, MIN(WIDTH(line) - localCol, *width));
1180
1181     /* 
1182     ** there are 4 cases of deleting a character from a line:
1183     **     Case 1:
1184     **       the cursor is past the end of the line (col >= WIDTH(line))
1185     **
1186     **     Case 2:
1187     **       the cursor is in the middle of the line (copyCount > 0)
1188     **          - move the remaining characters to the left
1189     **          - deleteEnhancement
1190     **
1191     **     Case 3:
1192     **       the cursor is at the end of the line (copyCount == 0 and
1193     **       col == WIDTH(line) - 1)
1194     **          - deleteEnhancement
1195     **          - adjust WIDTH and LENGTH
1196     */
1197     if (localCol >= WIDTH(line) || *width == 0)
1198     {
1199         /* 
1200         ** Handle Case 1...
1201         */
1202         if (returnChars)
1203         {
1204             *returnChars = NULL;
1205             *returnCount = 0;
1206         }
1207         return;
1208     }
1209
1210     _DtTermPrimGetCharacterInfo(tb, localRow, localCol, &charInfo);
1211     
1212     /* 
1213     ** Save the current characters before we overwrite them...
1214     */
1215     if (returnChars != NULL)
1216     {
1217         *returnCount = (*width * BYTES_PER_CHAR(tb));
1218         *returnChars = (termChar *)XtMalloc(*returnCount);
1219         memcpy(*returnChars, charInfo.u.pc, *returnCount);
1220     }
1221
1222     /* 
1223     ** Cases 2 and 3 require that we delete the enhancement...
1224     */
1225     if (DELETE_ENH(tb))
1226     {
1227         (*DELETE_ENH(tb))(tb, localRow, localCol, *width);
1228     }
1229
1230     copyCount = MAX(0, WIDTH(line) - *width - localCol);
1231     if (copyCount > 0)
1232     {
1233         /* 
1234         ** handle case 2
1235         */
1236         memmove(charInfo.u.pc, charInfo.u.pc + *width,
1237                 copyCount * BYTES_PER_CHAR(tb));
1238     }
1239
1240     /* 
1241     ** Case 2 and 3 require that we adjust the line width
1242     */
1243     WIDTH(line)  -= *width;
1244     LENGTH(line) -= *width;
1245 }
1246
1247 /*
1248 ** replace all characters between startCol, and stopCol with spaces,
1249 ** if startCol > stopCol, then simply return
1250 */
1251 void
1252 _DtTermPrimBufferErase
1253 (
1254     TermBuffer tb,
1255     short      row,
1256     short      startCol,
1257     short      stopCol
1258 )
1259 {
1260     TermCharInfoRec       charInfo;
1261     char                 *pchar;
1262
1263     /*
1264     ** Make sure we have a valid row, and if we have a valid
1265     ** row, make sure that startCol is <= stopCol
1266     */
1267     if (!(VALID_ROW(tb, row)) || (startCol > stopCol))
1268     {
1269         return;
1270     }
1271
1272 #ifdef  OLD_CODE
1273     /* before we modify the buffer, disown the selection... */
1274     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1275 #endif  /* OLD_CODE */
1276
1277     /* if this line is part of the selection, disown the selection... */
1278     if (IS_IN_SELECTION(LINE_OF_TBUF(tb, row), startCol, stopCol)) {
1279         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1280     }
1281
1282     if (BYTES_PER_CHAR(tb) > 1)
1283     {
1284         _DtTermPrimBufferEraseWc(tb, row, startCol, stopCol);
1285     }
1286     else
1287     {
1288         _DtTermPrimGetCharacterInfo(tb, row, startCol, &charInfo);
1289         pchar = charInfo.u.pc;
1290         
1291         _DtTermPrimGetCharacterInfo(tb, row, stopCol, &charInfo);
1292         
1293         /*
1294         ** replace the characters with spaces...
1295         */
1296         while(pchar <= charInfo.u.pc)
1297         {
1298             *pchar = ' ';
1299             pchar++;
1300         }
1301     }
1302 }
1303
1304
1305 /*
1306 ** Adjust the line width, (the line can never be shortened)
1307 */
1308 Boolean
1309 _DtTermPrimBufferSetLineWidth
1310 (
1311     const TermBuffer    tb,
1312     const short         row,
1313           short         newWidth
1314 )
1315 {
1316     TermLine    line;
1317     
1318     /*
1319     ** Make sure we have a valid row.
1320     */
1321     if (!VALID_ROW(tb, row))
1322     {
1323         return(False);
1324     }
1325
1326     line = LINE_OF_TBUF(tb, row);
1327
1328 #ifdef  OLD_CODE
1329     /* before we modify the buffer, disown the selection... */
1330     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1331 #endif  /* OLD_CODE */
1332
1333     /* if this line is part of the selection, disown the selection... */
1334     if (IS_IN_SELECTION(line, newWidth, MAX_SELECTION_COL)) {
1335         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1336     }
1337
1338     /*
1339     ** Clip the new width to the buffer width.
1340     */
1341     newWidth = MIN(newWidth, COLS(tb));
1342
1343     /*
1344     ** Make sure we actually have something to do.
1345     */
1346     if (WIDTH(line) >= newWidth)
1347     {
1348         return(True);
1349     }
1350
1351     WIDTH(line) = newWidth;
1352     if (BYTES_PER_CHAR(tb) == 1)
1353     {
1354         /*
1355         ** in single byte locales, we go ahead and set the line length too,
1356         ** it is the responsibility of wide-character routines to set the
1357         ** line length themselves...
1358         */ 
1359         LENGTH(line) = newWidth; /* works in single byte locales */
1360     }
1361     
1362     return(True);
1363 }
1364
1365 /*
1366 ** the line length is the lesser of the actual length, or the length
1367 ** necessary to get to the buffer width
1368 ** (in the case that the buffer shrank after the line was full)
1369 */
1370 short
1371 _DtTermPrimBufferGetLineLength
1372 (
1373     const TermBuffer    tb,
1374     const short         row
1375 )
1376 {
1377     short           retLen;
1378     short           width;
1379     TermCharInfoRec charInfo;
1380     
1381     if (VALID_ROW(tb, row)) 
1382     {
1383         width = MIN(WIDTH(LINE_OF_TBUF(tb, row)), COLS(tb));
1384         if (width > 0)
1385         {
1386             
1387             (void)_DtTermPrimGetCharacterInfo(tb, row, width - 1, &charInfo);
1388     
1389             /*
1390             ** a line can not end on column 1 of 2.  Note that we must
1391             ** add 1 to the column index to make it the line length...
1392             */
1393             if ((charInfo.width == 2) && (charInfo.startCol != width - 2)) {
1394                 /* only half of charInfo.idx fits in the line, so drop
1395                  * it...
1396                  */
1397                 retLen = charInfo.idx;
1398             } else {
1399                 /* single column character or double column character that
1400                  * fits on the line...
1401                  */
1402                 retLen = charInfo.idx + 1;
1403             }
1404         }
1405         else
1406         {
1407             retLen = 0;
1408         }
1409     }
1410     else
1411     {
1412         retLen = 0;
1413     }
1414     
1415     return(retLen);
1416
1417 }
1418
1419 /*
1420 ** the line width is the lesser of the actual width, or the buffer width
1421 ** (in the case that the buffer shrank after the line was full)
1422 */
1423 short
1424 _DtTermPrimBufferGetLineWidth
1425 (
1426     const TermBuffer    tb,
1427     const short         row
1428 )
1429 {
1430     short           width = 0;
1431     TermCharInfoRec charInfo;
1432     
1433     if (VALID_ROW(tb, row)) 
1434     {
1435         width = MIN(WIDTH(LINE_OF_TBUF(tb, row)), COLS(tb));
1436         if (width > 0)
1437         {
1438             
1439             (void)_DtTermPrimGetCharacterInfo(tb, row, width - 1, &charInfo);
1440     
1441             /*
1442             ** a line can not end on column 1 of 2...
1443             */
1444             if ((charInfo.width == 2) && (charInfo.startCol != width - 2)) {
1445                 /* only half of charInfo.idx fits in the line, so drop
1446                  * it...
1447                  */
1448                 (void) width--;
1449             } else {
1450                 /* single column character or double column character that
1451                  * fits on the line...
1452                  */
1453             }
1454         }
1455     }
1456     
1457     return(width);
1458 }
1459
1460
1461 /**************************************************************************
1462  *  Function:
1463  *      _DtTermPrimBufferMoveLockArea():
1464  *              move locked screen memory up or down in the screen buffer.
1465  *
1466  *  Parameters:
1467  *      TermBuffer tb: term buffer to use
1468  *      short dest: point to move the locked region to
1469  *      short src: point to move the locked region from
1470  *      short length: size of the locked region
1471  *
1472  *  Returns:
1473  *      <nothing>
1474  *
1475  *  Notes:
1476  */
1477 void
1478 _DtTermPrimBufferMoveLockArea
1479 (
1480     const TermBuffer tb, 
1481     const short dest,
1482     const short src,
1483     const short length
1484 )
1485 {
1486     TermLine lineCache[10];
1487     TermLine *holdLines = lineCache;
1488     TermLine *destPtr;
1489     TermLine *srcPtr;
1490     short cacheSize;
1491     short distance;
1492     int i;
1493     int refLineCount;
1494     TermLine *refLines;
1495
1496     DebugF('B', 1, RememberTermBuffer(tb,&refLines,&refLineCount));
1497
1498     /* don't bother if there is we are being asked to do nothing...
1499      */
1500     if ((length <= 0) || (dest == src))
1501         return;
1502
1503 #ifdef  OLD_CODE
1504     /* before we modify the buffer, disown the selection... */
1505     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1506 #endif  /* OLD_CODE */
1507
1508     /* before we modify the buffer, check and see if the leading or
1509      * trailing edge of the buffer crosses the selection and if the
1510      * destination line crosses the selection.  If it does, we need
1511      * to disown it...
1512      */
1513     /* leading edge of src... */
1514     if ((src > 0) &&
1515             IS_IN_SELECTION(LINE_OF_TBUF(tb, src - 1), MIN_SELECTION_COL,
1516             MAX_SELECTION_COL) &&
1517             IS_IN_SELECTION(LINE_OF_TBUF(tb, src), MIN_SELECTION_COL,
1518             MAX_SELECTION_COL)) {
1519         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1520     }
1521
1522     /* trailing edge of src... */
1523     if (((src + length) < ROWS(tb)) &&
1524             IS_IN_SELECTION(LINE_OF_TBUF(tb, src + length - 1),
1525             MIN_SELECTION_COL, MAX_SELECTION_COL) &&
1526             IS_IN_SELECTION(LINE_OF_TBUF(tb, src + length), MIN_SELECTION_COL,
1527             MAX_SELECTION_COL)) {
1528         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1529     }
1530
1531     /* destination... */
1532     if ((dest > 0) && IS_IN_SELECTION(LINE_OF_TBUF(tb, dest - 1),
1533             MIN_SELECTION_COL, MAX_SELECTION_COL) &&
1534             IS_IN_SELECTION(LINE_OF_TBUF(tb, dest),
1535             MIN_SELECTION_COL, MAX_SELECTION_COL)) {
1536         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1537     }
1538         
1539     /* we need to save the line positions of the smaller of the length
1540      * of the chunk to move, or the distance to move the chunk.
1541      */
1542     distance = (dest > src) ? (dest - src) : (src - dest);
1543     cacheSize = MIN(length, distance);
1544
1545     /* if we are moving more lines than will fit in the lineCache, we need
1546      * to malloc (and free) storage for the termLineRecs...
1547      */
1548     if (cacheSize > (sizeof(lineCache) / sizeof(lineCache[0]))) {
1549         holdLines = (TermLine *) XtMalloc(cacheSize * sizeof(TermLine));
1550     }
1551
1552      /*
1553      ** clear the wrap flag for the line preceding the block to be
1554      ** moved, and the last line of the block to be moved
1555      */
1556      _DtTermPrimBufferSetLineWrapFlag(tb, src - 1, FALSE);
1557      _DtTermPrimBufferSetLineWrapFlag(tb, src + length, FALSE);
1558
1559     /* cache them away... */
1560     if (distance > length) {
1561         /* save away the locked area... */
1562         destPtr = holdLines;
1563         srcPtr = &(LINE_OF_TBUF(tb, src));
1564
1565         for (i = 0; i < length; i++) {
1566             *destPtr++ = *srcPtr++;
1567         }
1568         /* move the area above/below the locked area... */
1569         if (dest > src) {
1570             /* we are moving the locked area down, so we must move
1571              * the scroll area up...
1572              */
1573             destPtr = &(LINE_OF_TBUF(tb, src));
1574             srcPtr = &(LINE_OF_TBUF(tb, src + length));
1575             for (i = 0; i < distance; i++) {
1576                 *destPtr++ = *srcPtr++;
1577             }
1578         } else {
1579             /* we are moving the locked area up, so we must move
1580              * the scroll area down...
1581              */
1582             destPtr = &(LINE_OF_TBUF(tb, src + length - 1));
1583             srcPtr = &(LINE_OF_TBUF(tb, src - 1));
1584             for (i = 0; i < distance; i++) {
1585                 *destPtr-- = *srcPtr--;
1586             }
1587         }
1588         /* restore the cached lock area... */
1589         destPtr = &(LINE_OF_TBUF(tb, dest));
1590         srcPtr = holdLines;
1591         for (i = 0; i < length; i++) {
1592             *destPtr++ = *srcPtr++;
1593         }
1594     } else {
1595         /* save away the area we will move the lock area over...
1596          */
1597         if (dest > src) {
1598             /* moving it down.  Save the area under the lock area...
1599              */
1600             destPtr = holdLines;
1601             srcPtr = &(LINE_OF_TBUF(tb, src + length));
1602             for (i = 0; i < distance; i++) {
1603                 *destPtr++ = *srcPtr++;
1604             }
1605             /* move the lock area down... */
1606             destPtr = &(LINE_OF_TBUF(tb, dest + length - 1));
1607             srcPtr = &(LINE_OF_TBUF(tb, src + length - 1));
1608             for (i = 0; i < length; i++) {
1609                 *destPtr-- = *srcPtr--;
1610             }
1611             /* restore the area under (that is now over) the lock area... */
1612             destPtr = &(LINE_OF_TBUF(tb, src));
1613             srcPtr = holdLines;
1614             for (i = 0; i < distance; i++) {
1615                 *destPtr++ = *srcPtr++;
1616             }
1617         } else {
1618             /* moving it up.  Save the area over the lock area...
1619              */
1620             destPtr = holdLines;
1621             srcPtr = &(LINE_OF_TBUF(tb, dest));
1622             for (i = 0; i < distance; i++) {
1623                 *destPtr++ = *srcPtr++;
1624             }
1625             /* move the lock area up... */
1626             destPtr = &(LINE_OF_TBUF(tb, dest));
1627             srcPtr = &(LINE_OF_TBUF(tb, src));
1628             for (i = 0; i < length; i++) {
1629                 *destPtr++ = *srcPtr++;
1630             }
1631             /* restore the area over (that is now under) the lock area... */
1632             destPtr = &(LINE_OF_TBUF(tb, dest + length));
1633             srcPtr = holdLines;
1634             for (i = 0; i < distance; i++) {
1635                 *destPtr++ = *srcPtr++;
1636             }
1637         }
1638     }
1639
1640     /* free up the holdLines (if we malloc'ed it)... */
1641     if (holdLines != lineCache) {
1642         (void) XtFree((char *) holdLines);
1643     }
1644
1645     DebugF('B', 1, CheckTermBuffer(tb,refLines,refLineCount));
1646 }
1647
1648
1649 /**************************************************************************
1650  *  Function:
1651  *      _DtTermPrimBufferInsertLine():  insert one or more lines of text from
1652  *              the end of the buffer below the insertion point, or from
1653  *              the beginning of the buffer above the insertion point.
1654  *
1655  *  Parameters:
1656  *      TermBuffer tb: term buffer to insert end/begining into
1657  *      short dest: point to insert below/above
1658  *      short length: number of lines to insert
1659  *      short src: source point of insertion
1660  *
1661  *  Returns:
1662  *      <nothing>
1663  *
1664  *  Notes:
1665  */
1666 void
1667 _DtTermPrimBufferInsertLine
1668 (
1669     const TermBuffer tb,
1670     const short dest,
1671     const short length,
1672     const short src
1673 )
1674 {
1675     TermLine lineCache[10];
1676     TermLine *holdLines = lineCache;
1677     TermLine *destPtr;
1678     TermLine *srcPtr;
1679     short distance;
1680     int i;
1681     int refLineCount;
1682     TermLine *refLines;
1683
1684     DebugF('B', 1, RememberTermBuffer(tb,&refLines,&refLineCount));
1685
1686     /* don't bother if there is we are being asked to do nothing...
1687      */
1688     if (length <= 0)
1689         return;
1690
1691 #ifdef  OLD_CODE
1692     /* before we modify the buffer, disown the selection... */
1693     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1694 #endif  /* OLD_CODE */
1695
1696     /* before we modify the buffer, check and see if the
1697      * destination line crosses the selection.  If it does, we need
1698      * to disown it...
1699      */
1700     /* destination... */
1701     if (dest > src) {
1702         if (((dest + length) < ROWS(tb)) &&
1703                 IS_IN_SELECTION(LINE_OF_TBUF(tb, dest + length),
1704                 MIN_SELECTION_COL, MAX_SELECTION_COL) &&
1705                 IS_IN_SELECTION(LINE_OF_TBUF(tb, dest + length + 1),
1706                 MIN_SELECTION_COL, MAX_SELECTION_COL)) {
1707             (void) _DtTermPrimSelectDisown(WIDGET(tb));
1708         }
1709     } else {
1710         if (dest > 1) {
1711             if (IS_IN_SELECTION(LINE_OF_TBUF(tb, dest),
1712                     MIN_SELECTION_COL, MAX_SELECTION_COL) &&
1713                     IS_IN_SELECTION(LINE_OF_TBUF(tb, dest - 1),
1714                     MIN_SELECTION_COL, MAX_SELECTION_COL)) {
1715                 (void) _DtTermPrimSelectDisown(WIDGET(tb));
1716             }
1717         }
1718     }
1719
1720     /* if we are being asked to move all (or more) the lines in the
1721      * buffer, then we can just clear them all out and return...
1722      */
1723     if (length >= ROWS(tb)) {
1724         for (i = 0; i < ROWS(tb); i++) {
1725             _DtTermPrimBufferClearLine(tb, i, 0);
1726         }
1727         return;
1728     }
1729
1730     /* if dest and src match, we can just clear them out and return...
1731      */
1732     if (src == dest) {
1733         for (i = 0; (i < length) && ((i + dest) < ROWS(tb)); i++) {
1734             _DtTermPrimBufferClearLine(tb, src + i, 0);
1735         }
1736         return;
1737     }
1738
1739     /* if we are moving more lines than will fit in the lineCache, we need
1740      * to malloc (and free) storage for the termLineRecs...
1741      */
1742     if (length > (sizeof(lineCache) / sizeof(lineCache[0]))) {
1743         holdLines = (TermLine *) XtMalloc(length * sizeof(TermLine));
1744     }
1745
1746     if (dest > src) {
1747         /* our src is above the destination.  Copy the lines to insert,
1748          * move the lines above the insertion pointer up, and insert
1749          * the saved lines...
1750          */
1751
1752         /* save away the top length lines... */
1753         destPtr = holdLines;
1754         srcPtr = &(LINE_OF_TBUF(tb, src));
1755         for (i = 0; i < length; i++) {
1756             /* these lines will be cleared, so we don't need to track
1757              * movement of them...
1758              */
1759             *destPtr++ = *srcPtr++;
1760         }
1761         /* copy the lines above the insertion point up... */
1762         destPtr = &(LINE_OF_TBUF(tb, src));
1763         srcPtr = &(LINE_OF_TBUF(tb, src + length));
1764         for (i = src; i < dest - length + 1; i++) {
1765             *destPtr++ = *srcPtr++;
1766         }
1767         /* restore the saved lines above the insertion point... */
1768         destPtr = &(LINE_OF_TBUF(tb, dest - length + 1));
1769         srcPtr = holdLines;
1770         for (i = 0; i < length; i++) {
1771             *destPtr = *srcPtr;
1772             /* clear the line... */
1773             _DtTermPrimBufferClearLine(tb, dest - length + 1 + i, 0);
1774             *destPtr++;
1775             *srcPtr++;
1776         }
1777
1778 #ifdef  DONT_DO_THIS_ANY_MORE
1779         /* Adjust or disown selection */
1780         _DtTermPrimSelectDeleteLine(tb,length) ;
1781 #endif  /* DONT_DO_THIS_ANY_MORE */
1782     } else {
1783         /* our src is below the destination.  Copy the lines to insert,
1784          * move the lines below the insertion pointer down, and insert
1785          * the saved lines...
1786          */
1787         /* save away the bottom length lines... */
1788         destPtr = holdLines;
1789         srcPtr = &(LINE_OF_TBUF(tb, src));
1790         for (i = 0; i < length; i++) {
1791             *destPtr++ = *srcPtr++;
1792         }
1793         /* copy the lines below the insertion point down... */
1794         destPtr = &(LINE_OF_TBUF(tb, src + length - 1));
1795         srcPtr = &(LINE_OF_TBUF(tb, src - 1));
1796         for (i = 0; i < src - dest; i++) {
1797             *destPtr-- = *srcPtr--;
1798         }
1799         /* restore the saved lines below the insertion point... */
1800         destPtr = &(LINE_OF_TBUF(tb, dest));
1801         srcPtr = holdLines;
1802         for (i = 0; i < length; i++) {
1803             *destPtr = *srcPtr;
1804             /* clear the line... */
1805             _DtTermPrimBufferClearLine(tb, dest + i, 0);
1806             *destPtr++;
1807             *srcPtr++;
1808         }
1809     }
1810
1811     /* free up the holdLines (if we malloc'ed it)... */
1812     if (holdLines != lineCache) {
1813         (void) XtFree((char *) holdLines);
1814     }
1815
1816     DebugF('B', 1, CheckTermBuffer(tb,refLines,refLineCount));
1817 }
1818
1819
1820 /**************************************************************************
1821  *  Function:
1822  *      _DtTermPrimBufferInsertLineFromTB():  insert one or more lines
1823  *              of text from the end of the buffer below the insertion
1824  *              point, or from the beginning of the buffer above the
1825  *              insertion point.
1826  *
1827  *  Parameters:
1828  *      TermBuffer tb: term buffer to insert end/begining into
1829  *      short dest: point to insert below/above
1830  *      short length: number of lines to insert
1831  *      InsertSource: source of insertion (end or beginning)
1832  *
1833  *  Returns:
1834  *      <nothing>
1835  *
1836  *  Notes:
1837  */
1838 void
1839 _DtTermPrimBufferInsertLineFromTB
1840 (
1841     const TermBuffer tb,
1842     const short dest,
1843     const short length,
1844     const InsertSource insertSource
1845 )
1846 {
1847     if (insertSource == insertFromTop) {
1848         (void) _DtTermPrimBufferInsertLine(tb, dest, length, 0);
1849     } else {
1850         (void) _DtTermPrimBufferInsertLine(tb, dest, length,
1851                 ROWS(tb) - length);
1852     }
1853 }
1854
1855 /**************************************************************************
1856  *  Function:
1857  *      _DtTermPrimBufferDeleteLine():  deletes one or more lines of text from
1858  *              the deletion poing and sticks them at the end of the
1859  *              line buffer.  It does not clear the line.  That is
1860  *              normally done when the line is inserted.
1861  *
1862  *  Parameters:
1863  *      TermBuffer tb: term buffer to insert end/begining into
1864  *      short source: point to insert below/above
1865  *      short length: number of lines to insert
1866  *      short lastUsedLine: number of last line in the buffer we need
1867  *              to keep
1868  *
1869  *  Returns:
1870  *      <nothing>
1871  *
1872  *  Notes:
1873  */
1874 void
1875 _DtTermPrimBufferDeleteLine
1876 (   
1877     const TermBuffer tb, 
1878     const short source,
1879     const short length,
1880     const short lastUsedRow
1881 )
1882 {
1883     TermLine lineCache[10];
1884     TermLine *holdLines = lineCache;
1885     TermLine *destPtr;
1886     TermLine *srcPtr;
1887     short copyLength;
1888     int i;
1889     int refLineCount;
1890     TermLine *refLines;
1891
1892     DebugF('B', 1, RememberTermBuffer(tb,&refLines,&refLineCount));
1893
1894     /* don't bother if there is we are being asked to do nothing...
1895      */
1896     if (length <= 0)
1897         return;
1898     /* if we are being asked to move the last usedLines, we don't have
1899      * to do anything either...
1900      */
1901     if (source + length >= lastUsedRow)
1902         return;
1903
1904 #ifdef  OLD_CODE
1905     /* before we modify the buffer, disown the selection... */
1906     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1907 #endif  /* OLD_CODE */
1908
1909     /* if we are moving more lines than will fit in the lineCache, we need
1910      * to malloc (and free) storage for the termLineRecs...
1911      */
1912     if (length > (sizeof(lineCache) / sizeof(lineCache[0]))) {
1913         holdLines = (TermLine *) XtMalloc(length * sizeof(TermLine));
1914     }
1915
1916     /* clear the deleted lines... */
1917     for (i = 0; i < length; i++) {
1918         (void) _DtTermPrimBufferClearLine(tb, source + i, 0);
1919     }
1920
1921 #ifdef  USE_MEMCPY
1922     (void) memcpy(holdLines, &(LINE_OF_TBUF(tb, source)), length * 
1923                   sizeof(TermLine));
1924 #else   /* USE_MEMCPY */
1925     destPtr = holdLines;
1926     srcPtr = &(LINE_OF_TBUF(tb, source));
1927     for (i = 0; i < length; i++) {
1928         *destPtr++ = *srcPtr++;
1929     }
1930 #endif  /* USE_MEMCPY */
1931
1932     /* ripple up the lines... */
1933     copyLength = MAX(0, MIN(ROWS(tb), lastUsedRow) - source - length);
1934     if (copyLength > 0) {
1935 #ifdef  USE_MEMCPY
1936         (void) memmove(&(LINE_OF_TBUF(tb, source)),
1937                       &(LINE_OF_TBUF(tb, source + length)),
1938                       copyLength * sizeof(TermLine));
1939 #else   /* USE_MEMCPY */
1940         destPtr = &(LINE_OF_TBUF(tb, source));
1941         srcPtr = &(LINE_OF_TBUF(tb, source + length));
1942         for (i = 0; i < copyLength; i++) {
1943             *destPtr++ = *srcPtr++;
1944         }
1945 #endif  /* USE_MEMCPY */
1946     }
1947
1948     /* copy back the deleted (cached) lines... */
1949 #ifdef  USE_MEMCPY
1950     (void) memcpy(&(LINE_OF_TBUF(tb, source + copyLength)), holdLines,
1951             length * sizeof(TermLine));
1952 #else   /* USE_MEMCPY */
1953     destPtr = &(LINE_OF_TBUF(tb, source + copyLength));
1954     srcPtr = holdLines;
1955     for (i = 0; i < length; i++) {
1956         *destPtr++ = *srcPtr++;
1957     }
1958 #endif  /* USE_MEMCPY */
1959
1960     /* free up the holdLines (if we malloc'ed it)... */
1961     if (holdLines != lineCache) {
1962         (void) XtFree((char *) holdLines);
1963     }
1964
1965     DebugF('B', 1, CheckTermBuffer(tb,refLines,refLineCount));
1966 }
1967
1968 /*
1969 ** Pad the requested row from the current width to 'newWidth' with spaces...
1970 */
1971 void
1972 _DtTermPrimBufferPadLine
1973 (
1974     const TermBuffer  tb,
1975     const short       row,
1976     const short       newWidth
1977 )
1978 {
1979     TermLine    line;
1980     
1981 #ifdef  OLD_CODE
1982     /* before we modify the buffer, disown the selection... */
1983     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
1984 #endif  /* OLD_CODE */
1985
1986     line = LINE_OF_TBUF(tb, row);
1987
1988     /* if this line is part of the selection, disown the selection... */
1989     if (IS_IN_SELECTION(line, MIN(newWidth, WIDTH(line)),
1990             MAX(newWidth, WIDTH(line)))) {
1991         (void) _DtTermPrimSelectDisown(WIDGET(tb));
1992     }
1993
1994     (void)memset(BUFFER(line) + WIDTH(line),
1995                  0x20, (newWidth - WIDTH(line)) * BYTES_PER_CHAR(tb));
1996     if (CLEAR_ENH(tb))
1997     {
1998         (*CLEAR_ENH(tb))(tb, row, WIDTH(line), newWidth - WIDTH(line));
1999     }
2000     _DtTermPrimBufferSetLineWidth(tb, row, newWidth);
2001 }
2002
2003 /*
2004 ** Clear the line to the new width (just reset the line width).
2005 ** NOTES:
2006 **     We also clear the wrapped flag as well.
2007 */
2008 Boolean
2009 _DtTermPrimBufferClearLine
2010 (
2011     const TermBuffer  tb,
2012     const short       row,
2013           short       newWidth
2014 )
2015 {
2016     TermLine    line;
2017
2018 #ifdef  OLD_CODE
2019     /* before we modify the buffer, disown the selection... */
2020     (void) _DtTermPrimSelectDisownIfNecessary(WIDGET(tb));
2021 #endif  /* OLD_CODE */
2022
2023     /*
2024     ** handle multi-byte locales...
2025     */
2026     if (BYTES_PER_CHAR(tb) > 1)
2027     {
2028         return(_DtTermPrimBufferClearLineWc(tb, row, newWidth));
2029     }
2030         
2031     /*
2032     ** Some simple bounds checking.
2033     */
2034     if (!VALID_ROW(tb, row))
2035     {
2036         return(False);
2037     }
2038
2039     line = LINE_OF_TBUF(tb, row);
2040
2041     /* if this line is part of the selection, disown the selection... */
2042     if (IS_IN_SELECTION(line, MIN(newWidth, WIDTH(line)),
2043             MAX(newWidth, WIDTH(line)))) {
2044         (void) _DtTermPrimSelectDisown(WIDGET(tb));
2045     }
2046
2047     /*
2048     ** Clip the new width to the buffer width.
2049     */
2050     newWidth = MIN(newWidth, COLS(tb));
2051
2052     /*
2053     ** force width to the desired value
2054     **
2055     ** (We take the direct approach because _DtTermPrimBufferSetLineWidth
2056     **  doesn't allow the line width to decrease.)
2057     */
2058     if (newWidth < WIDTH(line))
2059     {
2060         /* 
2061         ** Call the helper function if it exists
2062         */
2063         if (CLEAR_LINE(tb))
2064         {
2065             (*CLEAR_LINE(tb))(tb, row, newWidth);
2066         }
2067         WRAPPED(line) = False;
2068         WIDTH(line)   = newWidth;
2069         LENGTH(line)  = newWidth; /* this works in single-byte locales */
2070     }
2071     return(True);
2072 }
2073
2074 short
2075 _DtTermPrimBufferGetNextTab
2076 (
2077     const TermBuffer    tb,
2078           short         col
2079 )
2080 {
2081     if (!VALID_COL(tb, col))
2082     {
2083         return(0);
2084     }
2085
2086     /* find the next set tab stop... */
2087     for (col++; (col < COLS(tb)) && (!TABS(tb)[col]); col++)
2088         ;
2089
2090     /* did we go to the end of the line w/out hitting one?... */
2091     if (col >= COLS(tb)) {
2092         return(0);
2093     } else {
2094         return(col);
2095     }
2096 }
2097
2098
2099 short
2100 _DtTermPrimBufferGetPreviousTab
2101 (
2102     const TermBuffer    tb,
2103           short         col
2104 )
2105 {
2106     if (!VALID_COL(tb, col - 1))
2107     {
2108         return(0);
2109     }
2110
2111     /* find the next set tab stop... */
2112     for (col--; (col > 0) && (!TABS(tb)[col]); col--)
2113         ;
2114
2115     /* we can just return col.  If ther was not another tab stop, it will
2116      * be -1...
2117      */
2118     return(col);
2119 }
2120
2121
2122 Boolean
2123 _DtTermPrimBufferSetTab
2124 (
2125     const TermBuffer    tb,
2126     const short         col
2127 )
2128 {
2129     if (VALID_COL(tb, col))
2130     {
2131         TABS(tb)[col] = True;
2132         return(True);
2133     }
2134     return(False);
2135 }
2136
2137
2138 Boolean
2139 _DtTermPrimBufferClearTab
2140 (
2141     const TermBuffer    tb,
2142     const short         col
2143 )
2144 {
2145     if (VALID_COL(tb, col))
2146     {
2147         TABS(tb)[col] = False;
2148         return(True);
2149     }
2150     return(False);
2151 }
2152
2153
2154 Boolean
2155 _DtTermPrimBufferClearAllTabs
2156 (
2157     const TermBuffer      tb
2158 )
2159 {
2160     (void) memset(TABS(tb), '\0', COLS(tb) * sizeof(Boolean));
2161     return(True);
2162 }
2163
2164 /*
2165 ** Set the line wrap flag to the desired state.
2166 */
2167 void
2168 _DtTermPrimBufferSetLineWrapFlag
2169 (
2170     TermBuffer  tb,
2171     short       row,
2172     Boolean     state
2173 )
2174 {
2175     if (VALID_ROW(tb, row))
2176     {
2177         WRAPPED(LINE_OF_TBUF(tb, row)) = state;
2178     }
2179     return;
2180 }
2181
2182 /*
2183 ** Return the state of the line wrap flag.
2184 */
2185 Boolean
2186 _DtTermPrimBufferTestLineWrapFlag
2187 (
2188     TermBuffer  tb,
2189     short       row
2190 )
2191 {
2192     if (VALID_ROW(tb, row))
2193     {
2194         return(WRAPPED(LINE_OF_TBUF(tb, row)));
2195     }
2196     else 
2197     {
2198         return(FALSE);
2199     }
2200 }
2201
2202 /*
2203 ** Set the in selection flag to the desired state.
2204 */
2205 void
2206 _DtTermPrimBufferSetInSelectionFlag
2207 (
2208     TermBuffer  tb,
2209     short       row,
2210     TermLineSelection     state
2211 )
2212 {
2213     if (VALID_ROW(tb, row))
2214     {
2215         if (state) {
2216             START_SELECTION(LINE_OF_TBUF(tb, row)) = state->selectionStart;
2217             END_SELECTION(LINE_OF_TBUF(tb, row)) = state->selectionEnd;
2218         } else {
2219             START_SELECTION(LINE_OF_TBUF(tb, row)) = NON_SELECTION_COL;
2220             END_SELECTION(LINE_OF_TBUF(tb, row)) = NON_SELECTION_COL;
2221         }
2222     }
2223     return;
2224 }
2225
2226 /*
2227 ** Return the state of the in selection flag.
2228 */
2229 TermLineSelection
2230 _DtTermPrimBufferGetInSelectionFlag
2231 (
2232     TermBuffer  tb,
2233     short       row
2234 )
2235 {
2236     if (VALID_ROW(tb, row))
2237     {
2238         if ((START_SELECTION(LINE_OF_TBUF(tb, row)) != NON_SELECTION_COL) &&
2239                 (END_SELECTION(LINE_OF_TBUF(tb, row)) != NON_SELECTION_COL)) {
2240             return(&IN_SELECTION(LINE_OF_TBUF(tb, row)));
2241         }
2242     }
2243     return((TermLineSelection) 0);
2244 }
2245
2246 void
2247 _DtTermPrimBufferSetSelectLines
2248 (
2249     TermBuffer tb,
2250     short beginRow,
2251     short beginCol,
2252     short endRow,
2253     short endCol
2254 )
2255 {
2256     int                   i1;
2257
2258     for (i1 = 0; i1 < ROWS(tb); i1++) {
2259         if ((i1 >= beginRow) && (i1 <= endRow)) {
2260             if (i1 == beginRow) {
2261                 START_SELECTION(LINE_OF_TBUF(tb, i1)) = beginCol;
2262             } else {
2263                 START_SELECTION(LINE_OF_TBUF(tb, i1)) = MIN_SELECTION_COL;
2264             }
2265             if (i1 == endRow) {
2266                 END_SELECTION(LINE_OF_TBUF(tb, i1)) = endCol;
2267             } else {
2268                 END_SELECTION(LINE_OF_TBUF(tb, i1)) = MAX_SELECTION_COL;
2269             }
2270         } else {
2271             START_SELECTION(LINE_OF_TBUF(tb, i1)) = NON_SELECTION_COL;
2272             END_SELECTION(LINE_OF_TBUF(tb, i1)) = NON_SELECTION_COL;
2273         }
2274     }
2275 }
2276
2277 #if (defined(TEST) || defined(__CODECENTER__) || defined(DEBUG))
2278 static void
2279 _termBufferPrintLine
2280 (
2281     const TermBuffer tb,
2282     const short row
2283 )
2284 {
2285     TermLine    line;
2286     termChar   *pChar;
2287     short       j;
2288
2289     printf("Line: %d\n", row);
2290
2291     line = LINE_OF_TBUF(tb, row];
2292     printf("    width: %3d\n", WIDTH(line));
2293     printf("    length: %3d\n", LENGTH(line));
2294     if (LENGTH(line) > 0)
2295     {
2296         printf("    buffer: <");
2297         pChar = BUFFER(line);
2298         for (j = 0; j < LENGTH(line); j++)
2299         {
2300             printf("%X", *pChar++);
2301         }
2302         printf(">\n");
2303     }
2304 }
2305
2306 /*
2307 ** Print the contents of the TermBuffer.
2308 */
2309 static void
2310 _termBufferPrintBuffer
2311 (
2312     const TermBuffer tb
2313 )
2314 {
2315     short i;
2316     short j;
2317     short k;
2318
2319     if (tb == NULL) {
2320         printf("TermBuffer has been freed.\n");
2321         return;
2322     }
2323
2324     printf("TermBuffer dimensions:\n");
2325     printf("    rows: %d\n", ROWS(tb));
2326     printf("    cols: %d\n", COLS(tb));
2327
2328     for (i = 0; i < ROWS(tb); i++)
2329     {
2330         _termBufferPrintLine(tb, i);
2331     }
2332 }
2333 #endif /* (defined(TEST) || defined(__CODECENTER__)) */
2334
2335 #ifdef TEST
2336 /*
2337 ** Some simple tests of the termBuffer.
2338 */
2339 /* the following is to allow for a single main function in the code... */
2340 #define       termBufMain     main
2341 termBufMain()
2342 {
2343     const TermBuffer  myTB;
2344
2345     printf("Sizeof termEnhRec    : %d\n", sizeof(struct _termEnhRec));
2346     printf("Sizeof termBufferRec : %d\n", sizeof(struct _TermBufferRec));
2347
2348     myTB = _DtTermPrimBufferCreateBuffer(12, 80);
2349     _termBufferPrintBuffer(myTB);
2350
2351     printf("[0,0] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 0, enhVideo, BLINK));
2352     _termBufferPrintEnhancement(myTB, 0, 0);
2353     printf("[0,1] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 1, enhVideo, INVERSE));
2354     _termBufferPrintEnhancement(myTB, 0, 0);
2355     _termBufferPrintEnhancement(myTB, 0, 1);
2356     printf("[0,9] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 9, enhVideo, UNDERLINE));
2357     _termBufferPrintEnhancement(myTB, 0, 0);
2358     _termBufferPrintEnhancement(myTB, 0, 1);
2359     _termBufferPrintEnhancement(myTB, 0, 9);
2360     printf("[0,6] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 6, enhVideo, HALF_BRIGHT));
2361     _termBufferPrintEnhancement(myTB, 0, 0);
2362     _termBufferPrintEnhancement(myTB, 0, 1);
2363     _termBufferPrintEnhancement(myTB, 0, 6);
2364     _termBufferPrintEnhancement(myTB, 0, 9);
2365     _termBufferPrintBuffer(myTB);
2366
2367     _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
2368     _DtTermPrimBufferSetEnhancement(myTB, 10, 20, enhColor, 3);
2369     _termBufferPrintBuffer(myTB);
2370
2371     _DtTermPrimBufferResizeBuffer(&myTB,  6, 40);
2372     _termBufferPrintBuffer(myTB);
2373
2374     _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
2375     _DtTermPrimBufferResizeBuffer(&myTB, 12, 80);
2376     _termBufferPrintBuffer(myTB);
2377
2378     _DtTermPrimBufferFreeBuffer(myTB);
2379 }
2380 #endif /* TEST */
2381