1 /* ANSI-C code produced by gperf version 2.7.1 (19981006 egcs) */
2 /* Command-line: gperf -t -D -L ANSI-C confg.gperf  */	/* -*- C -*- */
3 /*
4  * confg.c
5  *
6  * Read and understanding everything about the options
7  * & (dynamic) configuration of a2ps.
8  * Copyright (c) 1988-1993 Miguel Santana
9  * Copyright (c) 1995-1999 Akim Demaille, Miguel Santana
10  */
11 
12 /*
13  * This file is part of a2ps.
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2, or (at your option)
18  * any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; see the file COPYING.  If not, write to
27  * the Free Software Foundation, 59 Temple Place - Suite 330,
28  * Boston, MA 02111-1307, USA.
29  */
30 
31 /************************************************************************/
32 /*									*/
33 /*			I n c l u d e   f i l e s			*/
34 /*                                                                      */
35 /************************************************************************/
36 #include "a2ps.h"
37 #include "routines.h"
38 #include "message.h"
39 #include "media.h"
40 #include "jobs.h"
41 #include "getshline.h"
42 #include "pathwalk.h"
43 #include "confg.h"
44 #include "useropt.h"
45 #include "path-concat.h"
46 #include "printers.h"
47 #include "options.h"
48 #include "metaseq.h"
49 #include "quotearg.h"
50 #include "dirname.h"
51 
52 extern char *program_name;
53 
54 /*
55  * Hooks used
56  */
57 config_hook delegation_hook = NULL;
58 config_hook toc_entry_hook = NULL;
59 
60 enum keyword_e
61 {
62   AppendLibraryPath,
63   DefaultPPD,
64   DefaultPrinter,
65   Delegation,
66   FileCommand,
67   Include,
68   LibraryPath,
69   Medium,
70   Obsolete,
71   Options,
72   OutputFirstLine,
73   PageLabelFormat,
74   PrependLibraryPath,
75   Printer,
76   UnknownPrinter,
77   UserOption,
78   Variable
79 };
80 
81 struct keyword_s
82 {
83   const char *name;
84   enum keyword_e code;
85   /* Number of arguments. */
86   int argc;
87   /* If true, the last argument composed of the rest of the line,
88      otherwise separated by spaces. */
89   bool line_token;
90 };
91 
92 #define TOTAL_KEYWORDS 21
93 #define MIN_WORD_LENGTH 6
94 #define MAX_WORD_LENGTH 19
95 #define MIN_HASH_VALUE 6
96 #define MAX_HASH_VALUE 36
97 /* maximum key range = 31, duplicates = 2 */
98 
99 #ifdef __GNUC__
100 __inline
101 #endif
102 static unsigned int
hash(register const char * str,register unsigned int len)103 hash (register const char *str, register unsigned int len)
104 {
105   static unsigned char asso_values[] =
106     {
107       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
108       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
109       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
110       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
111       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
112       37, 37, 37, 37, 37, 37, 37, 37,  0, 37,
113       37, 37, 37, 37, 37,  5, 37, 37,  0, 37,
114        5, 37, 37,  5, 37, 37, 10,  0, 37, 20,
115        0, 37, 37, 37,  5, 10,  0, 37, 37, 37,
116       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
117       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
118       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
119       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
120       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
121       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
122       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
123       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
124       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
125       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
126       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
127       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
128       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
129       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
130       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
131       37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
132       37, 37, 37, 37, 37, 37
133     };
134   return len + asso_values[(unsigned char)str[len - 1]] + asso_values[(unsigned char)str[0]];
135 }
136 
137 #ifdef __GNUC__
138 __inline
139 #endif
140 struct keyword_s *
in_word_set(register const char * str,register unsigned int len)141 in_word_set (register const char *str, register unsigned int len)
142 {
143   static struct keyword_s wordlist[] =
144     {
145       {"Media:",			Medium, 		2, true},
146       {"Medium:",		Medium, 		2, true},
147       {"Pattern:",		Obsolete, 		1, true},
148       {"Printer:",		Printer, 		2, true},
149       {"Variable:",		Variable,		2, true},
150       {"DefaultPPD:",		DefaultPPD,		1, false},
151       {"Delegation:",		Delegation, 		1, true},
152       {"PassThrough:",		Obsolete, 		1, true},
153       {"Include:",		Include,		1, false},
154       {"DefaultPrinter:",	DefaultPrinter,		1, true},
155       {"PageLabelFormat:",	PageLabelFormat, 	1, true},
156       {"FileCommand:",		FileCommand, 		1, true},
157       {"MacroMetaSequence:",	Variable, 		2, true},
158       {"PrependLibraryPath:",	PrependLibraryPath,	1, true},
159       {"UserOption:",		UserOption, 		2, true},
160       {"LibraryPath:",		LibraryPath, 		1, false},
161       {"AppendLibraryPath:",	AppendLibraryPath, 	1, true},
162       {"TemporaryDirectory:",	Obsolete,		1, true},
163       {"UnknownPrinter:",	UnknownPrinter,		1, true},
164       {"Options:",		Options, 		1, true},
165       {"OutputFirstLine:",	OutputFirstLine, 	1, true}
166     };
167 
168   static short lookup[] =
169     {
170        -1,  -1,  -1,  -1,  -1,  -1,   0,   1, -51,   4,
171        -1, -48,   7,   8,  -1,   9,  10,  11,  12,  13,
172        -1,  14,  15,  16,  17,  18, -16,  -2,  19, -19,
173        -2,  -1,  -1,  -1,  -1,  -1,  20
174     };
175 
176   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
177     {
178       register int key = hash (str, len);
179 
180       if (key <= MAX_HASH_VALUE && key >= 0)
181         {
182           register int index = lookup[key];
183 
184           if (index >= 0)
185             {
186               register const char *s = wordlist[index].name;
187 
188               if (*str == *s && !strcmp (str + 1, s + 1))
189                 return &wordlist[index];
190             }
191           else if (index < -TOTAL_KEYWORDS)
192             {
193               register int offset = - 1 - TOTAL_KEYWORDS - index;
194               register struct keyword_s *wordptr = &wordlist[TOTAL_KEYWORDS + lookup[offset]];
195               register struct keyword_s *wordendptr = wordptr + -lookup[offset + 1];
196 
197               while (wordptr < wordendptr)
198                 {
199                   register const char *s = wordptr->name;
200 
201                   if (*str == *s && !strcmp (str + 1, s + 1))
202                     return wordptr;
203                   wordptr++;
204                 }
205             }
206         }
207     }
208   return 0;
209 }
210 /*
211  * Read the configuration file
212  */
213 int
a2_read_config(a2ps_job * job,const char * path,const char * file)214 a2_read_config (a2ps_job * job, const char *path, const char *file)
215 {
216 /* The maximum number of args for an entry. */
217 #define MAX_ARGC 10
218   FILE *fp;
219   char * fname;
220   char *buf = NULL;
221   size_t bufsiz = 0;
222   int firstline = 0, lastline = 0;
223 
224   fname = xpath_concat (path, file, NULL);
225 
226   fp = fopen (fname, "r");
227   if (fp == NULL)
228     {
229       free (fname);
230       return 0;
231     }
232 
233   message (msg_opt | msg_file,
234 	   (stderr, "Reading configuration file `%s'\n", fname));
235 
236   while (getshline_numbered (&firstline, &lastline, &buf, &bufsiz, fp) != -1)
237     {
238       struct keyword_s *keyword;
239       /* An array of the arguments.  The first argument ARGV[0] is the
240 	 name of the entry, eg 'Options:'. */
241       int argc;
242       char *argv [MAX_ARGC];
243 
244       argv[0] = strtok (buf, " \t\n");
245 
246       /* Blank line, but not empty */
247       if (!argv[0])
248 	continue;
249 
250       keyword = in_word_set (argv[0], strlen (argv[0]));
251       if (!keyword)
252 	error_at_line (1, 0, fname, firstline,
253 		       _("invalid option `%s'"), quotearg (argv[0]));
254 
255       /* Fetch the arguments */
256       for (argc = 1 ; argc <= keyword->argc ; argc++)
257 	{
258 	  if (keyword->line_token && argc == keyword->argc)
259 	    argv [argc] = strtok (NULL, "\n");
260 	  else
261 	    argv [argc] = strtok (NULL, " \t\n");
262 	  if (argv [argc] == NULL)
263 	    error_at_line (1, 0, fname, firstline,
264 			   _("missing argument for `%s'"), quotearg (argv[0]));
265 	}
266       /* Check that there is no extra argument. */
267       if (strtok (NULL, "\n"))
268 	error_at_line (1, 0, fname, firstline,
269 		       "extra argument for `%s'", quotearg (argv[0]));
270 
271       /* Process the entry. */
272       switch (keyword->code)
273 	{
274 	case Include:	/* At this point, read another config file. */
275 	  {
276 	    char * dir;
277 	    if (*argv[1] == DIRECTORY_SEPARATOR)
278 	      /* Path is absolute */
279 	      dir = NULL;
280 	    else
281 	      /* Relative.  Give its root. */
282 	      dir = dir_name (fname);
283 
284 	    if (!a2_read_config (job, dir, argv[1]))
285 	      {
286 		char *included_file = xpath_concat (dir, argv[1], NULL);
287 		error_at_line (0, errno, fname, firstline,
288 			       _("cannot open file `%s'"),
289 			       quotearg (included_file));
290 		free (included_file);
291 	      }
292 	    XFREE (dir);
293 	  }
294 	  break;
295 
296 	case Options:
297 	  {
298 	    /* Set PROGRAM_NAME so that the error messages report the
299                file name and line. */
300 	    char *old_program_name = program_name;
301 	    program_name = ALLOCA (char,
302 				   strlen (program_name)
303 				   + strlen (fname)
304 				   + strlen ("%:%:999990"));
305 	    sprintf (program_name, "%s:%s:%d",
306 		     old_program_name, fname, firstline);
307 	    a2ps_handle_string_options (job, argv[1]);
308 	    program_name = old_program_name;
309 	  }
310 	  break;
311 
312 	case DefaultPPD:      /* Default PPD file */
313           a2ps_printers_default_ppdkey_set (job->printers, argv[1]);
314 	  break;
315 
316       /* Handling of the printers */
317 	case Printer:
318 	  if (!a2ps_printers_add (job->printers, argv[1], argv[2]))
319 	    error_at_line (1, 0, fname, firstline,
320 			   _("invalid definition for printer `%s': %s"),
321 			   argv[1], quotearg (argv[2]));
322 	  break;
323 
324 	case UnknownPrinter:
325 	  if (!a2ps_printers_add (job->printers, _("Unknown Printer"), argv[1]))
326 	    error_at_line (1, 0, fname, firstline,
327 			   _("invalid definition for printer `%s': %s"),
328 			   _("Unknown Printer"), quotearg (argv[1]));
329 	  break;
330 
331 	case DefaultPrinter:
332 	  if (!a2ps_printers_add (job->printers, _("Default Printer"), argv[1]))
333 	    error_at_line (1, 0, fname, firstline,
334 			   _("invalid definition for printer `%s': %s"),
335 			   _("Default Printer"), quotearg (argv[1]));
336 	  break;
337 
338 	case Delegation:
339 	  /* This is only for a2ps the program.  Read this only if
340 	   there's a reader */
341 	  if (delegation_hook)
342 	    (*delegation_hook) (fname, firstline, argv[1]);
343 	  break;
344 
345 	case UserOption:
346 	  user_option_add (job, argv[1], argv[2]);
347 	  break;
348 
349 	case OutputFirstLine:
350 	  xustrcpy (job->status->magic_number, argv[1]);
351 	  break;
352 
353 	case PageLabelFormat:
354 	  xustrcpy (job->status->page_label_format, argv[1]);
355 	  break;
356 
357 	case Medium:
358 	  {
359 	    int w, h, llx, lly, urx, ury;
360 
361 	    switch (sscanf (argv[2], "%d %d %d %d %d %d",
362 			    &w, &h, &llx, &lly, &urx, &ury))
363 	      {
364 	      case 6:
365 		/* BBox is also given */
366 		break;
367 
368 	      case 2:
369 		/* A short hand has been used: use 24 points as a
370 		  margin all around */
371 		llx = lly = 24;
372 		urx = w - 24;
373 		ury = h - 24;
374 		break;
375 
376 	      default:
377 		error_at_line (1, 0, fname, firstline,
378 			       "invalid number of arguments for `%s'",
379 			       quotearg (argv[0]));
380 	      }
381 	    add_medium (job, argv[1], w, h, llx, lly, urx, ury);
382 	  }
383 	  break;
384 
385 	case Variable:
386 	  if (!macro_meta_sequence_add (job, argv[1], argv[2]))
387 	    error_at_line (1, 0, fname, firstline,
388 			   _("invalid variable identifier `%s'"),
389 			   quotearg (argv[1]));
390 	  break;
391 
392 	  /* Handling of the library path */
393 	case LibraryPath:
394 	  XFREE (job->common.path);
395 	  job->common.path = pw_string_to_path (argv[1]);
396 	  break;
397 
398 	case AppendLibraryPath:
399 	  job->common.path = pw_append_string_to_path (job->common.path,
400 						       argv[1]);
401 	  break;
402 
403 	case PrependLibraryPath:
404 	  job->common.path = pw_prepend_string_to_path (job->common.path,
405 							argv[1]);
406 	  break;
407 
408 	case FileCommand:	/* How to call file */
409 	  xstrcpy (job->file_command, argv[1]);
410 	  break;
411 
412 	case Obsolete:
413 	  /* TRANS: The following message says that in a2ps.cfg there
414 	     is an entry (such as `Pattern:', or `PassThrough:') which
415 	     is no longer used. */
416 	  error_at_line (0, 0, fname, firstline,
417 			 _("obsolete `%s' entry.  Ignored"), argv[0]);
418 	  break;
419 	}
420     }
421   XFREE (fname);
422   fclose (fp);
423   /* BUF was allocated by getshlinenumbered. */
424   free (buf);
425   return 1;
426 }
427 
428 /* Global config.
429  *    This is really not an easy thing because, people may want
430  *    to check the package before the installation.  The worst
431  *    case is when an older a2ps is yet installed.  So we _must_
432  *    have a special way to deal with the tests.  This is why
433  *    I introduced an env-var: A2PS_CONFIG, which
434  *    points to a2ps.cfg.
435  *    Note that it also improves the robustness of `make distcheck'.
436  */
437 void
a2_read_sys_config(a2ps_job * job)438 a2_read_sys_config (a2ps_job * job)
439 {
440   const char *config_file;
441 
442   config_file = getenv ("A2PS_CONFIG");
443   if (!config_file)
444     config_file = SYSCONFFILE;
445 
446   /* I see no reason to end a2ps here if the file is not found: other
447      files follow.  Just say it.  */
448   if (a2_read_config (job, NULL, config_file))
449     return;
450   error (0, errno,
451 	 _("cannot open file `%s'"), quotearg (config_file));
452 }
453