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