1 /*        $NetBSD: addbytes.c,v 1.70 2024/12/23 02:58:03 blymn Exp $  */
2 
3 /*
4  * Copyright (c) 1987, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)addbytes.c  8.4 (Berkeley) 5/4/94";
36 #else
37 __RCSID("$NetBSD: addbytes.c,v 1.70 2024/12/23 02:58:03 blymn Exp $");
38 #endif
39 #endif                                  /* not lint */
40 
41 #include <stdlib.h>
42 #include <string.h>
43 #include "curses.h"
44 #include "curses_private.h"
45 #ifdef DEBUG
46 #include <assert.h>
47 #endif
48 
49 #ifndef _CURSES_USE_MACROS
50 
51 /*
52  * addbytes --
53  *      Add the characters to the current position in stdscr.
54  */
55 int
addbytes(const char * bytes,int count)56 addbytes(const char *bytes, int count)
57 {
58 
59           return _cursesi_waddbytes(stdscr, bytes, count, 0, 1);
60 }
61 
62 /*
63  * waddbytes --
64  *      Add the characters to the current position in the given window.
65  */
66 int
waddbytes(WINDOW * win,const char * bytes,int count)67 waddbytes(WINDOW *win, const char *bytes, int count)
68 {
69 
70           return _cursesi_waddbytes(win, bytes, count, 0, 1);
71 }
72 
73 /*
74  * mvaddbytes --
75  *      Add the characters to stdscr at the location given.
76  */
77 int
mvaddbytes(int y,int x,const char * bytes,int count)78 mvaddbytes(int y, int x, const char *bytes, int count)
79 {
80 
81           return mvwaddbytes(stdscr, y, x, bytes, count);
82 }
83 
84 /*
85  * mvwaddbytes --
86  *      Add the characters to the given window at the location given.
87  */
88 int
mvwaddbytes(WINDOW * win,int y,int x,const char * bytes,int count)89 mvwaddbytes(WINDOW *win, int y, int x, const char *bytes, int count)
90 {
91 
92           if (wmove(win, y, x) == ERR)
93                     return ERR;
94 
95           return _cursesi_waddbytes(win, bytes, count, 0, 1);
96 }
97 
98 #endif
99 
100 int
__waddbytes(WINDOW * win,const char * bytes,int count,attr_t attr)101 __waddbytes(WINDOW *win, const char *bytes, int count, attr_t attr)
102 {
103 
104           return _cursesi_waddbytes(win, bytes, count, attr, 1);
105 }
106 
107 /*
108  * _cursesi_waddbytes --
109  *        Add the characters to the current position in the given window.
110  * if char_interp is non-zero then character interpretation is done on
111  * the byte (i.e. \n to newline, \r to carriage return, \b to backspace
112  * and so on).
113  */
114 int
_cursesi_waddbytes(WINDOW * win,const char * bytes,int count,attr_t attr,int char_interp)115 _cursesi_waddbytes(WINDOW *win, const char *bytes, int count, attr_t attr,
116               int char_interp)
117 {
118           int                 *py = &win->cury, *px = &win->curx, err;
119           __LINE              *lp;
120 #ifdef HAVE_WCHAR
121           int                 n;
122           cchar_t             cc;
123           wchar_t             wc;
124           mbstate_t st;
125 #else
126           int                 c;
127 #endif
128 #ifdef DEBUG
129           int             i;
130 
131           if (__predict_false(win == NULL))
132                     return ERR;
133 
134           for (i = 0; i < win->maxy; i++) {
135                     assert(win->alines[i]->sentinel == SENTINEL_VALUE);
136           }
137 
138           __CTRACE(__CTRACE_INPUT, "ADDBYTES: add %d bytes\n", count);
139 #endif
140 
141           err = OK;
142           lp = win->alines[*py];
143 
144 #ifdef HAVE_WCHAR
145           (void)memset(&st, 0, sizeof(st));
146 #endif
147           while (count > 0) {
148 #ifndef HAVE_WCHAR
149                     c = *bytes++;
150                     __CTRACE(__CTRACE_INPUT, "ADDBYTES('%c', %x) at (%d, %d)\n",
151                         c, attr, *py, *px);
152                     err = _cursesi_addbyte(win, &lp, py, px, c, attr, char_interp);
153                     count--;
154 #else
155                     /*
156                      * For wide-character support only, try to convert the
157                      * given string into a wide character - we do this because
158                      * this is how ncurses behaves (not that I think this is
159                      * actually the correct thing to do but if we don't do it
160                      * a lot of things that rely on this behaviour will break
161                      * and we will be blamed).  If the conversion succeeds
162                      * then we eat the n characters used to make the wide char
163                      * from the string.
164                      */
165                     n = (int)mbrtowc(&wc, bytes, (size_t)count, &st);
166                     if (n < 0) {
167                               /* not a valid conversion just eat a char */
168                               wc = *bytes;
169                               n = 1;
170                               (void)memset(&st, 0, sizeof(st));
171                     } else if (wc == 0) {
172                               break;
173                     }
174 
175                     __CTRACE(__CTRACE_INPUT,
176                         "ADDBYTES WIDE(0x%04x [%s], %x) at (%d, %d), ate %d bytes\n",
177                         (unsigned)wc, unctrl((unsigned)wc), attr, *py, *px, n);
178                     cc.vals[0] = wc;
179                     cc.elements = 1;
180                     cc.attributes = attr;
181                     err = _cursesi_addwchar(win, &lp, py, px, &cc, char_interp);
182                     bytes += n;
183                     count -= n;
184 #endif
185           }
186 
187 #ifdef DEBUG
188           for (i = 0; i < win->maxy; i++) {
189                     assert(win->alines[i]->sentinel == SENTINEL_VALUE);
190           }
191 #endif
192 
193           return (err);
194 }
195 
196 /*
197  * _cursesi_addbyte -
198  *        Internal function to add a byte and update the row and column
199  * positions as appropriate.  If char_interp is non-zero then
200  * character interpretation is done on the byte.  This function is
201  * only used in the narrow character version of curses.
202  */
203 int
_cursesi_addbyte(WINDOW * win,__LINE ** lp,int * y,int * x,int c,attr_t attr,int char_interp)204 _cursesi_addbyte(WINDOW *win, __LINE **lp, int *y, int *x, int c,
205                      attr_t attr, int char_interp)
206 {
207           static char          blank[] = " ";
208           int                  tabsize;
209           int                  newx, i, wcols;
210           attr_t               attributes;
211 
212           if (__predict_false(win == NULL))
213                     return ERR;
214 
215           if (char_interp) {
216                     switch (c) {
217                     case '\t':
218                               tabsize = win->screen->TABSIZE;
219                               newx = tabsize - (*x % tabsize);
220                               /* if at the bottom of the window and
221                                  not allowed to scroll then just do
222                                  what we can */
223                               if ((*y == win->scr_b) &&
224                                   !(win->flags & __SCROLLOK)) {
225                                         if ((*lp)->flags & __ISPASTEOL) {
226                                                   return OK;
227                                         }
228 
229                                         if (*x + newx > win->maxx - 1)
230                                                   newx = win->maxx - *x - 1;
231                               }
232 
233                               for (i = 0; i < newx; i++) {
234                                         if (waddbytes(win, blank, 1) == ERR)
235                                                   return ERR;
236                               }
237                               return OK;
238 
239                     case '\n':
240                               wclrtoeol(win);
241                               (*lp)->flags |= __ISPASTEOL;
242                               break;
243 
244                     case '\r':
245                               *x = 0;
246                               return OK;
247 
248                     case '\b':
249                               if (--(*x) < 0)
250                                         *x = 0;
251                               return OK;
252                     }
253           }
254 
255           __CTRACE(__CTRACE_INPUT, "ADDBYTES(%p, %d, %d)\n", win, *y, *x);
256 
257           if (char_interp && ((*lp)->flags & __ISPASTEOL)) {
258                     *x = 0;
259                     (*lp)->flags &= ~__ISPASTEOL;
260                     if (*y == win->scr_b) {
261                               __CTRACE(__CTRACE_INPUT,
262                                   "ADDBYTES - on bottom of scrolling region\n");
263                               if (!(win->flags & __SCROLLOK))
264                                         return ERR;
265                               scroll(win);
266                     } else {
267                               (*y)++;
268                     }
269                     *lp = win->alines[*y];
270                     if (c == '\n')
271                               return OK;
272           }
273 
274           __CTRACE(__CTRACE_INPUT,
275               "ADDBYTES: 1: y = %d, x = %d, firstch = %d, lastch = %d\n",
276               *y, *x, *win->alines[*y]->firstchp, *win->alines[*y]->lastchp);
277 
278           attributes = (win->wattr | attr) & (__ATTRIBUTES & ~__COLOR);
279           if (attr & __COLOR)
280                     attributes |= attr & __COLOR;
281           else if (win->wattr & __COLOR)
282                     attributes |= win->wattr & __COLOR;
283 
284 
285           wcols = wcwidth(c);
286           if (wcols < 0)
287                     wcols = 1;
288 
289           /*
290            * Always update the change pointers.  Otherwise,
291            * we could end up not displaying 'blank' characters
292            * when overlapping windows are displayed.
293            */
294           newx = *x + win->ch_off;
295           (*lp)->flags |= __ISDIRTY;
296           /*
297            * firstchp/lastchp are shared between
298            * parent window and sub-window.
299            */
300           if (newx < *(*lp)->firstchp)
301                     *(*lp)->firstchp = newx;
302 
303           if (newx > *(*lp)->lastchp)
304                     *(*lp)->lastchp = newx;
305           __CTRACE(__CTRACE_INPUT, "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n",
306               *(*lp)->firstchp, *(*lp)->lastchp,
307               *(*lp)->firstchp - win->ch_off,
308               *(*lp)->lastchp - win->ch_off);
309           if (win->bch != ' ' && c == ' ') {
310                     (*lp)->line[*x].ch = win->bch;
311 #ifdef HAVE_CHAR
312                     (*lp)->line[*x].wcols = win->wcols;
313 #endif
314           } else {
315                     (*lp)->line[*x].ch = c;
316 #ifdef HAVE_CHAR
317                     (*lp)->line[*x].wcols = wcols;
318 #endif
319           }
320 
321           (*lp)->line[*x].cflags &= ~ (CA_BACKGROUND | CA_CONTINUATION);
322 
323           if (attributes & __COLOR)
324                     (*lp)->line[*x].attr =
325                               attributes | (win->battr & ~__COLOR);
326           else
327                     (*lp)->line[*x].attr = attributes | win->battr;
328 
329           if (*x == win->maxx - 1)
330                     (*lp)->flags |= __ISPASTEOL;
331           else
332                     (*x)++;
333 
334           __CTRACE(__CTRACE_INPUT,
335               "ADDBYTES: 2: y = %d, x = %d, firstch = %d, lastch = %d\n",
336               *y, *x, *win->alines[*y]->firstchp, *win->alines[*y]->lastchp);
337           __sync(win);
338           return OK;
339 }
340 
341 /*
342  * _cursesi_addwchar -
343  *        Internal function to add a wide character and update the row
344  * and column positions.
345  */
346 int
_cursesi_addwchar(WINDOW * win,__LINE ** lnp,int * y,int * x,const cchar_t * wch,int char_interp)347 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x,
348                       const cchar_t *wch, int char_interp)
349 {
350 #ifndef HAVE_WCHAR
351           return ERR;
352 #else
353           int sx = 0, ex = 0, cw = 0, i = 0, newx = 0, tabsize;
354           __LDATA *lp = &win->alines[*y]->line[*x], *tp = NULL;
355           nschar_t *np = NULL;
356           cchar_t cc;
357           attr_t attributes;
358 
359           if (__predict_false(win == NULL))
360                     return ERR;
361 
362           if (char_interp) {
363                     /* special characters handling */
364                     switch (wch->vals[0]) {
365                     case L'\b':
366                               if (--*x < 0)
367                                         *x = 0;
368                               return OK;
369                     case L'\r':
370                               *x = 0;
371                               return OK;
372                     case L'\n':
373                               if (*y == win->scr_b) {
374                                         if (!(win->flags & __SCROLLOK))
375                                                   return ERR;
376                                         wclrtoeol(win);
377                                         scroll(win);
378                               } else {
379                                         wclrtoeol(win);
380                                         (*y)++;
381                               }
382                               *x = 0;
383                               (*lnp)->flags &= ~__ISPASTEOL;
384                               return OK;
385                     case L'\t':
386                               cc.vals[0] = L' ';
387                               cc.elements = 1;
388                               cc.attributes = win->wattr;
389                               tabsize = win->screen->TABSIZE;
390                               newx = tabsize - (*x % tabsize);
391 
392                               /* if at the bottom of the window and
393                                  not allowed to scroll then just do
394                                  what we can */
395                               if ((*y == win->scr_b) &&
396                                   !(win->flags & __SCROLLOK)) {
397                                         if ((*lnp)->flags & __ISPASTEOL) {
398                                                   return ERR;
399                                         }
400 
401                                         if (*x + newx > win->maxx - 1)
402                                                   newx = win->maxx - *x - 1;
403                               }
404 
405                               for (i = 0; i < newx; i++) {
406                                         if (wadd_wch(win, &cc) == ERR)
407                                                   return ERR;
408                               }
409                               return OK;
410                     }
411           }
412 
413           /* check for non-spacing character */
414           if (!wcwidth(wch->vals[0])) {
415                     __CTRACE(__CTRACE_INPUT,
416                         "_cursesi_addwchar: char '%c' is non-spacing\n",
417                         wch->vals[0]);
418                     cw = (*lp).wcols;
419                     if (cw < 0) {
420                               lp += cw;
421                               *x += cw;
422                     }
423                     for (i = 0; i < wch->elements; i++) {
424                               if (!(np = (nschar_t *) malloc(sizeof(nschar_t))))
425                                         return ERR;
426                               np->ch = wch->vals[i];
427                               np->next = lp->nsp;
428                               lp->nsp = np;
429                     }
430                     (*lnp)->flags |= __ISDIRTY;
431                     newx = *x + win->ch_off;
432                     if (newx < *(*lnp)->firstchp)
433                               *(*lnp)->firstchp = newx;
434 
435                     if (newx > *(*lnp)->lastchp)
436                               *(*lnp)->lastchp = newx;
437                     __touchline(win, *y, *x, *x);
438                     return OK;
439           }
440           /* check for new line first */
441           if (char_interp && ((*lnp)->flags & __ISPASTEOL)) {
442                     *x = 0;
443                     (*lnp)->flags &= ~__ISPASTEOL;
444                     if (*y == win->scr_b) {
445                               if (!(win->flags & __SCROLLOK))
446                                         return ERR;
447                               scroll(win);
448                     } else {
449                               (*y)++;
450                     }
451                     (*lnp) = win->alines[*y];
452                     lp = &win->alines[*y]->line[*x];
453           }
454           /* clear out the current character */
455           cw = (*lp).wcols;
456           if (cw >= 0) {
457                     sx = *x;
458           } else {
459                     for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) {
460                               __CTRACE(__CTRACE_INPUT,
461                                   "_cursesi_addwchar: clear current char (%d,%d)\n",
462                                   *y, sx);
463                               tp = &win->alines[*y]->line[sx];
464                               tp->ch = win->bch;
465                               tp->cflags = CA_BACKGROUND;
466                               if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
467                                         return ERR;
468 
469                               tp->attr = win->battr;
470                               tp->wcols = win->wcols;
471                     }
472                     sx = *x + cw;
473                     (*lnp)->flags |= __ISDIRTY;
474                     newx = sx + win->ch_off;
475                     if (newx < *(*lnp)->firstchp)
476                               *(*lnp)->firstchp = newx;
477           }
478 
479           /* check for enough space before the end of line */
480           cw = wcwidth(wch->vals[0]);
481           if (cw < 0)
482                     cw = 1;
483 
484           if (cw > win->maxx - *x) {
485                     __CTRACE(__CTRACE_INPUT,
486                         "_cursesi_addwchar: clear EOL (%d,%d)\n", *y, *x);
487                     if (*y == win->scr_b) {
488                               if (!(win->flags & __SCROLLOK))
489                                         return ERR;
490                               scroll(win);
491                     }
492 
493                     (*lnp)->flags |= __ISDIRTY;
494                     newx = *x + win->ch_off;
495                     if (newx < *(*lnp)->firstchp)
496                               *(*lnp)->firstchp = newx;
497 
498                     for (tp = lp; *x < win->maxx; tp++, (*x)++) {
499                               tp->ch = win->bch;
500                               if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
501                                         return ERR;
502                               tp->attr = win->battr;
503                               tp->wcols = win->wcols;
504                               tp->cflags = CA_BACKGROUND;
505                     }
506                     newx = win->maxx - 1 + win->ch_off;
507                     if (newx > *(*lnp)->lastchp)
508                               *(*lnp)->lastchp = newx;
509                     __touchline(win, *y, sx, (int) win->maxx - 1);
510                     sx = *x = 0;
511                     if (*y != win->scr_b) {
512                               (*y)++;
513                     }
514                     lp = &win->alines[*y]->line[0];
515                     (*lnp) = win->alines[*y];
516           }
517 
518           /* add spacing character */
519           __CTRACE(__CTRACE_INPUT,
520               "_cursesi_addwchar: add character (%d,%d) 0x%x\n",
521               *y, *x, wch->vals[0]);
522           (*lnp)->flags |= __ISDIRTY;
523           newx = *x + win->ch_off;
524           if (newx < *(*lnp)->firstchp)
525                     *(*lnp)->firstchp = newx;
526 
527           if (lp->nsp) {
528                     __cursesi_free_nsp(lp->nsp);
529                     lp->nsp = NULL;
530           }
531 
532           lp->ch = wch->vals[0];
533           lp->cflags &= ~ (CA_BACKGROUND | CA_CONTINUATION);
534 
535           attributes = (win->wattr | wch->attributes)
536                     & (WA_ATTRIBUTES & ~__COLOR);
537           if (wch->attributes & __COLOR)
538                     attributes |= wch->attributes & __COLOR;
539           else if (win->wattr & __COLOR)
540                     attributes |= win->wattr & __COLOR;
541           if (attributes & __COLOR)
542                     lp->attr = attributes | (win->battr & ~__COLOR);
543           else
544                     lp->attr = attributes | win->battr;
545 
546           lp->wcols = cw;
547 
548           __CTRACE(__CTRACE_INPUT,
549               "_cursesi_addwchar: add spacing char 0x%x, attr 0x%x, width %d\n",
550               lp->ch, lp->attr, lp->wcols);
551 
552           if (wch->elements > 1) {
553                     for (i = 1; i < wch->elements; i++) {
554                               np = malloc(sizeof(nschar_t));
555                               if (!np)
556                                         return ERR;
557                               np->ch = wch->vals[i];
558                               np->next = lp->nsp;
559                               __CTRACE(__CTRACE_INPUT,
560                                   "_cursesi_addwchar: add non-spacing char 0x%x\n",
561                                   np->ch);
562                               lp->nsp = np;
563                     }
564           }
565           __CTRACE(__CTRACE_INPUT,
566               "_cursesi_addwchar: non-spacing list header: %p\n", lp->nsp);
567           __CTRACE(__CTRACE_INPUT,
568               "_cursesi_addwchar: add rest columns (%d:%d)\n",
569               sx + 1, sx + cw - 1);
570           __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: *x = %d, win->maxx = %d\n",
571               *x, win->maxx);
572           for (tp = lp + 1, *x = sx + 1, i = cw - 1; i > 0; tp++, (*x)++, i--) {
573                     __CTRACE(__CTRACE_INPUT,
574                         "_cursesi_addwchar: setting continuation at x %d\n", *x);
575                     if (tp->nsp) {
576                               __cursesi_free_nsp(tp->nsp);
577                               tp->nsp = NULL;
578                     }
579                     tp->ch = wch->vals[0];
580                     tp->attr = lp->attr & WA_ATTRIBUTES;
581                     /* Mark as "continuation" cell */
582                     tp->cflags |= CA_CONTINUATION;
583                     tp->cflags &= ~ CA_BACKGROUND;
584                     tp->wcols = i;
585           }
586 
587           if (*x >= win->maxx) {
588                     __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: do line wrap\n");
589                     if (*y == win->scr_b) {
590                               __CTRACE(__CTRACE_INPUT,
591                                   "_cursesi_addwchar: at bottom of screen\n");
592                               if (!(win->flags & __SCROLLOK))
593                                         return ERR;
594                               __CTRACE(__CTRACE_INPUT,
595                                   "_cursesi_addwchar: do a scroll\n");
596                               if (!__NONL)
597                                         *x = 0;
598                               scroll(win);
599                     }
600                     newx = win->maxx - 1 + win->ch_off;
601                     if (newx > *(*lnp)->lastchp)
602                               *(*lnp)->lastchp = newx;
603                     __touchline(win, *y, sx, (int) win->maxx - 1);
604                     *x = sx = 0;
605                     if (*y != win->scr_b) {
606                               (*y)++;
607                     }
608                     lp = &win->alines[*y]->line[0];
609                     (*lnp) = win->alines[*y];
610                     *(*lnp)->lastchp = win->ch_off + win->maxx - 1;
611           } else {
612                     /* clear the remaining of the current character */
613                     if (*x && *x < win->maxx) {
614                               ex = sx + cw;
615                               tp = &win->alines[*y]->line[ex];
616                               while (ex < win->maxx && tp->wcols < 0) {
617                                         __CTRACE(__CTRACE_INPUT,
618                                             "_cursesi_addwchar: clear "
619                                             "remaining of current char (%d,%d)nn",
620                                             *y, ex);
621                                         tp->ch = win->bch;
622                                         tp->cflags = CA_BACKGROUND;
623                                         if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
624                                                   return ERR;
625                                         tp->attr = win->battr;
626                                         tp->wcols = win->wcols;
627                                         tp++, ex++;
628                               }
629                               newx = ex - 1 + win->ch_off;
630                               if (newx > *(*lnp)->lastchp)
631                                         *(*lnp)->lastchp = newx;
632                               __touchline(win, *y, sx, ex - 1);
633                     }
634           }
635 
636           __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: %d : 0x%x\n",
637               lp->ch, lp->attr);
638           __CTRACE(__CTRACE_INPUT,
639               "_cursesi_addwchar: *x = %d, *y = %d, win->maxx = %d\n",
640               *x, *y, win->maxx);
641           __sync(win);
642           return OK;
643 #endif
644 }
645