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