Home | History | Annotate | Line # | Download | only in libcurses
      1 /*   $NetBSD: ins_wstr.c,v 1.25 2024/12/23 02:58:03 blymn Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2005 The NetBSD Foundation Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from code donated to the NetBSD Foundation
      8  * by Ruibiao Qiu <ruibiao (at) arl.wustl.edu,ruibiao (at) gmail.com>.
      9  *
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *	notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *	notice, this list of conditions and the following disclaimer in the
     18  *	documentation and/or other materials provided with the distribution.
     19  * 3. Neither the name of the NetBSD Foundation nor the names of its
     20  *	contributors may be used to endorse or promote products derived
     21  *	from this software without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
     24  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #ifndef lint
     39 __RCSID("$NetBSD: ins_wstr.c,v 1.25 2024/12/23 02:58:03 blymn Exp $");
     40 #endif						  /* not lint */
     41 
     42 #include <string.h>
     43 #include <stdlib.h>
     44 
     45 #include "curses.h"
     46 #include "curses_private.h"
     47 
     48 /*
     49  * ins_wstr --
     50  *	insert a multi-character wide-character string into the current window
     51  */
     52 int
     53 ins_wstr(const wchar_t *wstr)
     54 {
     55 	return wins_wstr(stdscr, wstr);
     56 }
     57 
     58 /*
     59  * ins_nwstr --
     60  *	insert a multi-character wide-character string into the current window
     61  *	with at most n characters
     62  */
     63 int
     64 ins_nwstr(const wchar_t *wstr, int n)
     65 {
     66 	return wins_nwstr(stdscr, wstr, n);
     67 }
     68 
     69 /*
     70  * mvins_wstr --
     71  *	  Do an insert-string on the line at (y, x).
     72  */
     73 int
     74 mvins_wstr(int y, int x, const wchar_t *wstr)
     75 {
     76 	return mvwins_wstr(stdscr, y, x, wstr);
     77 }
     78 
     79 /*
     80  * mvins_nwstr --
     81  *	  Do an insert-n-string on the line at (y, x).
     82  */
     83 int
     84 mvins_nwstr(int y, int x, const wchar_t *wstr, int n)
     85 {
     86 	return mvwins_nwstr(stdscr, y, x, wstr, n);
     87 }
     88 
     89 /*
     90  * mvwins_wstr --
     91  *	  Do an insert-string on the line at (y, x) in the given window.
     92  */
     93 int
     94 mvwins_wstr(WINDOW *win, int y, int x, const wchar_t *wstr)
     95 {
     96 	if (wmove(win, y, x) == ERR)
     97 		return ERR;
     98 
     99 	return wins_wstr(win, wstr);
    100 }
    101 
    102 /*
    103  * mvwins_nwstr --
    104  *	  Do an insert-n-string on the line at (y, x) in the given window.
    105  */
    106 int
    107 mvwins_nwstr(WINDOW *win, int y, int x, const wchar_t *wstr, int n)
    108 {
    109 	if (wmove(win, y, x) == ERR)
    110 		return ERR;
    111 
    112 	return wins_nwstr(win, wstr, n);
    113 }
    114 
    115 
    116 /*
    117  * wins_wstr --
    118  *	Do an insert-string on the line, leaving (cury, curx) unchanged.
    119  */
    120 int
    121 wins_wstr(WINDOW *win, const wchar_t *wstr)
    122 {
    123 	return wins_nwstr(win, wstr, -1);
    124 }
    125 
    126 /*
    127  * wins_nwstr --
    128  *	Do an insert-n-string on the line, leaving (cury, curx) unchanged.
    129  */
    130 int
    131 wins_nwstr(WINDOW *win, const wchar_t *wstr, int n)
    132 {
    133 	__LDATA	 *start, *temp1, *temp2;
    134 	__LINE	  *lnp;
    135 	const wchar_t *scp;
    136 	cchar_t cc;
    137 	wchar_t *lstr, *slstr;
    138 	int width, len, lx, sx, x, y, tx, ty, cw, pcw, newx, tn, w;
    139 	wchar_t ws[] = L"		";
    140 
    141 	if (__predict_false(win == NULL))
    142 		return ERR;
    143 
    144 	/* check for leading non-spacing character */
    145 	if (!wstr)
    146 		return OK;
    147 	cw = wcwidth(*wstr);
    148 	if (cw < 0)
    149 		cw = 1;
    150 	if (!cw)
    151 		return ERR;
    152 
    153 	lstr = malloc(sizeof(wchar_t) * win->maxx);
    154 	if (lstr == NULL)
    155 		return ERR;
    156 
    157 	scp = wstr + 1;
    158 	width = cw;
    159 	len = 1;
    160 	n--;
    161 	y = win->cury;
    162 	x = win->curx;
    163 	tn = n;
    164 
    165 	/*
    166 	 * Firstly, make sure the string will fit...
    167 	 */
    168 	while (*scp) {
    169 		if (!tn)
    170 			break;
    171 		switch (*scp) {
    172 			case L'\b':
    173 				if (--x < 0)
    174 					x = 0;
    175 				cw = wcwidth(*(scp - 1));
    176 				if (cw < 0)
    177 					cw = 1;
    178 				width -= cw;
    179 				scp++;
    180 				continue;
    181 
    182 			case L'\r':
    183 				width = 0;
    184 				x = 0;
    185 				scp++;
    186 				continue;
    187 
    188 			case L'\n':
    189 				if (y == win->scr_b) {
    190 					if (!(win->flags & __SCROLLOK)) {
    191 						free(lstr);
    192 						return ERR;
    193 					}
    194 				}
    195 				y++;
    196 				scp++;
    197 				continue;
    198 
    199 			case L'\t':
    200 				cw = wcwidth(ws[0]);
    201 				if (cw < 0)
    202 					cw = 1;
    203 				w = cw * (TABSIZE - (x % TABSIZE));
    204 				width += w;
    205 				x += w;
    206 				scp++;
    207 				continue;
    208 		}
    209 		w = wcwidth(*scp);
    210 		if (w < 0)
    211 			w = 1;
    212 		tn--, width += w;
    213 		scp++;
    214 	}
    215 	__CTRACE(__CTRACE_INPUT, "wins_nwstr: width=%d,len=%d\n", width, len);
    216 
    217 	if (width > win->maxx - win->curx + 1) {
    218 		free(lstr);
    219 		return ERR;
    220 	}
    221 
    222 	scp = wstr;
    223 	x = win->curx;
    224 	y = win->cury;
    225 	len = 0;
    226 	width = 0;
    227 	slstr = lstr;
    228 
    229 	while (*scp && n) {
    230 		lstr = slstr;
    231 		lx = x;
    232 		while (*scp) {
    233 			if (!n)
    234 				break;
    235 			switch (*scp) {
    236 				case L'\b':
    237 					if (--x < 0)
    238 						x = 0;
    239 					if (--len < 0)
    240 						len = 0;
    241 					cw = wcwidth(*(scp - 1));
    242 					if (cw < 0)
    243 						cw = 1;
    244 					width -= cw;
    245 					scp++;
    246 					if (lstr != slstr)
    247 						lstr--;
    248 					continue;
    249 
    250 				case L'\r':
    251 					width = 0;
    252 					len = 0;
    253 					x = 0;
    254 					scp++;
    255 					lstr = slstr;
    256 					continue;
    257 
    258 				case L'\n':
    259 					goto loopdone;
    260 
    261 				case L'\t':
    262 					cw = wcwidth(ws[0]);
    263 					if (cw < 0)
    264 						cw = 1;
    265 					w = cw * (TABSIZE - (x % TABSIZE));
    266 					width += w;
    267 					x += w;
    268 					len += w;
    269 					scp++;
    270 					*lstr = *ws;
    271 					lstr++;
    272 					continue;
    273 			}
    274 			w = wcwidth(*scp);
    275 			if (w < 0)
    276 				w = 1;
    277 			*lstr = *scp;
    278 			n--, len++, width += w;
    279 			scp++, lstr++;
    280 		}
    281 
    282 loopdone:
    283 		start = &win->alines[y]->line[x];
    284 		sx = x;
    285 		lnp = win->alines[y];
    286 		pcw = start->wcols;
    287 		if (pcw < 0) {
    288 			sx += pcw;
    289 			start += pcw;
    290 		}
    291 		__CTRACE(__CTRACE_INPUT, "wins_nwstr: start@(%d)\n", sx);
    292 		pcw = start->wcols;
    293 		lnp->flags |= __ISDIRTY;
    294 		newx = sx + win->ch_off;
    295 		if (newx < *lnp->firstchp)
    296 			*lnp->firstchp = newx;
    297 
    298 #ifdef DEBUG
    299 		{
    300 			int i;
    301 
    302 			__CTRACE(__CTRACE_INPUT, "========before=======\n");
    303 			for (i = 0; i < win->maxx; i++)
    304 			__CTRACE(__CTRACE_INPUT,
    305 				    "wins_nwstr: (%d,%d)=(%x,%x,%d,%x,%p)\n",
    306 				    y, i, win->alines[y]->line[i].ch,
    307 				    win->alines[y]->line[i].attr,
    308 				    win->alines[y]->line[i].wcols,
    309 				    win->alines[y]->line[i].cflags,
    310 				    win->alines[y]->line[i].nsp);
    311 		}
    312 #endif /* DEBUG */
    313 
    314 		/* shift all complete characters */
    315 		if (sx + width + pcw <= win->maxx) {
    316 			__CTRACE(__CTRACE_INPUT, "wins_nwstr: shift all characters by %d\n", width);
    317 			temp1 = &win->alines[y]->line[win->maxx - 1];
    318 			temp2 = temp1 - width;
    319 			pcw = (temp2 + 1)->wcols;
    320 			if (pcw < 0) {
    321 				__CTRACE(__CTRACE_INPUT,
    322 				    "wins_nwstr: clear from %d to EOL(%d)\n",
    323 				    win->maxx + pcw, win->maxx - 1);
    324 				temp2 += pcw;
    325 				while (temp1 > temp2 + width) {
    326 					temp1->ch = win->bch;
    327 					if (_cursesi_copy_nsp(win->bnsp, temp1) == ERR) {
    328 						free(lstr);
    329 						return ERR;
    330 					}
    331 					temp1->attr = win->battr;
    332 					temp1->cflags |= CA_BACKGROUND;
    333 					temp1->cflags &= ~CA_CONTINUATION;
    334 					temp1->wcols = 1;
    335 					__CTRACE(__CTRACE_INPUT,
    336 					    "wins_nwstr: empty cell(%p)\n", temp1);
    337 					temp1--;
    338 				}
    339 			}
    340 			while (temp2 >= start) {
    341 				(void)memcpy(temp1, temp2, sizeof(__LDATA));
    342 				temp1--, temp2--;
    343 			}
    344 #ifdef DEBUG
    345 			{
    346 				int i;
    347 
    348 				__CTRACE(__CTRACE_INPUT, "=====after shift====\n");
    349 				for (i = 0; i < win->maxx; i++)
    350 					__CTRACE(__CTRACE_INPUT,
    351 					    "wins_nwstr: (%d,%d)=(%x,%x,%x,%p)\n",
    352 					    y, i,
    353 					    win->alines[y]->line[i].ch,
    354 					    win->alines[y]->line[i].attr,
    355 					    win->alines[y]->line[i].cflags,
    356 					    win->alines[y]->line[i].nsp);
    357 				__CTRACE(__CTRACE_INPUT, "=====lstr====\n");
    358 				for (i = 0; i < len; i++)
    359 					__CTRACE(__CTRACE_INPUT,
    360 					    "wins_nwstr: lstr[%d]= %x,\n",
    361 					    i, (unsigned) slstr[i]);
    362 			}
    363 #endif /* DEBUG */
    364 		}
    365 
    366 		/* update string columns */
    367 		x = lx;
    368 		for (lstr = slstr, temp1 = start; len; len--, lstr++) {
    369 			lnp = win->alines[y];
    370 			cc.vals[0] = *lstr;
    371 			cc.elements = 1;
    372 			cc.attributes = win->wattr;
    373 			_cursesi_addwchar(win, &lnp, &y, &x, &cc, 0);
    374 		}
    375 
    376 #ifdef DEBUG
    377 		{
    378 			int i;
    379 
    380 			__CTRACE(__CTRACE_INPUT, "lx = %d, x = %x\n", lx, x);
    381 			__CTRACE(__CTRACE_INPUT, "========after=======\n");
    382 			for (i = 0; i < win->maxx; i++)
    383 				__CTRACE(__CTRACE_INPUT,
    384 				    "wins_nwstr: (%d,%d)=(%x,%x,%d,%x,%p)\n",
    385 				    y, i,
    386 				    win->alines[y]->line[i].ch,
    387 				    win->alines[y]->line[i].attr,
    388 				    win->alines[y]->line[i].wcols,
    389 				    win->alines[y]->line[i].cflags,
    390 				    win->alines[y]->line[i].nsp);
    391 		}
    392 #endif /* DEBUG */
    393 
    394 		__touchline(win, (int) y, lx, (int) win->maxx - 1);
    395 
    396 		/*
    397 		 * Handle the newline here - we don't need to check
    398 		 * if we are allowed to scroll because this was checked
    399 		 * already.
    400 		 */
    401 		if (*scp == '\n') {
    402 			tx = win->curx;
    403 			ty = win->cury;
    404 			win->curx = x;
    405 			win->cury = y;
    406 			wclrtoeol(win);
    407 			win->curx = tx;
    408 			win->cury = ty;
    409 			if (y == win->scr_b)
    410 				scroll(win);
    411 			else
    412 				y++;
    413 			scp++;
    414 
    415 		}
    416 
    417 		newx = win->maxx - 1 + win->ch_off;
    418 		if (newx > *lnp->lastchp)
    419 			*lnp->lastchp = newx;
    420 	}
    421 	free(lstr);
    422 	__sync(win);
    423 	return OK;
    424 }
    425