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 = 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 = 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 Perror("FORM: form data structure is nullptr");
127 }
128 if (form_parse_desc(form)) {
129 return 0;
130 }
132 display_form(init);
134 set_chyron_key(form->chyron, 1, "F1 Help", KEY_F01);
139 set_chyron_key(form->chyron, 9, "F9 Cancel", KEY_F09);
140 set_chyron_key(form->chyron, 10, "F10 Accept", KEY_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];
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);
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) {
250 tcflush(2, TCIFLUSH);
252 c = ui_get_event_multi(sfc, WIN, &event, -1);
253 ui_getmaxyx(sfc, WIN, &maxy, &maxx);
254 if (event.in_win == 1 && event.y == maxy - 1)
255 c = get_chyron_key(form->chyron, event.x);
256 }
257 switch (c) {
258 case KEY_F01:
259 return FA_HELP;
260 case KEY_F05: // F5 Edit
262 loop = false;
263 rc = FA_CONTINUE;
264 break;
265 }
266 continue;
267 case KEY_F09:
268 loop = false;
269 rc = FA_CANCEL;
270 break;
271 case KEY_F10:
272 loop = false;
273 rc = FA_ACCEPT;
274 break;
275 case KEY_MOUSE:
276 continue;
277 default:
278 break;
279 }
280 }
281 form->chyron->key[5]->active = false; // F5 Edit
282 return rc;
283}
284
285/** @brief Handle integration with a getter program which will provide field
286 data.
287 @ingroup form_engine
288 @param init A pointer to the Init structure containing form data and state.
289 @return An integer status code indicating the next action for the form
290 processing loop (e.g., FA_CONTINUE, FA_CANCEL, FA_ACCEPT, FA_HELP).
291 @details This function provides integration with getter programs.
292 The requirements are:
293 1. The form description file must have a line containing only 'C'
294 (calculate), 'Q' (query), org 'G' (getter) to indicate that the form supports
295 a getter.
296 2. The form description file must specify the provider command using a
297 line starting with '!' followed by the command and its arguments.
298 3. The getter program must be able to accept field data from a file,
299 from standard input, or as command line parameters.
300 4. The getter program must output field values in a format that can be
301 read by the form (e.g., one value per line), either to a file or to standard
302 output.
303
304 The sequence of operations is as follows:
305
306 1. The 'C', 'Q', or 'G' option causes Form to pause and display an
307 KEY_F05 Calculate, Query, or Getter. option on the chyron.
308 2. The user can then cancel the operation by pressing KEY_F09 or
309 activate the getter option by pressing KEY_F05.
310 3. Form outputs its data to file, standard output, or as command line
311 parameters.
312 4. Form executes the getter program.
313 5. The getter program processes the data and outputs the results.
314 6. Form reads the results and populates the appropriate form fields.
315 7. Form presents the user with an option to edit the data, and the
316 sequence restarts at 1.
317
318 This function forks and executes the getter executable as a child
319 process, creates a pipe to read the output from the provider command,
320 reads the output, and updates the Form fields.
321 */
322int form_process(Init *init) {
323 uint i, c;
324 int rc;
325 char earg_str[MAXLEN + 1];
326 char *eargv[MAXARGS];
327 int eargc;
328 char file_spec[MAXLEN];
329 bool loop = true;
330 pid_t pid;
331 int pipe_fd[2];
332 UiSurface *sfc = ui_surface[sfc_ptr];
333 UiEvent event;
334 Form *form = init->form;
335 ui_cursor_move(sfc, WIN, form->lines - 1, 0);
336 uint maxy, maxx;
337 //
338 // 1 F1 Help
339 // 2 F2 Process
340 // 3 F3 Calculate
341 // 4 F4 Query
342 // 5 F5 Edit
343 // 9 F9 Cancel
344 // 10 F10 Continue
345 // 11 F10 Commit
346 form->chyron->key[2]->active = form->f_process > 0 ? true : false;
347 form->chyron->key[3]->active = form->f_calculate > 0 ? true : false;
348 form->chyron->key[4]->active = form->f_query > 0 ? true : false; // PgUp
349 form->chyron->key[10]->active = false; // F10 Continue
350 form->chyron->key[18]->active = false; // INS Insert
351
352 while (loop) {
355 click_y = click_x = -1;
356 tcflush(2, TCIFLUSH);
358 c = ui_get_event_multi(sfc, WIN, &event, -1);
359 ui_getmaxyx(sfc, WIN, &maxy, &maxx);
360 if (event.in_win == 1 && event.y == maxy - 1)
361 c = get_chyron_key(form->chyron, event.x);
362
363 switch (c) {
364 case KEY_F01:
365 return FA_HELP;
366 case KEY_F02: // F2 Process
367 case KEY_F03: // F3 Calculate
368 case KEY_F04: // F4 Query
369 if (form->f_out_spec)
370 form_write(form);
371 if (form->f_provider_cmd) {
372 strnz__cpy(earg_str, form->provider_cmd, MAXLEN - 1);
373 for (i = 0; i < form->fcnt; i++) {
374 strnz__cat(earg_str, " ", MAXLEN - 1);
375 strnz__cat(earg_str, form->field[i]->accept_s, MAXLEN - 1);
376 }
377 eargc = str_to_args(eargv, earg_str, MAXARGS);
378 strnz__cpy(file_spec, eargv[0], MAXLEN - 1);
379 base_name(eargv[0], file_spec);
380 if (pipe(pipe_fd) == -1) {
381 destroy_argv(eargc, eargv);
382 Perror("pipe(pipe_fd) failed in init_form");
383 return (1);
384 }
385 if ((pid = fork()) == -1) {
386 destroy_argv(eargc, eargv);
387 Perror("fork() failed in init_form");
388 return (1);
389 }
390 if (pid == 0) { // Child
391 /** Prevent child process from writing to terminal */
392 int dev_null = open("/dev/null", O_WRONLY);
393 if (dev_null == -1) {
394 Perror("open(/dev/null) failed in init_pick child "
395 "process");
396 exit(EXIT_FAILURE);
397 }
398 dup2(dev_null, STDERR_FILENO);
399 close(dev_null);
400 close(pipe_fd[P_READ]);
401 dup2(pipe_fd[P_WRITE], STDOUT_FILENO);
402 stdio_fdnames(stdio_names_str, "form_engine.c:367");
403 close(pipe_fd[P_WRITE]);
404 execvp(eargv[0], eargv);
405 ssnprintf(em0, MAXLEN, "%s, line: %d", __FILE__,
406 __LINE__ - 2);
407 strnz__cpy(em1, "execvp(", MAXLEN - 1);
408 strnz__cat(em1, eargv[0], MAXLEN - 1);
409 strnz__cat(em1, ", ", MAXLEN - 1);
410 strnz__cat(em1, earg_str, MAXLEN - 1);
412 strerror_r(errno, em2, MAXLEN);
414 exit(EXIT_FAILURE);
415 } // Back to parent
416 close(pipe_fd[P_WRITE]);
417 form->in_fp = fdopen(pipe_fd[P_READ], "rb");
418 form->f_in_pipe = true;
420 close(pipe_fd[P_READ]);
421 waitpid(pid, nullptr, 0);
422 stdio_fdnames(stdio_names_str, "form_engine.c:387");
423 destroy_argv(eargc, eargv);
425 form->chyron->key[2]->active = false; // F2 Process
426 form->chyron->key[3]->active = false; // F3 Calculate
427 form->chyron->key[4]->active = false; // F4 Query
428 form->chyron->key[5]->active = true; // F5 Edit
429 form->chyron->key[10]->active = true; // F10 Accept
431 continue;
432 }
433 break;
434 case KEY_F05:
436 loop = false;
437 rc = FA_CONTINUE;
438 break;
439 }
440 continue;
441 case KEY_F09:
442 loop = false;
443 rc = FA_CANCEL;
444 break;
445 case KEY_F10:
446 loop = false;
447 rc = FA_ACCEPT;
448 break;
449 case KEY_MOUSE:
450 break;
451 default:
452 break;
453 }
454 }
455 form->chyron->key[2]->active = false; // F2 Process
456 form->chyron->key[3]->active = false; // F3 Calculate
457 form->chyron->key[4]->active = false; // F4 Query
458 form->chyron->key[5]->active = false; // F5 Edit
459 return rc;
460}
461/** @brief Handle user input for field entry, allowing navigation between fields
462 and looping until an exit action is selected.
463 @ingroup form_engine
464 @param form A pointer to the Form structure containing form data and state.
465 @return An integer status code indicating the next action for the form
466 processing loop (e.g., FA_ACCEPT, FA_HELP, FA_CALC, FA_CANCEL).
467 @details This function manages user input for field entry, including
468 navigation between fields and handling of special keys for accepting,
469 canceling, requesting help, or performing calculations. The function loops
470 until the user selects an exit action (e.g., accept or cancel). */
471int field_navigator(Form *form) {
472
473 if (form->fidx >= form->fcnt)
474 form->fidx = 0;
475 while (1) {
476 int cmd_key = field_editor(form);
477 switch (cmd_key) {
478 case KEY_F01: // F1 Help
479 return (FA_HELP);
480 case KEY_F02: // F2 Process
481 case KEY_F03: // F3 Calculate
482 case KEY_F04: // F4 Query
483 if (form->f_process)
484 return (FA_CALC);
485 break;
486 case KEY_F09: // F9 Cancel
487 return (FA_CANCEL);
488 case KEY_F10: // F10 Accept
489 return (FA_ACCEPT);
490 case 'k':
491 case KEY_UP:
492 if (form->fidx != 0)
493 form->fidx--;
494 break;
495 case '\r':
496 case KEY_ENTER:
497 if (form->fidx < form->fcnt - 1)
498 form->fidx++;
499 else if (form->fidx == form->fcnt - 1)
500 return (FA_ACCEPT);
501 break;
502 case 'j':
503 case KEY_DOWN:
504 if (form->fidx < form->fcnt - 1)
505 form->fidx++;
506 else if (form->fidx == form->fcnt - 1)
507 return (FA_ACCEPT);
508 break;
509 case KEY_MOUSE:
510 break;
511 default:
512 break;
513 }
514 }
515}
516uint form_yx_to_fidx(Form *form, uint y, uint x) {
517 for (uint i = 0; i < form->fcnt; i++) {
518 if (y == form->field[i]->line && x >= form->field[i]->col &&
519 x < form->field[i]->col + form->field[i]->len) {
520 return i;
521 }
522 }
523 return -1; // No field found at the given coordinates
524}
525
526/** @brief Display the form on the screen, including text elements and fields,
527 and set up the form window based on the form configuration.
528 @ingroup form_engine
529 @param init A pointer to the Init structure containing form data and state.
530 @return 0 on success, or a non-zero value if an error occurs while
531 creating the form window or rendering the form elements. */
532unsigned int display_form(Init *init) {
533 uint n, flin, fcol;
534 char tmp_str[MAXLEN];
535
536 Form *form = init->form;
537 form->lines = 0;
538 for (n = 0; n < form->dcnt; n++)
539 if (form->text[n]->line > form->lines)
540 form->lines = form->text[n]->line;
541 for (n = 0; n < form->fcnt; n++)
542 if (form->field[n]->line > form->lines)
543 form->lines = form->field[n]->line;
544 form->lines += 3;
545 if (form->lines > (LINES - form->begy))
546 form->lines = LINES - form->begy;
547 for (n = 0; n < form->fcnt; n++) {
548 if (form->field[n]->line >= (form->lines - 2))
549 form->fcnt = n;
550 }
551 form->cols += 2;
552 if (form->cols > (COLS - form->begx - 3))
553 form->cols = COLS - form->begx - 3;
555 strnz__cpy(tmp_str, "surface_box_win_new failed: ", MAXLEN - 1);
556 strnz__cat(tmp_str, form->title, MAXLEN - 1);
557 Perror(tmp_str);
558 return (1);
559 }
560 UiSurface *sfc = ui_surface[sfc_ptr];
561#ifdef DEBUG_IMMEDOK
562 ui_immedok(sfc, WIN, true);
563 ui_immedok(sfc, BOX, true);
564#endif
565 for (form->fidx = 0; form->fidx < form->fcnt; form->fidx++) {
566 if (form->brackets[0] != '\0' && form->brackets[1] != '\0') {
567 flin = form->field[form->fidx]->line + 1;
568 fcol = form->field[form->fidx]->col;
569 ui_mvwadd_wch(sfc, BOX, flin, fcol, &cell_brktl);
570 fcol += form->field[form->fidx]->len + 1;
571 ui_mvwadd_wch(sfc, BOX, flin, fcol, &cell_brktr);
572 }
573 }
574 for (n = 0; n < form->dcnt; n++) {
575 strnz(form->text[n]->str, form->cols - 3);
577 form->text[n]->str);
578 }
581 return 0;
582}
583/** @brief Display form fields on the screen, populating field values and
584 formatting them according to the form configuration.
585 @ingroup form_engine
586 @param form A pointer to the Form structure containing form data and
587 state.
588 @details This function iterates through the defined form fields, formats
589 their display values based on the specified fill character and field
590 length, and renders them on the form window. It also updates the chyron
591 with available commands for user interaction. */
592void form_display_fields(Form *form) {
593 uint y, x;
594 uint pos = 0;
595 for (form->fidx = 0; form->fidx < form->fcnt; form->fidx++) {
596 if (form->field[form->fidx]->col + form->field[form->fidx]->len + 2 >
597 form->cols)
598 form->field[form->fidx]->len =
599 form->cols - (form->field[form->fidx]->col + 2);
600 // Make filler_cc
601 size_t fl = form->field[form->fidx]->len;
602 char *d = form->field[form->fidx]->filler_s;
603 size_t char_bytes = strlen(form->fill_char);
604 for (size_t i = 0; i < fl; i++) {
605 memcpy(d, form->fill_char, char_bytes);
606 d += char_bytes;
607 }
608 d = nullptr;
609
610 if (form->field[form->fidx]->accept_s[0] != '\0') {
612 // d = form->field[form->fidx]->accept_s;
613 } else {
614 memset(form->field[form->fidx]->accept_s, 0, MAXLEN);
615 // d = form->field[form->fidx]->accept_s;
616 }
618
619 y = form->field[form->fidx]->line;
620 x = form->field[form->fidx]->col;
621
622 pos = 0;
624
625 UiSurface *sfc = ui_surface[sfc_ptr];
628
629 pos = 0;
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);
665 strerror_r(errno, em2, MAXLEN);
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 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 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 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 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 = display_error(em0, em1, em2, nullptr);
883 if (cmd_key == KEY_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 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 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 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 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, 0644);
1101 if (form->out_fd == -1) {
1102 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
1103 strnz__cpy(em1, "open ", MAXLEN - 1);
1105 strerror_r(errno, em2, MAXLEN);
1107 return (1);
1108 }
1109 dup2(form->out_fd, STDOUT_FILENO);
1110 form->out_fp = fdopen(STDOUT_FILENO, "w");
1111 form->f_out_spec = true;
1112 form->f_out_pipe = true;
1113 } else {
1114 if ((form->out_fp = fopen(form->out_spec, "w")) == nullptr) {
1115 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
1116 strerror_r(errno, em2, MAXLEN);
1118 return (1);
1119 }
1120 }
1121 if (form->out_fp == nullptr) {
1122 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
1123 strnz__cpy(em1, "fopen ", MAXLEN - 1);
1125 strerror_r(errno, em2, MAXLEN);
1127 return (1);
1128 }
1129 for (n = 0; n < form->fcnt; n++)
1130 fprintf(form->out_fp, "%s\n", form->field[n]->accept_s);
1131 if (form->out_fp != nullptr)
1132 fclose(form->out_fp);
1133 return (0);
1134}
1135/** @brief Handle errors encountered while parsing the form description
1136 file, providing detailed error messages that include the file name, line
1137 number, and the specific error encountered.
1138 @ingroup form_engine
1139 @param form A pointer to the Form structure containing form data and
1140 state.
1141 @param in_line_num The line number in the description file where the
1142 error occurred.
1143 @param in_buf The content of the line that caused the error, for
1144 context.
1145 @param em A specific error message describing the nature of the error.
1146 @return An integer status code indicating how the user responded to the
1147 error message (e.g., which key they pressed to acknowledge the error). */
1148int form_desc_error(Form *form, int in_line_num, char *in_buf, char *em) {
1149 int cmd_key;
1150 ssnprintf(em0, MAXLEN - 1, "%s: %s", __FILE__, em);
1151 ssnprintf(em1, MAXLEN - 1, "Desc file: %s, line: %d", form->mapp_spec,
1152 in_line_num);
1153 strnz__cpy(em2, in_buf, MAXLEN - 1);
1154 cmd_key = display_error(em0, em1, em2, nullptr);
1155 return cmd_key;
1156}
#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:33
#define P_READ
Definition common.h:50
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:51
char stdio_names_str[4096]
Definition futil.c:127
#define MAXARGS
Definition cm.h:50
char * stdio_fdnames(char *, char *)
Definition futil.c:1082
void ui_render()
Definition ui_ncurses.c:800
int ui_mvwadd_wchstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
int ui_cursor_move(UiSurface *s, uint w, uint y, uint x)
Definition ui_ncurses.c:727
int ui_mvwaddstr(UiSurface *s, uint w, uint y, uint x, const char *text)
int ui_mvwadd_wchnstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell, uint m)
uint COLS
Definition ui_backend.h:266
void ui_restore_wins()
UiSurface * ui_surface[UI_SFC_MAX]
Definition ui_ncurses.c:28
int ui_wclrtoeol(UiSurface *s, uint w)
uint LINES
int ui_get_event_multi(UiSurface *s, uint w, UiEvent *ev, int timeout_ms)
int sfc_ptr
Definition nckeys.c:47
void ui_endwin()
Definition ui_ncurses.c:341
int ui_mvwadd_wch(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
void ui_getmaxyx(UiSurface *s, uint w, uint *lines, uint *cols)
Definition ui_ncurses.c:676
#define BUFSIZ
Definition view.h:40
#define KEY_F04
#define KEY_F05
#define KEY_F01
#define KEY_F09
#define KEY_F03
#define KEY_F10
#define KEY_F02
#define KEY_DOWN
#define KEY_ENTER
#define KEY_MOUSE
#define KEY_IC
#define KEY_UP
#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
void mk_filler(char *, uint)
#define D_HELP
Definition form_engine.c:29
#define D_QUERY
Definition form_engine.c:32
#define D_COMMENT
Definition form_engine.c:25
uint form_yx_to_fidx(Form *, uint, uint)
#define D_HEADER
Definition form_engine.c:30
#define D_GETTER
Definition form_engine.c:33
int surface_box_win_new(uint wlines, uint wcols, uint wbegy, uint wbegx, char *wtitle)
Definition dwin.c:521
UiCell cell_nt
Definition dwin.c:94
UiCell cell_nt_hl_rev
Definition dwin.c:97
char em1[MAXLEN]
Definition dwin.c:143
char em2[MAXLEN]
Definition dwin.c:144
int click_x
Definition dwin.c:45
UiCell cell_brktl
Definition dwin.c:92
int click_y
Definition dwin.c:44
char em0[MAXLEN]
Definition dwin.c:142
UiCell cell_brktr
Definition dwin.c:93
UiCell cell_fill_char
Definition dwin.c:91
char ff_tbl[][26]
Definition fields.c:25
int cm_surface_destroy(UiSurface *sfc)
Destroy the most recently created surface.
Definition dwin.c:576
int Perror(char *emsg_str)
Display a simple error message window or print to stderr.
Definition dwin.c:841
void abend(int ec, char *s)
Abnormal program termination.
Definition dwin.c:1204
int display_error(char *msg0, char *msg1, char *msg2, char *msg3)
Display an error message window or print to stderr.
Definition dwin.c:778
void set_chyron_key_cb(Chyron *chyron, uint k, char *s, uint kc, UiCell cell_base)
Set chyron key with color pair (cp).
Definition dwin.c:1021
Chyron * destroy_chyron(Chyron *chyron)
Destroy Chyron structure.
Definition dwin.c:984
void compile_chyron(Chyron *chyron)
construct the chyron string from the chyron structure
Definition dwin.c:1110
int get_chyron_key(Chyron *chyron, uint x)
Get keycode from chyron.
Definition dwin.c:1186
uint mbstr_to_cellstr(UiCell *cmplx_buf, const char *str, const UiCell *cell_base, uint *pos, const uint atmost)
Convert multibyte string to complex character array.
Definition dwin.c:480
Chyron * new_chyron()
Create and initialize Chyron structure.
Definition dwin.c:947
bool is_set_chyron_key(Chyron *chyron, uint k)
Check if function key label is set.
Definition dwin.c:1005
void display_chyron(UiSurface *sfc, uint w, Chyron *chyron, uint line, uint col)
Display chyron on the specified line and column of the surface.
Definition dwin.c:1163
void set_chyron_key(Chyron *chyron, uint k, char *s, uint kc)
Set chyron key with default color pair (cp_nt_rev).
Definition dwin.c:1045
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:448
int field_editor(Form *)
Accept input for a field.
Definition fields.c:48
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 futil.c:537
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition futil.c:384
bool str_to_lower(char *)
Converts a string to lowercase.
Definition futil.c:501
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:608
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:413
int str_to_args(char **, char *, uint)
Converts a string into an array of argument strings.
Definition futil.c:433
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:566
char * rep_substring(const char *, const char *, const char *)
Replace all occurrences of "tgt_s" in "org_s" with "rep_s".
Definition futil.c:1423
int destroy_argv(uint argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:487
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:1106
Form * destroy_form(Init *init)
Destroy Form structure.
Definition mem.c:275
Form * new_form(Init *init, int argc, char **argv, uint begy, uint begx)
Create and initialize Form structure.
Definition mem.c:252
void sig_prog_mode()
Set up signal handlers for interrupt signals.
Definition sig.c:62
uint in_win
Definition ui_backend.h:110
bool active
Definition cm.h:378
ChyronKey * key[CHYRON_KEYS]
Definition cm.h:387
uint l
Definition cm.h:391
char title[MAXLEN]
Definition common.h:129
char mapp_help[MAXLEN]
Definition common.h:149
int begx
Definition common.h:118
Form * form
Definition common.h:185
int cols
Definition common.h:116
int begy
Definition common.h:117
int lines
Definition common.h:115
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
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
Chyron * chyron
Definition form.h:365
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