• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2018-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2003-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: demo_forms.c,v 1.63 2023/11/11 00:29:53 tom Exp $
31  *
32  * Demonstrate a variety of functions from the form library.
33  * Thomas Dickey - 2003/4/26
34  */
35 
36 #include <test.priv.h>
37 
38 #if USE_LIBFORM
39 
40 #include <edit_field.h>
41 
42 typedef struct {
43     char *name;
44     char *value;
45 } MY_DATA;
46 
47 static MY_DATA *my_data;
48 
49 static int d_option = 0;
50 static int j_value = 0;
51 static int m_value = 0;
52 static int o_value = 0;
53 static char *t_value = 0;
54 
55 static void
failed(const char * s)56 failed(const char *s)
57 {
58     perror(s);
59     ExitProgram(EXIT_FAILURE);
60 }
61 
62 static void
chomp(char * value)63 chomp(char *value)
64 {
65     size_t have = strlen(value);
66     while (have != 0 && (value[have - 1] == '\n' || value[have - 1] == '\r')) {
67 	value[--have] = '\0';
68     }
69 }
70 
71 static int
trimmed(const char * value)72 trimmed(const char *value)
73 {
74     int result = (int) strlen(value);
75     while (result > 0 && isspace(UChar(value[result - 1]))) {
76 	--result;
77     }
78     return result;
79 }
80 
81 static char *
get_data(const char * name)82 get_data(const char *name)
83 {
84     char *result = t_value;
85     if (my_data != 0) {
86 	int n;
87 	for (n = 0; my_data[n].name != 0; ++n) {
88 	    if (!strcmp(name, my_data[n].name)) {
89 		result = my_data[n].value;
90 		break;
91 	    }
92 	}
93     }
94     return result;
95 }
96 
97 /*
98  * Read (possibly) multi-line data with name+value pairs.
99  */
100 static void
read_data(const char * filename)101 read_data(const char *filename)
102 {
103     FILE *fp = fopen(filename, "r");
104 
105     if (fp != 0) {
106 	char buffer[BUFSIZ];
107 	char *colon;
108 	int more = 0;
109 	int item = 0;
110 
111 	my_data = typeCalloc(MY_DATA, (size_t) 100);	/* FIXME */
112 	while (fgets(buffer, sizeof(buffer), fp) != 0) {
113 	    chomp(buffer);
114 	    if (more) {
115 		if (strcmp(buffer, ".")) {
116 		    char *prior = my_data[more - 1].value;
117 		    size_t need = strlen(buffer) + 2 + strlen(prior);
118 		    char *value = typeRealloc(char, need, prior);
119 		    if (value == 0)
120 			failed("realloc");
121 		    _nc_STRCAT(value, "\n", need);
122 		    _nc_STRCAT(value, buffer, need);
123 		    my_data[more - 1].value = value;
124 		} else {
125 		    more = 0;
126 		}
127 	    } else if (*buffer == '#') {
128 		continue;
129 	    } else if ((colon = strchr(buffer, ':')) != 0) {
130 		char *name;
131 		char *value;
132 		*colon++ = '\0';
133 		name = strdup(buffer);
134 		value = strdup(colon);
135 		if (name == 0 || value == 0)
136 		    failed("strdup");
137 		my_data[item].name = name;
138 		my_data[item].value = value;
139 		more = ++item;
140 	    } else {
141 		failed("expected a colon");
142 	    }
143 	}
144 	fclose(fp);
145     } else {
146 	failed(filename);
147     }
148 }
149 
150 static FIELD *
make_label(const char * label,int frow,int fcol)151 make_label(const char *label, int frow, int fcol)
152 {
153     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
154 
155     if (f) {
156 	set_field_buffer(f, 0, label);
157 	set_field_opts(f, (int) ((unsigned) field_opts(f) & (unsigned) ~O_ACTIVE));
158     }
159     return (f);
160 }
161 
162 /*
163  * Define each field with an extra one, for reflecting "actual" text.
164  */
165 static FIELD *
make_field(const char * label,int frow,int fcol,int rows,int cols)166 make_field(const char *label, int frow, int fcol, int rows, int cols)
167 {
168     FIELD *f = new_field(rows, cols, frow, fcol, o_value, 1);
169 
170     if (f) {
171 	set_field_back(f, A_UNDERLINE);
172 	/*
173 	 * If -j and -d options are combined, -j loses.  It is documented in
174 	 * "Character User Interface Programming", page 12-15 that setting
175 	 * O_STATIC off makes the form library ignore justification.
176 	 */
177 	set_field_just(f, j_value);
178 	if (d_option) {
179 	    if (has_colors()) {
180 		set_field_fore(f, (chtype) COLOR_PAIR(2));
181 		set_field_back(f, (A_UNDERLINE | (chtype) COLOR_PAIR(3)));
182 	    } else {
183 		set_field_fore(f, A_BOLD);
184 	    }
185 	    /*
186 	     * The field_opts_off() call dumps core with Solaris curses,
187 	     * but that is a known bug in Solaris' form library -TD
188 	     */
189 	    field_opts_off(f, O_STATIC);
190 	    set_max_field(f, m_value);
191 	}
192 
193 	init_edit_field(f, get_data(label));
194     }
195     return (f);
196 }
197 
198 static void
display_form(FORM * f)199 display_form(FORM *f)
200 {
201     WINDOW *w;
202     int rows, cols;
203 
204     scale_form(f, &rows, &cols);
205 
206     /*
207      * Put the form at the upper-left corner of the display, with just a box
208      * around it.
209      */
210     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
211 	set_form_win(f, w);
212 	set_form_sub(f, derwin(w, rows, cols, 1, 2));
213 	box(w, 0, 0);
214 	keypad(w, TRUE);
215 
216 	if (post_form(f) != E_OK)
217 	    wrefresh(w);
218     }
219 }
220 
221 static void
erase_form(FORM * f)222 erase_form(FORM *f)
223 {
224     WINDOW *w = form_win(f);
225     WINDOW *s = form_sub(f);
226 
227     unpost_form(f);
228     werase(w);
229     wrefresh(w);
230     delwin(s);
231     delwin(w);
232 }
233 
234 static void
show_insert_mode(bool insert_mode)235 show_insert_mode(bool insert_mode)
236 {
237     MvAddStr(5, 57, (insert_mode
238 		     ? "form_status: insert "
239 		     : "form_status: overlay"));
240 }
241 
242 #define O_SELECTABLE (O_ACTIVE | O_VISIBLE)
243 
244 static FIELD *
another_field(FORM * form,const FIELD * const field)245 another_field(FORM *form, const FIELD *const field)
246 {
247     FIELD **f = form_fields(form);
248     FIELD *result = 0;
249     int n;
250 
251     for (n = 0; f[n] != 0; ++n) {
252 	if (f[n] != field) {
253 	    result = f[n];
254 	    field_opts_on(result, O_SELECTABLE);
255 	    break;
256 	}
257     }
258     return result;
259 }
260 
261 static int
my_form_driver(FORM * form,int c)262 my_form_driver(FORM *form, int c)
263 {
264     static bool insert_mode = TRUE;
265     FIELD *field;
266 
267     switch (c) {
268     case MY_QUIT:
269 	if (form_driver(form, REQ_VALIDATION) == E_OK)
270 	    return (TRUE);
271 	break;
272     case MY_HELP:
273 	help_edit_field();
274 	break;
275     case MY_EDT_MODE:
276 	if ((field = current_field(form)) != 0) {
277 	    set_current_field(form, another_field(form, field));
278 	    if ((unsigned) field_opts(field) & O_EDIT) {
279 		field_opts_off(field, O_EDIT);
280 		set_field_status(field, 0);
281 	    } else {
282 		field_opts_on(field, O_EDIT);
283 	    }
284 	    set_current_field(form, field);
285 	}
286 	break;
287     case MY_INS_MODE:
288 	/* there should be a form_status() function, but there is none */
289 	if (!insert_mode) {
290 	    if (form_driver(form, REQ_INS_MODE) == E_OK) {
291 		insert_mode = TRUE;
292 	    }
293 	} else {
294 	    if (form_driver(form, REQ_OVL_MODE) == E_OK) {
295 		insert_mode = FALSE;
296 	    }
297 	}
298 	show_insert_mode(insert_mode);
299 	refresh();
300 	break;
301     default:
302 	beep();
303 	break;
304     }
305     return (FALSE);
306 }
307 
308 static void
show_current_field(WINDOW * win,FORM * form)309 show_current_field(WINDOW *win, FORM *form)
310 {
311     FIELD *field;
312     int field_rows, field_cols, field_max;
313     int currow, curcol;
314 
315     if (has_colors()) {
316 	wbkgd(win, (chtype) COLOR_PAIR(1));
317     }
318     werase(win);
319     form_getyx(form, currow, curcol);
320     wprintw(win, "Cursor: %d,%d", currow, curcol);
321     if (data_ahead(form))
322 	waddstr(win, " ahead");
323     if (data_behind(form))
324 	waddstr(win, " behind");
325     waddch(win, '\n');
326 
327     if ((field = current_field(form)) != 0) {
328 	FIELDTYPE *type;
329 	int nbuf;
330 
331 	wprintw(win, "Page %d%s, Field %d/%d%s:",
332 		form_page(form),
333 		new_page(field) ? "*" : "",
334 		field_index(field), field_count(form),
335 		field_arg(field) ? "(arg)" : "");
336 	if ((type = field_type(field)) != 0) {
337 	    if (type == TYPE_ALNUM)
338 		waddstr(win, "ALNUM");
339 	    else if (type == TYPE_ALPHA)
340 		waddstr(win, "ALPHA");
341 	    else if (type == TYPE_ENUM)
342 		waddstr(win, "ENUM");
343 	    else if (type == TYPE_INTEGER)
344 		waddstr(win, "INTEGER");
345 #ifdef NCURSES_VERSION
346 	    else if (type == TYPE_IPV4)
347 		waddstr(win, "IPV4");
348 #endif
349 	    else if (type == TYPE_NUMERIC)
350 		waddstr(win, "NUMERIC");
351 	    else if (type == TYPE_REGEXP)
352 		waddstr(win, "REGEXP");
353 	    else
354 		waddstr(win, "other");
355 	}
356 
357 	if ((unsigned) field_opts(field) & O_EDIT)
358 	    waddstr(win, " editable");
359 	else
360 	    waddstr(win, " readonly");
361 
362 	if (field_status(field))
363 	    waddstr(win, " modified");
364 
365 	if (dynamic_field_info(field, &field_rows, &field_cols, &field_max)
366 	    != ERR) {
367 	    wprintw(win, " size %dx%d (max %d)",
368 		    field_rows, field_cols, field_max);
369 	}
370 
371 	waddch(win, ' ');
372 	(void) wattrset(win, AttrArg(field_fore(field), 0));
373 	waddstr(win, "fore");
374 	wattroff(win, (int) field_fore(field));
375 
376 	waddch(win, '/');
377 
378 	(void) wattrset(win, AttrArg(field_back(field), 0));
379 	waddstr(win, "back");
380 	wattroff(win, (int) field_back(field));
381 
382 	wprintw(win, ", pad '%c'", field_pad(field));
383 
384 	waddstr(win, "\n");
385 	for (nbuf = 0; nbuf <= 2; ++nbuf) {
386 	    char *buffer;
387 	    if ((buffer = field_buffer(field, nbuf)) != 0) {
388 		wprintw(win, "buffer %d:", nbuf);
389 		(void) wattrset(win, A_REVERSE);
390 		if (nbuf) {
391 		    waddnstr(win, buffer, trimmed(buffer));
392 		} else {
393 		    waddstr(win, buffer);
394 		}
395 		wattroff(win, A_REVERSE);
396 		waddstr(win, "\n");
397 	    }
398 	}
399     }
400     wrefresh(win);
401 }
402 
403 static void
demo_forms(void)404 demo_forms(void)
405 {
406     FORM *form;
407     FIELD *f[100];		/* will memset to zero */
408     int c;
409     unsigned n = 0;
410     int pg;
411     const char *fname;
412     static const char *my_enum[] =
413     {"first", "second", "third", 0};
414 
415 #ifdef NCURSES_MOUSE_VERSION
416     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
417 #endif
418 
419     help_edit_field();
420 
421     MvAddStr(4, 57, "Forms Entry Test");
422     show_insert_mode(TRUE);
423 
424     refresh();
425 
426     /* describe the form */
427     memset(f, 0, sizeof(f));
428     for (pg = 0; pg < 4; ++pg) {
429 	char label[80];
430 	_nc_SPRINTF(label, _nc_SLIMIT(sizeof(label))
431 		    "Sample Form Page %d", pg + 1);
432 	f[n++] = make_label(label, 0, 15);
433 	set_new_page(f[n - 1], TRUE);
434 
435 	switch (pg) {
436 	default:
437 	    fname = "Last Name";
438 	    f[n++] = make_label(fname, 2, 0);
439 	    f[n++] = make_field(fname, 3, 0, 1, 18);
440 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
441 
442 	    fname = "First Name";
443 	    f[n++] = make_label(fname, 2, 20);
444 	    f[n++] = make_field(fname, 3, 20, 1, 12);
445 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
446 
447 	    fname = "Middle Name";
448 	    f[n++] = make_label(fname, 2, 34);
449 	    f[n++] = make_field(fname, 3, 34, 1, 12);
450 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
451 	    break;
452 
453 	case 1:
454 	    fname = "Last Name";
455 	    f[n++] = make_label(fname, 2, 0);
456 	    f[n++] = make_field(fname, 3, 0, 1, 12);
457 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
458 
459 	    fname = "First Name";
460 	    f[n++] = make_label(fname, 2, 14);
461 	    f[n++] = make_field(fname, 3, 14, 1, 12);
462 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
463 
464 	    fname = "MI";
465 	    f[n++] = make_label(fname, 2, 28);
466 	    f[n++] = make_field(fname, 3, 28, 1, 1);
467 	    set_field_pad(f[n - 1], '?');
468 	    set_field_type(f[n - 1], TYPE_ALPHA, 1);
469 
470 	    fname = "First/Second/Third";
471 	    f[n++] = make_label(fname, 2, 32);
472 	    f[n++] = make_field(fname, 3, 32, 1, 12);
473 	    set_field_type(f[n - 1], TYPE_ENUM, my_enum, 0, 0);
474 	    break;
475 
476 	case 2:
477 	    fname = "Host Name";
478 	    f[n++] = make_label(fname, 2, 0);
479 	    f[n++] = make_field(fname, 3, 0, 1, 24);
480 	    set_field_type(f[n - 1], TYPE_ALNUM, 1);
481 
482 #ifdef NCURSES_VERSION
483 	    fname = "IP Address";
484 	    f[n++] = make_label(fname, 2, 26);
485 	    f[n++] = make_field(fname, 3, 26, 1, 16);
486 	    set_field_type(f[n - 1], TYPE_IPV4, 1);
487 #endif
488 	    break;
489 
490 	case 3:
491 	    fname = "Four digits";
492 	    f[n++] = make_label(fname, 2, 0);
493 	    f[n++] = make_field(fname, 3, 0, 1, 10);
494 	    set_field_type(f[n - 1], TYPE_INTEGER, 4, 0, 0);
495 
496 	    fname = "Numeric";
497 	    f[n++] = make_label(fname, 2, 13);
498 	    f[n++] = make_field(fname, 3, 13, 1, 12);
499 	    set_field_type(f[n - 1], TYPE_NUMERIC, 3, -10000.0, 100000000.0);
500 
501 	    fname = "Phone number";
502 	    f[n++] = make_label(fname, 2, 27);
503 	    f[n++] = make_field(fname, 3, 27, 1, 16);
504 	    set_field_type(f[n - 1], TYPE_REGEXP,
505 			   "^([0-9]-)?[0-9]{3}-[0-9]{3}-[0-9]{4} *$");;
506 	    break;
507 	}
508 
509 	fname = "Comments";
510 	f[n++] = make_label(fname, 5, 0);
511 	f[n++] = make_field(fname, 6, 0, 4, 46);
512 	init_edit_field(f[n - 1], get_data(fname));
513     }
514 
515     f[n] = (FIELD *) 0;
516 
517     if ((form = new_form(f)) != 0) {
518 	WINDOW *w;
519 	WINDOW *also;
520 	int finished = 0;
521 
522 	display_form(form);
523 
524 	w = form_win(form);
525 	also = newwin(getmaxy(stdscr) - getmaxy(w), COLS, getmaxy(w), 0);
526 	show_current_field(also, form);
527 
528 	while (!finished) {
529 	    switch (edit_field(form, &c)) {
530 	    case E_OK:
531 		break;
532 	    case E_UNKNOWN_COMMAND:
533 		finished = my_form_driver(form, c);
534 		break;
535 	    default:
536 		beep();
537 		break;
538 	    }
539 	    show_current_field(also, form);
540 	}
541 
542 	erase_form(form);
543 
544 	free_form(form);
545     }
546     for (c = 0; f[c] != 0; c++) {
547 	free_edit_field(f[c]);
548 	free_field(f[c]);
549     }
550     noraw();
551     nl();
552 
553 #ifdef NCURSES_MOUSE_VERSION
554     mousemask(0, (mmask_t *) 0);
555 #endif
556 }
557 
558 static void
usage(int ok)559 usage(int ok)
560 {
561     static const char *tbl[] =
562     {
563 	"Usage: demo_forms [options] [data file]"
564 	,""
565 	,USAGE_COMMON
566 	,"Options:"
567 	," -d       make fields dynamic"
568 	," -j NUM   justify (1=left, 2=center, 3=right)"
569 	," -m NUM   set maximum size of dynamic fields"
570 	," -o NUM   specify number of offscreen rows in new_field()"
571 	," -t NUM   specify text to fill fields initially"
572     };
573     unsigned int j;
574     for (j = 0; j < SIZEOF(tbl); ++j)
575 	fprintf(stderr, "%s\n", tbl[j]);
576     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
577 }
578 /* *INDENT-OFF* */
VERSION_COMMON()579 VERSION_COMMON()
580 /* *INDENT-ON* */
581 
582 int
583 main(int argc, char *argv[])
584 {
585     int ch;
586 
587     setlocale(LC_ALL, "");
588 
589     while ((ch = getopt(argc, argv, OPTS_COMMON "dj:m:o:t:")) != -1) {
590 	switch (ch) {
591 	case 'd':
592 	    d_option = TRUE;
593 	    break;
594 	case 'j':
595 	    j_value = atoi(optarg);
596 	    if (j_value < NO_JUSTIFICATION
597 		|| j_value > JUSTIFY_RIGHT)
598 		usage(FALSE);
599 	    break;
600 	case 'm':
601 	    m_value = atoi(optarg);
602 	    break;
603 	case 'o':
604 	    o_value = atoi(optarg);
605 	    break;
606 	case 't':
607 	    t_value = optarg;
608 	    break;
609 	case OPTS_VERSION:
610 	    show_version(argv);
611 	    ExitProgram(EXIT_SUCCESS);
612 	default:
613 	    usage(ch == OPTS_USAGE);
614 	    /* NOTREACHED */
615 	}
616     }
617     while (optind < argc) {
618 	read_data(argv[optind++]);
619     }
620 
621     initscr();
622     cbreak();
623     noecho();
624     raw();
625     nonl();			/* lets us read ^M's */
626     intrflush(stdscr, FALSE);
627     keypad(stdscr, TRUE);
628 
629     if (has_colors()) {
630 	start_color();
631 	init_pair(1, COLOR_WHITE, COLOR_BLUE);
632 	init_pair(2, COLOR_GREEN, COLOR_BLACK);
633 	init_pair(3, COLOR_CYAN, COLOR_BLACK);
634 	bkgd((chtype) COLOR_PAIR(1));
635 	refresh();
636     }
637 
638     demo_forms();
639 
640     endwin();
641     ExitProgram(EXIT_SUCCESS);
642 }
643 
644 #else
645 int
main(void)646 main(void)
647 {
648     printf("This program requires the curses form library\n");
649     ExitProgram(EXIT_FAILURE);
650 }
651 #endif
652