• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2020-2022,2023 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.22 2023/05/27 20:13:10 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
legend(WINDOW * win,int level,Options state,wchar_t * buffer,int length)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
ColOf(const wchar_t * const buffer,int length,int margin)117 ColOf(const wchar_t *const 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
ConvertCh(chtype source,cchar_t * target)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
MvWAddCh(WINDOW * win,int y,int x,chtype ch)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
MvAddCh(int y,int x,chtype ch)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
WAddCh(WINDOW * win,chtype ch)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
AddCh(chtype ch)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
recursive_test(int level)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
usage(int ok)470 usage(int ok)
471 {
472     static const char *tbl[] =
473     {
474 	"Usage: test_addwstr [options]"
475 	,""
476 	,USAGE_COMMON
477 	,"Options:"
478 	," -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"
482     };
483     unsigned n;
484     for (n = 0; n < SIZEOF(tbl); ++n)
485 	fprintf(stderr, "%s\n", tbl[n]);
486     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
487 }
488 /* *INDENT-OFF* */
VERSION_COMMON()489 VERSION_COMMON()
490 /* *INDENT-ON* */
491 
492 int
493 main(int argc, char *argv[])
494 {
495     int ch;
496 
497     setlocale(LC_ALL, "");
498 
499     while ((ch = getopt(argc, argv, OPTS_COMMON "f:mn:w")) != -1) {
500 	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;
515 	case OPTS_VERSION:
516 	    show_version(argv);
517 	    ExitProgram(EXIT_SUCCESS);
518 	default:
519 	    usage(ch == OPTS_USAGE);
520 	    /* NOTREACHED */
521 	}
522     }
523     if (optind < argc)
524 	usage(FALSE);
525 
526     recursive_test(0);
527     endwin();
528     ExitProgram(EXIT_SUCCESS);
529 }
530 #else
531 int
main(void)532 main(void)
533 {
534     printf("This program requires the wide-ncurses library\n");
535     ExitProgram(EXIT_FAILURE);
536 }
537 #endif
538