ncurses 6.2 - patch 20210227
[ncurses.git] / test / test_addwstr.c
1 /****************************************************************************
2  * Copyright 2020 Thomas E. Dickey                                          *
3  * Copyright 2009-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 /*
30  * $Id: test_addwstr.c,v 1.17 2020/02/02 23:34:34 tom Exp $
31  *
32  * Demonstrate the waddwstr() and wadd_wch functions.
33  * Thomas Dickey - 2009/9/12
34  *
35  * Note: to provide inputs for *add_wch(), we use setcchar().  A quirk of the
36  * X/Open definition for that function is that the string contains no
37  * characters with negative width.  Any control character (such as tab) falls
38  * into that category.  So it follows that *add_wch() cannot render a tab
39  * character because there is no legal way to construct a cchar_t containing
40  * one.  X/Open does not document this, and it would be logical to assume that
41  * *addwstr() has the same limitation, but it uses a wchar_t string directly,
42  * and does not document how tabs are handled.
43  */
44
45 #include <test.priv.h>
46
47 #if USE_WIDEC_SUPPORT
48
49 #define WIDE_LINEDATA
50 #include <linedata.h>
51
52 #undef AddCh
53 #undef MvAddCh
54 #undef MvAddStr
55 #undef MvWAddCh
56 #undef MvWAddStr
57 #undef WAddCh
58
59 /*
60  * redefinitions to simplify comparison between test_*str programs
61  */
62 #define AddNStr    addnwstr
63 #define AddStr     addwstr
64 #define MvAddNStr  (void) mvaddnwstr
65 #define MvAddStr   (void) mvaddwstr
66 #define MvWAddNStr (void) mvwaddnwstr
67 #define MvWAddStr  (void) mvwaddwstr
68 #define WAddNStr   waddnwstr
69 #define WAddStr    waddwstr
70
71 #define MY_TABSIZE 8
72
73 typedef enum {
74     oDefault = 0,
75     oMove = 1,
76     oWindow = 2,
77     oMoveWindow = 3
78 } Options;
79
80 static bool m_opt = FALSE;
81 static bool w_opt = FALSE;
82 static int n_opt = -1;
83
84 static void
85 legend(WINDOW *win, int level, Options state, wchar_t *buffer, int length)
86 {
87     const char *showstate;
88
89     switch (state) {
90     default:
91     case oDefault:
92         showstate = "";
93         break;
94     case oMove:
95         showstate = " (mvXXX)";
96         break;
97     case oWindow:
98         showstate = " (winXXX)";
99         break;
100     case oMoveWindow:
101         showstate = " (mvwinXXX)";
102         break;
103     }
104
105     wmove(win, 0, 0);
106     wprintw(win,
107             "The Strings/Chars displays should match.  Enter any characters, except:\n");
108     wprintw(win,
109             "down-arrow or ^N to repeat on next line, ^W for inner window, ESC to exit.\n");
110     wclrtoeol(win);
111     wprintw(win, "Level %d,%s added %d characters <", level, showstate, length);
112     waddwstr(win, buffer);
113     waddstr(win, ">");
114 }
115
116 static int
117 ColOf(wchar_t *buffer, int length, int margin)
118 {
119     int n;
120     int result;
121
122     for (n = 0, result = margin + 1; n < length; ++n) {
123         int ch = buffer[n];
124         switch (ch) {
125         case '\n':
126             /* actually newline should clear the remainder of the line
127              * and move to the next line - but that seems a little awkward
128              * in this example.
129              */
130         case '\r':
131             result = 0;
132             break;
133         case '\b':
134             if (result > 0)
135                 --result;
136             break;
137         case '\t':
138             result += (MY_TABSIZE - (result % MY_TABSIZE));
139             break;
140         case '\177':
141             result += 2;
142             break;
143         default:
144             result += wcwidth((wchar_t) ch);
145             if (ch < 32)
146                 ++result;
147             break;
148         }
149     }
150     return result;
151 }
152
153 static int
154 ConvertCh(chtype source, cchar_t *target)
155 {
156     wchar_t tmp_wchar[2];
157
158     tmp_wchar[0] = (wchar_t) source;
159     tmp_wchar[1] = 0;
160     if (setcchar(target, tmp_wchar, A_NORMAL, 0, (void *) 0) == ERR) {
161         beep();
162         return FALSE;
163     }
164     return TRUE;
165 }
166
167 static int
168 MvWAddCh(WINDOW *win, int y, int x, chtype ch)
169 {
170     int code;
171     cchar_t tmp_cchar;
172
173     if (ConvertCh(ch, &tmp_cchar)) {
174         code = mvwadd_wch(win, y, x, &tmp_cchar);
175     } else {
176         code = mvwaddch(win, y, x, ch);
177     }
178     return code;
179 }
180
181 static int
182 MvAddCh(int y, int x, chtype ch)
183 {
184     int code;
185     cchar_t tmp_cchar;
186
187     if (ConvertCh(ch, &tmp_cchar)) {
188         code = mvadd_wch(y, x, &tmp_cchar);
189     } else {
190         code = mvaddch(y, x, ch);
191     }
192     return code;
193 }
194
195 static int
196 WAddCh(WINDOW *win, chtype ch)
197 {
198     int code;
199     cchar_t tmp_cchar;
200
201     if (ConvertCh(ch, &tmp_cchar)) {
202         code = wadd_wch(win, &tmp_cchar);
203     } else {
204         code = waddch(win, ch);
205     }
206     return code;
207 }
208
209 static int
210 AddCh(chtype ch)
211 {
212     int code;
213     cchar_t tmp_cchar;
214
215     if (ConvertCh(ch, &tmp_cchar)) {
216         code = add_wch(&tmp_cchar);
217     } else {
218         code = addch(ch);
219     }
220     return code;
221 }
222
223 #define LEN(n) ((length - (n) > n_opt) ? n_opt : (length - (n)))
224 static void
225 recursive_test(int level)
226 {
227     static bool first = TRUE;
228
229     int ch;
230     int limit;
231     int row = 1;
232     int col;
233     int row2, col2;
234     int length;
235     wchar_t buffer[BUFSIZ];
236     WINDOW *look = 0;
237     WINDOW *work = 0;
238     WINDOW *show = 0;
239     int margin = (2 * MY_TABSIZE) - 1;
240     Options option = (Options) ((unsigned) (m_opt
241                                             ? oMove
242                                             : oDefault)
243                                 | (unsigned) ((w_opt || (level > 0))
244                                               ? oWindow
245                                               : oDefault));
246
247     if (first) {
248         static char cmd[80];
249         setlocale(LC_ALL, "");
250
251         _nc_STRCPY(cmd, "TABSIZE=8", sizeof(cmd));
252         putenv(cmd);
253
254         initscr();
255         (void) cbreak();        /* take input chars one at a time, no wait for \n */
256         (void) noecho();        /* don't echo input */
257         keypad(stdscr, TRUE);
258
259         /*
260          * Show the characters added in color, to distinguish from those that
261          * are shifted.
262          */
263         if (has_colors()) {
264             start_color();
265             init_pair(1, COLOR_WHITE, COLOR_BLUE);
266         }
267     }
268
269     limit = LINES - 5;
270     if (level > 0) {
271         look = newwin(limit, COLS - (2 * (level - 1)), 0, level - 1);
272         work = newwin(limit - 2, COLS - (2 * level), 1, level);
273         show = newwin(4, COLS, limit + 1, 0);
274         box(look, 0, 0);
275         wnoutrefresh(look);
276         limit -= 2;
277     } else {
278         work = stdscr;
279         show = derwin(stdscr, 4, COLS, limit + 1, 0);
280     }
281     keypad(work, TRUE);
282
283     for (col = margin + 1; col < COLS; col += MY_TABSIZE)
284         MvWVLine(work, row, col, '.', limit - 2);
285
286     MvWVLine(work, row, margin, ACS_VLINE, limit - 2);
287     MvWVLine(work, row, margin + 1, ACS_VLINE, limit - 2);
288     limit /= 2;
289
290     (void) mvwaddstr(work, 1, 2, "String");
291     (void) mvwaddstr(work, limit + 1, 2, "Chars");
292     wnoutrefresh(work);
293
294     buffer[length = 0] = '\0';
295     legend(show, level, option, buffer, length);
296     wnoutrefresh(show);
297
298     doupdate();
299
300     if (has_colors()) {
301         wbkgdset(work, (chtype) (COLOR_PAIR(1) | ' '));
302     }
303
304     while ((ch = read_linedata(work)) != ERR && !isQUIT(ch)) {
305         wmove(work, row, margin + 1);
306         switch (ch) {
307         case key_RECUR:
308             recursive_test(level + 1);
309
310             if (look)
311                 touchwin(look);
312             touchwin(work);
313             touchwin(show);
314
315             if (look)
316                 wnoutrefresh(look);
317             wnoutrefresh(work);
318             wnoutrefresh(show);
319
320             doupdate();
321             break;
322         case key_NEWLINE:
323             if (row < limit) {
324                 ++row;
325                 /* put the whole string in, all at once */
326                 col2 = margin + 1;
327                 switch (option) {
328                 case oDefault:
329                     if (n_opt > 1) {
330                         for (col = 0; col < length; col += n_opt) {
331                             col2 = ColOf(buffer, col, margin);
332                             if (move(row, col2) != ERR) {
333                                 AddNStr(buffer + col, LEN(col));
334                             }
335                         }
336                     } else {
337                         if (move(row, col2) != ERR) {
338                             AddStr(buffer);
339                         }
340                     }
341                     break;
342                 case oMove:
343                     if (n_opt > 1) {
344                         for (col = 0; col < length; col += n_opt) {
345                             col2 = ColOf(buffer, col, margin);
346                             MvAddNStr(row, col2, buffer + col, LEN(col));
347                         }
348                     } else {
349                         MvAddStr(row, col2, buffer);
350                     }
351                     break;
352                 case oWindow:
353                     if (n_opt > 1) {
354                         for (col = 0; col < length; col += n_opt) {
355                             col2 = ColOf(buffer, col, margin);
356                             if (wmove(work, row, col2) != ERR) {
357                                 WAddNStr(work, buffer + col, LEN(col));
358                             }
359                         }
360                     } else {
361                         if (wmove(work, row, col2) != ERR) {
362                             WAddStr(work, buffer);
363                         }
364                     }
365                     break;
366                 case oMoveWindow:
367                     if (n_opt > 1) {
368                         for (col = 0; col < length; col += n_opt) {
369                             col2 = ColOf(buffer, col, margin);
370                             MvWAddNStr(work, row, col2, buffer + col, LEN(col));
371                         }
372                     } else {
373                         MvWAddStr(work, row, col2, buffer);
374                     }
375                     break;
376                 }
377
378                 /* do the corresponding single-character add */
379                 row2 = limit + row;
380                 for (col = 0; col < length; ++col) {
381                     col2 = ColOf(buffer, col, margin);
382                     switch (option) {
383                     case oDefault:
384                         if (move(row2, col2) != ERR) {
385                             AddCh((chtype) buffer[col]);
386                         }
387                         break;
388                     case oMove:
389                         MvAddCh(row2, col2, (chtype) buffer[col]);
390                         break;
391                     case oWindow:
392                         if (wmove(work, row2, col2) != ERR) {
393                             WAddCh(work, (chtype) buffer[col]);
394                         }
395                         break;
396                     case oMoveWindow:
397                         MvWAddCh(work, row2, col2, (chtype) buffer[col]);
398                         break;
399                     }
400                 }
401             } else {
402                 beep();
403             }
404             break;
405         case KEY_BACKSPACE:
406             ch = '\b';
407             /* FALLTHRU */
408         default:
409             buffer[length++] = (wchar_t) ch;
410             buffer[length] = '\0';
411
412             /* put the string in, one character at a time */
413             col = ColOf(buffer, length - 1, margin);
414             switch (option) {
415             case oDefault:
416                 if (move(row, col) != ERR) {
417                     AddStr(buffer + length - 1);
418                 }
419                 break;
420             case oMove:
421                 MvAddStr(row, col, buffer + length - 1);
422                 break;
423             case oWindow:
424                 if (wmove(work, row, col) != ERR) {
425                     WAddStr(work, buffer + length - 1);
426                 }
427                 break;
428             case oMoveWindow:
429                 MvWAddStr(work, row, col, buffer + length - 1);
430                 break;
431             }
432
433             /* do the corresponding single-character add */
434             switch (option) {
435             case oDefault:
436                 if (move(limit + row, col) != ERR) {
437                     AddCh((chtype) ch);
438                 }
439                 break;
440             case oMove:
441                 MvAddCh(limit + row, col, (chtype) ch);
442                 break;
443             case oWindow:
444                 if (wmove(work, limit + row, col) != ERR) {
445                     WAddCh(work, (chtype) ch);
446                 }
447                 break;
448             case oMoveWindow:
449                 MvWAddCh(work, limit + row, col, (chtype) ch);
450                 break;
451             }
452
453             wnoutrefresh(work);
454
455             legend(show, level, option, buffer, length);
456             wnoutrefresh(show);
457
458             doupdate();
459             break;
460         }
461     }
462     delwin(show);
463     if (level > 0) {
464         delwin(work);
465         delwin(look);
466     }
467 }
468
469 static void
470 usage(void)
471 {
472     static const char *tbl[] =
473     {
474         "Usage: test_addwstr [options]"
475         ,""
476         ,"Options:"
477         ,"  -f FILE read data from given file"
478         ,"  -n NUM  limit string-adds to NUM bytes on ^N replay"
479         ,"  -m      perform wmove/move separately from add-functions"
480         ,"  -w      use window-parameter even when stdscr would be implied"
481     };
482     unsigned n;
483     for (n = 0; n < SIZEOF(tbl); ++n)
484         fprintf(stderr, "%s\n", tbl[n]);
485     ExitProgram(EXIT_FAILURE);
486 }
487
488 int
489 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
490 {
491     int ch;
492
493     setlocale(LC_ALL, "");
494
495     while ((ch = getopt(argc, argv, "f:mn:w")) != -1) {
496         switch (ch) {
497         case 'f':
498             init_linedata(optarg);
499             break;
500         case 'm':
501             m_opt = TRUE;
502             break;
503         case 'n':
504             n_opt = atoi(optarg);
505             if (n_opt == 0)
506                 n_opt = -1;
507             break;
508         case 'w':
509             w_opt = TRUE;
510             break;
511         default:
512             usage();
513             break;
514         }
515     }
516     if (optind < argc)
517         usage();
518
519     recursive_test(0);
520     endwin();
521     ExitProgram(EXIT_SUCCESS);
522 }
523 #else
524 int
525 main(void)
526 {
527     printf("This program requires the wide-ncurses library\n");
528     ExitProgram(EXIT_FAILURE);
529 }
530 #endif