• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2020-2021,2022 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_add_wchstr.c,v 1.34 2022/12/10 22:28:50 tom Exp $
31  *
32  * Demonstrate the waddwchstr() 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  * *addwchstr() 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 MvWAddChStr
57 #undef MvWAddStr
58 #undef WAddCh
59 
60 /*
61  * redefinitions to simplify comparison between test_*str programs
62  */
63 #define AddNStr    add_wchnstr
64 #define AddStr     add_wchstr
65 #define MvAddNStr  (void) mvadd_wchnstr
66 #define MvAddStr   (void) mvadd_wchstr
67 #define MvWAddNStr (void) mvwadd_wchnstr
68 #define MvWAddStr  (void) mvwadd_wchstr
69 #define MvWAddChStr(w,y,x,s)	(void) mvwadd_wchstr((w),(y),(x),(s))
70 #define WAddNStr   wadd_wchnstr
71 #define WAddStr    wadd_wchstr
72 
73 #define MY_TABSIZE 8
74 
75 typedef enum {
76     oDefault = 0,
77     oMove = 1,
78     oWindow = 2,
79     oMoveWindow = 3
80 } Options;
81 
82 static bool m_opt = FALSE;
83 static bool pass_ctls = FALSE;
84 static bool w_opt = FALSE;
85 static int n_opt = -1;
86 
87 static cchar_t *temp_buffer;
88 static size_t temp_length;
89 
90 #define TempBuffer(source_len, source_cast) \
91     if (source != 0) { \
92 	const char *temp; \
93 	size_t need = source_len + 1; \
94 	wchar_t have[2]; \
95 	int n = 0; \
96  \
97 	if (need > temp_length) { \
98 	    temp_length = need * 2; \
99 	    temp_buffer = typeRealloc(cchar_t, temp_length, temp_buffer); \
100 	    if (!temp_buffer) \
101 		failed("TempBuffer"); \
102 	} \
103 	have[0] = 0; \
104 	have[1] = 0; \
105 	do { \
106 	    have[0] = source_cast; \
107 	    if (!pass_ctls \
108 	     && have[0] != 0 \
109 	     && have[0] < 256 \
110 	     && (temp = unctrl((chtype) have[0])) != 0 \
111 	     && strlen(temp) > 1) { \
112 		while (*temp != '\0') { \
113 		    have[0] = (wchar_t) *temp++; \
114 		    setcchar(&temp_buffer[n++], have, A_NORMAL, 0, NULL); \
115 		} \
116 	    } else { \
117 		setcchar(&temp_buffer[n++], have, A_NORMAL, 0, NULL); \
118 	    } \
119 	} while (have[0] != 0); \
120     } else if (temp_buffer != 0) { \
121 	free(temp_buffer); \
122 	temp_buffer = 0; \
123 	temp_length = 0; \
124     } \
125     return temp_buffer;
126 
127 static size_t
ChWLen(const wchar_t * source)128 ChWLen(const wchar_t *source)
129 {
130     size_t result = wcslen(source);
131 
132     if (!pass_ctls) {
133 	size_t adjust = 0;
134 	size_t n;
135 
136 	for (n = 0; source[n] != 0; ++n) {
137 	    const char *s;
138 
139 	    if ((source[n] < 256) && (s = unctrl((chtype) source[n])) != 0) {
140 		adjust += (strlen(s) - 1);
141 	    }
142 	}
143 	result += adjust;
144     }
145     return result;
146 }
147 
148 static cchar_t *
ChStr(const char * source)149 ChStr(const char *source)
150 {
151     TempBuffer(strlen(source), UChar(*source++));
152 }
153 
154 static cchar_t *
ChWStr(const wchar_t * source)155 ChWStr(const wchar_t *source)
156 {
157     TempBuffer(ChWLen(source), *source++);
158 }
159 
160 static void
legend(WINDOW * win,int level,Options state,wchar_t * buffer,int length)161 legend(WINDOW *win, int level, Options state, wchar_t *buffer, int length)
162 {
163     const char *showstate;
164 
165     switch (state) {
166     default:
167     case oDefault:
168 	showstate = "";
169 	break;
170     case oMove:
171 	showstate = " (mvXXX)";
172 	break;
173     case oWindow:
174 	showstate = " (winXXX)";
175 	break;
176     case oMoveWindow:
177 	showstate = " (mvwinXXX)";
178 	break;
179     }
180 
181     wmove(win, 0, 0);
182     wprintw(win,
183 	    "The Strings/Chars displays should match.  Enter any characters, except:\n");
184     wprintw(win,
185 	    "down-arrow or ^N to repeat on next line, ^W for inner window, ESC to exit.\n");
186     wclrtoeol(win);
187     wprintw(win, "Level %d,%s added %d characters <", level,
188 	    showstate, length);
189     waddwstr(win, buffer);
190     waddstr(win, ">");
191 }
192 
193 static int
ColOf(const wchar_t * buffer,int length,int margin)194 ColOf(const wchar_t *buffer, int length, int margin)
195 {
196     int n;
197     int result;
198 
199     for (n = 0, result = margin + 1; n < length; ++n) {
200 	int ch = buffer[n];
201 	switch (ch) {
202 	case '\n':
203 	    /* actually newline should clear the remainder of the line
204 	     * and move to the next line - but that seems a little awkward
205 	     * in this example.
206 	     */
207 	case '\r':
208 	    result = 0;
209 	    break;
210 	case '\b':
211 	    if (result > 0)
212 		--result;
213 	    break;
214 	case '\t':
215 	    result += (MY_TABSIZE - (result % MY_TABSIZE));
216 	    break;
217 	case '\177':
218 	    result += 2;
219 	    break;
220 	default:
221 	    result += wcwidth((wchar_t) ch);
222 	    if (ch < 32)
223 		++result;
224 	    break;
225 	}
226     }
227     return result;
228 }
229 
230 static int
ConvertCh(chtype source,cchar_t * target)231 ConvertCh(chtype source, cchar_t *target)
232 {
233     wchar_t tmp_wchar[2];
234 
235     tmp_wchar[0] = (wchar_t) source;
236     tmp_wchar[1] = 0;
237     if (setcchar(target, tmp_wchar, A_NORMAL, 0, (void *) 0) == ERR) {
238 	beep();
239 	return FALSE;
240     }
241     return TRUE;
242 }
243 
244 static int
MvWAddCh(WINDOW * win,int y,int x,chtype ch)245 MvWAddCh(WINDOW *win, int y, int x, chtype ch)
246 {
247     int code;
248     cchar_t tmp_cchar;
249 
250     if (ConvertCh(ch, &tmp_cchar)) {
251 	code = mvwadd_wch(win, y, x, &tmp_cchar);
252     } else {
253 	code = mvwaddch(win, y, x, ch);
254     }
255     return code;
256 }
257 
258 static int
MvAddCh(int y,int x,chtype ch)259 MvAddCh(int y, int x, chtype ch)
260 {
261     int code;
262     cchar_t tmp_cchar;
263 
264     if (ConvertCh(ch, &tmp_cchar)) {
265 	code = mvadd_wch(y, x, &tmp_cchar);
266     } else {
267 	code = mvaddch(y, x, ch);
268     }
269     return code;
270 }
271 
272 static int
WAddCh(WINDOW * win,chtype ch)273 WAddCh(WINDOW *win, chtype ch)
274 {
275     int code;
276     cchar_t tmp_cchar;
277 
278     if (ConvertCh(ch, &tmp_cchar)) {
279 	code = wadd_wch(win, &tmp_cchar);
280     } else {
281 	code = waddch(win, ch);
282     }
283     return code;
284 }
285 
286 static int
AddCh(chtype ch)287 AddCh(chtype ch)
288 {
289     int code;
290     cchar_t tmp_cchar;
291 
292     if (ConvertCh(ch, &tmp_cchar)) {
293 	code = add_wch(&tmp_cchar);
294     } else {
295 	code = addch(ch);
296     }
297     return code;
298 }
299 
300 #define LEN(n) ((length - (n) > n_opt) ? n_opt : (length - (n)))
301 static void
recursive_test(int level)302 recursive_test(int level)
303 {
304     static bool first = TRUE;
305 
306     int ch;
307     int limit;
308     int row = 1;
309     int col;
310     int row2, col2;
311     int length;
312     wchar_t buffer[BUFSIZ];
313     WINDOW *look = 0;
314     WINDOW *work = 0;
315     WINDOW *show = 0;
316     int margin = (2 * MY_TABSIZE) - 1;
317     Options option = (Options) ((unsigned) (m_opt
318 					    ? oMove
319 					    : oDefault)
320 				| (unsigned) ((w_opt || (level > 0))
321 					      ? oWindow
322 					      : oDefault));
323 
324     if (first) {
325 	static char cmd[80];
326 	setlocale(LC_ALL, "");
327 
328 	_nc_STRCPY(cmd, "TABSIZE=8", sizeof(cmd));
329 	putenv(cmd);
330 
331 	initscr();
332 	(void) cbreak();	/* take input chars one at a time, no wait for \n */
333 	(void) noecho();	/* don't echo input */
334 	keypad(stdscr, TRUE);
335 
336 	/*
337 	 * Show the characters added in color, to distinguish from those that
338 	 * are shifted.
339 	 */
340 	if (has_colors()) {
341 	    start_color();
342 	    init_pair(1, COLOR_WHITE, COLOR_BLUE);
343 	}
344     }
345 
346     limit = LINES - 5;
347     if (level > 0) {
348 	look = newwin(limit, COLS - (2 * (level - 1)), 0, level - 1);
349 	work = newwin(limit - 2, COLS - (2 * level), 1, level);
350 	show = newwin(4, COLS, limit + 1, 0);
351 	box(look, 0, 0);
352 	wnoutrefresh(look);
353 	limit -= 2;
354     } else {
355 	work = stdscr;
356 	show = derwin(stdscr, 4, COLS, limit + 1, 0);
357     }
358     keypad(work, TRUE);
359 
360     for (col = margin + 1; col < COLS; col += MY_TABSIZE)
361 	MvWVLine(work, row, col, '.', limit - 2);
362 
363     MvWVLine(work, row, margin, ACS_VLINE, limit - 2);
364     MvWVLine(work, row, margin + 1, ACS_VLINE, limit - 2);
365     limit /= 2;
366 
367     MvWAddChStr(work, 1, 2, ChStr("String"));
368     MvWAddChStr(work, limit + 1, 2, ChStr("Chars"));
369     wnoutrefresh(work);
370 
371     buffer[length = 0] = '\0';
372     legend(show, level, option, buffer, length);
373     wnoutrefresh(show);
374 
375     doupdate();
376 
377     if (has_colors()) {
378 	wbkgdset(work, (chtype) (COLOR_PAIR(1) | ' '));
379     }
380 
381     while ((ch = read_linedata(work)) != ERR && !isQUIT(ch)) {
382 	wmove(work, row, margin + 1);
383 	switch (ch) {
384 	case key_RECUR:
385 	    recursive_test(level + 1);
386 
387 	    if (look)
388 		touchwin(look);
389 	    touchwin(work);
390 	    touchwin(show);
391 
392 	    if (look)
393 		wnoutrefresh(look);
394 	    wnoutrefresh(work);
395 	    wnoutrefresh(show);
396 
397 	    doupdate();
398 	    break;
399 	case key_NEWLINE:
400 	    if (row < limit) {
401 		++row;
402 		/* put the whole string in, all at once */
403 		col2 = margin + 1;
404 		switch (option) {
405 		case oDefault:
406 		    if (n_opt > 1) {
407 			for (col = 0; col < length; col += n_opt) {
408 			    col2 = ColOf(buffer, col, margin);
409 			    if (move(row, col2) != ERR) {
410 				AddNStr(ChWStr(buffer + col), LEN(col));
411 			    }
412 			}
413 		    } else {
414 			if (move(row, col2) != ERR) {
415 			    AddStr(ChWStr(buffer));
416 			}
417 		    }
418 		    break;
419 		case oMove:
420 		    if (n_opt > 1) {
421 			for (col = 0; col < length; col += n_opt) {
422 			    col2 = ColOf(buffer, col, margin);
423 			    MvAddNStr(row, col2, ChWStr(buffer + col), LEN(col));
424 			}
425 		    } else {
426 			MvAddStr(row, col2, ChWStr(buffer));
427 		    }
428 		    break;
429 		case oWindow:
430 		    if (n_opt > 1) {
431 			for (col = 0; col < length; col += n_opt) {
432 			    col2 = ColOf(buffer, col, margin);
433 			    if (wmove(work, row, col2) != ERR) {
434 				WAddNStr(work, ChWStr(buffer + col), LEN(col));
435 			    }
436 			}
437 		    } else {
438 			if (wmove(work, row, col2) != ERR) {
439 			    WAddStr(work, ChWStr(buffer));
440 			}
441 		    }
442 		    break;
443 		case oMoveWindow:
444 		    if (n_opt > 1) {
445 			for (col = 0; col < length; col += n_opt) {
446 			    col2 = ColOf(buffer, col, margin);
447 			    MvWAddNStr(work, row, col2, ChWStr(buffer +
448 							       col), LEN(col));
449 			}
450 		    } else {
451 			MvWAddStr(work, row, col2, ChWStr(buffer));
452 		    }
453 		    break;
454 		}
455 
456 		/* do the corresponding single-character add */
457 		row2 = limit + row;
458 		for (col = 0; col < length; ++col) {
459 		    col2 = ColOf(buffer, col, margin);
460 		    switch (option) {
461 		    case oDefault:
462 			if (move(row2, col2) != ERR) {
463 			    AddCh((chtype) buffer[col]);
464 			}
465 			break;
466 		    case oMove:
467 			MvAddCh(row2, col2, (chtype) buffer[col]);
468 			break;
469 		    case oWindow:
470 			if (wmove(work, row2, col2) != ERR) {
471 			    WAddCh(work, (chtype) buffer[col]);
472 			}
473 			break;
474 		    case oMoveWindow:
475 			MvWAddCh(work, row2, col2, (chtype) buffer[col]);
476 			break;
477 		    }
478 		}
479 	    } else {
480 		beep();
481 	    }
482 	    break;
483 	default:
484 	    buffer[length++] = (wchar_t) ch;
485 	    buffer[length] = '\0';
486 
487 	    /* put the string in, one character at a time */
488 	    col = ColOf(buffer, length - 1, margin);
489 	    switch (option) {
490 	    case oDefault:
491 		if (move(row, col) != ERR) {
492 		    AddStr(ChWStr(buffer + length - 1));
493 		}
494 		break;
495 	    case oMove:
496 		MvAddStr(row, col, ChWStr(buffer + length - 1));
497 		break;
498 	    case oWindow:
499 		if (wmove(work, row, col) != ERR) {
500 		    WAddStr(work, ChWStr(buffer + length - 1));
501 		}
502 		break;
503 	    case oMoveWindow:
504 		MvWAddStr(work, row, col, ChWStr(buffer + length - 1));
505 		break;
506 	    }
507 
508 	    /* do the corresponding single-character add */
509 	    switch (option) {
510 	    case oDefault:
511 		if (move(limit + row, col) != ERR) {
512 		    AddCh((chtype) ch);
513 		}
514 		break;
515 	    case oMove:
516 		MvAddCh(limit + row, col, (chtype) ch);
517 		break;
518 	    case oWindow:
519 		if (wmove(work, limit + row, col) != ERR) {
520 		    WAddCh(work, (chtype) ch);
521 		}
522 		break;
523 	    case oMoveWindow:
524 		MvWAddCh(work, limit + row, col, (chtype) ch);
525 		break;
526 	    }
527 
528 	    wnoutrefresh(work);
529 
530 	    legend(show, level, option, buffer, length);
531 	    wnoutrefresh(show);
532 
533 	    doupdate();
534 	    break;
535 	}
536     }
537     delwin(show);
538     if (level > 0) {
539 	delwin(work);
540 	delwin(look);
541     }
542 }
543 
544 static void
usage(int ok)545 usage(int ok)
546 {
547     static const char *tbl[] =
548     {
549 	"Usage: test_add_wchstr [options]"
550 	,""
551 	,USAGE_COMMON
552 	,"Options:"
553 	," -f FILE  read data from given file"
554 	," -n NUM   limit string-adds to NUM bytes on ^N replay"
555 	," -m       perform wmove/move separately from add-functions"
556 	," -p       pass-thru control characters without using unctrl()"
557 	," -w       use window-parameter even when stdscr would be implied"
558     };
559     unsigned n;
560     for (n = 0; n < SIZEOF(tbl); ++n)
561 	fprintf(stderr, "%s\n", tbl[n]);
562     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
563 }
564 /* *INDENT-OFF* */
VERSION_COMMON()565 VERSION_COMMON()
566 /* *INDENT-ON* */
567 
568 int
569 main(int argc, char *argv[])
570 {
571     int ch;
572 
573     setlocale(LC_ALL, "");
574 
575     while ((ch = getopt(argc, argv, OPTS_COMMON "f:mn:pw")) != -1) {
576 	switch (ch) {
577 	case 'f':
578 	    init_linedata(optarg);
579 	    break;
580 	case 'm':
581 	    m_opt = TRUE;
582 	    break;
583 	case 'n':
584 	    n_opt = atoi(optarg);
585 	    if (n_opt == 0)
586 		n_opt = -1;
587 	    break;
588 	case 'p':
589 	    pass_ctls = TRUE;
590 	    break;
591 	case 'w':
592 	    w_opt = TRUE;
593 	    break;
594 	case OPTS_VERSION:
595 	    show_version(argv);
596 	    ExitProgram(EXIT_SUCCESS);
597 	default:
598 	    usage(ch == OPTS_USAGE);
599 	    /* NOTREACHED */
600 	}
601     }
602     if (optind < argc)
603 	usage(FALSE);
604 
605     recursive_test(0);
606     endwin();
607 #if NO_LEAKS
608     free(temp_buffer);
609 #endif
610     ExitProgram(EXIT_SUCCESS);
611 }
612 #else
613 int
main(void)614 main(void)
615 {
616     printf("This program requires the wide-ncurses library\n");
617     ExitProgram(EXIT_FAILURE);
618 }
619 #endif
620