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