C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
form_engine.c
Go to the documentation of this file.
1/** @file form_engine.c
2 @brief The working part of C-Menu Form
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10/** @defgroup form_engine Form Engine
11 @brief Parses Form Descriptions, Handles User Input, and Integrates with
12 External Commands for Calculations and Data Processing.
13 */
14
15#include <common.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <termios.h>
22#include <unistd.h>
23#include <wait.h>
24
25#define D_COMMENT '#'
26#define D_CMD '!'
27#define D_FIELD 'F'
28#define D_TEXT 'T'
29#define D_HELP '?'
30#define D_HEADER 'H'
31#define D_CALC 'C'
32#define D_QUERY 'Q'
33#define D_GETTER 'G'
34
35unsigned int form_display_screen(Init *);
36void form_display_fields(Form *);
37int field_navigator(Form *);
38int form_parse_desc(Form *);
39int form_read_data(Form *);
40int form_write(Form *);
42int form_desc_error(int, char *, char *);
43int form_exec_cmd(Form *);
44int form_process(Init *);
45int form_post(Init *);
46int init_form(Init *, int, char **, int, int);
47int form_engine(Init *);
48int form_yx_to_fidx(Form *, int, int);
49
50/** @brief Initialize form data structure and parse description file
51 @ingroup form_engine
52 @param init A pointer to the Init structure containing form data and state.
53 @param argc The number of command line arguments passed to the form.
54 @param argv The array of command line arguments passed to the form.
55 @param begy The y-coordinate for the top-left corner of the form window.
56 @param begx The x-coordinate for the top-left corner of the form window.
57 @return 0 on success, or a non-zero value if an error occurs during
58*/
59int init_form(Init *init, int argc, char **argv, int begy, int begx) {
60 int rc;
61 char tmp_str[MAXLEN];
62 if (init->form != nullptr)
64 init->form = new_form(init, argc, argv, begy, begx);
65 form = init->form;
66 if (!form->f_mapp_spec) {
67 if (form->mapp_spec[0] == '\0') {
68 rc = Perror("Error: No form specification file given");
69 } else {
70 strnz__cpy(tmp_str, "form->mapp_spec: ", MAXLEN - 1);
72 strnz__cat(tmp_str, " not found", MAXLEN - 1);
73 rc = Perror(tmp_str);
74 }
76 return rc;
77 }
78 if (begy != 0)
79 form->begy = begy;
80 if (begx != 0)
81 form->begx = begx;
82 if ((form->f_in_spec && (form->in_spec[0] == '\0')) ||
83 (strcmp(form->in_spec, "-") == 0) ||
84 strcmp(form->in_spec, "/dev/stdin") == 0) {
85 strnz__cpy(form->in_spec, "/dev/stdin", MAXLEN - 1);
86 form->f_in_pipe = true;
87 }
88 if (form->title[0] == '\0')
90 rc = form_engine(init);
91 if (form->win)
94 return rc;
95}
96/** @brief Form main processing loop
97 @ingroup form_engine
98 @param init A pointer to the Init structure containing form data and state.
99 @return 0 on successful completion, or a non-zero value if the user cancels
100 the form or if an error occurs during processing.
101 1. Parse the form description file to populate the form data structure.
102 2. Read any initial data for the form fields from a specified input
103 source.
104 3. Display the form on the screen with the initial field values.
105 4. Enter a loop to handle user input for field entry, calculation, help,
106 and cancellation:
107 a. If the user selects the accept action, perform any necessary
108 calculations or post-processing, and then either return to field entry
109 or exit the loop if the form is accepted.
110 b. If the user selects the help action, display the help screen and
111 return to the form after the user exits the help screen.
112 c. If the user selects the cancel action, exit the loop and return a
113 cancel status. */
114int form_engine(Init *init) {
115 char tmp_str[MAXLEN];
116 int form_action;
117 int rc = 0;
118 char *eargv[MAXARGS];
119
120 form = init->form;
121 if (form == nullptr) {
122 Perror("FORM: form data structure is nullptr");
123 }
125 return 0;
126 }
127
130 form->fidx = 0;
131 form_action = 0;
132 while (1) {
133 if (form_action == 0 || form_action == P_CONTINUE)
134 form_action = field_navigator(form);
135 switch (form_action) {
136 case P_ACCEPT:
138 form_action = form_process(init);
139 else
140 form_action = form_post(init);
141 if (form_action == P_HELP || form_action == P_CANCEL ||
142 form_action == P_CONTINUE || form_action == P_END)
143 continue;
144 if (form_action == P_ACCEPT) {
145 form_action = P_END;
146 continue;
147 }
148 break;
149 case P_END:
150 if (form->f_out_spec || form->out_spec[0] != '\0')
151 rc = form_write(form);
154 return rc;
155 case P_HELP:
156 if (form->f_help_spec && form->help_spec[0] != '\0')
158 else {
159 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
160 strnz__cat(tmp_str, "/", MAXLEN - 1);
162 }
163 eargc = 0;
164 eargv[eargc++] = strdup("view");
165 eargv[eargc++] = strdup("-N");
166 eargv[eargc++] = strdup("f");
167 eargv[eargc++] = strdup(tmp_str);
168 eargv[eargc] = nullptr;
169 init->lines = 30;
170 init->cols = 66;
171 init->begy = form->begy + 1;
172 init->begx = form->begx + 1;
173 strnz__cpy(init->title, "Form Help", MAXLEN - 1);
174 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
175 init->begx);
177 form_action = P_CONTINUE;
178 break;
179 case P_CANCEL:
180 return P_CANCEL;
181 default:
182 form_action = P_CONTINUE;
183 break;
184 }
185 }
186 return 0;
187}
188/** @brief Handle post-processing after field entry, allowing user to edit data,
189 execute a provider command, or write data to an output file.
190 @ingroup form_engine
191 @param init A pointer to the Init structure containing form data and state.
192 @return An integer status code indicating the next action for the form
193 processing loop (e.g., P_CONTINUE, P_CANCEL, P_ACCEPT, P_HELP). */
194int form_post(Init *init) {
195 bool loop = true;
196 int c, rc;
197 click_y = click_x = -1;
198 form = init->form;
199 wmove(form->win, form->lines - 1, 0);
200 wclrtoeol(form->win);
202 set_chyron_key(form->chyron, 8, "F8 Edit", KEY_F(8));
203 set_chyron_key(form->chyron, 10, "F10 Commit", KEY_F(10));
205 rc = -1;
206 while (loop) {
207 if (rc == -1) {
210 tcflush(2, TCIFLUSH);
212 }
213 switch (c) {
214 case KEY_F(1):
215 return P_HELP;
216 case KEY_F(8):
218 loop = false;
219 rc = P_CONTINUE;
220 break;
221 }
222 continue;
223 case KEY_F(9):
224 loop = false;
225 rc = P_CANCEL;
226 break;
227 case KEY_F(10):
228 loop = false;
229 rc = P_ACCEPT;
230 break;
231 case KEY_MOUSE:
232 continue;
233 default:
234 break;
235 }
236 }
238 return rc;
239}
240
241/** @brief Handle integration with a getter program which will provide field
242 data.
243 @ingroup form_engine
244 @param init A pointer to the Init structure containing form data and state.
245 @return An integer status code indicating the next action for the form
246 processing loop (e.g., P_CONTINUE, P_CANCEL, P_ACCEPT, P_HELP).
247 @details This function provides integration with getter programs.
248 The requirements are:
249 1. The form description file must have a line containing only 'C'
250 (calculate), 'Q' (query), org 'G' (getter) to indicate that the form supports
251 a getter.
252 2. The form description file must specify the provider command using a
253 line starting with '!' followed by the command and its arguments.
254 3. The getter program must be able to accept field data from a file,
255 from standard input, or as command line parameters.
256 4. The getter program must output field values in a format that can be
257 read by the form (e.g., one value per line), either to a file or to standard
258 output.
259
260 The sequence of operations is as follows:
261
262 1. The 'C', 'Q', or 'G' option causes Form to pause and display an
263 KEY_F(5) Calculate, Query, or Getter. option on the chyron.
264 2. The user can then cancel the operation by pressing KEY_F(9) or
265 activate the getter option by pressing KEY_F(5).
266 3. Form outputs its data to file, standard output, or as command line
267 parameters.
268 4. Form executes the getter program.
269 5. The getter program processes the data and outputs the results.
270 6. Form reads the results and populates the appropriate form fields.
271 7. Form presents the user with an option to edit the data, and the
272 sequence restarts at 1.
273
274 This function forks and executes the getter executable as a child
275 process, creates a pipe to read the output from the provider command,
276 reads the output, and updates the Form fields.
277 */
278int form_process(Init *init) {
279 int i, c, rc;
280 char tmp_str[MAXLEN];
281 char earg_str[MAXLEN + 1];
282 char *eargv[MAXARGS];
283 char file_spec[MAXLEN];
284 bool loop = true;
285 pid_t pid;
286 int pipe_fd[2];
287
288 form = init->form;
289 wmove(form->win, form->lines - 1, 0);
290 wclrtoeol(form->win);
293 strnz__cpy(tmp_str, "F5 ", MAXLEN - 1);
294
295 if (form->f_process)
296 strnz__cat(tmp_str, "Process", MAXLEN - 1);
297 else if (form->f_calculate)
298 strnz__cat(tmp_str, "Calculate", MAXLEN - 1);
299 else if (form->f_query)
300 strnz__cat(tmp_str, "Query", MAXLEN - 1);
302
303 while (loop) {
307 click_y = click_x = -1;
308 tcflush(2, TCIFLUSH);
310 switch (c) {
311 case KEY_F(1):
312 return P_HELP;
313 case KEY_F(5):
318 for (i = 0; i < form->fcnt; i++) {
319 strnz__cat(earg_str, " ", MAXLEN - 1);
321 }
322 eargc = str_to_args(eargv, earg_str, MAXARGS);
323 strnz__cpy(file_spec, eargv[0], MAXLEN - 1);
324 base_name(eargv[0], file_spec);
325 if (pipe(pipe_fd) == -1) {
327 Perror("pipe(pipe_fd) failed in init_form");
328 return (1);
329 }
330 if ((pid = fork()) == -1) {
332 Perror("fork() failed in init_form");
333 return (1);
334 }
335 if (pid == 0) { // Child
336 close(pipe_fd[P_READ]);
337 dup2(pipe_fd[P_WRITE], STDOUT_FILENO);
338 close(pipe_fd[P_WRITE]);
339 execvp(eargv[0], eargv);
340 ssnprintf(em0, MAXLEN, "%s, line: %d", __FILE__,
341 __LINE__ - 2);
342 strnz__cpy(em1, "execvp(", MAXLEN - 1);
343 strnz__cat(em1, eargv[0], MAXLEN - 1);
344 strnz__cat(em1, ", ", MAXLEN - 1);
345 strnz__cat(em1, earg_str, MAXLEN - 1);
347 strerror_r(errno, em2, MAXLEN);
349 exit(EXIT_FAILURE);
350 } // Back to parent
351 close(pipe_fd[P_WRITE]);
352 form->in_fp = fdopen(pipe_fd[P_READ], "rb");
353 form->f_in_pipe = true;
355 close(pipe_fd[P_READ]);
359 set_chyron_key(form->chyron, 8, "F5 Edit", KEY_F(5));
360 set_chyron_key(form->chyron, 10, "F10 Commit", KEY_F(10));
362 continue;
363 }
364 break;
365 case KEY_F(8):
367 loop = false;
368 rc = P_CONTINUE;
369 break;
370 }
371 continue;
372 case KEY_F(9):
373 loop = false;
374 rc = P_CANCEL;
375 break;
376 case KEY_F(10):
377 loop = false;
378 rc = P_ACCEPT;
379 break;
380 case KEY_MOUSE:
381 break;
382 default:
383 break;
384 }
385 }
388 return rc;
389}
390/** @brief Handle user input for field entry, allowing navigation between fields
391 and looping until an exit action is selected.
392 @ingroup form_engine
393 @param form A pointer to the Form structure containing form data and state.
394 @return An integer status code indicating the next action for the form
395 processing loop (e.g., P_ACCEPT, P_HELP, P_CALC, P_CANCEL).
396 @note This function manages user input for field entry, including navigation
397 between fields and handling of special keys for accepting, canceling,
398 requesting help, or performing calculations. The function loops until the
399 user selects an exit action (e.g., accept or cancel). */
400int field_navigator(Form *form) {
401
402 if (form->fidx < 0)
403 return (-1);
404 while (1) {
406
407 switch (cmd_key) {
408 case KEY_F(10):
409 return (P_ACCEPT);
410 case KEY_F(1):
411 return (P_HELP);
412 case KEY_F(5):
413 if (form->f_process)
414 return (P_CALC);
415 break;
416 case KEY_F(9):
417 return (P_CANCEL);
418 case 'k':
419 case KEY_UP:
420 if (form->fidx != 0)
421 form->fidx--;
422 break;
423 case '\r':
424 case KEY_ENTER:
425 if (form->fidx < form->fcnt - 1)
426 form->fidx++;
427 else if (form->fidx == form->fcnt - 1)
428 return (P_ACCEPT);
429 break;
430 case 'j':
431 case KEY_DOWN:
432 if (form->fidx < form->fcnt - 1)
433 form->fidx++;
434 else if (form->fidx == form->fcnt - 1)
435 return (P_ACCEPT);
436 break;
437 case KEY_MOUSE:
438 break;
439 default:
440 break;
441 }
442 }
443}
444int form_yx_to_fidx(Form *form, int y, int x) {
445 for (int i = 0; i < form->fcnt; i++) {
446 if (y == form->field[i]->line && x >= form->field[i]->col &&
447 x < form->field[i]->col + form->field[i]->len) {
448 return i;
449 }
450 }
451 return -1; // No field found at the given coordinates
452}
453
454/** @brief Display the form on the screen, including text elements and fields,
455 and set up the form window based on the form configuration.
456 @ingroup form_engine
457 @param init A pointer to the Init structure containing form data and state.
458 @return 0 on success, or a non-zero value if an error occurs while
459 creating the form window or rendering the form elements. */
460unsigned int form_display_screen(Init *init) {
461 int n;
462 char tmp_str[MAXLEN];
463
464 form = init->form;
465 form->lines = 0;
466 for (n = 0; n < form->dcnt; n++)
469 for (n = 0; n < form->fcnt; n++)
472 form->lines += 3;
473 if (form->lines > (LINES - form->begy))
474 form->lines = LINES - form->begy;
475 for (n = 0; n < form->fcnt; n++) {
476 if (form->field[n]->line >= (form->lines - 2))
477 form->fcnt = n;
478 }
479 form->cols += 2;
480 if (form->cols > (COLS - form->begx - 3))
481 form->cols = COLS - form->begx - 3;
483 0)) {
484 strnz__cpy(tmp_str, "kwin_new failed: ", MAXLEN - 1);
486 Perror(tmp_str);
487 return (1);
488 }
489#ifdef NCDEBUG
490 immedok(form->win, TRUE);
491#endif
494 for (n = 0; n < form->dcnt; n++) {
497 form->text[n]->str);
498 }
500 return 0;
501}
502/** @brief Display form fields on the screen, populating field values and
503 formatting them according to the form configuration.
504 @ingroup form_engine
505 @param form A pointer to the Form structure containing form data and state.
506 @note This function iterates through the defined form fields, formats their
507 display values based on the specified fill character and field length, and
508 renders them on the form window. It also updates the chyron with available
509 commands for user interaction. */
510void form_display_fields(Form *form) {
511 int n;
512 char fill_char = form->fill_char[0];
513 for (n = 0; n < form->fcnt; n++) {
514 if (form->field[n]->col + form->field[n]->len + 2 > form->cols)
515 form->field[n]->len = form->cols - (form->field[n]->col + 2);
516 strnfill(form->field[n]->filler_s, fill_char, form->field[n]->len);
519 }
521 set_chyron_key(form->chyron, 1, "F1 Help", KEY_F(1));
522 set_chyron_key(form->chyron, 9, "F9 Cancel", KEY_F(9));
523 set_chyron_key(form->chyron, 10, "F10 Continue", KEY_F(10));
526 return;
527}
528/** @brief Parse the form description file to populate the Form data structure
529 with field definitions, text elements, and other configuration specified in
530 the description file.
531 @ingroup form_engine
532 @param form A pointer to the Form structure containing form data and state.
533 @return 0 on success, or a non-zero value if an error occurs while parsing
534 the description file (e.g., file not found, invalid format, missing
535 directives). */
536int form_parse_desc(Form *form) {
537 FILE *form_desc_fp;
538 char *token;
539 char *s;
540 int cols = 0;
541 int i, l;
542 int in_line_num = 0;
543 char in_buf[BUFSIZ];
544 char tmp_buf[BUFSIZ];
545 char *tmp_buf_p;
546 char tmp_str[BUFSIZ];
547 char delim[5];
548 char directive;
549
550 form_desc_fp = fopen(form->mapp_spec, "r");
551 if (form_desc_fp == nullptr) {
552 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
553 strnz__cpy(em1, "fopen ", MAXLEN - 1);
555 strerror_r(errno, em2, MAXLEN);
557 return (1);
558 }
559 for (i = 0; i < FIELD_MAXCNT; i++) {
560 form->field[i] = calloc(1, sizeof(Field));
561 if (!form->field[i]) {
562 sprintf(tmp_str, "FORM: calloc failed for fields");
563 abend(EXIT_FAILURE, tmp_str);
564 }
565 }
566 for (i = 0; i < FIELD_MAXCNT; i++) {
567 form->text[i] = calloc(1, sizeof(Text));
568 if (!form->field[i]) {
569 sprintf(tmp_str, "FORM: calloc failed for text");
570 abend(EXIT_FAILURE, tmp_str);
571 }
572 }
573 form->didx = 0;
574 form->fidx = 0;
575 form->fcnt = 0;
576 form->cols = 34;
577 while ((fgets(in_buf, MAXLEN, form_desc_fp)) != nullptr) {
578 s = in_buf;
579 in_line_num++;
580 l = trim(in_buf);
581 if (l == 0)
582 continue;
583 if (*s == D_COMMENT)
584 continue;
585 delim[0] = '\n';
586 delim[1] = in_buf[1];
587 delim[2] = '\0';
588 strnz__cpy(tmp_buf, in_buf, MAXLEN - 1);
589 tmp_buf_p = tmp_buf;
590 if (!(token = strtok(tmp_buf_p, delim))) {
591 continue;
592 }
593 directive = *token;
594 switch ((int)directive) {
595 case D_COMMENT:
596 break;
597 case D_CALC:
598 form->f_calculate = true;
599 break;
600 case D_QUERY:
601 form->f_query = true;
602 break;
603 case D_GETTER:
604 form->f_process = true;
605 break;
606 case D_CMD:
607 if (!(token = strtok(nullptr, delim))) {
608 form_desc_error(in_line_num, in_buf,
609 "FORM: receiver_cmd delimiter");
610 continue;
611 }
613 break;
614 case D_HELP:
615 if (!(token = strtok(nullptr, delim))) {
616 form_desc_error(in_line_num, in_buf,
617 "FORM: help_spec delimiter");
618 }
619 strnz__cpy(form->help_spec, token, MAXLEN - 1);
620 break;
621 case D_FIELD:
622 if (form->field[form->fidx] == nullptr) {
623 sprintf(tmp_str, "FORM: calloc failed for fields");
624 abend(EXIT_FAILURE, tmp_str);
625 }
626 if (!(token = strtok(nullptr, delim))) {
627 form_desc_error(in_line_num, in_buf,
628 "FORM: line number delimiter");
629 return 1;
630 }
631 form->field[form->fidx]->line = atoi(token);
632 if (form->field[form->fidx]->line < 0 ||
633 form->field[form->fidx]->line >= FIELD_MAXCNT) {
634 form_desc_error(in_line_num, in_buf,
635 "FORM: invalid line number");
636 return 1;
637 }
638 if (!(token = strtok(nullptr, delim))) {
639 form_desc_error(in_line_num, in_buf,
640 "FORM: column number delimiter");
641 return 1;
642 }
643 form->field[form->fidx]->col = atoi(token);
644 if (form->field[form->fidx]->col < 0 ||
645 form->field[form->fidx]->col >= FIELD_MAXLEN) {
646 form_desc_error(in_line_num, in_buf,
647 "FORM: invalid column number");
648 break;
649 }
650 if (!(token = strtok(nullptr, delim))) {
651 strnz__cpy(tmp_str, in_buf, MAXLEN - 1);
652 form_desc_error(in_line_num, tmp_str, "FORM: length delimiter");
653 break;
654 }
655 form->field[form->fidx]->len = atoi(token);
656 if (form->field[form->fidx]->len < 0 ||
657 form->field[form->fidx]->len > FIELD_MAXLEN) {
658 form_desc_error(in_line_num, in_buf, "FORM: invalid length");
659 break;
660 }
661 if (!(token = strtok(nullptr, delim))) {
662 form_desc_error(in_line_num, in_buf,
663 "FORM: validation code delimiter");
664 break;
665 }
666 form->field[form->fidx]->ff = -1;
667 for (i = 0; i < FF_INVALID; i++) {
668 str_to_lower(token);
670 if (!strcmp(token, ff_tbl[i])) {
671 form->field[form->fidx]->ff = i;
672 break;
673 }
674 }
675 if (form->field[form->fidx]->ff < 0 ||
676 form->field[form->fidx]->ff >= FF_INVALID) {
677 form_desc_error(in_line_num, in_buf,
678 "FORM: invalid format code");
679 break;
680 }
681 cols =
682 form->field[form->fidx]->col + form->field[form->fidx]->len + 1;
683 if (cols > form->cols)
684 form->cols = cols;
685 form->fidx++;
686 form->fcnt = form->fidx;
687 break;
688 case D_TEXT:
689 if (form->text[form->didx] == nullptr) {
690 sprintf(tmp_str, "FORM: calloc failed for text");
691 abend(EXIT_FAILURE, tmp_str);
692 }
693 if (!(token = strtok(nullptr, delim))) {
694 form_desc_error(in_line_num, in_buf,
695 "FORM: line number delimiter");
696 break;
697 }
698 form->text[form->didx]->line = atoi(token);
699 if (form->text[form->didx]->line < 0 ||
700 form->text[form->didx]->line >= FIELD_MAXCNT) {
701 form_desc_error(in_line_num, in_buf,
702 "FORM: invalid line number");
703 break;
704 }
705 if (!(token = strtok(nullptr, delim))) {
706 form_desc_error(in_line_num, in_buf,
707 "FORM: column number delimiter");
708 break;
709 }
710 form->text[form->didx]->col = atoi(token);
711 if (form->text[form->didx]->col < 0 ||
712 form->text[form->didx]->col >= FIELD_MAXLEN) {
713 form_desc_error(in_line_num, in_buf,
714 "FORM: invalid column number");
715 break;
716 }
717 if (!(token = strtok(nullptr, delim))) {
718 form_desc_error(in_line_num, in_buf, "FORM: text delimiter");
719 break;
720 }
721 strnz__cpy(form->text[form->didx]->str, token, MAXLEN - 1);
722 form->text[form->didx]->len = strlen(form->text[form->didx]->str);
723 if (form->text[form->didx]->len < 0 ||
724 form->text[form->didx]->len > FIELD_MAXLEN) {
725 form_desc_error(in_line_num, in_buf, "FORM: invalid length");
726 break;
727 }
728 cols =
729 form->text[form->didx]->col + form->text[form->didx]->len + 1;
730 if (cols > form->cols)
731 form->cols = cols;
732 form->didx++;
733 form->dcnt = form->didx;
734 break;
735 case D_HEADER:
736 if ((token = strtok(nullptr, delim))) {
737 strnz__cpy(form->title, token, MAXLEN - 1);
738 }
739 break;
740 default:
741 form_desc_error(in_line_num, in_buf, "invalid directive");
742 break;
743 }
744 }
745 fclose(form_desc_fp);
746 if (form->didx < 1 && form->fidx < 1) {
747 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__);
748 ssnprintf(em1, MAXLEN - 1, "%s", "Error in description file:");
751 return (1);
752 }
753 return (0);
754}
755/** @brief Read initial data for form fields from a specified input source, such
756 as a file or standard input, and populate the form fields with the data.
757 @ingroup form_engine
758 @param form A pointer to the Form structure containing form data and state.
759 @return 0 on success, or a non-zero value if an error occurs while reading
760 the data or if the specified input source is invalid or empty. */
761int form_read_data(Form *form) {
762 struct stat sb;
763 char in_buf[MAXLEN];
764 char field[MAXLEN];
765
766 if (!form->f_in_pipe) {
767 if (form->f_in_spec && form->in_spec[0] != '\0') {
768 if ((lstat(form->in_spec, &sb) == -1) || (sb.st_size == 0) ||
769 ((form->in_fp = fopen(form->in_spec, "rb")) == nullptr)) {
771 if (sb.st_size == 0)
772 strnz__cpy(em1, "File is empty", MAXLEN - 1);
773 else
774 strnz__cpy(em1, "File does not exist", MAXLEN - 1);
775 strnz__cpy(em2, "Fields will be blank or zero", MAXLEN - 1);
777 if (cmd_key == KEY_F(9))
778 return (1);
779 }
780 if (form->in_fp == nullptr)
781 return (1);
782 } else
783 return (0);
784 }
785 form->fidx = 0;
786 if (form->in_fp != nullptr) {
787 while ((fgets(in_buf, MAXLEN, form->in_fp)) != nullptr) {
788 if (form->fidx < FIELD_MAXCNT)
789 strnz__cpy(field, in_buf, MAXLEN - 1);
790 form_fmt_field(form, field);
791 form->fidx++;
792 }
793 fclose(form->in_fp);
794 }
795 return (0);
796}
797/** @brief Execute a provider command specified in the form description file,
798 passing form field values as arguments, and optionally redirecting output to
799 a file.
800 @ingroup form_engine
801 @param form A pointer to the Form structure containing form data and state.
802 @return 0 on success, or a non-zero value if an error occurs while
803 constructing or executing the command. */
804int form_exec_cmd(Form *form) {
805 char earg_str[MAXLEN + 1];
806 int i;
807 strnz__cpy(earg_str, form->receiver_cmd, MAXLEN - 1);
808 for (i = 0; i < form->fcnt; i++) {
809 strnz__cat(earg_str, " ", MAXLEN - 1);
810 strnz__cat(earg_str, form->field[i]->accept_s, MAXLEN - 1);
811 }
812 if (form->f_out_spec && form->f_process) {
813 strnz__cat(earg_str, " >", MAXLEN - 1);
814 strnz__cat(earg_str, form->out_spec, MAXLEN - 1);
815 }
816 shell(earg_str);
817 return 0;
818}
819/** @brief Write form field values to a specified output destination, such as a
820 file or standard output, based on the form configuration and user input.
821 @ingroup form_engine
822 @param form A pointer to the Form structure containing form data and state.
823 @return 0 on success, or a non-zero value if an error occurs while writing
824 the data or if the specified output destination is invalid. */
825int form_write(Form *form) {
826 int n;
827 if (form->out_spec[0] == '\0' || strcmp(form->out_spec, "-") == 0 ||
828 strcmp(form->out_spec, "/dev/stdout") == 0) {
829 strnz__cpy(form->out_spec, "/dev/stdout", MAXLEN - 1);
830 close(form->out_fd);
831 form->out_fd = open(form->out_spec, O_CREAT | O_RDWR | O_TRUNC, 0644);
832 if (form->out_fd == -1) {
833 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
834 strnz__cpy(em1, "open ", MAXLEN - 1);
836 strerror_r(errno, em2, MAXLEN);
838 return (1);
839 }
840 dup2(form->out_fd, STDOUT_FILENO);
841 form->out_fp = fdopen(STDOUT_FILENO, "w");
842 form->f_out_spec = true;
843 form->f_out_pipe = true;
844 } else {
845 if ((form->out_fp = fopen(form->out_spec, "w")) == nullptr) {
846 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
847 strerror_r(errno, em2, MAXLEN);
849 return (1);
850 }
851 }
852 if (form->out_fp == nullptr) {
853 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
854 strnz__cpy(em1, "fopen ", MAXLEN - 1);
856 strerror_r(errno, em2, MAXLEN);
858 return (1);
859 }
860 for (n = 0; n < form->fcnt; n++)
861 fprintf(form->out_fp, "%s\n", form->field[n]->accept_s);
862 if (form->out_fp != nullptr)
863 fclose(form->out_fp);
864 return (0);
865}
866/** @brief Handle errors encountered while parsing the form description file,
867 providing detailed error messages that include the file name, line number,
868 and the specific error encountered.
869 @ingroup form_engine
870 @param in_line_num The line number in the description file where the error
871 occurred.
872 @param in_buf The content of the line that caused the error, for context.
873 @param em A specific error message describing the nature of the error.
874 @return An integer status code indicating how the user responded to the
875 error message (e.g., which key they pressed to acknowledge the error). */
876int form_desc_error(int in_line_num, char *in_buf, char *em) {
877 int cmd_key;
878
879 ssnprintf(em0, MAXLEN - 1, "%s: %s", __FILE__, em);
880 ssnprintf(em1, MAXLEN - 1, "Desc file: %s, line: %d", form->mapp_spec,
881 in_line_num);
882 strnz__cpy(em2, in_buf, MAXLEN - 1);
884 return cmd_key;
885}
@ P_HELP
Definition form.h:32
@ P_CALC
Definition form.h:38
@ P_CONTINUE
Definition form.h:28
@ P_END
Definition form.h:43
@ P_ACCEPT
Definition form.h:30
@ P_CANCEL
Definition form.h:34
int form_yx_to_fidx(Form *, int, int)
#define FIELD_MAXLEN
Definition form.h:18
@ FF_INVALID
Definition form.h:69
Form * form
Definition mem.c:47
#define FIELD_MAXCNT
Definition form.h:19
#define FORM_HELP_FILE
Definition common.h:37
#define P_READ
Definition common.h:50
int popup_view(Init *, int, char **, int, int, int, int)
Definition popups.c:47
#define P_WRITE
Definition common.h:51
int eargc
Definition futil.c:41
#define MAXARGS
Definition cm.h:30
int wait_timeout
Definition futil.c:98
#define nullptr
Definition cm.h:23
#define BUFSIZ
Definition view.h:29
#define MAXLEN
Definition curskeys.c:15
#define D_CMD
Definition form_engine.c:26
#define D_FIELD
Definition form_engine.c:27
#define D_CALC
Definition form_engine.c:31
void form_usage()
#define D_TEXT
Definition form_engine.c:28
#define D_HELP
Definition form_engine.c:29
#define D_QUERY
Definition form_engine.c:32
#define D_COMMENT
Definition form_engine.c:25
#define D_HEADER
Definition form_engine.c:30
#define D_GETTER
Definition form_engine.c:33
unsigned int cmd_key
Definition dwin.c:117
WINDOW * win_win[MAXWIN]
Definition dwin.c:114
bool waitpid_with_timeout(pid_t pid, int timeout)
Definition dwin.c:1431
char em1[MAXLEN]
Definition dwin.c:133
void display_chyron(WINDOW *win, Chyron *chyron, int line, int col)
Definition dwin.c:297
int click_x
Definition dwin.c:45
int win_ptr
Definition dwin.c:121
int click_y
Definition dwin.c:44
char em0[MAXLEN]
Definition dwin.c:132
int cp_reverse_highlight
Definition dwin.c:140
void set_chyron_key(Chyron *, int, char *, int)
Definition dwin.c:245
WINDOW * win_box[MAXWIN]
Definition dwin.c:115
char em2[MAXLEN]
Definition dwin.c:134
char ff_tbl[][26]
Definition fields.c:40
int xwgetch(WINDOW *, Chyron *, int)
Wrapper for wgetch that handles signals, mouse events, checks for clicks on the chyron line,...
Definition dwin.c:1359
int win_new(int, int, int, int, char *, int)
Create a new window with optional box and title.
Definition dwin.c:783
WINDOW * win_del()
Delete the current window and its associated box window.
Definition dwin.c:902
bool is_set_chyron_key(Chyron *, int)
Check if function key label is set.
Definition dwin.c:217
Chyron * destroy_chyron(Chyron *chyron)
Destroy Chyron structure.
Definition dwin.c:198
void set_chyron_key_cp(Chyron *, int, char *, int, int)
Set chyron key.
Definition dwin.c:237
void compile_chyron(Chyron *)
construct the chyron string from the chyron structure
Definition dwin.c:268
void unset_chyron_key(Chyron *, int)
Unset chyron key.
Definition dwin.c:258
Chyron * new_chyron()
Create and initialize Chyron structure.
Definition dwin.c:183
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1110
int display_error(char *em0, char *em1, char *em2, char *em3)
Display an error message window or print to stderr.
Definition dwin.c:1054
void abend(int, char *)
Abnormal program termination.
Definition dwin.c:1331
int shell(char *)
Execute a shell command.
Definition exec.c:80
int form_fmt_field(Form *, char *)
Format field according to its format type.
Definition fields.c:421
int field_editor(Form *)
Accept input for a field.
Definition fields.c:59
int form_display_field_n(Form *, int)
Display field n.
Definition fields.c:351
int form_desc_error(int, char *, char *)
Handle errors encountered while parsing the form description file, providing detailed error messages ...
int form_read_data(Form *)
Read initial data for form fields from a specified input source, such as a file or standard input,...
int form_parse_desc(Form *)
Parse the form description file to populate the Form data structure with field definitions,...
int form_write(Form *)
Write form field values to a specified output destination, such as a file or standard output,...
int form_process(Init *)
Handle integration with a getter program which will provide field data.
int form_exec_cmd(Form *)
Execute a provider command specified in the form description file, passing form field values as argum...
int init_form(Init *, int, char **, int, int)
Initialize form data structure and parse description file.
Definition form_engine.c:59
int form_post(Init *)
Handle post-processing after field entry, allowing user to edit data, execute a provider command,...
int form_engine(Init *)
Form main processing loop.
void form_display_fields(Form *)
Display form fields on the screen, populating field values and formatting them according to the form ...
int field_navigator(Form *)
Handle user input for field entry, allowing navigation between fields and looping until an exit actio...
unsigned int form_display_screen(Init *)
Display the form on the screen, including text elements and fields, and set up the form window based ...
void destroy_argv(int argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:221
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:269
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition futil.c:118
bool str_to_lower(char *)
Converts a string to lowercase.
Definition futil.c:233
bool strnfill(char *, char, int)
Fills string s with character c n.
Definition futil.c:440
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:340
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:147
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:298
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:775
int str_to_args(char **, char *, int)
Converts a string into an array of argument strings.
Definition futil.c:167
Form * new_form(Init *, int, char **, int, int)
Create and initialize Form structure.
Definition mem.c:253
Form * destroy_form(Init *init)
Destroy Form structure.
Definition mem.c:276
char title[MAXLEN]
Definition common.h:123
char mapp_help[MAXLEN]
Definition common.h:140
int begx
Definition common.h:112
Form * form
Definition common.h:174
int cols
Definition common.h:110
int begy
Definition common.h:111
int lines
Definition common.h:109
int l
Definition cm.h:244
int len
Definition form.h:108
int col
Definition form.h:104
char str[SCR_COLS]
Definition form.h:106
int line
Definition form.h:102
int line
Definition form.h:115
char display_s[FIELD_MAXLEN]
Definition form.h:132
char accept_s[FIELD_MAXLEN]
Definition form.h:128
int ff
Definition form.h:121
int col
Definition form.h:117
int len
Definition form.h:119
char filler_s[FIELD_MAXLEN]
Definition form.h:137
FILE * out_fp
Definition form.h:161
Text * text[FIELD_MAXCNT]
Definition form.h:331
bool f_in_pipe
Definition form.h:221
bool f_mapp_spec
Definition form.h:192
WINDOW * box
Definition form.h:156
Field * field[FIELD_MAXCNT]
Definition form.h:344
char receiver_cmd[MAXLEN]
Definition form.h:178
bool f_process
Definition form.h:245
int didx
Definition form.h:316
int begy
Definition form.h:151
char provider_cmd[MAXLEN]
Definition form.h:170
bool f_query
Definition form.h:247
int out_fd
Definition form.h:165
int cols
Definition form.h:150
int lines
Definition form.h:149
int fcnt
Definition form.h:309
int begx
Definition form.h:153
bool f_out_pipe
Definition form.h:226
int fidx
Definition form.h:301
bool f_provider_cmd
Definition form.h:255
bool f_calculate
Definition form.h:243
char fill_char[2]
Definition form.h:290
bool f_help_spec
Definition form.h:231
FILE * in_fp
Definition form.h:159
char out_spec[MAXLEN]
Definition form.h:208
Chyron * chyron
Definition form.h:356
bool f_receiver_cmd
Definition form.h:262
char in_spec[MAXLEN]
Definition form.h:201
char mapp_spec[FIELD_MAXLEN]
Definition form.h:167
char title[MAXLEN]
Definition form.h:157
int dcnt
Definition form.h:323
bool f_out_spec
Definition form.h:218
WINDOW * win
Definition form.h:155
bool f_in_spec
Definition form.h:215
char help_spec[MAXLEN]
Definition form.h:194