/a2ps-4.14/src/select.c

# · C · 283 lines · 183 code · 33 blank · 67 comment · 29 complexity · 15b96fc9db4f22a382549c1e2ab9fc47 MD5 · raw file

  1. /*
  2. * select.c -- Selection of a style sheet
  3. * Copyright (c) 1995-99 Akim Demaille, Miguel Santana
  4. *
  5. */
  6. /*
  7. * This file is part of a2ps.
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 3, or (at your option)
  12. * any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; see the file COPYING. If not, write to
  21. * the Free Software Foundation, 59 Temple Place - Suite 330,
  22. * Boston, MA 02111-1307, USA.
  23. */
  24. #include "a2ps.h"
  25. #include "select.h"
  26. #include "routines.h"
  27. #include "xfnmatch.h"
  28. #include "getshline.h"
  29. #include "path-concat.h"
  30. #include "pathwalk.h"
  31. #include "filtdir.h"
  32. #include "message.h"
  33. #include "quotearg.h"
  34. extern char * style_request;
  35. /*
  36. * Take priviledged access to job :)
  37. */
  38. extern a2ps_job * job;
  39. extern struct darray * sheets_map;
  40. /*
  41. * The user has requested the style sheet him self.
  42. */
  43. void
  44. set_requested_style (const char * arg)
  45. {
  46. xstrcpy (style_request, arg);
  47. }
  48. /************************************************************************/
  49. /* sheets.map handling */
  50. /************************************************************************/
  51. /*
  52. * Association of suffixes rules, and corresponding style sheet
  53. */
  54. struct pattern_rule
  55. {
  56. const char * pattern;
  57. const char * command;
  58. int on_file_verdict;
  59. bool insensitive_p;
  60. };
  61. struct pattern_rule *
  62. pattern_rule_new (const char *pattern, int on_file_verdict, bool insensitive_p,
  63. const char *command)
  64. {
  65. NEW (struct pattern_rule, res);
  66. res->pattern = pattern;
  67. res->on_file_verdict = on_file_verdict;
  68. res->command = command;
  69. res->insensitive_p = insensitive_p;
  70. return res;
  71. }
  72. static void
  73. pattern_rule_self_print (struct pattern_rule * item, FILE * stream)
  74. {
  75. fprintf (stream, "%s/%s: %s/%s\n",
  76. item->on_file_verdict ? "file" : "name",
  77. item->pattern,
  78. item->command,
  79. item->insensitive_p ? "i" : "");
  80. }
  81. struct darray *
  82. sheets_map_new (void)
  83. {
  84. return da_new ("Sheets map", 200,
  85. da_linear, 20,
  86. (da_print_func_t) pattern_rule_self_print, NULL);
  87. }
  88. void
  89. sheets_map_add (const char * pattern, int on_file_verdict, bool insensitive_p,
  90. const char * key)
  91. {
  92. da_append (sheets_map,
  93. pattern_rule_new (pattern, on_file_verdict, insensitive_p, key));
  94. }
  95. /*
  96. * Read the sheets.map file
  97. */
  98. static int
  99. sheets_map_load_main (void)
  100. {
  101. char * file;
  102. /* System's */
  103. file = pw_find_file (job->common.path, "sheets.map", NULL);
  104. if (!file)
  105. {
  106. error (0, errno, _("cannot find file `%s'"), "sheets.map");
  107. /* sheets.map can not be found: there is no automatic prettyprinting */
  108. error (0, 0, _("automatic style selection cancelled"));
  109. return 0;
  110. }
  111. sheets_map_load (file);
  112. free (file);
  113. return 1;
  114. }
  115. /* escapes the name of a file so that the shell groks it in 'single' q.marks.
  116. The resulting pointer has to be free()ed when not longer used. */
  117. char *
  118. shell_escape(const char *fn)
  119. {
  120. size_t len = 0;
  121. const char *inp;
  122. char *retval, *outp;
  123. for(inp = fn; *inp; ++inp)
  124. switch(*inp)
  125. {
  126. case '\'': len += 4; break;
  127. default: len += 1; break;
  128. }
  129. outp = retval = malloc(len + 1);
  130. if(!outp)
  131. return NULL; /* perhaps one should do better error handling here */
  132. for(inp = fn; *inp; ++inp)
  133. switch(*inp)
  134. {
  135. case '\'': *outp++ = '\''; *outp++ = '\\'; *outp++ = '\'', *outp++ = '\''; break;
  136. default: *outp++ = *inp; break;
  137. }
  138. *outp = 0;
  139. return retval;
  140. }
  141. /* What says file about the type of a file (result is malloc'd). NULL
  142. if could not be run. */
  143. static char *
  144. file_verdict_on (const uchar *filename)
  145. {
  146. char *cp = NULL, * command;
  147. char buf [1024];
  148. FILE * file_out;
  149. if (IS_EMPTY (job->file_command))
  150. return NULL;
  151. filename = shell_escape(filename);
  152. if(filename == NULL)
  153. return NULL;
  154. /* Call file(1) with the correct option */
  155. command = ALLOCA (char, (4
  156. + strlen (job->file_command)
  157. + ustrlen (filename)));
  158. sprintf (command, "%s '%s'", job->file_command, (const char *) filename);
  159. free(filename);
  160. message (msg_tool, (stderr, "Reading pipe: `%s'\n", command));
  161. file_out = popen (command, "r");
  162. /* Check for failure */
  163. if (!file_out)
  164. {
  165. if (msg_test(msg_tool))
  166. error (0, errno, _("cannot open a pipe on `%s'"),
  167. quotearg (command));
  168. return NULL;
  169. }
  170. /* Get the answer */
  171. fgets (buf, sizeof (buf), file_out);
  172. pclose (file_out);
  173. message (msg_tool, (stderr, "file(1): %s", buf));
  174. /* File is expected to answer:
  175. filename: file-answer. */
  176. cp = buf;
  177. while (*cp && *cp != ':')
  178. cp++;
  179. cp++;
  180. while (*cp && ((*cp == ' ') || (*cp == '\t')))
  181. cp++;
  182. if (*cp)
  183. {
  184. if (cp)
  185. message (msg_tool, (stderr, "File's verdict: %s", cp));
  186. /* Don't return the `\n'. */
  187. return xstrndup (cp, strlen (cp) - 1);
  188. }
  189. return NULL;
  190. }
  191. /*
  192. * Get style name from FILENAME, using pattern rules
  193. * and file(1) rules if USE_FILE.
  194. */
  195. #define rule(_i_) ((struct pattern_rule *)sheets_map->content[_i_])
  196. const char *
  197. get_command (const uchar *name_to_match, const uchar *name_to_file)
  198. {
  199. int i;
  200. char *file_verdict;
  201. uchar *name_to_match_lc;
  202. /* We only want to read the sheets map if needed, hence,
  203. * from here (not needed if the sheet name is given by the
  204. * user) */
  205. if (da_is_empty (sheets_map))
  206. sheets_map_load_main ();
  207. /* Get file(1)'s verdict, and get a lowercase version of the
  208. filename. */
  209. file_verdict = file_verdict_on (name_to_file);
  210. if (name_to_match)
  211. {
  212. name_to_match_lc = ALLOCA (char, strlen (name_to_match) + 1);
  213. strcpylc (name_to_match_lc, name_to_match);
  214. }
  215. /* We look from bottom up, so that most recently defined rules are
  216. * honored. Make sure not to call fnmatch on an empty FILE_VERDICT
  217. * (thanks to Michael Taeschner <Michael.Taeschner@dlr.de>,
  218. * Christian Mondrup <scancm@biobase.dk> and Jens Henrik Leonhard
  219. * Jensen recjhl@mediator.uni-c.dk) */
  220. /* The loop is split to speed up */
  221. for (i = sheets_map->len - 1 ; i >= 0 ; i--)
  222. if (rule(i)->on_file_verdict)
  223. {
  224. /* Testing upon file's result */
  225. if (file_verdict
  226. && !fnmatch (rule(i)->pattern, file_verdict, 0))
  227. {
  228. free (file_verdict);
  229. return rule(i)->command;
  230. }
  231. }
  232. else
  233. {
  234. /* Upon file name */
  235. if (name_to_match
  236. && !fnmatch (rule(i)->pattern,
  237. (char *) (rule(i)->insensitive_p
  238. ? name_to_match_lc : name_to_match),
  239. 0))
  240. {
  241. XFREE (file_verdict);
  242. return rule(i)->command;
  243. }
  244. }
  245. XFREE (file_verdict);
  246. return "plain";
  247. }