blob: 9e51f49df1490172fb174508c81772710f3dc061 [file] [log] [blame]
Steve Kondikae271bc2015-11-15 02:50:53 +01001/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2020-2022,2023 Thomas E. Dickey *
3 * Copyright 2009-2016,2017 Free Software Foundation, Inc. *
Steve Kondikae271bc2015-11-15 02:50:53 +01004 * *
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/*
micky3879b9f5e72025-07-08 18:04:53 -040030 * $Id: test_addwstr.c,v 1.22 2023/05/27 20:13:10 tom Exp $
Steve Kondikae271bc2015-11-15 02:50:53 +010031 *
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
micky3879b9f5e72025-07-08 18:04:53 -040052#undef AddCh
Steve Kondikae271bc2015-11-15 02:50:53 +010053#undef MvAddCh
54#undef MvAddStr
55#undef MvWAddCh
56#undef MvWAddStr
micky3879b9f5e72025-07-08 18:04:53 -040057#undef WAddCh
Steve Kondikae271bc2015-11-15 02:50:53 +010058
micky3879b9f5e72025-07-08 18:04:53 -040059/*
60 * redefinitions to simplify comparison between test_*str programs
61 */
Steve Kondikae271bc2015-11-15 02:50:53 +010062#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
73typedef enum {
74 oDefault = 0,
75 oMove = 1,
76 oWindow = 2,
77 oMoveWindow = 3
78} Options;
79
80static bool m_opt = FALSE;
81static bool w_opt = FALSE;
82static int n_opt = -1;
83
84static void
85legend(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);
micky3879b9f5e72025-07-08 18:04:53 -0400111 wprintw(win, "Level %d,%s added %d characters <", level, showstate, length);
Steve Kondikae271bc2015-11-15 02:50:53 +0100112 waddwstr(win, buffer);
113 waddstr(win, ">");
114}
115
116static int
micky3879b9f5e72025-07-08 18:04:53 -0400117ColOf(const wchar_t *const buffer, int length, int margin)
Steve Kondikae271bc2015-11-15 02:50:53 +0100118{
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
153static int
154ConvertCh(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
167static int
168MvWAddCh(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
181static int
182MvAddCh(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
195static int
196WAddCh(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
209static int
210AddCh(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)))
224static void
micky3879b9f5e72025-07-08 18:04:53 -0400225recursive_test(int level)
Steve Kondikae271bc2015-11-15 02:50:53 +0100226{
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;
micky3879b9f5e72025-07-08 18:04:53 -0400240 Options option = (Options) ((unsigned) (m_opt
241 ? oMove
242 : oDefault)
243 | (unsigned) ((w_opt || (level > 0))
244 ? oWindow
245 : oDefault));
Steve Kondikae271bc2015-11-15 02:50:53 +0100246
247 if (first) {
248 static char cmd[80];
249 setlocale(LC_ALL, "");
250
micky3879b9f5e72025-07-08 18:04:53 -0400251 _nc_STRCPY(cmd, "TABSIZE=8", sizeof(cmd));
252 putenv(cmd);
Steve Kondikae271bc2015-11-15 02:50:53 +0100253
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 /*
micky3879b9f5e72025-07-08 18:04:53 -0400260 * Show the characters added in color, to distinguish from those that
Steve Kondikae271bc2015-11-15 02:50:53 +0100261 * 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:
micky3879b9f5e72025-07-08 18:04:53 -0400308 recursive_test(level + 1);
Steve Kondikae271bc2015-11-15 02:50:53 +0100309
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
micky3879b9f5e72025-07-08 18:04:53 -0400378 /* do the corresponding single-character add */
Steve Kondikae271bc2015-11-15 02:50:53 +0100379 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
micky3879b9f5e72025-07-08 18:04:53 -0400433 /* do the corresponding single-character add */
Steve Kondikae271bc2015-11-15 02:50:53 +0100434 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
469static void
micky3879b9f5e72025-07-08 18:04:53 -0400470usage(int ok)
Steve Kondikae271bc2015-11-15 02:50:53 +0100471{
472 static const char *tbl[] =
473 {
micky3879b9f5e72025-07-08 18:04:53 -0400474 "Usage: test_addwstr [options]"
Steve Kondikae271bc2015-11-15 02:50:53 +0100475 ,""
micky3879b9f5e72025-07-08 18:04:53 -0400476 ,USAGE_COMMON
Steve Kondikae271bc2015-11-15 02:50:53 +0100477 ,"Options:"
micky3879b9f5e72025-07-08 18:04:53 -0400478 ," -f FILE read data from given file"
479 ," -n NUM limit string-adds to NUM bytes on ^N replay"
480 ," -m perform wmove/move separately from add-functions"
481 ," -w use window-parameter even when stdscr would be implied"
Steve Kondikae271bc2015-11-15 02:50:53 +0100482 };
483 unsigned n;
484 for (n = 0; n < SIZEOF(tbl); ++n)
485 fprintf(stderr, "%s\n", tbl[n]);
micky3879b9f5e72025-07-08 18:04:53 -0400486 ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
Steve Kondikae271bc2015-11-15 02:50:53 +0100487}
micky3879b9f5e72025-07-08 18:04:53 -0400488/* *INDENT-OFF* */
489VERSION_COMMON()
490/* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +0100491
492int
micky3879b9f5e72025-07-08 18:04:53 -0400493main(int argc, char *argv[])
Steve Kondikae271bc2015-11-15 02:50:53 +0100494{
495 int ch;
496
497 setlocale(LC_ALL, "");
498
micky3879b9f5e72025-07-08 18:04:53 -0400499 while ((ch = getopt(argc, argv, OPTS_COMMON "f:mn:w")) != -1) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100500 switch (ch) {
501 case 'f':
502 init_linedata(optarg);
503 break;
504 case 'm':
505 m_opt = TRUE;
506 break;
507 case 'n':
508 n_opt = atoi(optarg);
509 if (n_opt == 0)
510 n_opt = -1;
511 break;
512 case 'w':
513 w_opt = TRUE;
514 break;
micky3879b9f5e72025-07-08 18:04:53 -0400515 case OPTS_VERSION:
516 show_version(argv);
517 ExitProgram(EXIT_SUCCESS);
Steve Kondikae271bc2015-11-15 02:50:53 +0100518 default:
micky3879b9f5e72025-07-08 18:04:53 -0400519 usage(ch == OPTS_USAGE);
520 /* NOTREACHED */
Steve Kondikae271bc2015-11-15 02:50:53 +0100521 }
522 }
523 if (optind < argc)
micky3879b9f5e72025-07-08 18:04:53 -0400524 usage(FALSE);
Steve Kondikae271bc2015-11-15 02:50:53 +0100525
micky3879b9f5e72025-07-08 18:04:53 -0400526 recursive_test(0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100527 endwin();
528 ExitProgram(EXIT_SUCCESS);
529}
530#else
531int
532main(void)
533{
534 printf("This program requires the wide-ncurses library\n");
535 ExitProgram(EXIT_FAILURE);
536}
537#endif