1 | /* |
1 | /* |
2 | configuration.c - Part of psiconv, a PSION 5 file formats converter |
2 | configuration.c - Part of psiconv, a PSION 5 file formats converter |
3 | Copyright (c) 1999, 2000,2003 Frodo Looijaard <frodol@dds.nl> |
3 | Copyright (c) 1999-2004 Frodo Looijaard <frodol@dds.nl> |
4 | |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; either version 2 of the License, or |
7 | the Free Software Foundation; either version 2 of the License, or |
8 | (at your option) any later version. |
8 | (at your option) any later version. |
… | |
… | |
17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
18 | */ |
18 | */ |
19 | |
19 | |
20 | #include "config.h" |
20 | #include "config.h" |
21 | #include "compat.h" |
21 | #include "compat.h" |
|
|
22 | #include "error.h" |
|
|
23 | #include "unicode.h" |
|
|
24 | |
22 | #include <stdio.h> |
25 | #include <stdio.h> |
23 | #include <stdlib.h> |
26 | #include <stdlib.h> |
24 | #include <string.h> |
27 | #include <string.h> |
|
|
28 | #include <strings.h> |
|
|
29 | #include <sys/types.h> |
|
|
30 | #include <sys/stat.h> |
|
|
31 | #include <unistd.h> |
|
|
32 | #include <fcntl.h> |
25 | |
33 | |
26 | #include "configuration.h" |
34 | #include "configuration.h" |
27 | #include "error.h" |
|
|
28 | |
35 | |
29 | #ifdef DMALLOC |
36 | #ifdef DMALLOC |
30 | #include <dmalloc.h> |
37 | #include <dmalloc.h> |
31 | #endif |
38 | #endif |
32 | |
39 | |
33 | #ifndef CONFIGURATION_SEARCH_PATH |
40 | #ifndef CONFIGURATION_SEARCH_PATH |
34 | #define CONFIGURATION_SEARCH_PATH "/etc/psiconv.conf:~/.psiconv.conf" |
41 | #define CONFIGURATION_SEARCH_PATH PSICONVETCDIR "/psiconv.conf:~/.psiconv.conf" |
35 | #endif |
42 | #endif |
36 | struct psiconv_config_s default_config = |
43 | static struct psiconv_config_s default_config = |
37 | { PSICONV_VERB_WARN, 2, psiconv_bool_true }; |
44 | { PSICONV_VERB_WARN, 2, 0,0,0,psiconv_bool_false,NULL,'?' }; |
38 | |
45 | |
39 | psiconv_config psiconv_config_read(const char *extra_config_file) |
46 | static void psiconv_config_parse_statement(const char *filename, |
|
|
47 | int linenr, |
|
|
48 | const char *var, int value, |
|
|
49 | psiconv_config *config); |
|
|
50 | |
|
|
51 | static void psiconv_config_parse_line(const char *filename, int linenr, |
|
|
52 | const char *line, psiconv_config *config); |
|
|
53 | |
|
|
54 | static void psiconv_config_parse_file(const char *filename, |
|
|
55 | psiconv_config *config); |
|
|
56 | |
|
|
57 | psiconv_config psiconv_config_default(void) |
40 | { |
58 | { |
41 | psiconv_config config; |
59 | psiconv_config result; |
42 | if (!(config = malloc(sizeof(*config)))) |
60 | result = malloc(sizeof(*result)); |
|
|
61 | *result = default_config; |
|
|
62 | return result; |
|
|
63 | } |
|
|
64 | |
|
|
65 | void psiconv_config_free(psiconv_config config) |
|
|
66 | { |
|
|
67 | free(config); |
|
|
68 | } |
|
|
69 | |
|
|
70 | void psiconv_config_parse_statement(const char *filename, |
|
|
71 | int linenr, |
|
|
72 | const char *var, int value, |
|
|
73 | psiconv_config *config) |
|
|
74 | { |
|
|
75 | int charnr; |
|
|
76 | |
|
|
77 | if (!(strcasecmp(var,"verbosity"))) { |
|
|
78 | if ((value >= 1) && (value <= 5)) |
|
|
79 | (*config)->verbosity = value; |
|
|
80 | else |
|
|
81 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
82 | "Verbosity should be between 1 and 5",filename,linenr); |
|
|
83 | } else if (!(strcasecmp(var,"color"))) { |
|
|
84 | if ((value == 0) || (value == 1)) |
|
|
85 | (*config)->color = value; |
|
|
86 | else |
|
|
87 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
88 | "Color should be 0 or 1",filename,linenr); |
|
|
89 | } else if (!(strcasecmp(var,"colordepth"))) { |
|
|
90 | if ((value > 0) && (value <= 32)) |
|
|
91 | (*config)->colordepth = value; |
|
|
92 | else |
|
|
93 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
94 | "ColorDepth should be between 1 and 32",filename,linenr); |
|
|
95 | } else if (!(strcasecmp(var,"redbits"))) { |
|
|
96 | if ((value >= 0) && (value <= 32)) |
|
|
97 | (*config)->redbits = value; |
|
|
98 | else |
|
|
99 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
100 | "RedBits should be between 1 and 32 or 0",filename,linenr); |
|
|
101 | } else if (!(strcasecmp(var,"greenbits"))) { |
|
|
102 | if ((value >= 0) && (value <= 32)) |
|
|
103 | (*config)->greenbits = value; |
|
|
104 | else |
|
|
105 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
106 | "GreenBits should be between 1 and 32 or 0",filename,linenr); |
|
|
107 | } else if (!(strcasecmp(var,"bluebits"))) { |
|
|
108 | if ((value >= 0) && (value <= 32)) |
|
|
109 | (*config)->bluebits = value; |
|
|
110 | else |
|
|
111 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
112 | "BlueBits should be between 1 and 32 or 0",filename,linenr); |
|
|
113 | } else if (!(strcasecmp(var,"characterset"))) { |
|
|
114 | if ((value >= 0) && (value <= 1)) |
|
|
115 | psiconv_unicode_select_characterset(*config,value); |
|
|
116 | else |
|
|
117 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
118 | "CharacterSet should be between 0 and 1", |
|
|
119 | filename,linenr); |
|
|
120 | } else if (!(strcasecmp(var,"unknownunicodechar"))) { |
|
|
121 | if ((value >= 1) && (value < 0x10000)) |
|
|
122 | (*config)->unknown_unicode_char = value; |
|
|
123 | else |
|
|
124 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
125 | "UnknownUnicodeChar should be between 1 and 65535", |
|
|
126 | filename,linenr); |
|
|
127 | } else if (!(strcasecmp(var,"unknownepocchar"))) { |
|
|
128 | if ((value >= 1) && (value < 0x100)) |
|
|
129 | (*config)->unknown_epoc_char = value; |
|
|
130 | else |
|
|
131 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
132 | "UnknownEPOCChar should be between 1 and 255", |
|
|
133 | filename,linenr); |
|
|
134 | } else if (sscanf(var,"char%d",&charnr) == strlen(var)) { |
|
|
135 | if ((charnr < 0) || (charnr > 255)) |
|
|
136 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
137 | "CharXXX should have XXX between 0 and 255", |
|
|
138 | filename,linenr); |
|
|
139 | if ((value >= 1) && (value <= 0x10000)) |
|
|
140 | (*config)->unicode_table[charnr] = value; |
|
|
141 | else |
|
|
142 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
143 | "CharXXX should be between 1 and 65535", |
|
|
144 | filename,linenr); |
|
|
145 | } else { |
|
|
146 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
147 | "Unknown variable %s",filename,linenr,var); |
|
|
148 | } |
|
|
149 | psiconv_debug(*config,0,0,"Configuration file %s, line %d: " |
|
|
150 | "Set variable %s to %d",filename,linenr,var,value); |
|
|
151 | } |
|
|
152 | |
|
|
153 | |
|
|
154 | void psiconv_config_parse_line(const char *filename, int linenr, |
|
|
155 | const char *line, psiconv_config *config) |
|
|
156 | { |
|
|
157 | |
|
|
158 | int sovar,eovar,soval,eoval,eol; |
|
|
159 | char *var; |
|
|
160 | long val; |
|
|
161 | |
|
|
162 | psiconv_debug(*config,0,0,"Going to parse line %d: %s",linenr,line); |
|
|
163 | sovar = 0; |
|
|
164 | while (line[sovar] && (line[sovar] < 32)) |
|
|
165 | sovar ++; |
|
|
166 | if (!line[sovar] || line[sovar] == '#') |
43 | return NULL; |
167 | return; |
44 | memcpy(config,&default_config,sizeof(*config)); |
168 | eovar = sovar; |
45 | return config; |
169 | while (line[eovar] && (((line[eovar] >= 'A') && (line[eovar] <= 'Z')) || |
46 | } |
170 | ((line[eovar] >= 'a') && (line[eovar] <= 'z')))) |
|
|
171 | eovar ++; |
|
|
172 | if (sovar == eovar) { |
|
|
173 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
174 | "Syntax error (no variable found)",filename,linenr); |
|
|
175 | return; |
|
|
176 | } |
|
|
177 | soval = eovar; |
|
|
178 | while (line[soval] && (line[soval] <= 32)) |
|
|
179 | soval ++; |
|
|
180 | if (line[soval] != '=') { |
|
|
181 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
182 | "Syntax error (no = token found)",filename,linenr); |
|
|
183 | return; |
|
|
184 | } |
|
|
185 | soval ++; |
|
|
186 | while (line[soval] && (line[soval] <= 32)) |
|
|
187 | soval ++; |
|
|
188 | eoval = soval; |
|
|
189 | while (line[eoval] && ((line[eoval] >= '0') && (line[eovar] <= '9'))) |
|
|
190 | eoval ++; |
|
|
191 | if (eoval == soval) { |
|
|
192 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
193 | "Syntax error (no value found)",filename,linenr); |
|
|
194 | return; |
|
|
195 | } |
|
|
196 | if (soval - eoval > 7) { |
|
|
197 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
198 | "Syntax error (value too large)",filename,linenr); |
|
|
199 | return; |
|
|
200 | } |
|
|
201 | eol = eoval; |
|
|
202 | while (line[eol] && (line[eol] < 32)) |
|
|
203 | eol ++; |
|
|
204 | if (line[eol]) { |
|
|
205 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
206 | "Syntax error (trailing garbage)",filename,linenr); |
|
|
207 | return; |
|
|
208 | } |
47 | |
209 | |
48 | /*TO DO */ |
210 | var = malloc(eovar - sovar + 1); |
|
|
211 | memcpy(var,line + sovar, eovar - sovar); |
|
|
212 | var[eovar-sovar] = 0; |
49 | |
213 | |
50 | #if 0 |
214 | val = atol(line + soval); |
51 | |
215 | |
52 | /* I hate string manipulation in C */ |
216 | psiconv_config_parse_statement(filename,linenr,var,val,config); |
53 | psiconv_config_t psiconv_config_read(const char *extra_config_files) |
217 | free(var); |
|
|
218 | } |
|
|
219 | |
|
|
220 | |
|
|
221 | void psiconv_config_parse_file(const char *filename, psiconv_config *config) |
54 | { |
222 | { |
55 | psiconv_config_t result = default_config; |
|
|
56 | char *path,pathptr; |
|
|
57 | int file; |
223 | int file,linenr; |
58 | struct stat stat_buf; |
224 | struct stat stat_buf; |
|
|
225 | off_t filesize,bytes_left,bytes_read,sol,eol; |
|
|
226 | char *filebuffer,*filebuffer_ptr; |
|
|
227 | |
|
|
228 | psiconv_progress(*config,0,0, |
|
|
229 | "Going to access configuration file %s",filename); |
|
|
230 | |
|
|
231 | /* Try to open the file; it may fail, if it does not exist for example */ |
|
|
232 | if ((file = open(filename,O_RDONLY)) == -1) |
|
|
233 | goto ERROR0; |
|
|
234 | |
|
|
235 | /* Read the contents of the file into filebuffer. This may fail */ |
|
|
236 | if (fstat(file,&stat_buf)) { |
|
|
237 | if (close(file)) |
|
|
238 | psiconv_error(*config,0,0,"Configuration file %s: " |
|
|
239 | "Couldn't close file",filename); |
|
|
240 | return; |
|
|
241 | } |
|
|
242 | |
|
|
243 | filesize = stat_buf.st_size; |
|
|
244 | if (!(filebuffer = malloc(filesize + 1))) { |
|
|
245 | psiconv_error(*config,0,0,"Configuration file %s: " |
|
|
246 | "Out of memory error",filename); |
|
|
247 | goto ERROR1; |
|
|
248 | } |
|
|
249 | |
|
|
250 | filebuffer_ptr = filebuffer; |
|
|
251 | bytes_left = filesize; |
|
|
252 | bytes_read = 1; /* Dummy for the first time through the loop */ |
|
|
253 | while ((bytes_read > 0) && bytes_left) { |
|
|
254 | bytes_read = read(file,filebuffer_ptr,bytes_left); |
|
|
255 | if (bytes_read > 0) { |
|
|
256 | filebuffer_ptr += bytes_read; |
|
|
257 | bytes_left -= bytes_read; |
|
|
258 | } |
|
|
259 | } |
|
|
260 | |
|
|
261 | /* On NFS, the first read may fail and this is not fatal */ |
|
|
262 | if (bytes_left && (bytes_left != filesize)) { |
|
|
263 | psiconv_error(*config,0,0,"Configuration file %s: " |
|
|
264 | "Couldn't read file into memory",filename); |
|
|
265 | goto ERROR2; |
|
|
266 | } |
|
|
267 | |
|
|
268 | if (close(file)) { |
|
|
269 | psiconv_error(*config,0,0,"Configuration file %s: " |
|
|
270 | "Couldn't close file",filename); |
|
|
271 | file = -1; |
|
|
272 | goto ERROR2; |
|
|
273 | } |
|
|
274 | file = -1; |
|
|
275 | |
|
|
276 | psiconv_progress(*config,0,0, |
|
|
277 | "Going to parse configuration file %s: ",filename); |
|
|
278 | /* Now we walk through the file to isolate lines */ |
|
|
279 | linenr = 0; |
|
|
280 | sol = 0; |
|
|
281 | |
|
|
282 | while (sol < filesize) { |
|
|
283 | linenr ++; |
|
|
284 | eol = sol; |
|
|
285 | while ((eol < filesize) && (filebuffer[eol] != 13) && |
|
|
286 | (filebuffer[eol] != 10) && (filebuffer[eol] != 0)) |
|
|
287 | eol ++; |
|
|
288 | |
|
|
289 | if ((eol < filesize) && (filebuffer[eol] == 0)) { |
|
|
290 | psiconv_error(*config,0,0,"Configuration file %s, line %d: " |
|
|
291 | "Unexpected character \000 found",filename,linenr); |
|
|
292 | goto ERROR2; |
|
|
293 | } |
|
|
294 | if ((eol < filesize + 1) && |
|
|
295 | (((filebuffer[eol] == 13) && (filebuffer[eol+1] == 10)) || |
|
|
296 | ((filebuffer[eol] == 10) && (filebuffer[eol+1] == 13)))) { |
|
|
297 | filebuffer[eol] = 0; |
|
|
298 | eol ++; |
|
|
299 | } |
|
|
300 | filebuffer[eol] = 0; |
|
|
301 | psiconv_config_parse_line(filename,linenr,filebuffer + sol,config); |
|
|
302 | sol = eol+1; |
|
|
303 | } |
|
|
304 | free(filebuffer); |
|
|
305 | return; |
|
|
306 | |
|
|
307 | ERROR2: |
|
|
308 | free(filebuffer); |
|
|
309 | ERROR1: |
|
|
310 | if ((file != -1) && close(file)) |
|
|
311 | psiconv_error(*config,0,0,"Configuration file %s: " |
|
|
312 | "Couldn't close file",filename); |
|
|
313 | ERROR0: |
|
|
314 | return; |
|
|
315 | } |
|
|
316 | |
|
|
317 | void psiconv_config_read(const char *extra_config_files, |
|
|
318 | psiconv_config *config) |
|
|
319 | { |
|
|
320 | char *path,*pathptr,*filename,*filename_old; |
|
|
321 | const char *home; |
|
|
322 | int filename_len; |
59 | |
323 | |
60 | /* Make path be the complete search path, colon separated */ |
324 | /* Make path be the complete search path, colon separated */ |
61 | if (extra_config_files && strlen(extra_config_files)) { |
325 | if (extra_config_files && strlen(extra_config_files)) { |
62 | path = malloc(strlen(CONFIGURATION_SEARCH_PATH) + |
326 | path = malloc(strlen(CONFIGURATION_SEARCH_PATH) + |
63 | strlen(extra_config_files) + 2); |
327 | strlen(extra_config_files) + 2); |
64 | strcpy(path,CONFIGURATION_SEARCH_PATH); |
328 | strcpy(path,CONFIGURATION_SEARCH_PATH); |
65 | strcat(path,":"); |
329 | strcat(path,":"); |
66 | strcat(path,extra_config_files) |
330 | strcat(path,extra_config_files); |
67 | } else { |
331 | } else { |
68 | path = strdup(CONFIGURATION_SEARCH_PATH) |
332 | path = strdup(CONFIGURATION_SEARCH_PATH); |
69 | } |
333 | } |
70 | |
334 | |
71 | pathptr = path; |
335 | pathptr = path; |
72 | while (strlen(pathptr)) { |
336 | while (strlen(pathptr)) { |
73 | /* Isolate the next filename */ |
337 | /* Isolate the next filename */ |
74 | filename_len = (path - index(':',path)) || strlen(path); |
338 | filename_len = (index(pathptr,':')?(index(pathptr,':') - pathptr): |
|
|
339 | strlen(pathptr)); |
75 | filename = malloc(filename_len + 1); |
340 | filename = malloc(filename_len + 1); |
76 | filename = strncpy(filename,path,filename_len); |
341 | filename = strncpy(filename,pathptr,filename_len); |
77 | filename[filename_len + 1] = 0; |
342 | filename[filename_len] = 0; |
78 | pathptr += filename_len; |
343 | pathptr += filename_len; |
79 | if (strlen(pathptr)) |
344 | if (strlen(pathptr)) |
80 | pathptr ++; |
345 | pathptr ++; |
81 | |
|
|
82 | /* Try to open the file; it may fail, if it does not exist for example */ |
|
|
83 | if ((file = open(filename,O_RDONLY)) == -1) |
|
|
84 | continue; |
|
|
85 | |
346 | |
86 | /* Read the contents of the file into filebuffer */ |
347 | /* Do ~ substitution */ |
87 | if (!fstat(file,&stat_buf)) |
348 | if ((filename[0] == '~') && ((filename[1] == '/') || filename[1] == 0)) { |
88 | continue; |
349 | home = getenv("HOME"); |
89 | filesize = stat_buf.off_t; |
350 | if (home) { |
90 | filebuffer = malloc(filesize + 1); |
351 | filename_old = filename; |
91 | filebuffer_ptr = filebuffer; |
352 | filename = malloc(strlen(filename_old) + strlen(home)); |
92 | bytes_left = filesize; |
353 | strcpy(filename,home); |
93 | bytes_read = 1 /* Dummy for the first time through the loop */ |
354 | strcpy(filename + strlen(filename),filename_old+1); |
94 | while ((bytes_read > 0) && bytes_left) { |
355 | free(filename_old); |
95 | bytes_read = read(file,filebuffer_ptr,bytes_left); |
|
|
96 | if (bytes_read == -1) { |
|
|
97 | filebuffer_ptr += bytes_read; |
|
|
98 | bytes_left -= bytes_read; |
|
|
99 | } |
356 | } |
100 | } |
357 | } |
101 | if (bytes_left) |
358 | |
102 | continue; |
359 | psiconv_config_parse_file(filename,config); |
103 | |
360 | free(filename); |
104 | /* Try to parse the filebuffer */ |
|
|
105 | filebuffer_ptr = file_buffer; |
|
|
106 | bytes_left = filesize; |
|
|
107 | while (bytes_left) { |
|
|
108 | /* Parse a line */ |
|
|
109 | line_length = index('' |
|
|
110 | |
|
|
111 | } |
361 | } |
|
|
362 | free(path); |
112 | } |
363 | } |
113 | |
|
|
114 | #endif |
|
|
115 | |
|
|