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// #define DEBUG_IMMEDOK true
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 display_form(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(Form *form, int, char *, char *);
43int form_exec_cmd(Form *);
44int form_exec_receiver(Init *);
45int form_process(Init *);
46int form_post(Init *);
47int init_form(Init *, int, char **, uint, uint);
48int form_engine(Init *);
49uint form_yx_to_fidx(Form *, uint, uint);
50void mk_filler(char *, uint);
51
52/** @brief Initialize form data structure and parse description file
53 @ingroup form_engine
54 @param init A pointer to the Init structure containing form data and state.
55 @param argc The number of command line arguments passed to the form.
56 @param argv The array of command line arguments passed to the form.
57 @param begy The y-coordinate for the top-left corner of the form window.
58 @param begx The x-coordinate for the top-left corner of the form window.
59 @return 0 on success, or a non-zero value if an error occurs during
60*/
61int init_form(Init *init, int argc, char **argv, uint begy, uint begx) {
62 int rc;
63 char tmp_str[MAXLEN];
64 if (init->form != nullptr)
66 init->form = new_form(init, argc, argv, begy, begx);
67 Form *form = init->form;
68 if (!form->f_mapp_spec) {
69 if (form->mapp_spec[0] == '\0') {
70 rc = ui_perror(_("Error: No form specification file given"));
71 } else {
72 strnz__cpy(tmp_str, "form->mapp_spec: ", MAXLEN - 1);
73 strnz__cat(tmp_str, form->mapp_spec, MAXLEN - 1);
74 strnz__cat(tmp_str, _(" not found"), MAXLEN - 1);
75 rc = ui_perror(tmp_str);
76 }
78 return rc;
79 }
80 if (begy != 0)
81 form->begy = begy;
82 if (begx != 0)
83 form->begx = begx;
84 if ((form->f_in_spec && (form->in_spec[0] == '\0')) ||
85 (strcmp(form->in_spec, "-") == 0) ||
86 strcmp(form->in_spec, "/dev/stdin") == 0) {
87 strnz__cpy(form->in_spec, "/dev/stdin", MAXLEN - 1);
88 form->f_in_pipe = true;
89 }
90 if (form->title[0] == '\0')
92 rc = form_engine(init);
93 UiSurface *sfc = ui_surface[sfc_ptr];
95 if (sfc)
98 return rc;
99}
100/** @brief Form main processing loop
101 @ingroup form_engine
102 @param init A pointer to the Init structure containing form data and state.
103 @return 0 on successful completion, or a non-zero value if the user cancels
104 the form or if an error occurs during processing.
105 1. Parse the form description file to populate the form data structure.
106 2. Read any initial data for the form fields from a specified input
107 source.
108 3. Display the form on the screen with the initial field values.
109 4. Enter a loop to handle user input for field entry, calculation, help,
110 and cancellation:
111 a. If the user selects the accept action, perform any necessary
112 calculations or post-processing, and then either return to field entry
113 or exit the loop if the form is accepted.
114 b. If the user selects the help action, display the help screen and
115 return to the form after the user exits the help screen.
116 c. If the user selects the cancel action, exit the loop and return a
117 cancel status. */
118int form_engine(Init *init) {
119 char tmp_str[MAXLEN];
120 int form_action;
121 char *eargv[MAXARGS];
122 int eargc;
123
124 Form *form = init->form;
125 if (form == nullptr) {
126 ui_perror(_("FORM: form data structure is nullptr"));
127 }
128 if (form_parse_desc(form)) {
129 return 0;
130 }
132 display_form(init);
140 ui_set_chyron_key(form->chyron, 10, _("F10 Accept"), UIKEY_F10);
142 form->chyron->key[2]->active = false; // F2 Process
143 form->chyron->key[3]->active = false; // F3 Calculate
144 form->chyron->key[4]->active = false; // F4 Query
145 form->chyron->key[5]->active = false; // F5 Edit
146 form->chyron->key[18]->active = false; // Insert
148 UiSurface *sfc = ui_surface[sfc_ptr];
149 ui_display_chyron(sfc, 1, form->chyron, form->lines - 1, form->chyron->l);
150 // 1 F1 Help
151 // 2 F2 Process
152 // 3 F3 Calculate
153 // 4 F4 Query
154 // 5 F5 Edit
155 // 9 F9 Cancel
156 // 10 F10 Accept
157 form->fidx = 0;
158 form_action = 0;
159 while (1) {
160 if (form_action == 0 || form_action == FA_CONTINUE)
161 form_action = field_navigator(form);
162 form->chyron->key[18]->active = false; // Insert
163 switch (form_action) {
164 case FA_ACCEPT:
165 if (form->f_process || form->f_calculate || form->f_query)
166 form_action = form_process(init);
167 else
168 form_action = form_post(init);
169 if (form_action == FA_HELP || form_action == FA_CANCEL ||
170 form_action == FA_CONTINUE || form_action == FA_END)
171 continue;
172 if (form_action == FA_ACCEPT) {
173 form_action = FA_END;
174 continue;
175 }
176 break;
177 case FA_END:
178 if (form->f_out_spec || form->out_spec[0] != '\0')
179 form_write(form);
180 if (form->f_receiver_cmd || form->receiver_cmd[0] != '\0')
182 break;
183 case FA_HELP:
184 if (form->f_help_spec && form->help_spec[0] != '\0')
185 strnz__cpy(tmp_str, form->help_spec, MAXLEN - 1);
186 else {
187 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
188 strnz__cat(tmp_str, "/", MAXLEN - 1);
190 }
191 eargc = 0;
192 eargv[eargc++] = strdup("view");
193 eargv[eargc++] = strdup("-Nf");
194 eargv[eargc++] = strdup(tmp_str);
195 eargv[eargc] = nullptr;
196 init->lines = 30;
197 init->cols = 66;
198 init->begy = form->begy + 1;
199 init->begx = form->begx + 1;
200 strnz__cpy(init->title, _("Form Help"), MAXLEN - 1);
201 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
202 init->begx);
203 destroy_argv(eargc, eargv);
204 form_action = FA_CONTINUE;
205 break;
206 case FA_CANCEL:
207 return FA_CANCEL;
208 default:
209 form_action = FA_CONTINUE;
210 break;
211 }
212 if (form_action == FA_END)
213 break;
214 }
215 return 0;
216}
217/** @brief Handle post-processing after field entry, allowing user to edit data,
218 execute a provider command, or write data to an output file.
219 @ingroup form_engine
220 @param init A pointer to the Init structure containing form data and state.
221 @return An integer status code indicating the next action for the form
222 processing loop (e.g., FA_CONTINUE, FA_CANCEL, FA_ACCEPT, FA_HELP). */
223int form_post(Init *init) {
224 bool loop = true;
225 int c, rc;
226 click_y = click_x = -1;
227 Form *form = init->form;
228 UiSurface *sfc = ui_surface[sfc_ptr];
229 UiEvent event;
230 uint maxy, maxx;
231 ui_cursor_move(sfc, WIN, form->lines - 1, 0);
232 ui_wclrtoeol(sfc, WIN);
233 // 1 F1 Help
234 // 2 F2 Process
235 // 3 F3 Calculate
236 // 4 F4 Query
237 // 5 F5 Edit
238 // 9 F9 Cancel
239 // 10 F10 Continue
240 // 11 F10 Commit
241 // 18 INS Insert
242 form->chyron->key[5]->active = true; // F5 Edit
243 form->chyron->key[10]->active = false; // F10 Continue
244 form->chyron->key[18]->active = false; // INS Insert
246 rc = -1;
247 while (loop) {
248 if (rc == -1) {
249 ui_display_chyron(sfc, WIN, form->chyron, form->lines - 1, form->chyron->l);
250 c = ui_get_event(sfc, WIN, form->chyron, &event, -1);
251 ui_getmaxyx(sfc, WIN, &maxy, &maxx);
252 if (event.in_win == 1 && event.y == maxy - 1)
253 c = ui_get_chyron_key(form->chyron, event.x);
254 }
255 switch (c) {
256 case UIKEY_F01:
257 return FA_HELP;
258 case UIKEY_F05: // F5 Edit
260 loop = false;
261 rc = FA_CONTINUE;
262 break;
263 }
264 continue;
265 case UIKEY_F09:
266 loop = false;
267 rc = FA_CANCEL;
268 break;
269 case UIKEY_F10:
270 loop = false;
271 rc = FA_ACCEPT;
272 break;
273 case UIKEY_BUTTON1:
274 continue;
275 default:
276 break;
277 }
278 }
279 form->chyron->key[5]->active = false; // F5 Edit
280 return rc;
281}
282
283/** @brief Handle integration with a getter program which will provide field
284 data.
285 @ingroup form_engine
286 @param init A pointer to the Init structure containing form data and state.
287 @return An integer status code indicating the next action for the form
288 processing loop (e.g., FA_CONTINUE, FA_CANCEL, FA_ACCEPT, FA_HELP).
289 @details This function provides integration with getter programs.
290 The requirements are:
291 1. The form description file must have a line containing only 'C'
292 (calculate), 'Q' (query), org 'G' (getter) to indicate that the form supports
293 a getter.
294 2. The form description file must specify the provider command using a
295 line starting with '!' followed by the command and its arguments.
296 3. The getter program must be able to accept field data from a file,
297 from standard input, or as command line parameters.
298 4. The getter program must output field values in a format that can be
299 read by the form (e.g., one value per line), either to a file or to standard
300 output.
301
302 The sequence of operations is as follows:
303
304 1. The 'C', 'Q', or 'G' option causes Form to pause and display an
305 UIKEY_F05 Calculate, Query, or Getter. option on the chyron.
306 2. The user can then cancel the operation by pressing UIKEY_F09 or
307 activate the getter option by pressing UIKEY_F05.
308 3. Form outputs its data to file, standard output, or as command line
309 parameters.
310 4. Form executes the getter program.
311 5. The getter program processes the data and outputs the results.
312 6. Form reads the results and populates the appropriate form fields.
313 7. Form presents the user with an option to edit the data, and the
314 sequence restarts at 1.
315
316 This function forks and executes the getter executable as a child
317 process, creates a pipe to read the output from the provider command,
318 reads the output, and updates the Form fields.
319 */
320int form_process(Init *init) {
321 uint i, c;
322 int rc;
323 char earg_str[MAXLEN + 1];
324 char *eargv[MAXARGS];
325 int eargc;
326 char file_spec[MAXLEN];
327 bool loop = true;
328 pid_t pid;
329 int pipe_fd[2];
330 UiSurface *sfc = ui_surface[sfc_ptr];
331 UiEvent event;
332 Form *form = init->form;
333 ui_cursor_move(sfc, WIN, form->lines - 1, 0);
334 uint maxy, maxx;
335 //
336 // 1 F1 Help
337 // 2 F2 Process
338 // 3 F3 Calculate
339 // 4 F4 Query
340 // 5 F5 Edit
341 // 9 F9 Cancel
342 // 10 F10 Continue
343 // 11 F10 Commit
344 form->chyron->key[2]->active = form->f_process > 0 ? true : false;
345 form->chyron->key[3]->active = form->f_calculate > 0 ? true : false;
346 form->chyron->key[4]->active = form->f_query > 0 ? true : false; // PgUp
347 form->chyron->key[10]->active = false; // F10 Continue
348 form->chyron->key[18]->active = false; // INS Insert
349
350 while (loop) {
352 ui_display_chyron(sfc, 1, form->chyron, form->lines - 1, form->chyron->l);
353 click_y = click_x = -1;
354 c = ui_get_event(sfc, WIN, form->chyron, &event, -1);
355 ui_getmaxyx(sfc, WIN, &maxy, &maxx);
356 if (event.in_win == 1 && event.y == maxy - 1)
357 c = ui_get_chyron_key(form->chyron, event.x);
358
359 switch (c) {
360 case UIKEY_F01:
361 return FA_HELP;
362 case UIKEY_F02: // F2 Process
363 case UIKEY_F03: // F3 Calculate
364 case UIKEY_F04: // F4 Query
365 if (form->f_out_spec)
366 form_write(form);
367 if (form->f_provider_cmd) {
368 strnz__cpy(earg_str, form->provider_cmd, MAXLEN - 1);
369 for (i = 0; i < form->fcnt; i++) {
370 strnz__cat(earg_str, " ", MAXLEN - 1);
371 strnz__cat(earg_str, form->field[i]->accept_s, MAXLEN - 1);
372 }
373 eargc = str_to_args(eargv, earg_str, MAXARGS);
374 strnz__cpy(file_spec, eargv[0], MAXLEN - 1);
375 base_name(eargv[0], file_spec);
376 if (pipe(pipe_fd) == -1) {
377 destroy_argv(eargc, eargv);
378 ui_perror(_("pipe(pipe_fd) failed in init_form"));
379 return (1);
380 }
381 if ((pid = fork()) == -1) {
382 destroy_argv(eargc, eargv);
383 ui_perror(_("fork() failed in init_form"));
384 return (1);
385 }
386 if (pid == 0) { // Child
387 /** Prevent child process from writing to terminal */
388 int dev_null = open("/dev/null", O_WRONLY);
389 if (dev_null == -1) {
390 ui_perror(_("open(/dev/null) failed in init_pick child process"));
391 exit(EXIT_FAILURE);
392 }
393 dup2(dev_null, STDERR_FILENO);
394 close(dev_null);
395 close(pipe_fd[P_READ]);
396 dup2(pipe_fd[P_WRITE], STDOUT_FILENO);
397 stdio_fdnames(stdio_names_str, "form_engine.c:367");
398 close(pipe_fd[P_WRITE]);
399 execvp(eargv[0], eargv);
400 ssnprintf(em0, MAXLEN, _("%s, line: %d"), __FILE__,
401 __LINE__ - 2);
402 strnz__cpy(em1, "execvp(", MAXLEN - 1);
403 strnz__cat(em1, eargv[0], MAXLEN - 1);
404 strnz__cat(em1, ", ", MAXLEN - 1);
405 strnz__cat(em1, earg_str, MAXLEN - 1);
409 exit(EXIT_FAILURE);
410 } // Back to parent
411 close(pipe_fd[P_WRITE]);
412 form->in_fp = fdopen(pipe_fd[P_READ], "rb");
413 form->f_in_pipe = true;
415 close(pipe_fd[P_READ]);
416 waitpid(pid, nullptr, 0);
417 stdio_fdnames(stdio_names_str, "form_engine.c:387");
418 destroy_argv(eargc, eargv);
420 form->chyron->key[2]->active = false; // F2 Process
421 form->chyron->key[3]->active = false; // F3 Calculate
422 form->chyron->key[4]->active = false; // F4 Query
423 form->chyron->key[5]->active = true; // F5 Edit
424 form->chyron->key[10]->active = true; // F10 Accept
426 continue;
427 }
428 break;
429 case UIKEY_F05:
431 loop = false;
432 rc = FA_CONTINUE;
433 break;
434 }
435 continue;
436 case UIKEY_F09:
437 loop = false;
438 rc = FA_CANCEL;
439 break;
440 case UIKEY_F10:
441 loop = false;
442 rc = FA_ACCEPT;
443 break;
444 case UIKEY_BUTTON1:
445 break;
446 default:
447 break;
448 }
449 }
450 form->chyron->key[2]->active = false; // F2 Process
451 form->chyron->key[3]->active = false; // F3 Calculate
452 form->chyron->key[4]->active = false; // F4 Query
453 form->chyron->key[5]->active = false; // F5 Edit
454 return rc;
455}
456/** @brief Handle user input for field entry, allowing navigation between fields
457 and looping until an exit action is selected.
458 @ingroup form_engine
459 @param form A pointer to the Form structure containing form data and state.
460 @return An integer status code indicating the next action for the form
461 processing loop (e.g., FA_ACCEPT, FA_HELP, FA_CALC, FA_CANCEL).
462 @details This function manages user input for field entry, including
463 navigation between fields and handling of special keys for accepting,
464 canceling, requesting help, or performing calculations. The function loops
465 until the user selects an exit action (e.g., accept or cancel). */
466int field_navigator(Form *form) {
467
468 if (form->fidx >= form->fcnt)
469 form->fidx = 0;
470 while (1) {
471 int cmd_key = field_editor(form);
472 switch (cmd_key) {
473 case UIKEY_F01: // F1 Help
474 return (FA_HELP);
475 case UIKEY_F02: // F2 Process
476 case UIKEY_F03: // F3 Calculate
477 case UIKEY_F04: // F4 Query
478 if (form->f_process)
479 return (FA_CALC);
480 break;
481 case UIKEY_F09: // F9 Cancel
482 return (FA_CANCEL);
483 case UIKEY_F10: // F10 Accept
484 return (FA_ACCEPT);
485 case 'k':
486 case UIKEY_UP:
487 if (form->fidx != 0)
488 form->fidx--;
489 break;
490 case '\r':
491 case UIKEY_ENTER:
492 if (form->fidx < form->fcnt - 1)
493 form->fidx++;
494 else if (form->fidx == form->fcnt - 1)
495 return (FA_ACCEPT);
496 break;
497 case 'j':
498 case UIKEY_DOWN:
499 if (form->fidx < form->fcnt - 1)
500 form->fidx++;
501 else if (form->fidx == form->fcnt - 1)
502 return (FA_ACCEPT);
503 break;
504 case UIKEY_BUTTON1:
505 break;
506 default:
507 break;
508 }
509 }
510}
511uint form_yx_to_fidx(Form *form, uint y, uint x) {
512 for (uint i = 0; i < form->fcnt; i++) {
513 if (y == form->field[i]->line && x >= form->field[i]->col &&
514 x < form->field[i]->col + form->field[i]->len) {
515 return i;
516 }
517 }
518 return -1; // No field found at the given coordinates
519}
520
521/** @brief Display the form on the screen, including text elements and fields,
522 and set up the form window based on the form configuration.
523 @ingroup form_engine
524 @param init A pointer to the Init structure containing form data and state.
525 @return 0 on success, or a non-zero value if an error occurs while
526 creating the form window or rendering the form elements. */
527unsigned int display_form(Init *init) {
528 uint n, flin, fcol;
529 char tmp_str[MAXLEN];
530
531 Form *form = init->form;
532 form->lines = 0;
533 for (n = 0; n < form->dcnt; n++)
534 if (form->text[n]->line > form->lines)
535 form->lines = form->text[n]->line;
536 for (n = 0; n < form->fcnt; n++)
537 if (form->field[n]->line > form->lines)
538 form->lines = form->field[n]->line;
539 form->lines += 3;
540 if (form->lines > (LINES - form->begy))
541 form->lines = LINES - form->begy;
542 for (n = 0; n < form->fcnt; n++) {
543 if (form->field[n]->line >= (form->lines - 2))
544 form->fcnt = n;
545 }
546 form->cols += 2;
547 if (form->cols > (COLS - form->begx - 3))
548 form->cols = COLS - form->begx - 3;
550 strnz__cpy(tmp_str, _("ui_tracked_sfc_box failed: "), MAXLEN - 1);
551 strnz__cat(tmp_str, form->title, MAXLEN - 1);
552 ui_perror(tmp_str);
553 return (1);
554 }
555 UiSurface *sfc = ui_surface[sfc_ptr];
556#ifdef DEBUG_IMMEDOK
557 ui_immedok(sfc, WIN, true);
558 ui_immedok(sfc, BOX, true);
559#endif
560 for (form->fidx = 0; form->fidx < form->fcnt; form->fidx++) {
561 if (form->brackets[0] != '\0' && form->brackets[1] != '\0') {
562 flin = form->field[form->fidx]->line + 1;
563 fcol = form->field[form->fidx]->col;
564 ui_mvwadd_wch(sfc, BOX, flin, fcol, &cell_brktl);
565 fcol += form->field[form->fidx]->len + 1;
566 ui_mvwadd_wch(sfc, BOX, flin, fcol, &cell_brktr);
567 }
568 }
569 for (n = 0; n < form->dcnt; n++) {
570 strnz(form->text[n]->str, form->cols - 3);
571 ui_mvwaddstr(sfc, WIN, form->text[n]->line, form->text[n]->col,
572 form->text[n]->str);
573 }
576 return 0;
577}
578/** @brief Display form fields on the screen, populating field values and
579 formatting them according to the form configuration.
580 @ingroup form_engine
581 @param form A pointer to the Form structure containing form data and
582 state.
583 @details This function iterates through the defined form fields, formats
584 their display values based on the specified fill character and field
585 length, and renders them on the form window. It also updates the chyron
586 with available commands for user interaction. */
587void form_display_fields(Form *form) {
588 uint y, x;
589 uint pos = 0;
590 for (form->fidx = 0; form->fidx < form->fcnt; form->fidx++) {
591 if (form->field[form->fidx]->col + form->field[form->fidx]->len + 2 >
592 form->cols)
593 form->field[form->fidx]->len =
594 form->cols - (form->field[form->fidx]->col + 2);
595 // Make filler_cc
596 size_t fl = form->field[form->fidx]->len;
597 char *d = form->field[form->fidx]->filler_s;
598 size_t char_bytes = strlen(form->fill_char);
599 for (size_t i = 0; i < fl; i++) {
600 memcpy(d, form->fill_char, char_bytes);
601 d += char_bytes;
602 }
603 d = nullptr;
604
605 if (form->field[form->fidx]->accept_s[0] != '\0') {
607 // d = form->field[form->fidx]->accept_s;
608 } else {
609 memset(form->field[form->fidx]->accept_s, 0, MAXLEN);
610 // d = form->field[form->fidx]->accept_s;
611 }
613
614 y = form->field[form->fidx]->line;
615 x = form->field[form->fidx]->col;
616
617 UiSurface *sfc = ui_surface[sfc_ptr];
618 pos = 0;
619 ui_mbstr_to_cellstr(sfc, WIN,
620 form->field[form->fidx]->filler_cc,
621 form->field[form->fidx]->filler_s,
623 &pos,
624 form->field[form->fidx]->len + 1);
625
628
629 pos = 0;
630 ui_mbstr_to_cellstr(sfc, WIN, form->field[form->fidx]->display_cc, form->field[form->fidx]->display_s, &cell_nt, &pos, form->field[form->fidx]->len + 1);
631
634 }
635 return;
636}
637/** @brief Parse the form description file to populate the Form data
638 structure with field definitions, text elements, and other configuration
639 specified in the description file.
640 @ingroup form_engine
641 @param form A pointer to the Form structure containing form data and
642 state.
643 @return 0 on success, or a non-zero value if an error occurs while
644 parsing the description file (e.g., file not found, invalid format,
645 missing directives). */
646int form_parse_desc(Form *form) {
647 FILE *form_desc_fp;
648 char *token;
649 char *s;
650 uint cols = 0;
651 uint i, l;
652 int in_line_num = 0;
653 char in_buf[BUFSIZ];
654 char tmp_buf[BUFSIZ];
655 char *tmp_buf_p;
656 char tmp_str[BUFSIZ];
657 char delim[5];
658 char directive;
659
660 form_desc_fp = fopen(form->mapp_spec, "r");
661 if (form_desc_fp == nullptr) {
662 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
663 strnz__cpy(em1, "fopen ", MAXLEN - 1);
667 return (1);
668 }
669 for (i = 0; i < FIELD_MAXCNT; i++) {
670 form->field[i] = calloc(1, sizeof(Field));
671 if (!form->field[i]) {
672 sprintf(tmp_str, _("FORM: calloc failed for fields"));
673 ui_abend(EXIT_FAILURE, tmp_str);
674 }
675 }
676 for (i = 0; i < FIELD_MAXCNT; i++) {
677 form->text[i] = calloc(1, sizeof(Text));
678 if (!form->text[i]) {
679 sprintf(tmp_str, _("FORM: calloc failed for text"));
680 ui_abend(EXIT_FAILURE, tmp_str);
681 }
682 }
683 form->didx = 0;
684 form->fidx = 0;
685 form->fcnt = 0;
686 form->cols = 34;
687 while ((fgets(in_buf, MAXLEN, form_desc_fp)) != nullptr) {
688 s = in_buf;
689 in_line_num++;
690 l = trim(in_buf);
691 if (l == 0)
692 continue;
693 if (*s == D_COMMENT)
694 continue;
695 delim[0] = '\n';
696 delim[1] = in_buf[1];
697 delim[2] = '\0';
698 strnz__cpy(tmp_buf, in_buf, MAXLEN - 1);
699 tmp_buf_p = tmp_buf;
700 if (!(token = strtok(tmp_buf_p, delim))) {
701 continue;
702 }
703 directive = *token;
704 switch ((int)directive) {
705 case D_COMMENT:
706 break;
707 case D_CALC:
708 form->f_calculate = true;
709 break;
710 case D_QUERY:
711 form->f_query = true;
712 break;
713 case D_GETTER:
714 form->f_process = true;
715 break;
716 case D_CMD:
717 if (!(token = strtok(nullptr, delim))) {
718 form_desc_error(form, in_line_num, in_buf,
719 _("FORM: receiver_cmd delimiter"));
720 continue;
721 }
723 break;
724 case D_HELP:
725 if (!(token = strtok(nullptr, delim))) {
726 form_desc_error(form, in_line_num, in_buf,
727 _("FORM: help_spec delimiter"));
728 }
729 strnz__cpy(form->help_spec, token, MAXLEN - 1);
730 break;
731 case D_FIELD:
732 if (form->field[form->fidx] == nullptr) {
733 sprintf(tmp_str, _("FORM: calloc failed for fields"));
734 ui_abend(EXIT_FAILURE, tmp_str);
735 }
736 if (!(token = strtok(nullptr, delim))) {
737 form_desc_error(form, in_line_num, in_buf,
738 _("FORM: line number delimiter"));
739 return 1;
740 }
741 form->field[form->fidx]->line = atoi(token);
742 if (form->field[form->fidx]->line >= FIELD_MAXCNT) {
743 form_desc_error(form, in_line_num, in_buf,
744 _("FORM: invalid line number"));
745 return 1;
746 }
747 if (!(token = strtok(nullptr, delim))) {
748 form_desc_error(form, in_line_num, in_buf,
749 _("FORM: column number delimiter"));
750 return 1;
751 }
752 form->field[form->fidx]->col = atoi(token);
753 if (form->field[form->fidx]->col >= FIELD_MAXLEN) {
754 form_desc_error(form, in_line_num, in_buf,
755 _("FORM: invalid column number"));
756 break;
757 }
758 if (!(token = strtok(nullptr, delim))) {
759 strnz__cpy(tmp_str, in_buf, MAXLEN - 1);
760 form_desc_error(form, in_line_num, tmp_str, _("FORM: length delimiter"));
761 break;
762 }
763 form->field[form->fidx]->len = atoi(token);
764 if (form->field[form->fidx]->len > FIELD_MAXLEN) {
765 form_desc_error(form, in_line_num, in_buf, _("FORM: invalid length"));
766 break;
767 }
768 if (!(token = strtok(nullptr, delim))) {
769 form_desc_error(form, in_line_num, in_buf,
770 _("FORM: validation code delimiter"));
771 break;
772 }
773 form->field[form->fidx]->ff = 0;
774 for (i = 0; i < FF_INVALID; i++) {
775 str_to_lower(token);
777 if (!strcmp(token, ff_tbl[i])) {
778 form->field[form->fidx]->ff = i;
779 break;
780 }
781 }
782 if (form->field[form->fidx]->ff >= FF_INVALID) {
783 form_desc_error(form, in_line_num, in_buf,
784 _("FORM: invalid format code"));
785 break;
786 }
787 cols =
788 form->field[form->fidx]->col + form->field[form->fidx]->len + 1;
789 if (cols > form->cols)
790 form->cols = cols;
791 form->fidx++;
792 form->fcnt = form->fidx;
793 break;
794 case D_TEXT:
795 if (form->text[form->didx] == nullptr) {
796 sprintf(tmp_str, _("FORM: calloc failed for text"));
797 ui_abend(EXIT_FAILURE, tmp_str);
798 }
799 if (!(token = strtok(nullptr, delim))) {
800 form_desc_error(form, in_line_num, in_buf,
801 _("FORM: line number delimiter"));
802 break;
803 }
804 form->text[form->didx]->line = atoi(token);
805 if (form->text[form->didx]->line >= FIELD_MAXCNT) {
806 form_desc_error(form, in_line_num, in_buf,
807 _("FORM: invalid line number"));
808 break;
809 }
810 if (!(token = strtok(nullptr, delim))) {
811 form_desc_error(form, in_line_num, in_buf,
812 _("FORM: column number delimiter"));
813 break;
814 }
815 form->text[form->didx]->col = atoi(token);
816 if (form->text[form->didx]->col >= FIELD_MAXLEN) {
817 form_desc_error(form, in_line_num, in_buf,
818 _("FORM: invalid column number"));
819 break;
820 }
821 if (!(token = strtok(nullptr, delim))) {
822 form_desc_error(form, in_line_num, in_buf, _("FORM: text delimiter"));
823 break;
824 }
825 strnz__cpy(form->text[form->didx]->str, token, MAXLEN - 1);
826 form->text[form->didx]->len = strlen(form->text[form->didx]->str);
827 if (form->text[form->didx]->len > FIELD_MAXLEN) {
828 form_desc_error(form, in_line_num, in_buf, _("FORM: invalid length"));
829 break;
830 }
831 cols =
832 form->text[form->didx]->col + form->text[form->didx]->len + 1;
833 if (cols > form->cols)
834 form->cols = cols;
835 form->didx++;
836 form->dcnt = form->didx;
837 break;
838 case D_HEADER:
839 if ((token = strtok(nullptr, delim))) {
840 strnz__cpy(form->title, token, MAXLEN - 1);
841 }
842 break;
843 default:
844 form_desc_error(form, in_line_num, in_buf, _("invalid directive"));
845 break;
846 }
847 }
848 fclose(form_desc_fp);
849 if (form->didx < 1 && form->fidx < 1) {
850 ssnprintf(em0, MAXLEN - 1, _("%s, line: %d"), __FILE__, __LINE__);
851 ssnprintf(em1, MAXLEN - 1, "%s", _("Error in description file:"));
854 return (1);
855 }
856 return (0);
857}
858/** @brief Read initial data for form fields from a specified input source,
859 such as a file or standard input, and populate the form fields with the
860 data.
861 @ingroup form_engine
862 @param form A pointer to the Form structure containing form data and
863 state.
864 @return 0 on success, or a non-zero value if an error occurs while
865 reading the data or if the specified input source is invalid or empty. */
866int form_read_data(Form *form) {
867 struct stat sb;
868 char in_buf[MAXLEN];
869 char field[MAXLEN];
870 int cmd_key;
871
872 if (!form->f_in_pipe) {
873 if (form->f_in_spec && form->in_spec[0] != '\0') {
874 if ((lstat(form->in_spec, &sb) == -1) || (sb.st_size == 0) ||
875 ((form->in_fp = fopen(form->in_spec, "rb")) == nullptr)) {
877 if (sb.st_size == 0)
878 strnz__cpy(em1, _("File is empty"), MAXLEN - 1);
879 else
880 strnz__cpy(em1, _("File does not exist"), MAXLEN - 1);
881 strnz__cpy(em2, _("Fields will be blank or zero"), MAXLEN - 1);
882 cmd_key = ui_display_error(em0, em1, em2, nullptr);
883 if (cmd_key == UIKEY_F09)
884 return (1);
885 }
886 if (form->in_fp == nullptr)
887 return (1);
888 } else
889 return (0);
890 }
891 form->fidx = 0;
892 if (form->in_fp != nullptr) {
893 while ((fgets(in_buf, MAXLEN, form->in_fp)) != nullptr) {
894 if (form->fidx < FIELD_MAXCNT)
895 strnz__cpy(field, in_buf, MAXLEN - 1);
896 form_fmt_field(form, field);
897 form->fidx++;
898 }
899 fclose(form->in_fp);
900 }
901 return (0);
902}
903/** @brief Execute a command specified in the form description file, passing
904 form field values as arguments, and optionally redirecting output to a file.
905 @ingroup form_engine
906 @param form A pointer to the Form structure containing form data and state.
907 @return 0 on success, or a non-zero value if an error occurs while
908 constructing or executing the command. */
909int form_exec_cmd(Form *form) {
910 char earg_str[MAXLEN + 1];
911 uint i;
912 strnz__cpy(earg_str, form->receiver_cmd, MAXLEN - 1);
913 for (i = 0; i < form->fcnt; i++) {
914 strnz__cat(earg_str, " ", MAXLEN - 1);
915 strnz__cat(earg_str, form->field[i]->accept_s, MAXLEN - 1);
916 }
917 if (form->f_out_spec && form->f_process) {
918 strnz__cat(earg_str, " >", MAXLEN - 1);
919 strnz__cat(earg_str, form->out_spec, MAXLEN - 1);
920 }
921 shell(earg_str);
922 return 0;
923}
924/** @brief Execute a command specified by the -R option on the form command line
925 @ingroup form_engine
926 @param init A pointer to the Init structure
927 @return 0 on success, or a non-zero value if an error occurs while
928 constructing or executing the command.
929 @details This function constructs a command by taking the receiver_cmd
930 appending field values as arguments, and handling special cases such as
931 multiple command arguments or placeholder substitution. It then executes the
932 command, either displaying the output in a popup view if the command is
933 "view", or executing it directly in a child process for other commands. The
934 function also handles error cases and ensures that resources are properly
935 freed. */
936int form_exec_receiver(Init *init) {
937 int rc = -1;
938 uint eargc;
939 char *eargv[MAXARGS];
940 char tmp_str[MAXLEN] = {'\0'};
941 char title[MAXLEN];
942 char sav_arg[MAXLEN];
943 char *out_s;
944 uint eargx = 0;
945 uint i = 0;
946 pid_t pid = 0;
947 bool f_append_values = false;
948
949 title[0] = '\0';
950 Form *form = init->form;
951 if (form->receiver_cmd[0] == '\0')
952 return -1;
953 // Eliminate leading and trailing quotes
954 if (form->receiver_cmd[0] == '\\' || form->receiver_cmd[0] == '\"') {
955 size_t len = strlen(form->receiver_cmd);
956 if (len > 1 && form->receiver_cmd[len - 1] == '\"') {
957 memmove(form->receiver_cmd, form->receiver_cmd + 1, len - 2);
958 form->receiver_cmd[len - 2] = '\0';
959 }
960 }
961 eargc = str_to_args(eargv, form->receiver_cmd, MAXARGS - 1);
962 tmp_str[0] = '\0';
963 if (form->f_multiple_cmd_args) {
964 // concatenate fields into a single argument
965 for (i = 0; i < form->fcnt; i++) {
966 if (form->field[i]->accept_s[0] && eargc < MAXARGS) {
967 if (tmp_str[0] != '\0')
968 strnz__cat(tmp_str, " ", MAXLEN - 1);
969 strnz__cat(tmp_str, form->field[i]->accept_s, MAXLEN - 1);
970 }
971 }
972 eargv[eargc++] = strdup(tmp_str);
973 } else {
974 f_append_values = false;
975 // find lines with "%%" to determine where to insert field values
976 i = 0;
977 while (i < eargc) {
978 /** This is the line that gets the selected objects */
979 if (strstr(eargv[i], "%%") != nullptr) {
980 tmp_str[0] = '\0';
981 f_append_values = true;
982 strnz__cpy(sav_arg, eargv[i], MAXLEN - 1);
983 eargx = i;
984 break;
985 }
986 i++;
987 }
988 for (i = 0; i < form->fcnt; i++) {
989 // concatenate field values onto tmp_str
990 if (form->field[i]->accept_s[0] && eargc < MAXARGS - 1) {
991 if (f_append_values == true) {
992 if (tmp_str[0] != '\0')
993 strnz__cat(tmp_str, " ", MAXLEN - 1);
994 strnz__cat(tmp_str, form->field[i]->accept_s, MAXLEN - 1);
995 continue;
996 }
997 eargv[eargc++] = strdup(form->field[i]->accept_s);
998 }
999 }
1000 // replace "%%" with concatenated field values
1001 if (f_append_values == true) {
1002 if (eargv[eargx] != nullptr) {
1003 free(eargv[eargx]);
1004 eargv[eargx] = nullptr;
1005 }
1006 out_s = rep_substring(sav_arg, "%%", tmp_str);
1007 if (out_s == nullptr || out_s[0] == '\0') {
1008 i = 0;
1009 while (i < eargc) {
1010 if (eargv[i] != nullptr)
1011 free(eargv[i]);
1012 i++;
1013 }
1014 ui_perror(_("rep_substring() failed in form_exec_objects"));
1015 return 1;
1016 }
1017 strnz__cpy(title, out_s, MAXLEN - 1);
1018 eargv[eargx] = strdup(out_s);
1019 if (out_s != nullptr) {
1020 free(out_s);
1021 out_s = nullptr;
1022 }
1023 }
1024 }
1025 if (eargc > 0)
1026 eargv[eargc] = nullptr;
1027 base_name(tmp_str, eargv[0]);
1028 if (tmp_str[0] != '\0' && (strcmp(tmp_str, "view") == 0)) {
1029 /** initialize popup_view arguments and execute popup_view to display
1030 command output within form interface */
1031 init->lines = 60;
1032 init->cols = 80;
1033 init->begy = form->begy + 1;
1034 init->begx = form->begx + 1;
1035 if (title[0] != '\0')
1036 strnz__cpy(init->title, title, MAXLEN - 1);
1037 else
1038 strnz__cpy(init->title, eargv[eargc], MAXLEN - 1);
1039 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
1040 init->begx);
1041 i = 0;
1042 while (i < eargc) {
1043 if (eargv[i] != nullptr)
1044 free(eargv[i]);
1045 i++;
1046 }
1047 return 0;
1048 } else {
1050 if ((pid = fork()) == -1) {
1051 /** fork failed, free eargv and return error */
1052 i = 0;
1053 while (i < eargc) {
1054 if (eargv[i] != nullptr)
1055 free(eargv[i]);
1056 i++;
1057 }
1058 ui_perror(_("fork() failed in form_exec_objects"));
1059 return (1);
1060 }
1061 if (pid == 0) {
1062 /** Prevent child process from writing to terminal */
1063 int dev_null = open("/dev/null", O_WRONLY);
1064 if (dev_null == -1) {
1065 ui_perror(_("open(/dev/null) failed in init_form child process"));
1066 exit(EXIT_FAILURE);
1067 }
1068 dup2(dev_null, STDERR_FILENO);
1069 close(dev_null);
1070 /** Child process to execute command */
1071 execvp(eargv[0], eargv);
1072 /** If execvp returns, it means execution failed, so free eargv
1073 and print error message before exiting */
1074 strnz__cpy(tmp_str, _("Can't exec form cmd: "), MAXLEN - 1);
1075 strnz__cat(tmp_str, eargv[0], MAXLEN - 1);
1076 ui_perror(tmp_str);
1077 exit(EXIT_FAILURE);
1078 }
1079 }
1080 waitpid(pid, nullptr, 0);
1081 destroy_argv(eargc, eargv);
1084 return rc;
1085}
1086/** @brief Write form field values to a specified output destination, such
1087 as a file or standard output, based on the form configuration and user
1088 input.
1089 @ingroup form_engine
1090 @param form A pointer to the Form structure containing form data and
1091 state.
1092 @return 0 on success, or a non-zero value if an error occurs while
1093 writing the data or if the specified output destination is invalid. */
1094int form_write(Form *form) {
1095 uint n;
1096 if (form->out_spec[0] == '\0' || strcmp(form->out_spec, "-") == 0 ||
1097 strcmp(form->out_spec, "/dev/stdout") == 0) {
1098 strnz__cpy(form->out_spec, "/dev/stdout", MAXLEN - 1);
1099 close(form->out_fd);
1100 // form->out_fd = open(form->out_spec, O_CREAT | O_RDWR | O_TRUNC,
1101 // 0644);
1102 form->out_fd = dup(STDOUT_FILENO);
1103 if (form->out_fd == -1) {
1104 ssnprintf(em0, MAXLEN - 1, _("%s, line: %d"), __FILE__, __LINE__ - 1);
1105 strnz__cpy(em1, "open ", MAXLEN - 1);
1109 return (1);
1110 }
1111 // dup2(form->out_fd, STDOUT_FILENO);
1112 form->out_fp = fdopen(form->out_fd, "w");
1113 form->f_out_spec = true;
1114 form->f_out_pipe = true;
1115 } else {
1116 if ((form->out_fp = fopen(form->out_spec, "w")) == nullptr) {
1117 ssnprintf(em0, MAXLEN - 1, _("%s, line: %d"), __FILE__, __LINE__ - 1);
1120 return (1);
1121 }
1122 }
1123 if (form->out_fp == nullptr) {
1124 ssnprintf(em0, MAXLEN - 1, _("%s, line: %d"), __FILE__, __LINE__ - 1);
1125 strnz__cpy(em1, "fopen ", MAXLEN - 1);
1129 return (1);
1130 }
1131 for (n = 0; n < form->fcnt; n++)
1132 fprintf(form->out_fp, "%s\n", form->field[n]->accept_s);
1133 if (form->out_fp != nullptr)
1134 fclose(form->out_fp);
1135 return (0);
1136}
1137/** @brief Handle errors encountered while parsing the form description
1138 file, providing detailed error messages that include the file name, line
1139 number, and the specific error encountered.
1140 @ingroup form_engine
1141 @param form A pointer to the Form structure containing form data and
1142 state.
1143 @param in_line_num The line number in the description file where the
1144 error occurred.
1145 @param in_buf The content of the line that caused the error, for
1146 context.
1147 @param em A specific error message describing the nature of the error.
1148 @return An integer status code indicating how the user responded to the
1149 error message (e.g., which key they pressed to acknowledge the error). */
1150int form_desc_error(Form *form, int in_line_num, char *in_buf, char *em) {
1151 int cmd_key;
1152 ssnprintf(em0, MAXLEN - 1, "%s: %s", __FILE__, em);
1153 ssnprintf(em1, MAXLEN - 1, _("Desc file: %s, line: %d"), form->mapp_spec,
1154 in_line_num);
1155 strnz__cpy(em2, in_buf, MAXLEN - 1);
1156 cmd_key = ui_display_error(em0, em1, em2, nullptr);
1157 return cmd_key;
1158}
#define FIELD_MAXLEN
Definition form.h:18
@ FF_INVALID
Definition form.h:68
@ FA_END
Definition form.h:42
@ FA_ACCEPT
Definition form.h:29
@ FA_CALC
Definition form.h:37
@ FA_HELP
Definition form.h:31
@ FA_CANCEL
Definition form.h:33
@ FA_CONTINUE
Definition form.h:27
#define FIELD_MAXCNT
Definition form.h:19
#define FORM_HELP_FILE
Definition common.h:36
#define P_READ
Definition common.h:53
int popup_view(Init *, int, char **, uint, uint, uint, uint)
instantiate a view popup window
Definition popups.c:145
#define P_WRITE
Definition common.h:54
char stdio_names_str[4096]
Definition futil.c:122
#define MAXARGS
Definition cm.h:64
char * stdio_fdnames(char *, char *)
Definition futil.c:1108
char * rep_substring(const char *, const char *, const char *) __attribute__((nonnull(1
Replace all occurrences of "tgt_s" in "org_s" with "rep_s".
Definition futil.c:1449
void ui_render()
Render the UI by updating panels and refreshing the display.
Definition ui_ncurses.c:884
UiCell cell_nt
Definition ui_common.c:106
UiCell cell_nt_hl_rev
Definition ui_common.c:109
char em1[MAXLEN]
Definition ui_common.c:47
void ui_getmaxyx(UiSurface *s, ss_t w, uint *lines, uint *cols)
Get the maximum dimensions of a UiSurface.
Definition ui_ncurses.c:690
char em2[MAXLEN]
Definition ui_common.c:48
int ui_cursor_move(UiSurface *s, ss_t w, uint y, uint x)
Move the cursor to a specified position within a UiSurface.
Definition ui_ncurses.c:666
int ui_cm_surface_destroy(UiSurface *sfc)
Destroy the most recently created surface.
Definition ui_common.c:466
void ui_restore_wins()
Restore the state of all windows in the UI. This function refreshes the standard screen and all surfa...
int click_x
Definition ui_common.c:30
int sfc_ptr
Definition ui_ncurses.c:42
UiCell cell_brktl
Definition ui_common.c:104
int click_y
Definition ui_common.c:29
char em0[MAXLEN]
Definition ui_common.c:46
void ui_endwin()
End the UI application, cleaning up resources and shutting down the UI runtime.
Definition ui_ncurses.c:412
int ui_tracked_sfc_box(uint wlines, uint wcols, uint wbegy, uint wbegx, const char *wtitle)
Create a new surface with a box window.
Definition ui_common.c:387
int ui_wclrtoeol(UiSurface *s, ss_t w)
Clear to the end of the line in a surface window.
UiCell cell_brktr
Definition ui_common.c:105
UiCell cell_fill_char
Definition ui_common.c:103
#define BUFSIZ
Definition view.h:38
#define UIKEY_F02
#define UIKEY_F03
#define UIKEY_F09
#define UIKEY_IC
#define UIKEY_F01
#define UIKEY_BUTTON1
#define UIKEY_UP
#define UIKEY_F10
#define UIKEY_F05
#define UIKEY_ENTER
#define UIKEY_DOWN
#define UIKEY_F04
int ui_get_event(UiSurface *s, ss_t w, UiChyron *chyron, UiEvent *ev, int timeout_ms)
Read input events.
void ui_set_chyron_key(UiChyron *chyron, uint k, char *s, uint kc)
Set chyron key with default color pair (cp_nt_rev).
Definition ui_common.c:717
UiChyron * ui_new_chyron(UiSurface *sfc, ss_t w)
Create new chyron structure.
Definition ui_common.c:601
void ui_display_chyron(UiSurface *sfc, ss_t w, UiChyron *chyron, uint line, uint col)
Display chyron on the specified line and column of the surface.
Definition ui_common.c:837
void ui_set_chyron_key_cb(UiChyron *chyron, uint k, char *s, uint kc, UiCell cell_base)
Set chyron key with color pair (cp).
Definition ui_common.c:693
bool ui_is_set_chyron_key(UiChyron *chyron, uint k)
Check if function key label is set.
Definition ui_common.c:677
void ui_abend(int ec, char *s)
Convert multibyte string to complex character array.
Definition ui_common.c:362
UiChyron * ui_destroy_chyron(UiChyron *chyron)
Destroy UiChyron structure.
Definition ui_common.c:656
int ui_get_chyron_key(UiChyron *chyron, uint x)
Get keycode from chyron.
Definition ui_common.c:861
void ui_compile_chyron(UiChyron *chyron)
construct the chyron string from the chyron structure
Definition ui_common.c:782
UiSurface * ui_surface[UI_SFC_MAX]
Definition ui_ncurses.c:40
int ui_mvwadd_wchnstr(UiSurface *s, ss_t w, uint y, uint x, const UiCell *cmplx_buf, uint n)
Draw a complex character string at a specific position in a surface window, with a maximum length.
int ui_mvwadd_wchstr(UiSurface *s, ss_t w, uint y, uint x, const UiCell *cmplx_buf)
Draw a complex character string at a specific position in a surface window.
int ui_mvwaddstr(UiSurface *s, ss_t w, uint y, uint x, const char *text)
Draw a text string at a specific position in a surface window.
int ui_mvwadd_wch(UiSurface *s, ss_t w, uint y, uint x, const UiCell *cell)
Draw a complex character at a specific position in a surface window.
#define MAXLEN
Definition curskeys.c:15
void form_usage()
void mk_filler(char *, uint)
uint form_yx_to_fidx(Form *, uint, uint)
#define _(String)
Definition amort.c:23
char ff_tbl[][26]
Definition fields.c:25
int shell(char *)
Execute a shell command.
Definition exec.c:74
int form_fmt_field(Form *form, char *s)
Format field according to its format type.
Definition fields.c:449
int field_editor(Form *)
Accept input for a field.
Definition fields.c:47
int form_exec_receiver(Init *)
Execute a command specified by the -R option on the form command line.
int form_desc_error(Form *form, 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 command specified in the form description file, passing form field values as arguments,...
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 init_form(Init *, int, char **, uint, uint)
Initialize form data structure and parse description file.
Definition form_engine.c:61
int field_navigator(Form *)
Handle user input for field entry, allowing navigation between fields and looping until an exit actio...
unsigned int display_form(Init *)
Display the form on the screen, including text elements and fields, and set up the form window based ...
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition whence.c:291
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition futil.c:361
bool str_to_lower(char *)
Converts a string to lowercase.
Definition futil.c:482
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:599
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:390
int str_to_args(char **, char *, uint)
Converts a string into an array of argument strings.
Definition futil.c:412
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition whence.c:320
char * strerror__r(int errnum, char *buf, size_t buflen)
A thread-safe alternative to strerror() that writes the error message corresponding to errnum into th...
Definition futil.c:748
int destroy_argv(uint argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:468
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:1132
Form * destroy_form(Init *init)
Destroy Form structure.
Definition mem.c:267
Form * new_form(Init *init, int argc, char **argv, uint begy, uint begx)
Create and initialize Form structure.
Definition mem.c:244
void sig_prog_mode()
Set up signal handlers for interrupt signals.
Definition sig.c:61
int ui_perror(char *emsg_str)
Display a simple error message window or print to stderr.
Definition ui_common.c:1257
int ui_display_error(char *msg0, char *msg1, char *msg2, char *msg3)
Display an error message window or print to stderr.
Definition ui_common.c:1200
ss_t in_win
Definition ui_backend.h:112
char title[MAXLEN]
Definition common.h:132
char mapp_help[MAXLEN]
Definition common.h:152
int begx
Definition common.h:121
Form * form
Definition common.h:189
int cols
Definition common.h:119
int begy
Definition common.h:120
int lines
Definition common.h:118
uint col
Definition form.h:102
char str[SCR_COLS]
Definition form.h:104
uint len
Definition form.h:106
uint line
Definition form.h:100
char display_s[FIELD_MAXLEN]
Definition form.h:130
UiCell display_cc[FIELD_MAXLEN]
Definition form.h:135
char accept_s[FIELD_MAXLEN]
Definition form.h:126
uint col
Definition form.h:115
uint ff
Definition form.h:119
uint len
Definition form.h:117
UiCell filler_cc[FIELD_MAXLEN]
Definition form.h:141
uint line
Definition form.h:113
char filler_s[FIELD_MAXLEN]
Definition form.h:136
uint dcnt
Definition form.h:332
FILE * out_fp
Definition form.h:163
Text * text[FIELD_MAXCNT]
Definition form.h:340
uint cols
Definition form.h:151
bool f_in_pipe
Definition form.h:223
bool f_mapp_spec
Definition form.h:194
Field * field[FIELD_MAXCNT]
Definition form.h:353
char receiver_cmd[MAXLEN]
Definition form.h:180
bool f_process
Definition form.h:248
uint fidx
Definition form.h:310
char provider_cmd[MAXLEN]
Definition form.h:172
bool f_query
Definition form.h:250
UiChyron * chyron
Definition form.h:365
int out_fd
Definition form.h:167
uint didx
Definition form.h:325
bool f_out_pipe
Definition form.h:228
uint begy
Definition form.h:152
bool f_provider_cmd
Definition form.h:258
bool f_calculate
Definition form.h:246
char fill_char[4]
Definition form.h:299
bool f_help_spec
Definition form.h:233
FILE * in_fp
Definition form.h:161
char out_spec[MAXLEN]
Definition form.h:210
uint lines
Definition form.h:150
bool f_receiver_cmd
Definition form.h:265
bool f_multiple_cmd_args
Definition form.h:245
uint begx
Definition form.h:154
char in_spec[MAXLEN]
Definition form.h:203
char mapp_spec[FIELD_MAXLEN]
Definition form.h:169
char title[MAXLEN]
Definition form.h:159
char brackets[3]
Definition form.h:281
bool f_out_spec
Definition form.h:220
bool f_in_spec
Definition form.h:217
uint fcnt
Definition form.h:318
char help_spec[MAXLEN]
Definition form.h:196