source: git/kernel/feResource.cc @ 216772

spielwiese
Last change on this file since 216772 was 216772, checked in by Oliver Wienand <wienand@…>, 19 years ago
feResource.cc --> Resource 'C' hinzugefügt {"HtmlHelpFile", 'C', feResFile, "SINGULAR_CHM_FILE", "%r/doc/Manual.chm", ""}, git-svn-id: file:///usr/local/Singular/svn/trunk@8447 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 19.6 KB
Line 
1/****************************************
2*  Computer Algebra System SINGULAR     *
3****************************************/
4/* $Id: feResource.cc,v 1.6 2005-07-25 09:09:41 wienand Exp $ */
5/*
6* ABSTRACT: management of resources
7*/
8
9#include <stdlib.h>
10#include <unistd.h>
11#include <string.h>
12
13#include "mod2.h"
14#ifdef AIX_4
15#define HAVE_PUTENV 1
16#endif
17
18#if defined(HPUX_10) || defined(HPUX_9)
19extern "C" int setenv(const char *name, const char *value, int overwrite);
20#endif
21
22
23#include "distrib.h"
24#include "dError.h"
25#if !defined(ESINGULAR) && !defined(TSINGULAR)
26#include "febase.h"
27#include "omalloc.h"
28#else
29char* feResource(const char id, int warn = -1);
30char* feResource(const char* key, int warn = -1);
31#include "dError.c"
32#endif
33
34// define RESOURCE_DEBUG for chattering about resource management
35// #define RESOURCE_DEBUG
36
37#if defined(MAKE_DISTRIBUTION)
38#if defined(ix86_Win) && ! defined(__CYGWIN__)
39#define SINGULAR_DEFAULT_DIR "/Singular/"S_VERSION1
40#elif defined(macintosh)
41#define SINGULAR_DEFAULT_DIR "Macintosh HD:Singular:"S_VERSION1
42#else // unix
43#define SINGULAR_DEFAULT_DIR "/usr/local/Singular/"S_VERSION1
44#endif
45#else // ! defined(MAKE_DISTRIBUTION)
46#define SINGULAR_DEFAULT_DIR S_ROOT_DIR
47#endif // defined(MAKE_DISTRIBUTION)
48
49/*****************************************************************
50 *
51 * Declarations: Data  structures
52 *
53 *****************************************************************/
54typedef enum {feResUndef = 0, feResBinary, feResDir, feResFile, feResUrl, feResPath} feResourceType;
55
56typedef struct feResourceConfig_s
57{
58  char*           key;   // key to identify resource
59  char            id;    // char id to identify resource
60  feResourceType  type;  // type of Resource
61  char*           env;   // env variable to look for
62  char*           fmt;   // format string -- see below for epxlaination
63  char*                        value; // what it was set to
64} feResourceConfig_s;
65typedef feResourceConfig_s * feResourceConfig;
66
67// feSprintf transforms format strings as follows:
68// 1.) substrings of the form %c (c being a letter) are replaced by respective resource value
69// 2.) substrings of the form $string are replaced by value of resp. env variable
70
71// feCleanResource makes furthermore  the following transformations (except for URL resources)
72// 1.) '/' characters are replaced by respective directory - separators
73// 2.) ';' characters are replaced by respective path separators
74static feResourceConfig_s feResourceConfigs[] =
75{
76  {"SearchPath",    's', feResPath,  NULL,
77   "$SINGULARPATH;"
78   "%b/LIB;"
79   "%b/MOD;"
80   "%r/LIB;"
81   "%r/../LIB;"
82   "%d/LIB;"
83   "%d/../LIB"
84   ""},
85  {"Singular",  'S',    feResBinary,"SINGULAR_EXECUTABLE",  "%d/"S_UNAME"/Singular",""},
86  {"BinDir",    'b',    feResDir,   "SINGULAR_BIN_DIR",     "%d/"S_UNAME            ""},
87  {"RootDir",   'r',    feResDir,   "SINGULAR_ROOT_DIR",    "%b/..",                ""},
88  {"DefaultDir",'d',    feResDir,   "SINGULAR_DEFAULT_DIR",  SINGULAR_DEFAULT_DIR,  ""},
89  {"InfoFile",  'i',    feResFile,  "SINGULAR_INFO_FILE",   "%r/info/singular.hlp", ""},
90  {"IdxFile",   'x',    feResFile,  "SINGULAR_IDX_FILE",    "%r/doc/singular.idx",  ""},
91  {"HtmlDir",   'h',    feResDir,   "SINGULAR_HTML_DIR",    "%r/html",              ""},
92#ifdef ix86_Win
93  {"HtmlHelpFile",   'C',    feResFile,   "SINGULAR_CHM_FILE",    "%r/doc/Manual.chm",              ""},
94#endif
95  {"ManualUrl", 'u',    feResUrl,   "SINGULAR_URL",         "http://www.singular.uni-kl.de/Manual/"S_VERSION1,    ""},
96  {"ExDir",      'm',   feResDir,   "SINGULAR_EXAMPLES_DIR","%r/examples",              ""},
97//#if !defined(macintosh)
98//  {"netscape",  'N',    feResBinary,"NETSCAPE",             "%b/netscape",          ""},
99//  {"info",      'I',    feResBinary,"INFO",                 "%b/info",              ""},
100//  {"tkinfo",    'T',    feResBinary,"TKINFO",               "%b/tkinfo",            ""},
101  {"Path",      'p',    feResPath,  NULL,                   "%b;$PATH",         ""},
102//#endif // ! defined(macintosh)
103
104#ifdef ESINGULAR
105  {"emacs",    'E',    feResBinary, "ESINGULAR_EMACS",      "%b/emacs",              ""},
106  {"xemacs",    'A',    feResBinary, "ESINGULAR_EMACS",      "%b/xemacs",              ""},
107  {"SingularEmacs",'M',feResBinary, "ESINGULAR_SINGULAR",    "%b/Singular",           ""},
108  {"EmacsLoad",'l',    feResFile,   "ESINGULAR_EMACS_LOAD",  "%e/.emacs-singular",             ""},
109  {"EmacsDir",  'e',    feResDir,   "ESINGULAR_EMACS_DIR",   "%r/emacs",             ""},
110#elif defined(TSINGULAR)
111  {"SingularXterm",'M',feResBinary, "TSINGULAR_SINGULAR",    "%b/Singular",           ""},
112#ifdef ix86_Win
113  {"rxvt",     'X',    feResBinary,"RXVT",                "%b/rxvt",             ""},
114#else
115  {"xterm",     'X',    feResBinary,"XTERM",                "%b/xterm",             ""},
116#endif
117#else
118  {"EmacsDir",  'e',    feResDir,   "SINGULAR_EMACS_DIR",   "%r/emacs",             ""},
119#endif
120  {NULL, 0, feResUndef, NULL, NULL, NULL}, // must be the last record
121};
122
123
124/*****************************************************************
125 *
126 * Declarations: Local variables / functions
127 *
128 *****************************************************************/
129char* feArgv0=NULL;
130#define MAXRESOURCELEN 5*MAXPATHLEN
131
132char fePathSep =
133#if defined(ix86_Win)
134';'
135#elif defined(macintosh)
136','
137#else
138':'
139#endif
140;
141
142static feResourceConfig feGetResourceConfig(const char id);
143static feResourceConfig feGetResourceConfig(const char* key);
144static char* feResource(feResourceConfig config, int warn);
145static char* feResourceDefault(feResourceConfig config);
146static char* feInitResource(feResourceConfig config, int warn);
147static char* feGetExpandedExecutable();
148static BOOLEAN feVerifyResourceValue(feResourceType type, char* value);
149static char* feCleanResourceValue(feResourceType type, char* value);
150static char* feCleanUpFile(char* fname);
151static char* feCleanUpPath(char* path);
152static void mystrcpy(char* d, char* s);
153static char* feSprintf(char* s, const char* fmt, int warn = -1);
154#if defined(ix86_Win) && defined(__GNUC__)
155// utility function of Cygwin32:
156extern "C" int cygwin32_posix_path_list_p (const char *path);
157#endif
158
159/*****************************************************************
160 *
161 * Public functions
162 *
163 *****************************************************************/
164char* feResource(const char* key, int warn)
165{
166  return feResource(feGetResourceConfig(key), warn);
167}
168
169char* feGetResource(const char id)
170{
171  return feResource(feGetResourceConfig(id), -1);
172}
173
174char* feResource(const char id, int warn)
175{
176  return feResource(feGetResourceConfig(id), warn);
177}
178
179char* feResourceDefault(const char id)
180{
181  return feResourceDefault(feGetResourceConfig(id));
182}
183
184char* feResourceDefault(const char* key)
185{
186  return feResourceDefault(feGetResourceConfig(key));
187}
188
189void feInitResources(char* argv0)
190{
191#if defined(ix86_Win) && defined(__GNUC__)
192  if (cygwin32_posix_path_list_p (getenv("PATH")))
193    fePathSep = ':';
194#endif
195  feArgv0 = omStrDup(argv0);
196#ifdef RESOURCE_DEBUG
197  printf("feInitResources: entering with argv0=%s=\n", argv0);
198#endif
199  // init some Resources
200  feResource('b');
201  feResource('r');
202  // don't complain about stuff when initializing SingularPath
203  feResource('s',0);
204
205#if defined(HAVE_SETENV) || defined(HAVE_PUTENV)
206  char* path = feResource('p');
207#ifdef RESOURCE_DEBUG
208  printf("feInitResources: setting path with argv0=%s=\n", path);
209#endif
210#ifdef HAVE_PUTENV
211  if (path != NULL) { char *s=(char *)omAlloc0(strlen(path)+6);
212                      sprintf(s,"PATH=%s",path);
213                      putenv(s);
214                    }
215#else
216  if (path != NULL) setenv("PATH", path, 1);
217#endif
218#endif
219}
220
221void feReInitResources()
222{
223  int i = 0;
224  while (feResourceConfigs[i].key != NULL)
225  {
226    if (feResourceConfigs[i].value != "")
227    {
228      if (feResourceConfigs[i].value != NULL)
229        omFree(feResourceConfigs[i].value);
230      feResourceConfigs[i].value = "";
231    }
232    i++;
233  }
234#ifdef RESOURCE_DEBUG
235  printf("feInitResources: entering with argv0=%s=\n", feArgv0);
236#endif
237  // init some Resources
238  feResource('b');
239  feResource('r');
240  // don't complain about stuff when initializing SingularPath
241  feResource('s',0);
242}
243
244/*****************************************************************
245 *
246 * Local functions
247 *
248 *****************************************************************/
249static feResourceConfig feGetResourceConfig(const char id)
250{
251  int i = 0;
252  while (feResourceConfigs[i].key != NULL)
253  {
254    if (feResourceConfigs[i].id == id) return &(feResourceConfigs[i]);
255    i++;
256  }
257  return NULL;
258}
259
260static feResourceConfig feGetResourceConfig(const char* key)
261{
262  int i = 0;
263  while (feResourceConfigs[i].key != NULL)
264  {
265    if (strcmp(feResourceConfigs[i].key, key) == 0)
266      return &(feResourceConfigs[i]);
267    i++;
268  }
269  return NULL;
270}
271
272static char* feResource(feResourceConfig config, int warn)
273{
274  if (config == NULL) return NULL;
275  if (config->value != NULL && *(config->value) != '\0') return config->value;
276  return feInitResource(config, warn);
277}
278
279static char* feResourceDefault(feResourceConfig config)
280{
281  if (config == NULL) return NULL;
282  char* value = (char*) omAlloc(MAXRESOURCELEN);
283  feSprintf(value, config->fmt, -1);
284  return value;
285}
286
287static char* feInitResource(feResourceConfig config, int warn)
288{
289  assume(config != NULL);
290#ifdef RESOURCE_DEBUG
291  printf("feInitResource: entering for %s\n", config->key);
292#endif
293
294  char value[MAXRESOURCELEN];
295  // now we have to work
296  // First, check Environment variable
297  if (config->env != NULL)
298  {
299    char* evalue = getenv(config->env);
300    if (evalue != NULL)
301    {
302#ifdef RESOURCE_DEBUG
303      printf("feInitResource: Found value from env:%s\n", evalue);
304#endif
305      strcpy(value, evalue);
306      if (config->type == feResBinary  // do not verify binaries
307          ||
308          feVerifyResourceValue(config->type,
309                                feCleanResourceValue(config->type, value)))
310      {
311#ifdef RESOURCE_DEBUG
312        printf("feInitResource: Set value of %s to =%s=\n", config->key, value);
313#endif
314        config->value = omStrDup(value);
315        return config->value;
316      }
317    }
318  }
319
320  *value = '\0';
321  // Special treatment of executable
322  if (config->id == 'S')
323  {
324    char* executable = feGetExpandedExecutable();
325    if (executable != NULL)
326    {
327#ifdef RESOURCE_DEBUG
328      printf("exec:%s\n", executable);
329#endif
330      strcpy(value, executable);
331#ifdef RESOURCE_DEBUG
332      printf("value:%s\n", value);
333#endif
334      omFree(executable);
335    }
336  }
337  // and bindir
338  else if (config->id == 'b')
339  {
340    char* executable = feResource('S');
341#ifdef RESOURCE_DEBUG
342      printf("feInitResource: Get %s from %s\n", config->key, executable);
343#endif
344    if (executable != NULL)
345    {
346      strcpy(value, executable);
347      executable = strrchr(value, DIR_SEP);
348      if (executable != NULL) *executable = '\0';
349    }
350  }
351
352#ifdef RESOURCE_DEBUG
353  printf("value:%s\n", value);
354#endif
355
356  if (*value == '\0' && config->fmt != NULL )
357  {
358    feSprintf(value, config->fmt, warn);
359  }
360  else if (config->fmt == NULL)
361  {
362    sprintf(value, "Wrong Resource Specification of %s", config->key);
363    dReportBug(value);
364    return NULL;
365  }
366
367  // Clean and verify
368  if (feVerifyResourceValue(config->type,
369                            feCleanResourceValue(config->type, value)))
370  {
371#ifdef RESOURCE_DEBUG
372    printf("feInitResource: Set value of %s to =%s=\n", config->key, value);
373#endif
374    config->value = omStrDup(value);
375    return config->value;
376  }
377  else if (config->type == feResBinary)
378  {
379    // for binaries, search through PATH once more
380    char* executable = omFindExec(config->key, value);
381    if (executable != NULL)
382    {
383      if (feVerifyResourceValue(config->type,
384                                feCleanResourceValue(config->type, value)))
385      {
386        config->value = omStrDup(value);
387#ifdef RESOURCE_DEBUG
388        printf("feInitResource: Set value of %s to =%s=\n", config->key, config->value);
389#endif
390        return config->value;
391      }
392    }
393  }
394
395  // issue warning if explicitely requested, or if
396  // this value is gotten for the first time
397  if (warn > 0 || (warn < 0 && config->value != NULL))
398  {
399    Warn("Could not get %s. ", config->key);
400    Warn("Either set environment variable %s to %s,",
401         config->env, config->key);
402    feSprintf(value, config->fmt, warn);
403    Warn("or make sure that %s is at %s", config->key, value);
404  }
405#ifdef RESOURCE_DEBUG
406      printf("feInitResource: Set value of %s to NULL", config->key);
407#endif
408  config->value = NULL;
409  return NULL;
410}
411
412static char* feGetExpandedExecutable()
413{
414  if (feArgv0 == NULL || *feArgv0 == '\0')
415  {
416    if (feArgv0 == NULL) dReportBug("feArgv0 == NULL");
417    else dReportBug("feArgv0 == ''");
418    return NULL;
419  }
420#ifndef macintosh
421#ifdef ix86_Win // stupid WINNT sometimes gives you argv[0] within ""
422  if (*feArgv0 == '"')
423  {
424    int l = strlen(feArgv0);
425    if (feArgv0[l-1] == '"')
426    {
427      feArgv0[l-1] = '\0';
428      feArgv0++;
429    }
430  }
431#endif
432#ifdef RESOURCE_DEBUG
433  printf("feGetExpandedExecutable: calling find_exec with =%s=\n", feArgv0);
434#endif
435  char executable[MAXRESOURCELEN];
436  char* value = omFindExec(feArgv0, executable);
437#ifdef RESOURCE_DEBUG
438  printf("feGetExpandedExecutable: find_exec exited with =%s=%d\n", executable, access(executable, X_OK));
439#endif
440  if (value == NULL)
441  {
442    char message[MAXRESOURCELEN];
443    sprintf(message, "Could not get expanded executable from %s", feArgv0);
444    dReportBug(message);
445    return NULL;
446  }
447  return omStrDup(value);
448#else // macintosh
449  return feArgv0;
450#endif
451}
452
453
454static BOOLEAN feVerifyResourceValue(feResourceType type, char* value)
455{
456#ifdef RESOURCE_DEBUG
457  printf("feVerifyResourceValue: entering with =%s=\n", value);
458  printf("%d:%d\n", access(value, R_OK), access(value, X_OK));
459#endif
460  switch(type)
461  {
462      case feResUrl:
463      case feResPath:
464        return TRUE;
465
466      case feResFile:
467        return ! access(value, R_OK);
468
469      case feResBinary:
470      case feResDir:
471        return ! access(value, X_OK);
472
473      default:
474        return FALSE;
475  }
476}
477
478/*****************************************************************
479 *
480 * Cleaning/Transformations of resource values
481 *
482 *****************************************************************/
483
484static char* feCleanResourceValue(feResourceType type, char* value)
485{
486  if (value == NULL || *value == '\0') return value;
487#ifdef RESOURCE_DEBUG
488      printf("Clean value:%s\n", value);
489#endif
490#ifdef ix86_Win
491#ifdef RESOURCE_DEBUG
492      printf("Clean WINNT value:%s\n", value);
493#endif
494  if (type == feResBinary)
495  {
496    int l = strlen(value);
497    if (l < 4 || (strcmp(&value[l-4], ".exe") != 0 &&
498                  strcmp(&value[l-4], ".EXE") != 0))
499      strcat(value, ".exe");
500  }
501#endif
502  if (type == feResFile || type == feResBinary || type == feResDir)
503    return feCleanUpFile(value);
504  if (type == feResPath)
505    return feCleanUpPath(value);
506  return value;
507}
508
509static char* feCleanUpFile(char* fname)
510{
511  char* fn, *s;
512
513#ifdef RESOURCE_DEBUG
514  printf("feCleanUpFile: entering with =%s=\n", fname);
515#endif
516  // Remove unnecessary .. and //
517  for (fn = fname; *fn != '\0'; fn++)
518  {
519    if (*fn == '/')
520    {
521      if (*(fn+1) == '\0')
522      {
523        if (fname != fn) *fn = '\0';
524        break;
525      }
526      if (*(fn + 1) == '/' && (fname != fn))
527      {
528        mystrcpy(fn, fn+1);
529        fn--;
530      }
531      else if (*(fn+1) == '.')
532      {
533        if (*(fn+2) == '.' && (*(fn + 3) == '/' || *(fn + 3) == '\0'))
534        {
535          *fn = '\0';
536          s = strrchr(fname, '/');
537          if (s != NULL)
538          {
539            mystrcpy(s+1, fn + (*(fn + 3) != '\0' ? 4 : 3));
540            fn = s-1;
541          }
542          else
543          {
544            *fn = '/';
545          }
546        }
547        else if (*(fn+2) == '/' || *(fn+2) == '\0')
548        {
549          mystrcpy(fn+1, fn+3);
550          fn--;
551        }
552      }
553    }
554  }
555
556#ifdef macintosh
557  // replace / and .. by DIR_SEP and UP_DIR
558  fn = fname;
559  while (*fn != NULL)
560  {
561    if (*fn == '.' && *(fn + 1) == '.')
562    {
563      mystrcpy(fn , fn + 2);
564    }
565    else
566    {
567      if (*fn == '/') *fn = DIR_SEP;
568      fn++;
569    }
570  }
571#endif
572
573#ifdef RESOURCE_DEBUG
574  printf("feCleanUpFile: leaving with =%s=\n", fname);
575#endif
576  return fname;
577}
578
579// remove duplicates dir resp. those which do not exist
580static char* feCleanUpPath(char* path)
581{
582#ifdef RESOURCE_DEBUG
583  printf("feCleanUpPath: entering with: =%s=\n", path);
584#endif
585  if (path == NULL) return path;
586
587  int n_comps = 1, i, j;
588  char* opath = path;
589  char** path_comps;
590
591  for (; *path != '\0'; path++)
592  {
593    if (*path == fePathSep) n_comps++;
594    if (*path == ';')
595    {
596      *path = fePathSep;
597      n_comps++;
598    }
599  }
600
601  path_comps = (char**) omAlloc(n_comps*sizeof(char*));
602  path_comps[0]=opath;
603  path=opath;
604  i = 1;
605
606  if (i < n_comps)
607  {
608    while (1)
609    {
610      if (*path == fePathSep)
611      {
612        *path = '\0';
613        path_comps[i] = path+1;
614        i++;
615        if (i == n_comps) break;
616      }
617      path++;
618    }
619  }
620
621  for (i=0; i<n_comps; i++)
622    path_comps[i] = feCleanUpFile(path_comps[i]);
623#ifdef RESOURCE_DEBUG
624  PrintS("feCleanUpPath: after CleanUpName: ");
625  for (i=0; i<n_comps; i++)
626    Print("%s:", path_comps[i]);
627  Print("\n");
628#endif
629
630  for (i=0; i<n_comps;)
631  {
632#ifdef RESOURCE_DEBUG
633    if (access(path_comps[i], X_OK | R_OK))
634      Print("feCleanUpPath: remove %d:%s -- can not access\n", i, path_comps[i]);
635#endif
636    if ( ! access(path_comps[i], X_OK | R_OK))
637    {
638      // x- permission is granted -- we assume that it is a dir
639      for (j=0; j<i; j++)
640      {
641        if (strcmp(path_comps[j], path_comps[i]) == 0)
642        {
643          // found a duplicate
644#ifdef RESOURCE_DEBUG
645          Print("feCleanUpPath: remove %d:%s -- equal to %d:%s\n", j, path_comps[j], i, path_comps[i]);
646#endif
647          j = i+1;
648          break;
649        }
650      }
651      if (j == i)
652      {
653        i++;
654        continue;
655      }
656    }
657    // now we can either not access or found a duplicate
658    path_comps[i] = NULL;
659    for (j=i+1; j<n_comps; j++)
660        path_comps[j-1] = path_comps[j];
661    n_comps--;
662  }
663
664
665  // assemble everything again
666  for (path=opath, i=0;i<n_comps-1;i++)
667  {
668    strcpy(path, path_comps[i]);
669    path += strlen(path);
670    *path = fePathSep;
671    path++;
672  }
673  if (n_comps)
674  {
675    strcpy(path, path_comps[i]);
676  }
677  else
678  {
679    *opath = '\0';
680  }
681  omFree(path_comps);
682#ifdef RESOURCE_DEBUG
683  Print("feCleanUpPath: leaving with path=%s=\n", opath);
684#endif
685  return opath;
686}
687
688// strcpy where source and destination may overlap
689static void mystrcpy(char* d, char* s)
690{
691  assume(d != NULL && s != NULL);
692  while (*s != '\0')
693  {
694    *d = *s;
695    d++;
696    s++;
697  }
698  *d = '\0';
699}
700
701/*****************************************************************
702 *
703 * feSprintf
704 *
705 *****************************************************************/
706static char* feSprintf(char* s, const char* fmt, int warn)
707{
708  char* s_in = s;
709  if (fmt == NULL) return NULL;
710
711  while (*fmt != '\0')
712  {
713    *s = *fmt;
714
715    if (*fmt == '%' && *(fmt + 1) != '\0')
716    {
717      fmt++;
718      char* r = feResource(*fmt, warn);
719      if (r != NULL)
720      {
721        strcpy(s, r);
722        s += strlen(r) - 1;
723      }
724      else
725      {
726        s++;
727        *s = *fmt;
728      }
729    }
730    else if (*fmt == '$' && *(fmt + 1) != '\0')
731    {
732      fmt++;
733      char* v = s + 1;
734      while (*fmt == '_' ||
735             (*fmt >= 'A' && *fmt <= 'Z') ||
736             (*fmt >= 'a' && *fmt <= 'z'))
737      {
738        *v = *fmt;
739        v++;
740        fmt++;
741      }
742      fmt--;
743      *v = '\0';
744      v = getenv(s + 1);
745      if (v != NULL) strcpy(s, v);
746      s += strlen(s) - 1;
747    }
748    s++;
749    fmt++;
750  }
751  *s = '\0';
752  return s_in;
753}
754
755void feStringAppendResources(int warn)
756{
757  int i = 0;
758  char* r;
759  StringAppend("%-10s:\t%s\n", "argv[0]", feArgv0);
760  while (feResourceConfigs[i].key != NULL)
761  {
762    r = feResource(feResourceConfigs[i].key, warn);
763    StringAppend("%-10s:\t%s\n", feResourceConfigs[i].key,
764                 (r != NULL ? r : ""));
765    i++;
766  }
767}
Note: See TracBrowser for help on using the repository browser.