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