"Fossies" - the Fresh Open Source Software Archive

Member "singular-4.2.1/resources/feResource.cc" (9 Jun 2021, 19852 Bytes) of package /linux/misc/singular-4.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "feResource.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.2.0p3_vs_4.2.1.

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