1
2 /* cfg.c - Read config files and provide lookup routines
3
4 Copyright (C) 1995-2002 The Free Software Foundation
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 This file is available from http://sourceforge.net/projects/latex2rtf/
21
22 Authors:
23 1995-1997 Ralf Schlatterbeck
24 1998-2000 Georg Lehner
25 2001-2002 Scott Prahl
26
27 * LEG200698 I would have prefered to make the reading of the language file
28 * separate, since the language is known some steps after reading the
29 * configuration files. Since the search functions rely on the index into
30 * configinfo this is not trivial. So I reread the language file to the array
31 * at the moment the name is known.
32
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <ctype.h>
40
41 #include "main.h"
42 #include "convert.h"
43 #include "funct1.h"
44 #include "cfg.h"
45 #include "utils.h"
46
47 typedef struct ConfigInfoT {
48 char *filename;
49 ConfigEntryT **config_info;
50 int config_info_size;
51 int remove_leading_backslash;
52 } ConfigInfoT;
53
54 static ConfigInfoT configinfo[] = {
55 {"direct.cfg", NULL, 0, FALSE},
56 {"fonts.cfg", NULL, 0, FALSE},
57 {"ignore.cfg", NULL, 0, FALSE},
58 {"style.cfg", NULL, 0, FALSE},
59 {"english.cfg", NULL, 0, FALSE},
60 };
61
62 #define CONFIG_SIZE 5
63 #define BUFFER_INCREMENT 1024
64
65 char *ReadUptoMatch(FILE * infile, const char *scanchars);
66
67 /****************************************************************************
68 * purpose: compare-function for bsearch
69 * params: el1, el2: Config Entries to be compared
70 ****************************************************************************/
cfg_compare(ConfigEntryT ** el1,ConfigEntryT ** el2)71 static int cfg_compare(ConfigEntryT ** el1, ConfigEntryT ** el2)
72 {
73 /*diagnostics(1, "'%s'<=>'%s'", (**el1).TexCommand, (**el2).TexCommand);*/
74 return strcmp((**el1).TexCommand, (**el2).TexCommand);
75 }
76
77 /****************************************************************************
78 * purpose: append path to .cfg file name and open
79 return NULL upon failure,
80 return filepointer otherwise
81 ****************************************************************************/
try_path(const char * path,const char * cfg_file)82 static FILE *try_path(const char *path, const char *cfg_file)
83 {
84 char *both;
85 FILE *fp;
86 char separator[2];
87
88 separator[0] = PATHSEP;
89 separator[1] = '\0';
90
91 if (path == NULL || cfg_file == NULL)
92 return NULL;
93
94 /* fix path ending if needed */
95 if (path[strlen(path)] != PATHSEP)
96 both = strdup_together3(path,separator,cfg_file);
97 else
98 both = strdup_together(path,cfg_file);
99
100 diagnostics(2, "trying to open '%s'", both);
101
102 fp = fopen(both, "rb");
103 free(both);
104 return fp;
105 }
106
107 /*****************************************************
108 * run through 'path' (from environment, e.g. $PATH)
109 * try to find a file called 'name'
110 * if 'subdir' is given, prepend it to 'name'
111 *****************************************************/
open_path(const char * path,const char * name,const char * subdir)112 static FILE *open_path(const char *path, const char* name, const char* subdir)
113 {
114 FILE *fp = NULL;
115 char *pf = strdup(path);
116 char *p = pf;
117
118 while (NULL != p) {
119 char *p1 = strchr(p, ENVSEP);
120 if (NULL != p1)
121 *p1 = '\0';
122 if (NULL != subdir) {
123 char *pft = strdup_together(p, subdir);
124 fp = try_path(pft, name);
125 free(pft);
126 } else {
127 fp = try_path(p, name);
128 }
129 if (NULL != fp)
130 break;
131
132 p = (p1) ? p1 + 1 : NULL;
133 }
134 free(pf);
135 return fp;
136 }
137
138 /*
139 * if the environment variable 'env' is defined
140 * try to find a file called 'name' using it's contents
141 * as a path. if subdir is given prepend it to 'name'
142 */
open_env(const char * env,const char * name,const char * subdir)143 static FILE *open_env(const char *env, const char* name, const char*subdir)
144 {
145 FILE *result = NULL;
146 char *p = getenv(env);
147
148 if (NULL != p)
149 result = open_path(p,name,subdir);
150
151 return result;
152 }
153
154
open_cfg(const char * name,int quit_on_error)155 FILE *open_cfg(const char *name, int quit_on_error)
156 /****************************************************************************
157 purpose: open config by trying multiple paths
158 ****************************************************************************/
159 {
160 FILE *fp;
161
162 /* try path specified on the line */
163 fp = try_path(g_config_path, name);
164 if (fp)
165 return fp;
166
167 /* try the environment variable RTFPATH */
168 fp = open_env("RTFPATH", name, NULL);
169 if (NULL != fp) return fp;
170
171 /* try the environment variable PROGRAMFILES */
172 fp = open_env("PROGRAMFILES", name, "/latex2rtf/cfg");
173 if (NULL != fp) return fp;
174
175 /* last resort. try CFGDIR */
176 fp = open_path(CFGDIR, name, NULL);
177 if (NULL != fp) return fp;
178
179 /* failed ... give some feedback */
180 if (quit_on_error) {
181 diagnostics(WARNING, "Cannot open the latex2rtf config file '%s'",name);
182 diagnostics(WARNING, "Locate the directory containing the .cfg files and");
183 diagnostics(WARNING, " (1) define the environment variable RTFPATH, *or*");
184 diagnostics(WARNING, " (2) use command line path option \"-P /path/to/cfg/file\", *or*");
185 diagnostics(WARNING, " (3) recompile latex2rtf with CFGDIR defined properly");
186 diagnostics(WARNING, "Current RTFPATH: %s", getenv("RTFPATH"));
187 diagnostics(WARNING, "Current CFGDIR: %s", CFGDIR);
188 diagnostics(ERROR, " Giving up. Please don't hate me.");
189 }
190 return NULL;
191 }
192
read_cfg(FILE * cfgfile,ConfigEntryT *** pointer_array,int do_remove_backslash)193 static int read_cfg(FILE * cfgfile, ConfigEntryT *** pointer_array, int do_remove_backslash)
194
195 /****************************************************************************
196 * purpose: Read config file and provide sorted lookup table
197 ****************************************************************************/
198 {
199 int bufindex = 0, bufsize = 0;
200 char *line, *cmdend;
201
202 if (*pointer_array == NULL) {
203 *pointer_array = (struct ConfigEntryT **) malloc(BUFFER_INCREMENT * sizeof(char *));
204 bufsize = BUFFER_INCREMENT;
205 if (*pointer_array == NULL)
206 diagnostics(ERROR, "Cannot allocate memory for pointer list");
207 }
208
209 while ((line = ReadUptoMatch(cfgfile, "\n")) != NULL) {
210
211 /* skip newline */
212 getc(cfgfile);
213
214 /* Skip leading white space */
215 while (isspace((unsigned char) *line))
216 line++;
217
218 /* Skip comment line */
219 if (*line == '#')
220 continue;
221
222 /* Skip blank line */
223 if (*line == '\0')
224 continue;
225
226 /* Find period that terminates command */
227 cmdend = strrchr(line, '.');
228 if (cmdend == NULL){
229 diagnostics(ERROR, "Bad config file, missing final period\nBad line is \"%s\"", line);
230 exit(1);
231 }
232
233 /* Replace period with NULL */
234 *cmdend = '\0';
235
236 /* Skip backslash if specified */
237 if (do_remove_backslash) {
238 if (*line != '\\')
239 diagnostics(ERROR, "Bad config file, missing initial'\\'\nBad line is\"%s\"", line);
240 else
241 line++;
242 }
243
244 /* resize buffer if needed */
245 if (bufindex >= bufsize) {
246 bufsize += BUFFER_INCREMENT;
247 *pointer_array = (struct ConfigEntryT **) realloc(*pointer_array, bufsize * sizeof(char *));
248 if (*pointer_array == NULL)
249 diagnostics(ERROR, "Cannot allocate memory for pointer list");
250 }
251
252 /* find start of definition */
253 line = strdup(line);
254 cmdend = strchr(line, ',');
255 if (cmdend == NULL) {
256 diagnostics(ERROR, "Bad config file, missing ',' between elements\nBad line is\"%s\"", line);
257 exit(1);
258 }
259
260 /* terminate command */
261 *cmdend = '\0';
262
263 (*pointer_array)[bufindex] = (struct ConfigEntryT *) malloc(sizeof(ConfigEntryT));
264
265 if ((*pointer_array)[bufindex] == NULL)
266 diagnostics(ERROR, "Cannot allocate memory for config entry");
267
268 (*pointer_array)[bufindex]->TexCommand = line;
269 (*pointer_array)[bufindex]->RtfCommand = cmdend + 1;
270 (*pointer_array)[bufindex]->original_id = bufindex;
271 bufindex++;
272 diagnostics(6,"%3d Tex='%s' RTF='%s'", bufindex, line, cmdend+1);
273 }
274
275 qsort(*pointer_array, bufindex, sizeof(**pointer_array), (fptr) cfg_compare);
276
277 return bufindex;
278 }
279
ReadCfg(void)280 void ReadCfg(void)
281
282 /****************************************************************************
283 * purpose: opens config-files & reads them
284 * globals: Direct-, Font- IgnoreArray[Size/Root]
285 ****************************************************************************/
286 {
287 int i;
288 FILE *fp;
289 char *fname;
290
291 for (i = 0; i < CONFIG_SIZE; i++) {
292 fname = configinfo[i].filename;
293 fp = (FILE *) open_cfg(fname, TRUE);
294
295 configinfo[i].config_info_size = read_cfg(fp, &(configinfo[i].config_info)
296 , configinfo[i].remove_leading_backslash);
297 (void) fclose(fp);
298
299 diagnostics(2, "read %d entries for file %s", configinfo[i].config_info_size, fname);
300
301 }
302 }
303
SearchCfgEntry(const char * theTexCommand,int WhichCfg)304 ConfigEntryT **SearchCfgEntry(const char *theTexCommand, int WhichCfg)
305
306 /****************************************************************************
307 * purpose: search theTexCommand in specified config data and return
308 * pointer to the data
309 ****************************************************************************/
310 {
311 ConfigEntryT compare_item;
312 ConfigEntryT *compare_ptr, **p, **base;
313 int size;
314
315 compare_item.TexCommand = theTexCommand;
316 compare_item.RtfCommand = "";
317 compare_item.original_id= 0;
318 compare_ptr = &compare_item;
319
320 size = configinfo[WhichCfg].config_info_size;
321 base = configinfo[WhichCfg].config_info;
322
323 if (theTexCommand == NULL) return NULL;
324
325 assert(WhichCfg >= 0 && WhichCfg < CONFIG_SIZE);
326 assert(configinfo[WhichCfg].config_info != NULL);
327
328 diagnostics(5, "seeking '%s' in %d of size %d ", theTexCommand, WhichCfg, size);
329 p = (ConfigEntryT **) bsearch(&compare_ptr, base, size, sizeof(compare_ptr), (fptr) cfg_compare);
330
331 if (p)
332 diagnostics(5, "seeking '%s' found '%s'", theTexCommand, (**p).TexCommand);
333 return p;
334 }
335
SearchCfgEntryByID(const int id,int WhichCfg)336 ConfigEntryT **SearchCfgEntryByID(const int id, int WhichCfg)
337
338 /****************************************************************************
339 * purpose: Get the entry with the given id
340 ****************************************************************************/
341 {
342 int i, max;
343 ConfigEntryT ** entry;
344
345 max = configinfo[WhichCfg].config_info_size;
346 if (id > (int) max) return NULL;
347
348 /* now iterate through all the entries looking for the right one */
349 entry = (ConfigEntryT **) configinfo[WhichCfg].config_info;
350 for (i=0; i<max; i++) {
351 if ( id == (**entry).original_id ) return entry;
352 entry++;
353 }
354
355 /* not found, should not be reached */
356 return NULL;
357 }
358
SearchCfgRtf(const char * theTexCommand,int WhichCfg)359 char *SearchCfgRtf(const char *theTexCommand, int WhichCfg)
360
361 /****************************************************************************
362 * purpose: search theTexCommand in a specified config data and return
363 * pointer to the corresponding RTF string
364 ****************************************************************************/
365 {
366 ConfigEntryT **p;
367
368 p = SearchCfgEntry(theTexCommand, WhichCfg);
369
370 if (p == NULL)
371 return NULL;
372 else
373 return (char *) (**p).RtfCommand;
374 }
375
CfgStartIterate(void)376 ConfigEntryT **CfgStartIterate(void)
377
378 /****************************************************************************
379 * purpose: Start iterating of configuration data
380 ****************************************************************************/
381 {
382 return NULL;
383 }
384
CfgNext(int WhichCfg,ConfigEntryT ** last)385 ConfigEntryT **CfgNext(int WhichCfg, ConfigEntryT ** last)
386
387 /****************************************************************************
388 * purpose: Get the next entry from specified configuration data
389 ****************************************************************************/
390 {
391 if (last == NULL)
392 return (ConfigEntryT **) configinfo[WhichCfg].config_info;
393
394 last++;
395 if (last > (ConfigEntryT **) configinfo[WhichCfg].config_info + configinfo[WhichCfg].config_info_size - 1)
396 return NULL;
397
398 return last;
399 }
400
CfgNextByInsertion(int WhichCfg,ConfigEntryT ** last)401 ConfigEntryT **CfgNextByInsertion(int WhichCfg, ConfigEntryT ** last)
402
403 /****************************************************************************
404 * purpose: Get the next entry from specified configuration data
405 ****************************************************************************/
406 {
407 int next_id;
408
409 if (last == NULL)
410 next_id = 0;
411 else
412 next_id = (**last).original_id + 1;
413
414 return SearchCfgEntryByID(next_id, WhichCfg);
415 }
416
417 /****************************************************************************
418 * opens and reads the language configuration file named in lang
419
420 Opens language file & builds a search tree for the translation of
421 "Hardcoded" latex headings like "Part", "References", ...
422 The file format is:
423 LATEXTOKEN,Translation.
424
425 ****************************************************************************/
ReadLanguage(char * lang)426 void ReadLanguage(char *lang)
427 {
428 FILE *fp;
429 char *langfn;
430
431 if (lang == NULL) return;
432 langfn = strdup_together(lang, ".cfg");
433
434 fp = (FILE *) open_cfg(langfn, TRUE);
435 free(langfn);
436 if (fp == NULL) return;
437
438 configinfo[LANGUAGE_A].config_info_size
439 = read_cfg(fp, &(configinfo[LANGUAGE_A].config_info), configinfo[LANGUAGE_A].remove_leading_backslash);
440
441 (void) fclose(fp);
442 }
443
444 /****************************************************************************
445 purpose : returns a pointer to the Printout name of a Heading, since
446 this is read from a language file it provides translation
447 capability.
448 params : name, name of heading.
449 ****************************************************************************/
ConvertBabelName(char * name)450 void ConvertBabelName(char *name)
451 {
452 char *s = SearchCfgRtf(name, LANGUAGE_A);
453
454 if (s != NULL)
455 ConvertString(s);
456 }
457
GetBabelName(char * name)458 char *GetBabelName(char *name)
459 {
460 char *s = NULL;
461 s = SearchCfgRtf(name, LANGUAGE_A);
462 return s;
463 }
464
465
466 static char *buffer = NULL;
467 static int bufsize = 0;
468
469 #define CR (char) 0x0d
470 #define LF (char) 0x0a
471
472 /*
473 * This function assumes there are no '\0' characters in the input.
474 * if there are any, they are ignored.
475 */
ReadUptoMatch(FILE * infile,const char * scanchars)476 char *ReadUptoMatch(FILE * infile, const char *scanchars)
477 {
478 int bufindex = 0;
479 int c;
480
481 if (feof(infile) != 0)
482 return NULL;
483
484 if (buffer == NULL) {
485 buffer = (char *) malloc(BUFFER_INCREMENT * sizeof(char));
486 if (buffer == NULL) {
487 diagnostics(ERROR, "Cannot allocate memory for input buffer");
488 exit(1);
489 }
490 bufsize = BUFFER_INCREMENT;
491 }
492
493 while ((c = getc(infile)) != EOF) {
494
495 if (c == CR || c == LF)
496 c = '\n';
497
498 if (strchr(scanchars, c))
499 break;
500
501 if (c == (int) '\0')
502 continue;
503
504 buffer[bufindex++] = (char) c;
505 if (bufindex >= bufsize) {
506 bufsize += BUFFER_INCREMENT;
507 buffer = (char *) realloc(buffer, bufsize);
508 if (buffer == NULL) {
509 diagnostics(ERROR, "Cannot allocate memory for input buffer");
510 exit(1);
511 }
512 }
513 }
514 buffer[bufindex] = '\0';
515 if (c != EOF)
516 ungetc(c, infile);
517
518 return buffer;
519 }
520