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

/utils/recutl.c

#
C | 648 lines | 481 code | 95 blank | 72 comment | 54 complexity | 644b6749e9caac3a038ba60e92ef34a1 MD5 | raw file
Possible License(s): GPL-3.0
  1. /* -*- mode: C -*-
  2. *
  3. * File: recutl.c
  4. * Date: Thu Apr 22 17:30:48 2010
  5. *
  6. * GNU recutils - Common code for the utilities.
  7. *
  8. */
  9. /* Copyright (C) 2010-2014 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 <stdlib.h>
  25. #include <getopt.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <stdarg.h>
  29. #include <closeout.h>
  30. #include <xalloc.h>
  31. #include <unistd.h>
  32. #include <locale.h>
  33. #include <gettext.h>
  34. #define _(str) gettext (str)
  35. #if defined REC_CRYPT_SUPPORT
  36. # include <gcrypt.h>
  37. #endif
  38. #include <progname.h>
  39. #include <sys/stat.h>
  40. #include <readline.h>
  41. #include <regex.h>
  42. #include <stdint.h>
  43. #include <time.h>
  44. #include <unistd.h>
  45. #include <getpass.h>
  46. #include <rec.h>
  47. #include <recutl.h>
  48. #include "read-file.h"
  49. /*
  50. * Global variables.
  51. */
  52. static bool recutl_sort_p = false;
  53. static char *recutl_order_rset = NULL;
  54. static char *recutl_order_by_field = NULL;
  55. static bool recutl_interactive_p = false;
  56. static size_t *recutl_indexes = NULL;
  57. static size_t recutl_indexes_size = 0;
  58. void recutl_print_help (void); /* Forward prototype. */
  59. void
  60. recutl_init (char *util_name)
  61. {
  62. set_program_name (xstrdup (util_name));
  63. #if defined REC_CRYPT_SUPPORT
  64. /* Initialize libgcrypt */
  65. gcry_check_version (NULL);
  66. gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
  67. #endif
  68. /* Initialize librec */
  69. rec_init ();
  70. /* Even exiting has subtleties. On exit, if any writes failed, change
  71. the exit status. The /dev/full device on GNU/Linux can be used for
  72. testing; for instance, hello >/dev/full should exit unsuccessfully.
  73. This is implemented in the Gnulib module "closeout". */
  74. atexit (close_stdout);
  75. /* i18n */
  76. setlocale (LC_ALL, "");
  77. bindtextdomain (PACKAGE, LOCALEDIR);
  78. textdomain (PACKAGE);
  79. /* Detect whether the tool has been invoked interactively. */
  80. recutl_interactive_p = isatty (fileno(stdin));
  81. /* Initially there are no indexes. */
  82. recutl_reset_indexes ();
  83. }
  84. bool
  85. recutl_interactive (void)
  86. {
  87. return recutl_interactive_p;
  88. }
  89. void
  90. recutl_print_help_footer (void)
  91. {
  92. /* TRANSLATORS: --help output 5+ (reports)
  93. TRANSLATORS: the placeholder indicates the bug-reporting address
  94. for this application. Please add _another line_ with the
  95. address for translation bugs.
  96. no-wrap */
  97. printf (_("\
  98. Report bugs to: %s\n"), PACKAGE_BUGREPORT);
  99. #ifdef PACKAGE_PACKAGER_BUG_REPORTS
  100. printf (_("Report %s bugs to: %s\n"), PACKAGE_PACKAGER,
  101. PACKAGE_PACKAGER_BUG_REPORTS);
  102. #endif
  103. #ifdef PACKAGE_URL
  104. printf (_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
  105. #else
  106. printf (_("%s home page: <http://www.gnu.org/software/recutils/>\n"),
  107. PACKAGE_NAME, PACKAGE);
  108. #endif
  109. fputs (_("General help using GNU software: <http://www.gnu.org/gethelp/>\n"),
  110. stdout);
  111. }
  112. void
  113. recutl_print_help_common (void)
  114. {
  115. /* TRANSLATORS: --help output, common arguments.
  116. no-wrap */
  117. fputs (_("\
  118. --help print a help message and exit.\n\
  119. --version show version and exit.\n"),
  120. stdout);
  121. }
  122. void
  123. recutl_print_help_record_selection (void)
  124. {
  125. /* TRANSLATORS: --help output, record selection arguments
  126. no-wrap */
  127. fputs (_("\
  128. Record selection options:\n\
  129. -i, --case-insensitive make strings case-insensitive in selection\n\
  130. expressions.\n\
  131. -t, --type=TYPE operate on records of the specified type only.\n\
  132. -e, --expression=RECORD_EXPR selection expression.\n\
  133. -q, --quick=STR select records with fields containing a string.\n\
  134. -n, --number=NUM,... select specific records by position, with ranges.\n\
  135. -m, --random=NUM select a given number of random records.\n"),
  136. stdout);
  137. }
  138. void
  139. recutl_print_version (void)
  140. {
  141. printf ("%s (GNU %s) %s\n",
  142. program_name,
  143. PACKAGE,
  144. VERSION);
  145. /* xgettext: no-wrap */
  146. puts ("");
  147. /* It is important to separate the year from the rest of the message,
  148. as done here, to avoid having to retranslate the message when a new
  149. year comes around. */
  150. printf (_("\
  151. Copyright (C) %s Jose E. Marchesi.\n\
  152. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
  153. This is free software: you are free to change and redistribute it.\n\
  154. There is NO WARRANTY, to the extent permitted by law.\n"), "2010, 2011, 2012, 2013, 2014");
  155. puts (_("\
  156. \n\
  157. Written by Jose E. Marchesi."));
  158. }
  159. void
  160. recutl_fatal (const char *fmt, ...)
  161. {
  162. va_list ap;
  163. va_start (ap, fmt);
  164. fputs (program_name, stderr);
  165. fputs (_(": error: "), stderr);
  166. vfprintf (stderr, fmt, ap);
  167. va_end (ap);
  168. exit (EXIT_FAILURE);
  169. }
  170. void
  171. recutl_out_of_memory (void)
  172. {
  173. recutl_fatal (_("out of memory\n"));
  174. }
  175. void
  176. recutl_error (const char *fmt, ...)
  177. {
  178. va_list ap;
  179. va_start (ap, fmt);
  180. fputs (program_name, stderr);
  181. fputs (_(": error: "), stderr);
  182. vfprintf (stderr, fmt, ap);
  183. va_end (ap);
  184. }
  185. void
  186. recutl_warning (const char *fmt, ...)
  187. {
  188. va_list ap;
  189. va_start (ap, fmt);
  190. fputs (program_name, stderr);
  191. fputs (_(": warning: "), stderr);
  192. vfprintf (stderr, fmt, ap);
  193. va_end (ap);
  194. }
  195. bool
  196. recutl_parse_db_from_file (FILE *in,
  197. char *file_name,
  198. rec_db_t db)
  199. {
  200. bool res;
  201. rec_rset_t rset;
  202. rec_parser_t parser;
  203. res = true;
  204. parser = rec_parser_new (in, file_name);
  205. while (rec_parse_rset (parser, &rset))
  206. {
  207. char *rset_type;
  208. /* XXX: check for consistency!!!. */
  209. rset_type = rec_rset_type (rset);
  210. if (rec_db_type_p (db, rset_type))
  211. {
  212. if (rset_type)
  213. recutl_fatal (_("duplicated record set '%s' from %s.\n"),
  214. rset_type, file_name);
  215. else
  216. {
  217. /* Special case: the database already contains anonymous
  218. records (with no type) and the record set to be
  219. inserted also contains anonyous records. In this
  220. case we just append the records and comments in the
  221. anonymous record set. */
  222. rec_rset_t anon_rset = rec_db_get_rset_by_type (db, NULL);
  223. rec_mset_iterator_t iter = rec_mset_iterator (rec_rset_mset (rset));
  224. rec_mset_elem_t elem;
  225. while (rec_mset_iterator_next (&iter, MSET_ANY, NULL, &elem))
  226. {
  227. void *data = rec_mset_elem_dup_data (elem);
  228. if (!data
  229. || !rec_mset_append (rec_rset_mset (anon_rset),
  230. rec_mset_elem_type (elem),
  231. data,
  232. MSET_ANY))
  233. return false;
  234. }
  235. rec_mset_iterator_free (&iter);
  236. return true;
  237. }
  238. }
  239. if (!rec_db_insert_rset (db, rset, rec_db_size (db)))
  240. {
  241. /* Error. */
  242. res = false;
  243. break;
  244. }
  245. }
  246. if (rec_parser_error (parser))
  247. {
  248. /* Report parsing errors. */
  249. rec_parser_perror (parser, "%s", file_name);
  250. res = false;
  251. }
  252. rec_parser_destroy (parser);
  253. return res;
  254. }
  255. rec_db_t
  256. recutl_build_db (int argc, char **argv)
  257. {
  258. rec_db_t db;
  259. char *file_name;
  260. FILE *in;
  261. db = rec_db_new ();
  262. if (!db)
  263. {
  264. return NULL;
  265. }
  266. /* Register the default functions in the database. */
  267. /* Process the input files, if any. Otherwise use the standard
  268. input to read the rec data. */
  269. if (optind < argc)
  270. {
  271. while (optind < argc)
  272. {
  273. file_name = argv[optind++];
  274. if (!(in = fopen (file_name, "r")))
  275. {
  276. recutl_fatal (_("cannot read file %s\n"), file_name);
  277. }
  278. else
  279. {
  280. if (!recutl_parse_db_from_file (in, file_name, db))
  281. {
  282. free (db);
  283. db = NULL;
  284. }
  285. fclose (in);
  286. }
  287. }
  288. }
  289. else
  290. {
  291. if (!recutl_parse_db_from_file (stdin, "stdin", db))
  292. {
  293. free (db);
  294. db = NULL;
  295. }
  296. }
  297. return db;
  298. }
  299. rec_db_t
  300. recutl_read_db_from_file (char *file_name)
  301. {
  302. rec_db_t db;
  303. FILE *in;
  304. db = rec_db_new ();
  305. if (file_name)
  306. {
  307. in = fopen (file_name, "r");
  308. if (in == NULL)
  309. {
  310. return NULL;
  311. }
  312. }
  313. else
  314. {
  315. /* Process the standard input. */
  316. file_name = "stdin";
  317. in = stdin;
  318. }
  319. if (!recutl_parse_db_from_file (in,
  320. file_name,
  321. db))
  322. {
  323. rec_db_destroy (db);
  324. db = NULL;
  325. }
  326. return db;
  327. }
  328. bool
  329. recutl_file_is_writable (char *file_name)
  330. {
  331. return !file_name || (euidaccess (file_name, W_OK) == 0);
  332. }
  333. void
  334. recutl_write_db_to_file (rec_db_t db,
  335. char *file_name)
  336. {
  337. FILE *out;
  338. char tmp_file_name[10]="recXXXXXX";
  339. rec_writer_t writer;
  340. int des;
  341. struct stat st1;
  342. int stat_result;
  343. if (!file_name)
  344. {
  345. out = stdout;
  346. }
  347. else
  348. {
  349. /* Record the original file attributes. */
  350. stat_result = stat (file_name, &st1);
  351. /* Create a temporary file with the results. */
  352. des = mkstemp (tmp_file_name);
  353. if (des == -1)
  354. {
  355. recutl_fatal (_("cannot create a unique name.\n"));
  356. }
  357. out = fdopen (des, "w+");
  358. }
  359. writer = rec_writer_new (out);
  360. rec_write_db (writer, db);
  361. if (file_name)
  362. {
  363. fclose (out);
  364. }
  365. rec_db_destroy (db);
  366. if (file_name)
  367. {
  368. /* Rename the temporary file to file_name. */
  369. if (rename (tmp_file_name, file_name) == -1)
  370. {
  371. remove (tmp_file_name);
  372. recutl_fatal (_("renaming file %s to %s\n"), tmp_file_name, file_name);
  373. }
  374. /* Restore the attributes of the original file. */
  375. if (stat_result != -1)
  376. {
  377. chmod (file_name, st1.st_mode);
  378. }
  379. }
  380. }
  381. char *
  382. recutl_read_file (char *file_name)
  383. {
  384. char *result;
  385. FILE *in;
  386. result = NULL;
  387. in = fopen (file_name, "r");
  388. if (in)
  389. {
  390. size_t file_size;
  391. result = fread_file (in, &file_size);
  392. fclose (in);
  393. }
  394. return result;
  395. }
  396. void
  397. recutl_check_integrity (rec_db_t db,
  398. bool verbose_p,
  399. bool external_p)
  400. {
  401. rec_buf_t errors_buf;
  402. char *errors_str;
  403. size_t errors_str_size;
  404. errors_buf = rec_buf_new (&errors_str, &errors_str_size);
  405. if (rec_int_check_db (db, true, external_p, errors_buf) > 0)
  406. {
  407. rec_buf_close (errors_buf);
  408. if (!verbose_p)
  409. {
  410. recutl_error (_("operation aborted due to integrity failures.\n"));
  411. recutl_error (_("use --verbose to get a detailed report.\n"));
  412. }
  413. else
  414. {
  415. fprintf (stderr, "%s", errors_str);
  416. }
  417. recutl_fatal (_("use --force to skip the integrity check.\n"));
  418. }
  419. }
  420. bool
  421. recutl_yesno (char *prompt)
  422. {
  423. bool res = false;
  424. char *line = NULL;
  425. while (1)
  426. {
  427. line = readline (prompt);
  428. if (line)
  429. {
  430. if (strcmp (line, "yes") == 0)
  431. {
  432. res = true;
  433. break;
  434. }
  435. else if (strcmp (line, "no") == 0)
  436. {
  437. res = false;
  438. break;
  439. }
  440. }
  441. printf ("Please answer 'yes' or 'no'.\n");
  442. }
  443. return res;
  444. }
  445. #define INDEX_LIST_ENTRY_RE "[0-9]+(-[0-9]+)?"
  446. #define INDEX_LIST_RE "(" INDEX_LIST_ENTRY_RE ",)*" INDEX_LIST_ENTRY_RE
  447. bool
  448. recutl_index_list_parse (const char *str)
  449. {
  450. regex_t regexp;
  451. bool res = true;
  452. const char *p;
  453. long int number;
  454. char *end;
  455. size_t i;
  456. /* Initialize the list structure. An pessimistic estimation of the
  457. number of indexes encoded in the string is used. */
  458. free (recutl_indexes);
  459. recutl_indexes = xmalloc (sizeof (size_t) * (strlen (str) * 2 + 2));
  460. for (i = 0; i < (strlen (str) * 2 + 2); i++)
  461. {
  462. recutl_indexes[i] = REC_Q_NOINDEX;
  463. }
  464. /* Make sure the string is valid. The code below relies on this
  465. fact. */
  466. if (regcomp (&regexp, "^" INDEX_LIST_RE "$", REG_EXTENDED) != 0)
  467. {
  468. recutl_fatal (_("internal error: recutl_index_list_parse: error compiling regexp.\n"));
  469. return false;
  470. }
  471. if (regexec (&regexp, str, 0, NULL, 0) != 0)
  472. {
  473. regfree (&regexp);
  474. return false;
  475. }
  476. regfree (&regexp);
  477. /* Parse the string. */
  478. p = str;
  479. while (true)
  480. {
  481. /* Get the 'min' part of the entry. */
  482. number = strtol (p, &end, 10);
  483. recutl_indexes[recutl_indexes_size] = (size_t) number;
  484. p = end;
  485. /* Get the 'max' part of the entry, if any. */
  486. if (*p == '-')
  487. {
  488. p++;
  489. number = strtol (p, &end, 10);
  490. recutl_indexes[recutl_indexes_size+1] = (size_t) number;
  491. p = end;
  492. }
  493. recutl_indexes_size = recutl_indexes_size + 2;
  494. /* Exit or pass the separator. */
  495. if (*p == '\0')
  496. {
  497. break;
  498. }
  499. else
  500. {
  501. p++;
  502. }
  503. }
  504. return res;
  505. }
  506. void
  507. recutl_reset_indexes (void)
  508. {
  509. free (recutl_indexes);
  510. recutl_indexes = NULL;
  511. recutl_indexes_size = 0;
  512. }
  513. size_t
  514. recutl_num_indexes (void)
  515. {
  516. return recutl_indexes_size;
  517. }
  518. size_t *
  519. recutl_index (void)
  520. {
  521. return recutl_indexes;
  522. }
  523. char *
  524. recutl_getpass (bool asktwice)
  525. {
  526. char *ret = NULL;
  527. char *pass = getpass (_("Password: "));
  528. if (pass)
  529. {
  530. ret = xstrdup (pass);
  531. if (asktwice)
  532. {
  533. pass = getpass (_("Password again: "));
  534. if (pass)
  535. {
  536. if (strcmp (ret, pass) != 0)
  537. {
  538. recutl_fatal (_("the provided passwords don't match.\n"));
  539. memset (ret, 0, strlen (ret));
  540. memset (pass, 0, strlen (pass));
  541. }
  542. }
  543. else
  544. {
  545. memset (ret, 0, strlen (ret));
  546. free (ret);
  547. ret = NULL;
  548. }
  549. }
  550. }
  551. return ret;
  552. }
  553. /* End of recutl.c */