update devspec.en_US/1.0.general.md.
[devspec.git] / devspec.en_US / project / recutils / utils / recfmt.c
blob3858208316f78bec3f7e1b670a27a8c657e85b3f
1 /* -*- mode: C -*-
3 * File: recfmt.c
4 * Date: Wed Dec 22 18:20:20 2010
6 * GNU recutils - recfmt
8 */
10 /* Copyright (C) 2010-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <string.h>
29 #include <getopt.h>
30 #include <stdlib.h>
31 #include <xalloc.h>
32 #include <regex.h>
33 #include <gettext.h>
34 #define _(str) gettext (str)
36 #include <rec.h>
37 #include <recutl.h>
39 /* Forward prototypes. */
40 void recfmt_parse_args (int argc, char **argv);
41 bool recfmt_process_data (rec_db_t db);
42 void recfmt_process_db (rec_db_t db, char *template);
43 char *recfmt_apply_template (rec_record_t record, char *template);
44 char *recfmt_get_subst (rec_record_t record, char *str);
47 * Global variables
50 char *recfmt_template = NULL;
53 * Command line options management.
56 enum
58 COMMON_ARGS,
59 FILE_ARG
62 static const struct option GNU_longOptions[] =
64 COMMON_LONG_ARGS,
65 {"file", required_argument, NULL, FILE_ARG},
66 {NULL, 0, NULL, 0}
70 * Functions.
73 void
74 recutl_print_help (void)
76 /* TRANSLATORS: --help output, recfmt synopsis.
77 no-wrap */
78 printf (_("\
79 Usage: recfmt [OPTION]... [TEMPLATE]\n"));
81 /* TRANSLATORS: --help output, recfmt arguments.
82 no-wrap */
83 fputs(_("\
84 Apply a template to records read from standard input.\n"), stdout);
86 puts ("");
87 /* TRANSLATORS: --help output, recfmt arguments.
88 no-wrap */
89 fputs(_("\
90 -f, --file=FILENAME read the template to apply from a file.\n"),
91 stdout);
93 recutl_print_help_common ();
94 puts ("");
95 recutl_print_help_footer ();
98 void
99 recfmt_parse_args (int argc,
100 char **argv)
102 char c;
103 int ret;
105 while ((ret = getopt_long (argc,
106 argv,
107 "f:",
108 GNU_longOptions,
109 NULL)) != -1)
111 c = ret;
112 switch (c)
114 COMMON_ARGS_CASES
115 case FILE_ARG:
116 case 'f':
118 /* Read the template from the specified file and store it
119 in recfmt_template. */
120 recfmt_template = recutl_read_file (optarg);
121 if (!recfmt_template)
123 recutl_fatal (_("can't open file %s for reading.\n"),
124 optarg);
127 break;
129 default:
131 exit (EXIT_FAILURE);
136 /* See if the template is specified in the command line. */
137 if (optind < argc)
139 if (recfmt_template)
141 recutl_fatal (_("don't specify a template in the command line and -f at the same time.\n"));
144 if ((argc - optind) != 1)
146 recutl_print_help ();
147 exit (EXIT_FAILURE);
150 recfmt_template = xstrdup (argv[optind++]);
154 char *
155 recfmt_get_subst (rec_record_t record,
156 char *str)
158 char *res;
159 rec_sex_t sex;
161 sex = rec_sex_new (false);
162 if (!rec_sex_compile (sex, str))
164 recutl_fatal (_("invalid expression in a template slot.\n"));
167 res = rec_sex_eval_str (sex, record);
168 if (!res)
170 recutl_fatal (_("error evaluating expression in a template slot.\n"));
173 rec_sex_destroy (sex);
175 return res;
178 char *
179 recfmt_apply_template (rec_record_t record,
180 char *template)
182 rec_buf_t result_buf;
183 char *result;
184 char *tmp;
185 size_t tmp_size;
186 size_t result_size;
187 char *p;
188 regex_t regexp;
189 regmatch_t matches;
190 char *subst_str;
192 /* Replace occurrences of:
194 {{Name[N]}}
196 where Name[N] is the name of a field with an optional subscript.
197 If the subscript is not present then it is assumed to be 0. If
198 the contents between {{...}} are not a field name then replace the
199 slot with the empty string.
202 if (regcomp (&regexp, "\\{\\{" "[^}]*" "\\}\\}", REG_EXTENDED) != 0)
204 recutl_fatal (_("recfmt_apply_template: error compiling regexp. Please report this.\n"));
207 result_buf = rec_buf_new (&result, &result_size);
208 p = template;
209 while (*p
210 && (regexec (&regexp, p, 1, &matches, 0) == 0)
211 && (matches.rm_so != -1))
213 /* Add the prolog, if any. */
214 if (matches.rm_so > 0)
216 tmp = xmalloc (matches.rm_so + 1);
217 memcpy (tmp, p, matches.rm_so);
218 tmp[matches.rm_so] = '\0';
219 rec_buf_puts (tmp, result_buf);
220 free (tmp);
223 /* Get the match. */
224 tmp_size = matches.rm_eo - matches.rm_so - 4;
225 tmp = xmalloc (tmp_size + 1);
226 memcpy (tmp, p + matches.rm_so + 2, tmp_size);
227 tmp[tmp_size] = '\0';
229 /* Advance p. */
230 p = p + matches.rm_eo;
232 /* Get the substitution text and append it to the result. */
233 subst_str = recfmt_get_subst (record, tmp);
234 if (subst_str)
236 rec_buf_puts (subst_str, result_buf);
237 free (subst_str);
239 free (tmp);
242 /* Add the epilog, if any. */
243 if (*p)
245 rec_buf_puts (p, result_buf);
248 rec_buf_close (result_buf);
250 return result;
253 void
254 recfmt_process_db (rec_db_t db, char *template)
256 size_t n_rset;
257 rec_rset_t rset;
258 rec_record_t record;
259 char *result;
260 rec_mset_iterator_t iter;
262 /* Iterate on all the record sets. */
263 for (n_rset = 0; n_rset < rec_db_size (db); n_rset++)
265 rset = rec_db_get_rset (db, n_rset);
267 /* Iterate on all the records. */
269 iter = rec_mset_iterator (rec_rset_mset (rset));
270 while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void**) &record, NULL))
272 /* Apply the template to this record. */
273 result = recfmt_apply_template (record, template);
274 if (result && (*result != '\0'))
276 printf ("%s", result);
277 free (result);
281 rec_mset_iterator_free (&iter);
286 main (int argc, char *argv[])
288 rec_db_t db;
290 recutl_init ("recfmt");
292 recfmt_parse_args (argc, argv);
294 db = recutl_read_db_from_file (NULL);
295 if (db && recfmt_template)
297 recfmt_process_db (db, recfmt_template);
300 return EXIT_SUCCESS;
303 /* End of recfmt.c */