/* recfmt.c - Format records and generate reports. */ /* Copyright (C) 2010-2022 Jose E. Marchesi */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #define _(str) gettext (str) #include #include /* * Global variables */ char *recfmt_template = NULL; /* * Command line options management. */ enum { COMMON_ARGS, FILE_ARG }; static const struct option GNU_longOptions[] = { COMMON_LONG_ARGS, {"file", required_argument, NULL, FILE_ARG}, {NULL, 0, NULL, 0} }; /* * Functions. */ void recutl_print_help (void) { /* TRANSLATORS: --help output, recfmt synopsis. no-wrap */ printf (_("\ Usage: recfmt [OPTION]... [TEMPLATE]\n")); /* TRANSLATORS: --help output, recfmt arguments. no-wrap */ fputs(_("\ Apply a template to records read from standard input.\n"), stdout); puts (""); /* TRANSLATORS: --help output, recfmt arguments. no-wrap */ fputs(_("\ -f, --file=FILENAME read the template to apply from a file.\n"), stdout); recutl_print_help_common (); puts (""); recutl_print_help_footer (); } void recfmt_parse_args (int argc, char **argv) { char c; int ret; while ((ret = getopt_long (argc, argv, "f:", GNU_longOptions, NULL)) != -1) { c = ret; switch (c) { COMMON_ARGS_CASES case FILE_ARG: case 'f': /* Read the template from the specified file and store it in recfmt_template. */ recfmt_template = recutl_read_file (optarg); if (!recfmt_template) recutl_fatal (_("can't open file %s for reading.\n"), optarg); break; default: exit (EXIT_FAILURE); } } /* See if the template is specified in the command line. */ if (optind < argc) { if (recfmt_template) recutl_fatal (_("don't specify a template in the command line\ and -f at the same time.\n")); if ((argc - optind) != 1) { recutl_print_help (); exit (EXIT_FAILURE); } recfmt_template = xstrdup (argv[optind++]); } } char * recfmt_get_subst (rec_record_t record, char *str) { char *res; rec_sex_t sex; sex = rec_sex_new (false); if (!rec_sex_compile (sex, str)) recutl_fatal (_("invalid expression in a template slot.\n")); res = rec_sex_eval_str (sex, record); if (!res) recutl_fatal (_("error evaluating expression in a template slot.\n")); rec_sex_destroy (sex); return res; } char * recfmt_apply_template (rec_record_t record, char *template) { rec_buf_t result_buf; char *result; char *tmp; size_t tmp_size; size_t result_size; char *p; regex_t regexp; regmatch_t matches; char *subst_str; /* Replace occurrences of: {{Name[N]}} where Name[N] is the name of a field with an optional subscript. If the subscript is not present then it is assumed to be 0. If the contents between {{...}} are not a field name then replace the slot with the empty string. */ if (regcomp (®exp, "\\{\\{" "[^{}]*" "\\}\\}", REG_EXTENDED) != 0) recutl_fatal (_("recfmt_apply_template: error compiling regexp.\ Please report this.\n")); result_buf = rec_buf_new (&result, &result_size); p = template; while (*p && (regexec (®exp, p, 1, &matches, 0) == 0) && (matches.rm_so != -1)) { /* Add the prolog, if any. */ if (matches.rm_so > 0) { tmp = xmalloc (matches.rm_so + 1); memcpy (tmp, p, matches.rm_so); tmp[matches.rm_so] = '\0'; rec_buf_puts (tmp, result_buf); free (tmp); } /* Get the match. */ tmp_size = matches.rm_eo - matches.rm_so - 4; tmp = xmalloc (tmp_size + 1); memcpy (tmp, p + matches.rm_so + 2, tmp_size); tmp[tmp_size] = '\0'; /* Advance p. */ p = p + matches.rm_eo; /* Get the substitution text and append it to the result. */ subst_str = recfmt_get_subst (record, tmp); if (subst_str) { rec_buf_puts (subst_str, result_buf); free (subst_str); } free (tmp); } /* Add the epilog, if any. */ if (*p) rec_buf_puts (p, result_buf); rec_buf_close (result_buf); return result; } void recfmt_process_db (rec_db_t db, char *template) { size_t n_rset; rec_rset_t rset; rec_record_t record; char *result; rec_mset_iterator_t iter; /* Iterate on all the record sets. */ for (n_rset = 0; n_rset < rec_db_size (db); n_rset++) { rset = rec_db_get_rset (db, n_rset); /* Iterate on all the records. */ iter = rec_mset_iterator (rec_rset_mset (rset)); while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void**) &record, NULL)) { /* Apply the template to this record. */ result = recfmt_apply_template (record, template); if (result && (*result != '\0')) { printf ("%s", result); free (result); } } rec_mset_iterator_free (&iter); } } int main (int argc, char *argv[]) { rec_db_t db; recutl_init ("recfmt"); recfmt_parse_args (argc, argv); db = recutl_read_db_from_file (NULL); if (db && recfmt_template) recfmt_process_db (db, recfmt_template); return EXIT_SUCCESS; }