addbytes.c revision 1.32.4.1 1 /* $NetBSD: addbytes.c,v 1.32.4.1 2008/01/09 01:36:22 matt 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.32.4.1 2008/01/09 01:36:22 matt Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <stdlib.h>
42 #include "curses.h"
43 #include "curses_private.h"
44 #ifdef DEBUG
45 #include <assert.h>
46 #endif
47
48 #define SYNCH_IN {y = win->cury; x = win->curx;}
49 #define SYNCH_OUT {win->cury = y; win->curx = x;}
50 #define PSYNCH_IN {*y = win->cury; *x = win->curx;}
51 #define PSYNCH_OUT {win->cury = *y; win->curx = *x;}
52
53 #ifndef _CURSES_USE_MACROS
54
55 /*
56 * addbytes --
57 * Add the character to the current position in stdscr.
58 */
59 int
60 addbytes(const char *bytes, int count)
61 {
62 return __waddbytes(stdscr, bytes, count, 0);
63 }
64
65 /*
66 * waddbytes --
67 * Add the character to the current position in the given window.
68 */
69 int
70 waddbytes(WINDOW *win, const char *bytes, int count)
71 {
72 return __waddbytes(win, bytes, count, 0);
73 }
74
75 /*
76 * mvaddbytes --
77 * Add the characters to stdscr at the location given.
78 */
79 int
80 mvaddbytes(int y, int x, const char *bytes, int count)
81 {
82 return mvwaddbytes(stdscr, y, x, bytes, count);
83 }
84
85 /*
86 * mvwaddbytes --
87 * Add the characters to the given window at the location given.
88 */
89 int
90 mvwaddbytes(WINDOW *win, int y, int x, const char *bytes, int count)
91 {
92 if (wmove(win, y, x) == ERR)
93 return ERR;
94
95 return __waddbytes(win, bytes, count, 0);
96 }
97
98 #endif
99
100 /*
101 * waddbytes --
102 * Add the character to the current position in the given window.
103 */
104 int
105 __waddbytes(WINDOW *win, const char *bytes, int count, attr_t attr)
106 {
107 int x, y, err;
108 __LINE *lp;
109 #ifdef HAVE_WCHAR
110 int n;
111 cchar_t cc;
112 wchar_t wc;
113 #else
114 int c;
115 #endif
116 #ifdef DEBUG
117 int i;
118
119 for (i = 0; i < win->maxy; i++) {
120 assert(win->lines[i]->sentinel == SENTINEL_VALUE);
121 }
122
123 __CTRACE(__CTRACE_INPUT, "ADDBYTES: add %d bytes\n", count);
124 #endif
125
126 err = OK;
127 SYNCH_IN;
128 lp = win->lines[y];
129
130 while (count > 0) {
131 #ifndef HAVE_WCHAR
132 c = *bytes++;
133 #ifdef DEBUG
134 __CTRACE(__CTRACE_INPUT, "ADDBYTES('%c', %x) at (%d, %d)\n",
135 c, attr, y, x);
136 #endif
137 err = _cursesi_addbyte(win, &lp, &y, &x, c, attr);
138 count--;
139 #else
140 /*
141 * For wide character support only, try and convert the
142 * given string into a wide character - we do this because
143 * this is how ncurses behaves (not that I think this is
144 * actually the correct thing to do but if we don't do it
145 * a lot of things that rely on this behaviour will break
146 * and we will be blamed). If the conversion succeeds
147 * then we eat the n characters used to make the wide char
148 * from the string.
149 */
150 n = mbtowc(&wc, bytes, count);
151 if (n == 0)
152 break;
153 else if (n < 0) { /* not a valid conversion just eat a char */
154 wc = *bytes;
155 n = 1;
156 }
157
158
159 #ifdef DEBUG
160 __CTRACE(__CTRACE_INPUT,
161 "ADDBYTES WIDE(0x%x [%s], %x) at (%d, %d), ate %d bytes\n",
162 (unsigned) wc, unctrl((unsigned) wc), attr, y, x, n);
163 #endif
164 cc.vals[0] = wc;
165 cc.elements = 1;
166 cc.attributes = attr;
167 err = _cursesi_addwchar(win, &lp, &y, &x, &cc);
168 bytes += n;
169 count -= n;
170 #endif
171 }
172
173 SYNCH_OUT;
174
175 #ifdef DEBUG
176 for (i = 0; i < win->maxy; i++) {
177 assert(win->lines[i]->sentinel == SENTINEL_VALUE);
178 }
179 #endif
180
181 return (err);
182 }
183
184 /*
185 * _cursesi_addbyte -
186 * Internal function to add a byte and update the row and column
187 * positions as appropriate. This function is only used in the narrow
188 * character version of curses.
189 */
190 int
191 _cursesi_addbyte(WINDOW *win, __LINE **lp, int *y, int *x, int c,
192 attr_t attr)
193 {
194 static char blanks[] = " ";
195 int newx;
196 attr_t attributes;
197
198 switch (c) {
199 case '\t':
200 PSYNCH_OUT;
201 if (waddbytes(win, blanks, 8 - (*x % 8)) == ERR)
202 return (ERR);
203 PSYNCH_IN;
204 break;
205
206 default:
207 #ifdef DEBUG
208 __CTRACE(__CTRACE_INPUT, "ADDBYTES(%p, %d, %d)\n",
209 win, *y, *x);
210 #endif
211
212 if ((*lp)->flags & __ISPASTEOL) {
213 newline:
214 *x = 0;
215 (*lp)->flags &= ~__ISPASTEOL;
216 if (*y == win->scr_b) {
217 #ifdef DEBUG
218 __CTRACE(__CTRACE_INPUT,
219 "ADDBYTES - on bottom "
220 "of scrolling region\n");
221 #endif
222 if (!(win->flags & __SCROLLOK))
223 return ERR;
224 PSYNCH_OUT;
225 scroll(win);
226 PSYNCH_IN;
227 } else {
228 (*y)++;
229 }
230 *lp = win->lines[*y];
231 if (c == '\n')
232 break;
233 }
234
235 attributes = (win->wattr | attr) &
236 (__ATTRIBUTES & ~__COLOR);
237 if (attr & __COLOR)
238 attributes |= attr & __COLOR;
239 else if (win->wattr & __COLOR)
240 attributes |= win->wattr & __COLOR;
241 #ifdef DEBUG
242 __CTRACE(__CTRACE_INPUT,
243 "ADDBYTES: 1: y = %d, x = %d, firstch = %d, "
244 "lastch = %d\n",
245 *y, *x, *win->lines[*y]->firstchp,
246 *win->lines[*y]->lastchp);
247 #endif
248 /*
249 * Always update the change pointers. Otherwise,
250 * we could end up not displaying 'blank' characters
251 * when overlapping windows are displayed.
252 */
253 newx = *x + win->ch_off;
254 (*lp)->flags |= __ISDIRTY;
255 /*
256 * firstchp/lastchp are shared between
257 * parent window and sub-window.
258 */
259 if (newx < *(*lp)->firstchp)
260 *(*lp)->firstchp = newx;
261 if (newx > *(*lp)->lastchp)
262 *(*lp)->lastchp = newx;
263 #ifdef DEBUG
264 __CTRACE(__CTRACE_INPUT,
265 "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n",
266 *(*lp)->firstchp, *(*lp)->lastchp,
267 *(*lp)->firstchp - win->ch_off,
268 *(*lp)->lastchp - win->ch_off);
269 #endif
270 if (win->bch != ' ' && c == ' ')
271 (*lp)->line[*x].ch = win->bch;
272 else
273 (*lp)->line[*x].ch = c;
274
275 if (attributes & __COLOR)
276 (*lp)->line[*x].attr =
277 attributes | (win->battr & ~__COLOR);
278 else
279 (*lp)->line[*x].attr = attributes | win->battr;
280
281 if (*x == win->maxx - 1)
282 (*lp)->flags |= __ISPASTEOL;
283 else
284 (*x)++;
285 #ifdef DEBUG
286 __CTRACE(__CTRACE_INPUT,
287 "ADDBYTES: 2: y = %d, x = %d, firstch = %d, "
288 "lastch = %d\n",
289 *y, *x, *win->lines[*y]->firstchp,
290 *win->lines[*y]->lastchp);
291 #endif
292 break;
293 case '\n':
294 PSYNCH_OUT;
295 wclrtoeol(win);
296 PSYNCH_IN;
297 goto newline;
298 case '\r':
299 *x = 0;
300 break;
301 case '\b':
302 if (--(*x) < 0)
303 *x = 0;
304 break;
305 }
306
307 return (OK);
308 }
309
310 /*
311 * _cursesi_addwchar -
312 * Internal function to add a wide character and update the row
313 * and column positions.
314 */
315 int
316 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x,
317 const cchar_t *wch)
318 {
319 #ifndef HAVE_WCHAR
320 return (ERR);
321 #else
322 int sx = 0, ex = 0, cw = 0, i = 0, newx = 0;
323 __LDATA *lp = &win->lines[*y]->line[*x], *tp = NULL;
324 nschar_t *np = NULL;
325 cchar_t cc;
326 attr_t attributes;
327
328 /* special characters handling */
329 switch (wch->vals[0]) {
330 case L'\b':
331 if (--*x < 0)
332 *x = 0;
333 win->curx = *x;
334 return OK;
335 case L'\r':
336 *x = 0;
337 return OK;
338 case L'\n':
339 wclrtoeol(win);
340 PSYNCH_IN;
341 *x = 0;
342 (*lnp)->flags &= ~__ISPASTEOL;
343 if (*y == win->scr_b) {
344 if (!(win->flags & __SCROLLOK))
345 return ERR;
346 PSYNCH_OUT;
347 scroll(win);
348 PSYNCH_IN;
349 } else {
350 (*y)++;
351 }
352 PSYNCH_OUT;
353 return OK;
354 case L'\t':
355 cc.vals[0] = L' ';
356 cc.elements = 1;
357 cc.attributes = win->wattr;
358 for (i = 0; i < 8 - (*x % 8); i++) {
359 if (wadd_wch(win, &cc) == ERR)
360 return ERR;
361 }
362 return OK;
363 }
364
365 /* check for non-spacing character */
366 if (!wcwidth(wch->vals[0])) {
367 #ifdef DEBUG
368 __CTRACE(__CTRACE_INPUT,
369 "_cursesi_addwchar: char '%c' is non-spacing\n",
370 wch->vals[0]);
371 #endif /* DEBUG */
372 cw = WCOL(*lp);
373 if (cw < 0) {
374 lp += cw;
375 *x += cw;
376 }
377 for (i = 0; i < wch->elements; i++) {
378 if (!(np = (nschar_t *) malloc(sizeof(nschar_t))))
379 return ERR;;
380 np->ch = wch->vals[i];
381 np->next = lp->nsp;
382 lp->nsp = np;
383 }
384 (*lnp)->flags |= __ISDIRTY;
385 newx = *x + win->ch_off;
386 if (newx < *(*lnp)->firstchp)
387 *(*lnp)->firstchp = newx;
388 if (newx > *(*lnp)->lastchp)
389 *(*lnp)->lastchp = newx;
390 __touchline(win, *y, *x, *x);
391 return OK;
392 }
393 /* check for new line first */
394 if ((*lnp)->flags & __ISPASTEOL) {
395 *x = 0;
396 (*lnp)->flags &= ~__ISPASTEOL;
397 if (*y == win->scr_b) {
398 if (!(win->flags & __SCROLLOK))
399 return ERR;
400 PSYNCH_OUT;
401 scroll(win);
402 PSYNCH_IN;
403 } else {
404 (*y)++;
405 }
406 (*lnp) = win->lines[*y];
407 lp = &win->lines[*y]->line[*x];
408 }
409 /* clear out the current character */
410 cw = WCOL(*lp);
411 if (cw >= 0) {
412 sx = *x;
413 } else {
414 for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) {
415 #ifdef DEBUG
416 __CTRACE(__CTRACE_INPUT,
417 "_cursesi_addwchar: clear current char (%d,%d)\n",
418 *y, sx);
419 #endif /* DEBUG */
420 tp = &win->lines[*y]->line[sx];
421 tp->ch = (wchar_t) btowc((int) win->bch);
422 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
423 return ERR;
424
425 tp->attr = win->battr;
426 SET_WCOL(*tp, 1);
427 }
428 sx = *x + cw;
429 (*lnp)->flags |= __ISDIRTY;
430 newx = sx + win->ch_off;
431 if (newx < *(*lnp)->firstchp)
432 *(*lnp)->firstchp = newx;
433 }
434
435 /* check for enough space before the end of line */
436 cw = wcwidth(wch->vals[0]);
437 if (cw > win->maxx - *x) {
438 #ifdef DEBUG
439 __CTRACE(__CTRACE_INPUT,
440 "_cursesi_addwchar: clear EOL (%d,%d)\n",
441 *y, *x);
442 #endif /* DEBUG */
443 (*lnp)->flags |= __ISDIRTY;
444 newx = *x + win->ch_off;
445 if (newx < *(*lnp)->firstchp)
446 *(*lnp)->firstchp = newx;
447 for (tp = lp; *x < win->maxx; tp++, (*x)++) {
448 tp->ch = (wchar_t) btowc((int) win->bch);
449 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
450 return ERR;
451 tp->attr = win->battr;
452 SET_WCOL(*tp, 1);
453 }
454 newx = win->maxx - 1 + win->ch_off;
455 if (newx > *(*lnp)->lastchp)
456 *(*lnp)->lastchp = newx;
457 __touchline(win, *y, sx, (int) win->maxx - 1);
458 sx = *x = 0;
459 if (*y == win->scr_b) {
460 if (!(win->flags & __SCROLLOK))
461 return ERR;
462 PSYNCH_OUT;
463 scroll(win);
464 PSYNCH_IN;
465 } else {
466 (*y)++;
467 }
468 lp = &win->lines[*y]->line[0];
469 (*lnp) = win->lines[*y];
470 }
471 win->cury = *y;
472
473 /* add spacing character */
474 #ifdef DEBUG
475 __CTRACE(__CTRACE_INPUT,
476 "_cursesi_addwchar: add character (%d,%d) 0x%x\n",
477 *y, *x, wch->vals[0]);
478 #endif /* DEBUG */
479 (*lnp)->flags |= __ISDIRTY;
480 newx = *x + win->ch_off;
481 if (newx < *(*lnp)->firstchp)
482 *(*lnp)->firstchp = newx;
483 if (lp->nsp) {
484 __cursesi_free_nsp(lp->nsp);
485 lp->nsp = NULL;
486 }
487
488 lp->ch = wch->vals[0];
489
490 attributes = (win->wattr | wch->attributes)
491 & (WA_ATTRIBUTES & ~__COLOR);
492 if (wch->attributes & __COLOR)
493 attributes |= wch->attributes & __COLOR;
494 else if (win->wattr & __COLOR)
495 attributes |= win->wattr & __COLOR;
496 if (attributes & __COLOR)
497 lp->attr = attributes | (win->battr & ~__COLOR);
498 else
499 lp->attr = attributes | win->battr;
500
501 SET_WCOL(*lp, cw);
502
503 #ifdef DEBUG
504 __CTRACE(__CTRACE_INPUT,
505 "_cursesi_addwchar: add spacing char 0x%x, attr 0x%x\n",
506 lp->ch, lp->attr);
507 #endif /* DEBUG */
508
509 if (wch->elements > 1) {
510 for (i = 1; i < wch->elements; i++) {
511 np = (nschar_t *)malloc(sizeof(nschar_t));
512 if (!np)
513 return ERR;;
514 np->ch = wch->vals[i];
515 np->next = lp->nsp;
516 #ifdef DEBUG
517 __CTRACE(__CTRACE_INPUT,
518 "_cursesi_addwchar: add non-spacing char 0x%x\n", np->ch);
519 #endif /* DEBUG */
520 lp->nsp = np;
521 }
522 }
523 #ifdef DEBUG
524 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: non-spacing list header: %p\n",
525 lp->nsp);
526 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: add rest columns (%d:%d)\n",
527 sx + 1, sx + cw - 1);
528 #endif /* DEBUG */
529 for (tp = lp + 1, *x = sx + 1; *x - sx <= cw - 1; tp++, (*x)++) {
530 if (tp->nsp) {
531 __cursesi_free_nsp(tp->nsp);
532 tp->nsp = NULL;
533 }
534 tp->ch = wch->vals[0];
535 tp->attr = lp->attr & WA_ATTRIBUTES;
536 /* Mark as "continuation" cell */
537 tp->attr |= __WCWIDTH;
538 }
539 if (*x == win->maxx) {
540 (*lnp)->flags |= __ISPASTEOL;
541 newx = win->maxx - 1 + win->ch_off;
542 if (newx > *(*lnp)->lastchp)
543 *(*lnp)->lastchp = newx;
544 __touchline(win, *y, sx, (int) win->maxx - 1);
545 win->curx = sx;
546 } else {
547 win->curx = *x;
548
549 /* clear the remining of the current characer */
550 if (*x && *x < win->maxx) {
551 ex = sx + cw;
552 tp = &win->lines[*y]->line[ex];
553 while (ex < win->maxx && WCOL(*tp) < 0) {
554 #ifdef DEBUG
555 __CTRACE(__CTRACE_INPUT,
556 "_cursesi_addwchar: clear "
557 "remaining of current char (%d,%d)nn",
558 *y, ex);
559 #endif /* DEBUG */
560 tp->ch = (wchar_t) btowc((int) win->bch);
561 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
562 return ERR;
563 tp->attr = win->battr;
564 SET_WCOL(*tp, 1);
565 tp++, ex++;
566 }
567 newx = ex - 1 + win->ch_off;
568 if (newx > *(*lnp)->lastchp)
569 *(*lnp)->lastchp = newx;
570 __touchline(win, *y, sx, ex - 1);
571 }
572 }
573
574 #ifdef DEBUG
575 __CTRACE(__CTRACE_INPUT, "add_wch: %d : 0x%x\n", lp->ch, lp->attr);
576 #endif /* DEBUG */
577 return OK;
578 #endif
579 }
580