PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/utils/recfmt.c

#
C | 303 lines | 196 code | 48 blank | 59 comment | 27 complexity | e5d31f0799bb2afb24816aa0916be073 MD5 | raw file
Possible License(s): GPL-3.0
  1. /* -*- mode: C -*-
  2. *
  3. * File: recfmt.c
  4. * Date: Wed Dec 22 18:20:20 2010
  5. *
  6. * GNU recutils - recfmt
  7. *
  8. */
  9. /* Copyright (C) 2010-2013 Jose E. Marchesi */
  10. /* This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. #include <config.h>
  24. #include <string.h>
  25. #include <getopt.h>
  26. #include <stdlib.h>
  27. #include <xalloc.h>
  28. #include <regex.h>
  29. #include <gettext.h>
  30. #define _(str) gettext (str)
  31. #include <rec.h>
  32. #include <recutl.h>
  33. /* Forward prototypes. */
  34. void recfmt_parse_args (int argc, char **argv);
  35. bool recfmt_process_data (rec_db_t db);
  36. void recfmt_process_db (rec_db_t db, char *template);
  37. char *recfmt_apply_template (rec_record_t record, char *template);
  38. char *recfmt_get_subst (rec_record_t record, char *str);
  39. /*
  40. * Global variables
  41. */
  42. char *recfmt_template = NULL;
  43. /*
  44. * Command line options management.
  45. */
  46. enum
  47. {
  48. COMMON_ARGS,
  49. FILE_ARG
  50. };
  51. static const struct option GNU_longOptions[] =
  52. {
  53. COMMON_LONG_ARGS,
  54. {"file", required_argument, NULL, FILE_ARG},
  55. {NULL, 0, NULL, 0}
  56. };
  57. /*
  58. * Functions.
  59. */
  60. void
  61. recutl_print_help (void)
  62. {
  63. /* TRANSLATORS: --help output, recfmt synopsis.
  64. no-wrap */
  65. printf (_("\
  66. Usage: recfmt [OPTION]... [TEMPLATE]\n"));
  67. /* TRANSLATORS: --help output, recfmt arguments.
  68. no-wrap */
  69. fputs(_("\
  70. Apply a template to records read from standard input.\n"), stdout);
  71. puts ("");
  72. /* TRANSLATORS: --help output, recfmt arguments.
  73. no-wrap */
  74. fputs(_("\
  75. -f, --file=FILENAME read the template to apply from a file.\n"),
  76. stdout);
  77. recutl_print_help_common ();
  78. puts ("");
  79. recutl_print_help_footer ();
  80. }
  81. void
  82. recfmt_parse_args (int argc,
  83. char **argv)
  84. {
  85. char c;
  86. int ret;
  87. while ((ret = getopt_long (argc,
  88. argv,
  89. "f:",
  90. GNU_longOptions,
  91. NULL)) != -1)
  92. {
  93. c = ret;
  94. switch (c)
  95. {
  96. COMMON_ARGS_CASES
  97. case FILE_ARG:
  98. case 'f':
  99. {
  100. /* Read the template from the specified file and store it
  101. in recfmt_template. */
  102. recfmt_template = recutl_read_file (optarg);
  103. if (!recfmt_template)
  104. {
  105. recutl_fatal (_("can't open file %s for reading.\n"),
  106. optarg);
  107. }
  108. break;
  109. }
  110. default:
  111. {
  112. exit (EXIT_FAILURE);
  113. }
  114. }
  115. }
  116. /* See if the template is specified in the command line. */
  117. if (optind < argc)
  118. {
  119. if (recfmt_template)
  120. {
  121. recutl_fatal (_("don't specify a template in the command line and -f at the same time.\n"));
  122. }
  123. if ((argc - optind) != 1)
  124. {
  125. recutl_print_help ();
  126. exit (EXIT_FAILURE);
  127. }
  128. recfmt_template = xstrdup (argv[optind++]);
  129. }
  130. }
  131. char *
  132. recfmt_get_subst (rec_record_t record,
  133. char *str)
  134. {
  135. char *res;
  136. rec_sex_t sex;
  137. sex = rec_sex_new (false);
  138. if (!rec_sex_compile (sex, str))
  139. {
  140. recutl_fatal (_("invalid expression in a template slot.\n"));
  141. }
  142. res = rec_sex_eval_str (sex, record);
  143. if (!res)
  144. {
  145. recutl_fatal (_("error evaluating expression in a template slot.\n"));
  146. }
  147. rec_sex_destroy (sex);
  148. return res;
  149. }
  150. char *
  151. recfmt_apply_template (rec_record_t record,
  152. char *template)
  153. {
  154. rec_buf_t result_buf;
  155. char *result;
  156. char *tmp;
  157. size_t tmp_size;
  158. size_t result_size;
  159. char *p;
  160. regex_t regexp;
  161. regmatch_t matches;
  162. char *subst_str;
  163. /* Replace occurrences of:
  164. {{Name[N]}}
  165. where Name[N] is the name of a field with an optional subscript.
  166. If the subscript is not present then it is assumed to be 0. If
  167. the contents between {{...}} are not a field name then replace the
  168. slot with the empty string.
  169. */
  170. if (regcomp (&regexp, "\\{\\{" "[^}]*" "\\}\\}", REG_EXTENDED) != 0)
  171. {
  172. recutl_fatal (_("recfmt_apply_template: error compiling regexp. Please report this.\n"));
  173. }
  174. result_buf = rec_buf_new (&result, &result_size);
  175. p = template;
  176. while (*p
  177. && (regexec (&regexp, p, 1, &matches, 0) == 0)
  178. && (matches.rm_so != -1))
  179. {
  180. /* Add the prolog, if any. */
  181. if (matches.rm_so > 0)
  182. {
  183. tmp = xmalloc (matches.rm_so + 1);
  184. memcpy (tmp, p, matches.rm_so);
  185. tmp[matches.rm_so] = '\0';
  186. rec_buf_puts (tmp, result_buf);
  187. free (tmp);
  188. }
  189. /* Get the match. */
  190. tmp_size = matches.rm_eo - matches.rm_so - 4;
  191. tmp = xmalloc (tmp_size + 1);
  192. memcpy (tmp, p + matches.rm_so + 2, tmp_size);
  193. tmp[tmp_size] = '\0';
  194. /* Advance p. */
  195. p = p + matches.rm_eo;
  196. /* Get the substitution text and append it to the result. */
  197. subst_str = recfmt_get_subst (record, tmp);
  198. if (subst_str)
  199. {
  200. rec_buf_puts (subst_str, result_buf);
  201. free (subst_str);
  202. }
  203. free (tmp);
  204. }
  205. /* Add the epilog, if any. */
  206. if (*p)
  207. {
  208. rec_buf_puts (p, result_buf);
  209. }
  210. rec_buf_close (result_buf);
  211. return result;
  212. }
  213. void
  214. recfmt_process_db (rec_db_t db, char *template)
  215. {
  216. size_t n_rset;
  217. rec_rset_t rset;
  218. rec_record_t record;
  219. char *result;
  220. rec_mset_iterator_t iter;
  221. /* Iterate on all the record sets. */
  222. for (n_rset = 0; n_rset < rec_db_size (db); n_rset++)
  223. {
  224. rset = rec_db_get_rset (db, n_rset);
  225. /* Iterate on all the records. */
  226. iter = rec_mset_iterator (rec_rset_mset (rset));
  227. while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void**) &record, NULL))
  228. {
  229. /* Apply the template to this record. */
  230. result = recfmt_apply_template (record, template);
  231. if (result && (*result != '\0'))
  232. {
  233. printf ("%s", result);
  234. free (result);
  235. }
  236. }
  237. rec_mset_iterator_free (&iter);
  238. }
  239. }
  240. int
  241. main (int argc, char *argv[])
  242. {
  243. rec_db_t db;
  244. recutl_init ("recfmt");
  245. recfmt_parse_args (argc, argv);
  246. db = recutl_read_db_from_file (NULL);
  247. if (db && recfmt_template)
  248. {
  249. recfmt_process_db (db, recfmt_template);
  250. }
  251. return EXIT_SUCCESS;
  252. }
  253. /* End of recfmt.c */