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@arl.wustl.edu,ruibiao@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
ins_wstr(const wchar_t * wstr)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
ins_nwstr(const wchar_t * wstr,int n)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
mvins_wstr(int y,int x,const wchar_t * wstr)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
mvins_nwstr(int y,int x,const wchar_t * wstr,int n)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
mvwins_wstr(WINDOW * win,int y,int x,const wchar_t * wstr)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
mvwins_nwstr(WINDOW * win,int y,int x,const wchar_t * wstr,int n)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
wins_wstr(WINDOW * win,const wchar_t * wstr)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
wins_nwstr(WINDOW * win,const wchar_t * wstr,int n)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