• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2020,2022 Thomas E. Dickey                                     *
3  * Copyright 2002-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: ins_wide.c,v 1.30 2022/12/10 22:28:50 tom Exp $
31  *
32  * Demonstrate the wins_wstr() and wins_wch functions.
33  * Thomas Dickey - 2002/11/23
34  *
35  * Note: to provide inputs for *ins_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 *ins_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  * *ins_wstr() 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 /* definitions to make it simpler to compare with inserts.c */
53 #define InsNStr    ins_nwstr
54 #define InsStr     ins_wstr
55 #define MvInsNStr  (void) mvins_nwstr
56 #define MvInsStr   (void) mvins_wstr
57 #define MvWInsNStr (void) mvwins_nwstr
58 #define MvWInsStr  (void) mvwins_wstr
59 #define WInsNStr   wins_nwstr
60 #define WInsStr    wins_wstr
61 
62 #define MY_TABSIZE 8
63 
64 typedef enum {
65     oDefault = 0,
66     oMove = 1,
67     oWindow = 2,
68     oMoveWindow = 3
69 } Options;
70 
71 static bool m_opt = FALSE;
72 static bool w_opt = FALSE;
73 static int n_opt = -1;
74 
75 static void
legend(WINDOW * win,int level,Options state,wchar_t * buffer,int length)76 legend(WINDOW *win, int level, Options state, wchar_t *buffer, int length)
77 {
78     const char *showstate;
79 
80     switch (state) {
81     default:
82     case oDefault:
83 	showstate = "";
84 	break;
85     case oMove:
86 	showstate = " (mvXXX)";
87 	break;
88     case oWindow:
89 	showstate = " (winXXX)";
90 	break;
91     case oMoveWindow:
92 	showstate = " (mvwinXXX)";
93 	break;
94     }
95 
96     wmove(win, 0, 0);
97     wprintw(win,
98 	    "The Strings/Chars displays should match.  Enter any characters, except:\n");
99     wprintw(win,
100 	    "down-arrow or ^N to repeat on next line, ^W for inner window, ESC to exit.\n");
101     wclrtoeol(win);
102     wprintw(win, "Level %d,%s inserted %d characters <", level,
103 	    showstate, length);
104     waddwstr(win, buffer);
105     waddstr(win, ">");
106 }
107 
108 static int
ColOf(const wchar_t * buffer,int length,int margin)109 ColOf(const wchar_t *buffer, int length, int margin)
110 {
111     int n;
112     int result;
113 
114     for (n = 0, result = margin + 1; n < length; ++n) {
115 	int ch = buffer[n];
116 	switch (ch) {
117 	case '\n':
118 	    /* actually newline should clear the remainder of the line
119 	     * and move to the next line - but that seems a little awkward
120 	     * in this example.
121 	     */
122 	case '\r':
123 	    result = 0;
124 	    break;
125 	case '\b':
126 	    if (result > 0)
127 		--result;
128 	    break;
129 	case '\t':
130 	    result += (MY_TABSIZE - (result % MY_TABSIZE));
131 	    break;
132 	case '\177':
133 	    result += 2;
134 	    break;
135 	default:
136 	    result += wcwidth((wchar_t) ch);
137 	    if (ch < 32)
138 		++result;
139 	    break;
140 	}
141     }
142     return result;
143 }
144 
145 static int
ConvertCh(chtype source,cchar_t * target)146 ConvertCh(chtype source, cchar_t *target)
147 {
148     wchar_t tmp_wchar[2];
149 
150     tmp_wchar[0] = (wchar_t) source;
151     tmp_wchar[1] = 0;
152     if (setcchar(target, tmp_wchar, A_NORMAL, 0, (void *) 0) == ERR) {
153 	beep();
154 	return FALSE;
155     }
156     return TRUE;
157 }
158 
159 static int
MvWInsCh(WINDOW * win,int y,int x,chtype ch)160 MvWInsCh(WINDOW *win, int y, int x, chtype ch)
161 {
162     int code;
163     cchar_t tmp_cchar;
164 
165     if (ConvertCh(ch, &tmp_cchar)) {
166 	code = mvwins_wch(win, y, x, &tmp_cchar);
167     } else {
168 	code = mvwinsch(win, y, x, ch);
169     }
170     return code;
171 }
172 
173 static int
MvInsCh(int y,int x,chtype ch)174 MvInsCh(int y, int x, chtype ch)
175 {
176     int code;
177     cchar_t tmp_cchar;
178 
179     if (ConvertCh(ch, &tmp_cchar)) {
180 	code = mvins_wch(y, x, &tmp_cchar);
181     } else {
182 	code = mvinsch(y, x, ch);
183     }
184     return code;
185 }
186 
187 static int
WInsCh(WINDOW * win,chtype ch)188 WInsCh(WINDOW *win, chtype ch)
189 {
190     int code;
191     cchar_t tmp_cchar;
192 
193     if (ConvertCh(ch, &tmp_cchar)) {
194 	code = wins_wch(win, &tmp_cchar);
195     } else {
196 	code = winsch(win, ch);
197     }
198     return code;
199 }
200 
201 static int
InsCh(chtype ch)202 InsCh(chtype ch)
203 {
204     int code;
205     cchar_t tmp_cchar;
206 
207     if (ConvertCh(ch, &tmp_cchar)) {
208 	code = ins_wch(&tmp_cchar);
209     } else {
210 	code = insch(ch);
211     }
212     return code;
213 }
214 
215 #define LEN(n) ((length - (n) > n_opt) ? n_opt : (length - (n)))
216 static void
test_inserts(int level)217 test_inserts(int level)
218 {
219     static bool first = TRUE;
220 
221     int ch;
222     int limit;
223     int row = 1;
224     int col;
225     int row2, col2;
226     int length;
227     wchar_t buffer[BUFSIZ];
228     WINDOW *look = 0;
229     WINDOW *work = 0;
230     WINDOW *show = 0;
231     int margin = (2 * MY_TABSIZE) - 1;
232     Options option = (Options) ((int) (m_opt ? oMove : oDefault)
233 				| (int) ((w_opt || (level > 0))
234 					 ? oWindow : oDefault));
235 
236     if (first) {
237 	static char cmd[80];
238 	setlocale(LC_ALL, "");
239 
240 	_nc_STRCPY(cmd, "TABSIZE=8", sizeof(cmd));
241 	putenv(cmd);
242 
243 	initscr();
244 	(void) cbreak();	/* take input chars one at a time, no wait for \n */
245 	(void) noecho();	/* don't echo input */
246 	keypad(stdscr, TRUE);
247 
248 	/*
249 	 * Show the characters inserted in color, to distinguish from those
250 	 * that are shifted.
251 	 */
252 	if (has_colors()) {
253 	    start_color();
254 	    init_pair(1, COLOR_WHITE, COLOR_BLUE);
255 	}
256     }
257 
258     limit = LINES - 5;
259     if (level > 0) {
260 	look = newwin(limit, COLS - (2 * (level - 1)), 0, level - 1);
261 	work = newwin(limit - 2, COLS - (2 * level), 1, level);
262 	show = newwin(4, COLS, limit + 1, 0);
263 	box(look, 0, 0);
264 	wnoutrefresh(look);
265 	limit -= 2;
266     } else {
267 	work = stdscr;
268 	show = derwin(stdscr, 4, COLS, limit + 1, 0);
269     }
270     keypad(work, TRUE);
271 
272     for (col = margin + 1; col < COLS; col += MY_TABSIZE)
273 	MvWVLine(work, row, col, '.', limit - 2);
274 
275     MvWVLine(work, row, margin, ACS_VLINE, limit - 2);
276     MvWVLine(work, row, margin + 1, ACS_VLINE, limit - 2);
277     limit /= 2;
278 
279     MvWAddStr(work, 1, 2, "String");
280     MvWAddStr(work, limit + 1, 2, "Chars");
281     wnoutrefresh(work);
282 
283     buffer[length = 0] = '\0';
284     legend(show, level, option, buffer, length);
285     wnoutrefresh(show);
286 
287     doupdate();
288 
289     if (has_colors()) {
290 	wbkgdset(work, (chtype) (COLOR_PAIR(1) | ' '));
291     }
292 
293     while ((ch = read_linedata(work)) != ERR && !isQUIT(ch)) {
294 	wmove(work, row, margin + 1);
295 	switch (ch) {
296 	case key_RECUR:
297 	    test_inserts(level + 1);
298 
299 	    if (look)
300 		touchwin(look);
301 	    touchwin(work);
302 	    touchwin(show);
303 
304 	    if (look)
305 		wnoutrefresh(look);
306 	    wnoutrefresh(work);
307 	    wnoutrefresh(show);
308 
309 	    doupdate();
310 	    break;
311 	case key_NEWLINE:
312 	    if (row < limit) {
313 		++row;
314 		/* put the whole string in, all at once */
315 		col2 = margin + 1;
316 		switch (option) {
317 		case oDefault:
318 		    if (n_opt > 1) {
319 			for (col = 0; col < length; col += n_opt) {
320 			    col2 = ColOf(buffer, col, margin);
321 			    if (move(row, col2) != ERR) {
322 				InsNStr(buffer + col, LEN(col));
323 			    }
324 			}
325 		    } else {
326 			if (move(row, col2) != ERR) {
327 			    InsStr(buffer);
328 			}
329 		    }
330 		    break;
331 		case oMove:
332 		    if (n_opt > 1) {
333 			for (col = 0; col < length; col += n_opt) {
334 			    col2 = ColOf(buffer, col, margin);
335 			    MvInsNStr(row, col2, buffer + col, LEN(col));
336 			}
337 		    } else {
338 			MvInsStr(row, col2, buffer);
339 		    }
340 		    break;
341 		case oWindow:
342 		    if (n_opt > 1) {
343 			for (col = 0; col < length; col += n_opt) {
344 			    col2 = ColOf(buffer, col, margin);
345 			    if (wmove(work, row, col2) != ERR) {
346 				WInsNStr(work, buffer + col, LEN(col));
347 			    }
348 			}
349 		    } else {
350 			if (wmove(work, row, col2) != ERR) {
351 			    WInsStr(work, buffer);
352 			}
353 		    }
354 		    break;
355 		case oMoveWindow:
356 		    if (n_opt > 1) {
357 			for (col = 0; col < length; col += n_opt) {
358 			    col2 = ColOf(buffer, col, margin);
359 			    MvWInsNStr(work, row, col2, buffer + col, LEN(col));
360 			}
361 		    } else {
362 			MvWInsStr(work, row, col2, buffer);
363 		    }
364 		    break;
365 		}
366 
367 		/* do the corresponding single-character insertion */
368 		row2 = limit + row;
369 		for (col = 0; col < length; ++col) {
370 		    col2 = ColOf(buffer, col, margin);
371 		    switch (option) {
372 		    case oDefault:
373 			if (move(row2, col2) != ERR) {
374 			    InsCh((chtype) buffer[col]);
375 			}
376 			break;
377 		    case oMove:
378 			MvInsCh(row2, col2, (chtype) buffer[col]);
379 			break;
380 		    case oWindow:
381 			if (wmove(work, row2, col2) != ERR) {
382 			    WInsCh(work, (chtype) buffer[col]);
383 			}
384 			break;
385 		    case oMoveWindow:
386 			MvWInsCh(work, row2, col2, (chtype) buffer[col]);
387 			break;
388 		    }
389 		}
390 	    } else {
391 		beep();
392 	    }
393 	    break;
394 	default:
395 	    if (length >= BUFSIZ - 2)
396 		break;
397 	    buffer[length++] = (wchar_t) ch;
398 	    buffer[length] = '\0';
399 
400 	    /* put the string in, one character at a time */
401 	    col = ColOf(buffer, length - 1, margin);
402 	    switch (option) {
403 	    case oDefault:
404 		if (move(row, col) != ERR) {
405 		    InsStr(buffer + length - 1);
406 		}
407 		break;
408 	    case oMove:
409 		MvInsStr(row, col, buffer + length - 1);
410 		break;
411 	    case oWindow:
412 		if (wmove(work, row, col) != ERR) {
413 		    WInsStr(work, buffer + length - 1);
414 		}
415 		break;
416 	    case oMoveWindow:
417 		MvWInsStr(work, row, col, buffer + length - 1);
418 		break;
419 	    }
420 
421 	    /* do the corresponding single-character insertion */
422 	    switch (option) {
423 	    case oDefault:
424 		if (move(limit + row, col) != ERR) {
425 		    InsCh((chtype) ch);
426 		}
427 		break;
428 	    case oMove:
429 		MvInsCh(limit + row, col, (chtype) ch);
430 		break;
431 	    case oWindow:
432 		if (wmove(work, limit + row, col) != ERR) {
433 		    WInsCh(work, (chtype) ch);
434 		}
435 		break;
436 	    case oMoveWindow:
437 		MvWInsCh(work, limit + row, col, (chtype) ch);
438 		break;
439 	    }
440 
441 	    wnoutrefresh(work);
442 
443 	    legend(show, level, option, buffer, length);
444 	    wnoutrefresh(show);
445 
446 	    doupdate();
447 	    break;
448 	}
449     }
450     if (level > 0) {
451 	delwin(work);
452 	delwin(look);
453     }
454     delwin(show);
455 }
456 
457 static void
usage(int ok)458 usage(int ok)
459 {
460     static const char *tbl[] =
461     {
462 	"Usage: ins_wide [options]"
463 	,""
464 	,USAGE_COMMON
465 	,"Options:"
466 	," -f FILE  read data from given file"
467 	," -n NUM   limit string-inserts to NUM bytes on ^N replay"
468 	," -m       perform wmove/move separately from insert-functions"
469 	," -w       use window-parameter even when stdscr would be implied"
470     };
471     unsigned n;
472     for (n = 0; n < SIZEOF(tbl); ++n)
473 	fprintf(stderr, "%s\n", tbl[n]);
474     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
475 }
476 /* *INDENT-OFF* */
VERSION_COMMON()477 VERSION_COMMON()
478 /* *INDENT-ON* */
479 
480 int
481 main(int argc, char *argv[])
482 {
483     int ch;
484 
485     setlocale(LC_ALL, "");
486 
487     while ((ch = getopt(argc, argv, OPTS_COMMON "f:mn:w")) != -1) {
488 	switch (ch) {
489 	case 'f':
490 	    init_linedata(optarg);
491 	    break;
492 	case 'm':
493 	    m_opt = TRUE;
494 	    break;
495 	case 'n':
496 	    n_opt = atoi(optarg);
497 	    if (n_opt == 0)
498 		n_opt = -1;
499 	    break;
500 	case 'w':
501 	    w_opt = TRUE;
502 	    break;
503 	case OPTS_VERSION:
504 	    show_version(argv);
505 	    ExitProgram(EXIT_SUCCESS);
506 	default:
507 	    usage(ch == OPTS_USAGE);
508 	    /* NOTREACHED */
509 	}
510     }
511     if (optind < argc)
512 	usage(FALSE);
513 
514     test_inserts(0);
515     endwin();
516     ExitProgram(EXIT_SUCCESS);
517 }
518 #else
519 int
main(void)520 main(void)
521 {
522     printf("This program requires the wide-ncurses library\n");
523     ExitProgram(EXIT_FAILURE);
524 }
525 #endif
526