Remove Unixware and openserver support
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimBufferWc.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 #ifndef lint
24 #ifdef  VERBOSE_REV_INFO
25 static char rcs_id[] = "$XConsortium: TermPrimBufferWc.c /main/1 1996/04/21 19:16:59 drk $";
26 #endif  /* VERBOSE_REV_INFO */
27 #endif  /* lint */
28
29 /*                                                                      *
30  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company               *
31  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
32  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.                *
33  * (c) Copyright 1993, 1994, 1996 Novell, Inc.                          *
34  * (c) Copyright 1996 Digital Equipment Corporation.                    *
35  * (c) Copyright 1996 FUJITSU LIMITED.                                  *
36  * (c) Copyright 1996 Hitachi.                                          *
37  */
38
39 #define USE_MEMCPY      /* use memcpy for line movement... */
40
41 #include <stdlib.h>
42 #include <wchar.h>
43 #include <Xm/Xm.h>
44 #include "TermHeader.h"       /* for MIN/MAX */
45 #include "TermPrim.h"
46 #include "TermPrimOSDepI.h"
47 #include "TermPrimBufferP.h"
48 #include "TermPrimDebug.h"
49
50 static void
51 _termBufferValidateLineWc
52 (
53     const TermBuffer tb,
54     const short row
55 );
56
57 static void
58 _patchUpChar
59 (
60     TermBuffer   tb,
61     short        row,
62     short       *col,
63     TermCharInfo charInfo
64 );
65
66 static void
67 _countWidth
68 (
69           wchar_t *wcBuffer,
70           short    length,
71           short    maxWidth,
72           short   *retCount,
73           short   *retWidth 
74 );
75
76 static void
77 _primBufferInsertWc
78 (
79     const TermBuffer  tb,
80     const short       row,
81           short      *col,
82           wchar_t    *newChars,
83           short       numChars,
84           short      *lengthInc,
85           short      *widthInc,
86           short      *widthInsert,  /* width of inserted characters           */
87           termChar   *returnChars,  /* pointer to overflow buffer             */
88           short      *returnCount   /* count of characters in overflow buffer */
89 );
90
91 static void
92 _primBufferOverwriteWc
93 (
94     const TermBuffer  tb,
95     const short       row,
96           short      *col,
97           wchar_t    *newChars,
98           short       numChars,
99           short      *lengthInc,
100           short      *widthInc,
101           short      *widthInsert,  /* width of inserted characters           */
102           termChar   *returnChars,  /* pointer to overflow buffer             */
103           short      *returnCount   /* count of characters in overflow buffer */
104 );
105
106 #ifdef USE_SUN_WCWIDTH_PATCH
107 #include <limits.h>
108 /* 
109 **  A small workaround for systems that don't have wcwidth...
110 */
111 int
112 sun_wcwidth
113 (
114     const wchar_t wc
115 )
116 {
117     int status;
118     char mbstr[MB_LEN_MAX + 1]; 
119
120     status = wctomb(mbstr, wc);
121
122     if (status > 0)
123        return(euccol(mbstr));
124     else
125        return(status);
126 }
127 #endif /*  USE_SUN_WCWIDTH_PATCH */
128
129 short
130 _DtTermPrimBufferGetTextWc
131 (
132     const TermBuffer      tb,
133     const short           row,
134     const short           col,
135     const short           length,
136           char           *buffer,
137     const Boolean         needWideChar
138 )
139 {
140     short   len;
141     
142     if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
143     {
144         return(0);
145     }
146
147     len = MIN(length, LENGTH(LINES(tb)[row]) - col);
148
149     if (length > 0)
150     {
151         memcpy(buffer, BUFFER(LINES(tb)[row]) + col, len);
152     }
153     return(len);
154 }
155
156 /*
157 ** this is a helper function, that replaces a 2 column character with
158 ** two spaces if "col" falls on column 2 of 2...
159 */
160 static void
161 _patchUpChar
162 (
163     TermBuffer   tb,
164     short        row,
165     short       *col,
166     TermCharInfo charInfo
167 )
168 {
169     TermLine line;
170     
171     line = LINE_OF_TBUF(tb, row);
172
173     /*
174     ** make sure we are not trying to overwrite the second column
175     ** of a two column character.
176     */
177     _DtTermPrimGetCharacterInfo(tb, row, *col, charInfo);
178     if ((charInfo->width == 2) &&
179         (charInfo->startCol == *col - 1))
180     {
181         /*
182         ** we are on the second column of a two column character,
183         ** replace the character with 2 spaces before we proceed
184         **
185         ** first replace the character with a space, then slide the
186         ** characters one to the right and bump the LENGTH
187         ** (the width remains constant)
188         */
189         *charInfo->u.pwc = L' ';
190         memmove(charInfo->u.pwc + 1, charInfo->u.pwc, 
191                 (LENGTH(line) - charInfo->idx) * sizeof(wchar_t));
192         LENGTH(line) += 1;
193
194         /*
195         ** remember the old starting column for later...
196         */
197         (*col)--;
198         
199         /*
200         ** now update the charInfo (since we replaced the 2 column
201         ** character with 2 single column spaces)...
202         */
203         charInfo->startCol++;
204         charInfo->u.pwc++;
205         charInfo->idx++;
206         charInfo->width    = 1;
207     }
208 }
209
210 /*
211 ** determine how many wide characters in the buffer we can have before the 
212 ** maxWidth is exceeded...
213 */
214 static void
215 _countWidth
216 (
217           wchar_t *wcBuffer, /* buffer of wide characters                     */
218           short    wcLen,    /* number of wide chars in buffer                */
219           short    maxWidth, /* maximum width desired                         */
220           short   *length,   /* max num of chars with total width <= maxWidth */
221           short   *width     /* actual width of the retCount wide chars       */
222 )
223 {
224           wchar_t *pwc;
225           short    charWidth;
226
227     *width = 0;
228     for (pwc = wcBuffer; pwc < wcBuffer + wcLen; pwc++)
229     {
230         switch (charWidth = wcwidth(*pwc))
231         {
232           case -1:
233             /* 
234             ** invalid character, (but this should have been handled earlier)
235             */
236             /* replace the character with a space... */
237             *pwc = L' ';
238             /* and set the width to 1... */
239             charWidth = 1;
240             break;
241           case 0:
242             /* 
243             ** its a null character, but is still has a width for our
244             ** purposes...
245             */
246             charWidth = 1;
247             break;
248           default:
249             break;
250         }    
251         if (*width + charWidth > maxWidth)
252         {
253             break;
254         }
255         else
256         {
257             *width += charWidth;
258         }
259     }
260     *length = pwc - wcBuffer;
261 }
262
263 /*
264 ** This is a helper function that inserts characters into the specified
265 ** buffer in insert mode.
266 */
267 static void
268 _primBufferInsertWc
269 (
270     const TermBuffer  tb,
271     const short       row,
272           short      *col,
273           wchar_t    *newChars,
274           short       numChars,
275           short      *lengthInc,
276           short      *widthInc,
277           short      *widthInsert,  /* width of inserted characters      */
278           termChar   *returnChars,  /* pointer to overflow buffer             */
279           short      *returnLength  /* count of characters in overflow buffer */
280 )
281 {
282     short             charWidth;
283     short             lengthInsert;
284     short             insertOverflow; /* # of newChars that would overflow */
285     short             overflowLength;
286     short             overflowWidth;
287     short             localCol;
288     TermLine          line;
289     wchar_t          *pwc = NULL;
290     TermCharInfoRec   charInfo;
291
292     /*
293     ** make a copy of *col because it may be modified by
294     ** _patchUpChar()
295     */
296     localCol = *col;
297     
298     /*
299     ** first decide how many characters we can insert before
300     ** running off the end of the line...
301     */
302     _countWidth(newChars, numChars, COLS(tb) - localCol, &lengthInsert,
303                 widthInsert);
304            
305     /*
306     ** return any extra characters...
307     */
308     *returnLength = numChars - lengthInsert;
309            
310     if (*returnLength > 0)
311     {
312         /*
313         ** we have some overflow...
314         */
315         memcpy(returnChars, newChars + lengthInsert,
316                *returnLength * sizeof(wchar_t));
317     }
318         
319     /*
320     ** make sure we are not trying to overwrite the second column
321     ** of a two column character.
322     */
323     _patchUpChar(tb, row, col, &charInfo);
324            
325     /*
326     ** Decide how many characters we can insert before running off the
327     ** end of the buffer...
328     */
329     line = LINE_OF_TBUF(tb, row);
330     if (WIDTH(line) + *widthInsert <= COLS(tb))
331     {
332         /*
333         ** there is no overflow, we can insert all "lengthInsert" characters...
334         */
335         *widthInc      = *widthInsert;
336         *lengthInc     = lengthInsert;
337         overflowLength = 0;
338     }
339     else
340     {
341         overflowWidth = WIDTH(line) + *widthInsert - COLS(tb);
342         
343         /* 
344         ** inserting the new characters will overflow the line buffer,
345         ** remove as many of the current characters on the line
346         ** as necessary to prevent buffer overflow
347         */
348         if (overflowWidth > 0)
349         {
350             *widthInc = *widthInsert - overflowWidth;
351             
352             for (pwc = ((wchar_t *)BUFFER(line)) + LENGTH(line) - 1; 
353                  pwc >= charInfo.u.pwc; pwc--)
354             {
355                 overflowWidth -= MAX(1, wcwidth(*pwc));
356
357                 if (overflowWidth <= 0)
358                 {
359                     /* 
360                     ** we've removed enough characters, pwc points to the
361                     ** first character to remove...
362                     */
363                     break;
364                 }
365             }
366
367             /*
368             ** final adjustment to widthInc (at this point overflowWidth
369             ** is either 0 (we removed exactly "overflowWidth" worth of
370             ** characters) or -1 (we removed "overflowWidth + 1" worth of
371             ** characters because we removed some 2 column characters)
372             */
373             *widthInc += overflowWidth;
374         }
375
376         overflowLength = ((wchar_t *)BUFFER(line)) + LENGTH(line) - pwc; 
377
378         if (overflowLength > 0)
379         {
380             *lengthInc = lengthInsert - overflowLength;
381             
382             /* 
383             ** copy the displaced characters from the line to the 
384             ** overflow buffer...
385             */
386             memcpy(returnChars + *returnLength, pwc,
387                    overflowLength * sizeof(wchar_t));
388             *returnLength += overflowLength;
389         }
390     }
391
392     /* 
393     ** Any overflow has been taken care of, now it's time to make
394     ** room for the new characters...
395     **
396     ** at this point:
397     **      charInfo.pchar points to the character at col
398     */
399     memmove(charInfo.u.pwc + lengthInsert, charInfo.u.pwc,
400             (((LENGTH(line) - charInfo.idx) - overflowLength) * 
401              sizeof(wchar_t)));
402
403     /*
404     ** copy the new characters into the buffer
405     */     
406     memcpy(charInfo.u.pwc, newChars, lengthInsert * sizeof(wchar_t));
407 }
408
409 /*
410 ** This is a helper function that inserts characters into the specified
411 ** buffer in overwrite mode.
412 */
413 static void
414 _primBufferOverwriteWc
415 (
416     const TermBuffer  tb,
417     const short       row,
418           short      *col,
419           wchar_t    *newChars,
420           short       numChars,
421           short      *lengthInc,
422           short      *widthInc,
423           short      *widthInsert,  /* width of inserted characters           */
424           termChar   *returnChars,  /* pointer to overflow buffer             */
425           short      *returnLength  /* count of characters in overflow buffer */
426 )
427 {
428            short        charWidth;
429            short        insertOverflow; /* # of newChars that would overflow */
430            short        lengthInsert;
431            short        localCol;
432            TermLine     line;
433     const  char        *pStart;
434     TermCharInfoRec     charInfo;
435     TermCharInfoRec     startCharInfo;
436            
437     line = LINE_OF_TBUF(tb, row);
438
439     /*
440     ** make a copy of *col because it may be modified by
441     ** _patchUpChar()
442     */
443     localCol = *col;
444     
445     /*
446     ** first decide how many characters we can overwrite before
447     ** running off the end of the line.
448     */
449     _countWidth(newChars, numChars, COLS(tb) - localCol, &lengthInsert,
450                 widthInsert);
451            
452     /* 
453     ** We are overwriting:
454     **      - determine the length and width increments
455     **      - put any extra new characters into the overflow buffer
456     */
457     if (localCol == WIDTH(line))
458     {
459         /*
460         ** we are appending to the end of the line, this is easy...
461         */
462         *widthInc  = *widthInsert;
463         *lengthInc = lengthInsert;
464         pStart    = (char *)BUFFER(line) +  (LENGTH(line) * sizeof(wchar_t));
465     }
466     else
467     {
468         /*
469         ** we are overwriting characters in the middle of the line...
470         **
471         ** make sure we are not trying to overwrite the second column
472         ** of a two column character.
473         */
474         _patchUpChar(tb, row, col, &startCharInfo);
475         
476         /*
477         ** see if we have to deal with the end of the line...
478         */
479         if (localCol + *widthInsert < WIDTH(line))
480         {
481             /*
482             ** the line width will remain constant, but the length may 
483             ** change...
484             */
485             *widthInc = 0;
486
487             /*
488             ** make sure we are not trying to overwrite the first column
489             ** of a two column character.
490             */
491             _DtTermPrimGetCharacterInfo(tb, row, localCol + *widthInsert,
492                                         &charInfo);
493             if ((charInfo.width == 2) &&
494                 (charInfo.startCol == localCol + *widthInsert - 1))
495             {
496                 /*
497                 ** We are about to overwrite column 1 of a 2 column
498                 ** character.  Replace it with a space before we proceed
499                 ** (we make adjustments later to make it look like we
500                 ** replaced the second column with a space).
501                 */
502                 *charInfo.u.pwc = L' ';
503             
504                 /*
505                 ** now update the charInfo (since we replaced the 2 column
506                 ** character with a single column space)...
507                 */
508                 charInfo.width = 1;
509             }
510
511             /*
512             ** at this point, startCharInfo points to the first
513             ** character to replace, now we want charInfo.u.pwc
514             ** to point to the character one past the last one
515             ** we want to replace
516             */
517             *lengthInc = lengthInsert - (charInfo.u.pwc - startCharInfo.u.pwc);
518             
519             if (*lengthInc != 0)
520             {
521                 memmove(charInfo.u.pwc + *lengthInc, charInfo.u.pwc,
522                         (LENGTH(line) - charInfo.idx) * sizeof(wchar_t));
523             }
524         }
525         else
526         {
527             /*
528             ** the line may get wider and longer...
529             */
530             *widthInc  = localCol + *widthInsert - WIDTH(line);
531             *lengthInc = startCharInfo.idx + lengthInsert - LENGTH(line);
532         }
533         pStart = startCharInfo.u.pc;
534     }
535
536     /*
537     ** now insert the new characters...
538     */
539     memcpy((void *)pStart, newChars, lengthInsert * sizeof(wchar_t));
540
541     /*
542     ** put any overflow into the overflow buffer
543     */
544     *returnLength = numChars - lengthInsert;
545     if (*returnLength > 0)
546     {
547         memcpy(returnChars, newChars + lengthInsert,
548                *returnLength * sizeof(wchar_t));
549     }
550 }
551
552 /*
553 ** Insert as many characters as possible at the specified row,col
554 ** return a count of the number of characters bumped off the end of the line
555 ** in 'returnLength' and a pointer to a buffer containing those characters
556 ** 'returnChars'.
557 ** 
558 ** The the new column width of the line is returned as the value of the
559 ** function.
560 **
561 ** NOTES:
562 **      We are trying to implement mechanism and not policy.  This
563 **      routine does a minimum of checking for boundary conditions.
564 */
565 short
566 _DtTermPrimBufferInsertWc
567 (
568     const TermBuffer  tb,
569     const short       row,
570     const short       col,
571           wchar_t    *newChars,
572           short       numChars,
573           Boolean     insertFlag,   /* if TRUE, insert, else overwrite        */
574           termChar  **returnChars,  /* pointer to overflow buffer             */
575           short      *returnLength  /* count of characters in overflow buffer */
576 )   
577 {
578            short    widthInc;       /* incremental change in line width  */
579            short    lengthInc;      /* incremental change in line length */
580            short    widthInsert;    /* column width of chars inserted    */
581            short    localCol;
582            TermLine line;
583     
584     if (!VALID_ROW(tb, row) || !VALID_COL(tb, col))
585     {
586         *returnLength = 0;
587         return(0);
588     }
589     
590     if (isDebugFSet('i', 1)) {
591 #ifdef  BBA
592 #pragma BBA_IGNORE
593 #endif  /*BBA*/
594         (void) _termBufferValidateLineWc(tb, row);
595     }
596
597     line     = LINE_OF_TBUF(tb, row);
598     localCol = col;
599           
600     if (WIDTH(line) < col)
601     {
602         /*
603         ** We're adding characters past the current end of line,
604         ** pad it out.
605         */
606         _DtTermPrimBufferPadLineWc(tb, row, col);
607     }
608
609     /*
610     ** It doesn't matter if we're overwriting, or inserting at the end 
611     ** of the line, the result is the same...
612     */
613     if (insertFlag == False || col == WIDTH(line))
614     {
615         _primBufferOverwriteWc(tb, row, &localCol, newChars, numChars,
616                                &lengthInc, &widthInc, &widthInsert,
617                                *returnChars, returnLength);
618     }
619     else
620     {
621         _primBufferInsertWc(tb, row, &localCol, newChars, numChars,
622                             &lengthInc, &widthInc, &widthInsert,
623                             *returnChars, returnLength);
624     }
625
626     /* 
627     ** Everything's ready:
628     **     - put the characters into the line
629     **     - adjust the line width (_DtTermPrimBufferSetLineWidth won't
630     **       let the line get shorter)...
631     **     - adjust the line length
632     **     - update the enhancements
633     */
634     WIDTH(line)  += widthInc;
635     LENGTH(line) += lengthInc;
636     
637     /*
638     ** fix up the enhancments...
639     */
640     if (INSERT_ENH(tb))
641     {
642         (*INSERT_ENH(tb))(tb, row, col, widthInsert, insertFlag);
643     }
644
645     if (isDebugFSet('i', 1)) {
646 #ifdef  BBA
647 #pragma BBA_IGNORE
648 #endif  /*BBA*/
649         (void) _termBufferValidateLineWc(tb, row);
650     }
651
652     return(WIDTH(line));
653 }
654
655 void
656 _DtTermPrimBufferDeleteWc
657 (
658     TermBuffer  tb,
659     short      *row,
660     short      *col,
661     short      *width,
662     termChar  **returnChars,  /* pointer to delete buffer        */
663     short      *returnCount  /* count of bytes in delete buffer */
664 )
665 {
666     int      copyCount;
667     TermLine line;
668     short    localRow;
669     short    localCol;
670     TermCharInfoRec startCharInfo;
671     TermCharInfoRec stopCharInfo;
672
673     if (!VALID_ROW(tb, *row) || !VALID_COL(tb, *col))
674     {
675         if (returnChars)
676         {
677             *returnChars = NULL;
678             *returnCount = 0;
679         }
680         return; 
681     }
682
683     /*
684     ** save some local copies, to originals may get modified
685     */
686     localRow = *row;
687     localCol = *col;
688     line     = LINE_OF_TBUF(tb, *row);
689     *width   = MAX(0, MIN(WIDTH(line) - localCol, *width));
690
691     /* 
692     ** there are 3 cases of deleting a character from a line:
693     **     Case 1:
694     **       the cursor is at least 2 positions past the end of the
695     **       line (col - WIDTH(line) > 0)
696     **
697     **     Case 2:
698     **       the cursor is in the middle of the line (copyCount > 0)
699     **          - move the remaining characters to the left
700     **          - deleteEnhancement
701     **          - adjust WIDTH and LENGTH
702     **
703     **     Case 3:
704     **       the cursor is at the end of the line (copyCount == 0 and
705     **       col == WIDTH(line))
706     **          - deleteEnhancement
707     **          - adjust WIDTH and LENGTH
708     */
709     if (localCol >= WIDTH(line) || *width == 0)
710     {
711         /* 
712         ** Handle Case 1...
713         */
714         if (returnChars)
715         {
716             *returnChars = NULL;
717             *returnCount = 0;
718         }
719         return;
720     }
721
722     /*
723     ** make any necessary adjustments to 2 column characters...
724     */
725     _patchUpChar(tb, *row, col, &startCharInfo);
726
727     (void) _DtTermPrimGetCharacterInfo(tb, localRow,
728                MIN(WIDTH(line), localCol + *width), &stopCharInfo);
729
730     if ((stopCharInfo.width == 2) && 
731         (stopCharInfo.startCol == localCol + *width - 1))
732     {
733         /*
734         ** do not try to delete column 1 of 2...
735         **
736         ** replace the 2 column character with two one column spaces...
737         */
738         *stopCharInfo.u.pwc = L' ';
739         memmove(stopCharInfo.u.pwc + 1, stopCharInfo.u.pwc,
740                 (LENGTH(line) - stopCharInfo.idx) * sizeof(wchar_t));
741
742         /*
743         ** now make stopCharInfo point at the new space
744         */
745         stopCharInfo.width = 1;
746         stopCharInfo.startCol++;
747         stopCharInfo.u.pwc++;
748         stopCharInfo.idx++;
749         LENGTH(line)++;
750     }
751     
752     /* 
753     ** Save the current characters before we overwrite them
754     ** (if returnChars is non-NULL 0)
755     */
756     if (returnChars != NULL)
757     {
758         *returnCount = (stopCharInfo.idx - startCharInfo.idx) * sizeof(wchar_t);
759         *returnChars = (termChar *)XtMalloc(*returnCount);
760         memmove(*returnChars, startCharInfo.u.pwc, *returnCount);
761     }
762     
763     /* 
764     ** Cases 2, 3, and 4 require that we delete the enhancement...
765     */
766     if (DELETE_ENH(tb))
767     {
768         (*DELETE_ENH(tb))(tb, localRow, localCol, *width);
769     }
770
771     copyCount = MAX(0, LENGTH(line) - stopCharInfo.idx);
772     if (copyCount > 0)
773     {
774         /* 
775         ** handle case 2
776         */
777         memmove(startCharInfo.u.pwc, stopCharInfo.u.pwc,
778                 copyCount * sizeof(wchar_t));
779     }
780
781     /* 
782     ** Cases 2 and 3 require that we decrement the line length...
783     */
784     WIDTH(line)  -= *width;
785     LENGTH(line) -= (stopCharInfo.idx - startCharInfo.idx);
786
787     /*
788     ** update info we need to return...
789     */
790     *width = stopCharInfo.startCol - startCharInfo.startCol;
791 }
792
793 /*
794 ** Pad the requested row from the current width to 'newWidth' with spaces...
795 */
796 void
797 _DtTermPrimBufferPadLineWc
798 (
799     const TermBuffer  tb,
800     const short       row,
801     const short       width
802 )
803 {
804     short       i;
805     short       widthInc;
806     TermLine    line;
807     wchar_t    *pwc;
808     
809     line = LINE_OF_TBUF(tb, row);
810
811     if (isDebugFSet('i', 1)) {
812 #ifdef  BBA
813 #pragma BBA_IGNORE
814 #endif  /*BBA*/
815         (void) _termBufferValidateLineWc(tb, row);
816     }
817
818     /*
819     ** if this line is part of the selection, disown the selection...
820     */
821     if (IS_IN_SELECTION(line, MIN(width, WIDTH(line)),
822                         MAX(width, WIDTH(line))))
823     {
824         (void) _DtTermPrimSelectDisown(WIDGET(tb));
825     }
826
827     widthInc = MIN(COLS(tb), width) - WIDTH(line);
828
829     for (i = 0, pwc = (wchar_t *)BUFFER(line) + MAX(0, LENGTH(line));
830          i < widthInc;
831          i++, pwc++)
832     {
833         *pwc = L' ';
834         LENGTH(line)++;
835     }
836     if (CLEAR_ENH(tb))
837     {
838         (*CLEAR_ENH(tb))(tb, row, WIDTH(line), widthInc);
839     }
840     _DtTermPrimBufferSetLineWidth(tb, row, WIDTH(line) + widthInc);
841     if (isDebugFSet('i', 1)) {
842 #ifdef  BBA
843 #pragma BBA_IGNORE
844 #endif  /*BBA*/
845         _termBufferValidateLineWc(tb, row);
846     }
847 }
848
849 /*
850 ** Clear the line to the new width (just reset the line width).
851 */
852 Boolean
853 _DtTermPrimBufferClearLineWc
854 (
855     const TermBuffer  tb,
856     const short       row,
857           short       newWidth
858 )
859 {
860     TermLine        line;
861     TermCharInfoRec charInfo;
862     short           newLength;
863
864     /*
865     ** Some simple bounds checking.
866     */
867     if (!VALID_ROW(tb, row))
868     {
869         return(False);
870     }
871
872     /*
873     ** force the width to the desired value
874     **
875     ** (We take the direct approach because _DtTermPrimBufferSetLineWidth
876     **  doesn't allow the line width to decrease.)
877     */
878     line = LINE_OF_TBUF(tb, row);
879
880     /*
881     ** if this line is part of the selection, disown the selection...
882     */
883     if (IS_IN_SELECTION(line, MIN(newWidth, WIDTH(line)),
884                         MAX(newWidth, WIDTH(line))))
885     {
886         (void) _DtTermPrimSelectDisown(WIDGET(tb));
887     }
888
889     /*
890     ** Clip the new width to the buffer width.
891     */
892     newWidth = MIN(newWidth, COLS(tb));
893
894     if (newWidth < WIDTH(line))
895     {
896         if (newWidth == 0)
897         {
898             newLength = 0;
899         }
900         else
901         {
902             /*
903             ** handle the case of clearing the second column of a two column
904             ** character...
905             */
906             _DtTermPrimGetCharacterInfo(tb, row, MAX(0, newWidth - 1),
907                                         &charInfo);
908             
909             if ((charInfo.width == 2 ) && 
910                 (charInfo.startCol == MAX(0, newWidth - 1)))
911             {
912                 /*
913                 ** we are clearing column 2 of 2, replace column 1 of 1 with
914                 ** a space...
915                 */
916                 *charInfo.u.pwc = L' ';
917             }
918             newLength = charInfo.idx + 1;
919         }
920         /* 
921         ** Call the helper function if it exists
922         */
923         if (CLEAR_LINE(tb))
924         {
925             (*CLEAR_LINE(tb))(tb, row, newWidth);
926         }
927         WRAPPED(line) = False;
928         WIDTH(line)   = newWidth;
929         LENGTH(line)  = newLength;
930     }
931     return(True);
932 }
933
934 /*
935 ** replace all characters from startCol upto stopCol with spaces,
936 **    
937 ** NOTE:
938 **    we are dealing with double width characters, the width needs
939 **    to remain constant, but the length may have to change since we
940 **    may replace a double width character with a single width
941 **    character.
942 */
943 void
944 _DtTermPrimBufferEraseWc
945 (
946     TermBuffer tb,
947     short      row,
948     short      startCol,
949     short      stopCol
950 )
951 {
952     TermCharInfoRec       startCharInfo;
953     TermCharInfoRec       stopCharInfo;
954     short                 localCol;
955     TermLine              line;
956     wchar_t              *pwchar;
957     short                 lengthErase;
958     short                 lengthInc;
959     
960     /*
961     ** make sure we are not trying to erase the second column
962     ** of a two column character.
963     */
964     localCol = startCol;
965     _patchUpChar(tb, row, &localCol, &startCharInfo);
966     
967     /*
968     ** make sure we are not trying to erase the first column
969     ** of a two column character.
970     */
971     _DtTermPrimGetCharacterInfo(tb, row, stopCol, &stopCharInfo);
972
973     if ((stopCharInfo.width == 2) &&
974         (stopCharInfo.startCol == stopCol))
975     {
976 #ifdef  NOTDEF
977         /*
978         ** We are about to overwrite column 1 of a 2 column
979         ** character.  Replace it with a space before we proceed
980         ** (we make adjustments later to make it look like we
981         ** replaced the second column with a space).
982         */
983         *stopCharInfo.u.pwc = L' ';
984         
985         /*
986         ** now update the charInfo (since we replaced the 2 column
987         ** character with a single column space)...
988         */
989         stopCharInfo.width = 1;
990 #endif  /* NOTDEF */
991         (void) stopCol++;
992     }
993
994     /*
995     ** at this point, startCharInfo points to the first character
996     ** to erase, and stopCharInfo points to the last character
997     ** we want to erase, make sure there is enough space between
998     ** the two to accommodate the replacement spaces...
999     */
1000     lengthErase = stopCol - startCol + 1;
1001     lengthInc   = lengthErase - (stopCharInfo.u.pwc - startCharInfo.u.pwc + 1);
1002
1003     if (lengthInc != 0)
1004     {
1005         /*
1006         ** the length will have to change, make the necessary adjustments
1007         */
1008         memmove(stopCharInfo.u.pwc + lengthInc, stopCharInfo.u.pwc,
1009                 (LENGTH(LINE_OF_TBUF(tb, row)) - stopCharInfo.idx) *
1010                 sizeof(wchar_t));
1011         LENGTH(LINE_OF_TBUF(tb, row)) += lengthInc;
1012     }
1013     
1014     /*
1015     ** replace the characters with spaces...
1016     */
1017     for (pwchar = startCharInfo.u.pwc;
1018          pwchar < startCharInfo.u.pwc + lengthErase;
1019          pwchar++)
1020     {
1021         *pwchar = L' ';
1022     }
1023 }
1024
1025 #ifdef  BBA
1026 #pragma BBA_IGNORE
1027 #endif  /*BBA*/
1028 static void
1029 _termBufferValidateLineWc
1030 (
1031     const TermBuffer tb,
1032     const short row
1033 )
1034 {
1035     wchar_t    *pwc;
1036     TermLine    line;
1037     
1038     line = LINE_OF_TBUF(tb, row);
1039     for (pwc = (wchar_t *)BUFFER(line);
1040          pwc < (wchar_t *)BUFFER(line) + LENGTH(line);
1041          pwc++)
1042     {
1043         if (wcwidth(*pwc) == -1)
1044         {
1045             fprintf(stderr, "_termBufferValidateLineWc: invalid wide char\n");
1046             /* replace the character with a space... */
1047             *pwc = L' ';
1048             break;
1049         }
1050     }
1051 }
1052
1053 #if (defined(TEST) || defined(__CODECENTER__) || defined(DEBUG))
1054 static void
1055 _termBufferPrintLine
1056 (
1057     const TermBuffer tb,
1058     const short row
1059 )
1060 {
1061     TermLine    line;
1062     wchar_t   *pChar;
1063     short       j;
1064
1065     printf("Line: %d\n", row);
1066
1067     line = LINE_OF_TBUF(tb, row);
1068     printf("    length: %3d\n", WIDTH(line));
1069     if (WIDTH(line) > 0)
1070     {
1071         printf("    buffer: <");
1072         pChar = BUFFER(line);
1073         for (j = 0; j < WIDTH(line); j++)
1074         {
1075             printf("%X", *pChar++);
1076         }
1077         printf(">\n");
1078     }
1079 }
1080
1081 /*
1082 ** Print the contents of the TermBuffer.
1083 */
1084 static void
1085 _termBufferPrintBuffer
1086 (
1087     const TermBuffer tb
1088 )
1089 {
1090     short i;
1091     short j;
1092     short k;
1093
1094     if (tb == NULL) {
1095         printf("TermBuffer has been freed.\n");
1096         return;
1097     }
1098
1099     printf("TermBuffer dimensions:\n");
1100     printf("    rows: %d\n", ROWS(tb));
1101     printf("    cols: %d\n", COLS(tb));
1102
1103     for (i = 0; i < ROWS(tb); i++)
1104     {
1105         _termBufferPrintLine(tb, i);
1106     }
1107 }
1108 #endif /* (defined(TEST) || defined(__CODECENTER__)) */
1109
1110 #ifdef TEST
1111 /*
1112 ** Some simple tests of the termBuffer.
1113 */
1114 /* the following is to allow for a single main function in the code... */
1115 #define       termBufMain     main
1116 termBufMain()
1117 {
1118     const TermBuffer  myTB;
1119
1120     printf("Sizeof termEnhRec    : %d\n", sizeof(struct _termEnhRec));
1121     printf("Sizeof termBufferRec : %d\n", sizeof(struct _TermBufferRec));
1122
1123     myTB = _DtTermPrimBufferCreateBuffer(12, 80);
1124     _termBufferPrintBuffer(myTB);
1125
1126     printf("[0,0] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 0, enhVideo, BLINK));
1127     _termBufferPrintEnhancement(myTB, 0, 0);
1128     printf("[0,1] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 1, enhVideo, INVERSE));
1129     _termBufferPrintEnhancement(myTB, 0, 0);
1130     _termBufferPrintEnhancement(myTB, 0, 1);
1131     printf("[0,9] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 9, enhVideo, UNDERLINE));
1132     _termBufferPrintEnhancement(myTB, 0, 0);
1133     _termBufferPrintEnhancement(myTB, 0, 1);
1134     _termBufferPrintEnhancement(myTB, 0, 9);
1135     printf("[0,6] %d\n", _DtTermPrimBufferSetEnhancement(myTB, 0, 6, enhVideo, HALF_BRIGHT));
1136     _termBufferPrintEnhancement(myTB, 0, 0);
1137     _termBufferPrintEnhancement(myTB, 0, 1);
1138     _termBufferPrintEnhancement(myTB, 0, 6);
1139     _termBufferPrintEnhancement(myTB, 0, 9);
1140     _termBufferPrintBuffer(myTB);
1141
1142     _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
1143     _DtTermPrimBufferSetEnhancement(myTB, 10, 20, enhColor, 3);
1144     _termBufferPrintBuffer(myTB);
1145
1146     _DtTermPrimBufferResizeBuffer(&myTB,  6, 40);
1147     _termBufferPrintBuffer(myTB);
1148
1149     _DtTermPrimBufferSetEnhancement(myTB, 10, 10, enhVideo, BLINK);
1150     _DtTermPrimBufferResizeBuffer(&myTB, 12, 80);
1151     _termBufferPrintBuffer(myTB);
1152
1153     _DtTermPrimBufferFreeBuffer(myTB);
1154 }
1155 #endif /* TEST */
1156