tentative fix for issue 3 (ex 53)
[mplib] / src / texk / kpathsea / cnf.c
1 /* cnf.c: read config files.
2
3    Copyright 1994, 1995, 1996, 1997, 2008 Karl Berry.
4    Copyright 1997-2005 Olaf Weber.
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public License
17    along with this library; if not, see <http://www.gnu.org/licenses/>.  */
18
19 #include <kpathsea/config.h>
20 #include <kpathsea/c-fopen.h>
21 #include <kpathsea/c-ctype.h>
22 #include <kpathsea/c-pathch.h>
23 #include <kpathsea/cnf.h>
24 #include <kpathsea/db.h>
25 #include <kpathsea/hash.h>
26 #include <kpathsea/line.h>
27 #include <kpathsea/paths.h>
28 #include <kpathsea/pathsearch.h>
29 #include <kpathsea/progname.h>
30 #include <kpathsea/recorder.h>
31 #include <kpathsea/tex-file.h>
32 #include <kpathsea/variable.h>
33
34 /* By using our own hash table, instead of the environment, we
35    complicate variable expansion (because we have to look in two
36    places), but we don't bang so much on the system.  DOS and System V
37    have very limited environment space.  Also, this way
38    `kpse_init_format' can distinguish between values originating from
39    the cnf file and ones from environment variables, which can be useful
40    for users trying to figure out what's going on.  */
41 static hash_table_type cnf_hash;
42 #define CNF_HASH_SIZE 751
43 #define CNF_NAME "texmf.cnf"
44 \f
45 /* Do a single line in a cnf file: if it's blank or a comment, skip it.
46    Otherwise, parse <variable>[.<program>] [=] <value>.  Do
47    this even if the <variable> is already set in the environment, since
48    the envvalue might contain a trailing :, in which case we'll be
49    looking for the cnf value.  */
50
51 static void
52 do_line P1C(string, line)
53 {
54   unsigned len;
55   string start;
56   string value, var;
57   string prog = NULL;
58   
59   /* Skip leading whitespace.  */
60   while (ISSPACE (*line))
61     line++;
62   
63   /* More to do only if we have non-comment material left.  */
64   if (*line == 0 || *line == '%' || *line == '#')
65     return;
66   
67   /* The variable name is everything up to the next space or = or `.'.  */
68   start = line;
69   while (!ISSPACE (*line) && *line != '=' && *line != '.')
70     line++;
71
72   /* `line' is now one character past the end of the variable name.  */
73   len = line - start;
74   var = (string)xmalloc (len + 1);
75   strncpy (var, start, len);
76   var[len] = 0;
77   
78   /* If the variable is qualified with a program name, find out which. */
79   while (ISSPACE (*line))
80     line++;
81   if (*line == '.') {
82     /* Skip spaces, then everything up to the next space or =.  */
83     line++;
84     while (ISSPACE (*line))
85       line++;
86     start = line;
87     while (!ISSPACE (*line) && *line != '=')
88       line++;
89
90     /* It's annoying to repeat all this, but making a tokenizing
91        subroutine would be just as long and annoying.  */
92     len = line - start;
93     prog = (string)xmalloc (len + 1);
94     strncpy (prog, start, len);
95     prog[len] = 0;
96   }
97
98   /* Skip whitespace, an optional =, more whitespace.  */
99   while (ISSPACE (*line))
100     line++;
101   if (*line == '=') {
102     line++;
103     while (ISSPACE (*line))
104       line++;
105   }
106   
107   /* The value is whatever remains.  Remove trailing whitespace.  */
108   start = line;
109   len = strlen (start);
110   while (len > 0 && ISSPACE (start[len - 1]))
111     len--;
112   
113   value = (string)xmalloc (len + 1);
114   strncpy (value, start, len);
115   value[len] = 0;
116
117   /* Suppose we want to write a single texmf.cnf that can be used under
118      both NT and Unix.  This is feasible except for the path separators
119      : on Unix, ; on NT.  We can't switch NT to allowing :'s, since :
120      is the drive separator.  So we switch Unix to allowing ;'s.  On the
121      other hand, we don't want to change IS_ENV_SEP and all the rest.
122      
123      So, simply translate all ;'s in the path
124      values to :'s if we are a Unix binary.  (Fortunately we don't use ;
125      in other kinds of texmf.cnf values.)  */
126      
127   if (IS_ENV_SEP(':')) {
128       string loc;
129       for (loc = value; *loc; loc++) {
130           if (*loc == ';')
131               *loc = ':';
132       }
133   }
134
135   /* We want TEXINPUTS.prog to override plain TEXINPUTS.  The simplest
136      way is to put both in the hash table (so we don't have to write
137      hash_delete and hash_replace, and keep track of values' sources),
138      and then look up the .prog version first in `kpse_cnf_get'.  */
139   if (prog) {
140     string lhs = concat3 (var, ".", prog);
141     free (var);
142     free (prog);
143     var = lhs;
144   }
145   hash_insert (&cnf_hash, var, value);
146   
147   /* We could check that anything remaining is preceded by a comment
148      character, but let's not bother.  */
149 }
150 \f
151 /* Read all the configuration files in the path.  */
152
153 static void
154 read_all_cnf P1H(void)
155 {
156   string *cnf_files;
157   string *cnf;
158   const_string cnf_path = kpse_init_format (kpse_cnf_format);
159
160   cnf_hash = hash_create (CNF_HASH_SIZE);
161
162   cnf_files = kpse_all_path_search (cnf_path, CNF_NAME);
163   if (cnf_files && *cnf_files) {
164     for (cnf = cnf_files; *cnf; cnf++) {
165       string line;
166       FILE *cnf_file = xfopen (*cnf, FOPEN_R_MODE);
167       if (kpse_record_input)
168         kpse_record_input (*cnf);
169
170       while ((line = read_line (cnf_file)) != NULL) {
171         unsigned len = strlen (line);
172         /* Strip trailing spaces. */
173         while (len > 0 && ISSPACE(line[len-1])) {
174           line[len - 1] = 0;
175           --len;
176         }
177         /* Concatenate consecutive lines that end with \.  */
178         while (len > 0 && line[len - 1] == '\\') {
179           string next_line = read_line (cnf_file);
180           line[len - 1] = 0;
181           if (!next_line) {
182             WARNING1 ("%s: Last line ends with \\", *cnf);
183           } else {
184             string new_line;
185             new_line = concat (line, next_line);
186             free (line);
187             line = new_line;
188             len = strlen (line);
189           }
190         }
191
192         do_line (line);
193         free (line);
194       }
195
196       xfclose (cnf_file, *cnf);
197       free (*cnf);
198     }
199     free (cnf_files);
200   } else
201     WARNING1 ("Configuration file texmf.cnf not found! Searched these directories:\n%s\nTrying to proceed..", cnf_path);
202 }
203 \f
204 /* Read the cnf files on the first call.  Return the first value in the
205    returned list -- this will be from the last-read cnf file.  */
206
207 string
208 kpse_cnf_get P1C(const_string, name)
209 {
210   string ret, ctry;
211   string *ret_list;
212   static boolean doing_cnf_init = false;
213
214   /* When we expand the compile-time value for DEFAULT_TEXMFCNF,
215      we end up needing the value for TETEXDIR and other variables,
216      so kpse_var_expand ends up calling us again.  No good.  */
217   if (doing_cnf_init)
218     return NULL;
219     
220   if (cnf_hash.size == 0) {
221     doing_cnf_init = true;
222     read_all_cnf ();
223     doing_cnf_init = false;
224     
225     /* Here's a pleasant kludge: Since `kpse_init_dbs' recursively calls
226        us, we must call it from outside a `kpse_path_element' loop
227        (namely, the one in `read_all_cnf' above): `kpse_path_element' is
228        not reentrant.  */
229     kpse_init_db ();
230   }
231   
232   /* First look up NAME.`kpse_program_name', then NAME.  */
233   assert (kpse_program_name);
234   ctry = concat3 (name, ".", kpse_program_name);
235   ret_list = hash_lookup (cnf_hash, ctry);
236   free (ctry);
237   if (ret_list) {
238     ret = *ret_list;
239     free (ret_list);
240   } else {
241     ret_list = hash_lookup (cnf_hash, name);
242     if (ret_list) {
243       ret = *ret_list;
244       free (ret_list);
245     } else {
246       ret = NULL;
247     }
248   }
249   
250   return ret;
251 }