Back to index

tetex-bin  3.0
progname.c
Go to the documentation of this file.
00001 /* progname.c: the executable name we were invoked as; general initialization.
00002 
00003 Copyright (C) 1994, 96, 97 Karl Berry.
00004 
00005 This library is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU Library General Public
00007 License as published by the Free Software Foundation; either
00008 version 2 of the License, or (at your option) any later version.
00009 
00010 This library is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 Library General Public License for more details.
00014 
00015 You should have received a copy of the GNU Library General Public
00016 License along with this library; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <kpathsea/config.h>
00020 #include <kpathsea/absolute.h>
00021 #include <kpathsea/c-pathch.h>
00022 #include <kpathsea/c-stat.h>
00023 #include <kpathsea/pathsearch.h>
00024 /* For kpse_reset_progname */
00025 #include <kpathsea/tex-file.h>
00026 
00027 #if defined (WIN32) || defined (DJGPP)
00028 #include <kpathsea/c-pathmx.h>
00029 #endif
00030 
00031 #if defined(__i386_pc_gnu__)
00032 #ifndef _S_ISUID
00033 #define _S_ISUID    04000  /* Set user ID on execution.  */
00034 #endif
00035 #ifndef _S_ISGID
00036 #define _S_ISGID    02000  /* Set group ID on execution.  */
00037 #endif
00038 #ifndef _S_ISVTX
00039 #define _S_ISVTX    01000  /* Save swapped text after use (sticky).  */
00040 #endif
00041 #ifndef _S_IREAD
00042 #define _S_IREAD    0400   /* Read by owner.  */
00043 #endif
00044 #ifndef _S_IWRITE
00045 #define _S_IWRITE   0200   /* Write by owner.  */
00046 #endif
00047 #ifndef _S_IEXEC
00048 #define _S_IEXEC    0100   /* Execute by owner.  */
00049 #endif
00050 #endif
00051 
00052 /* NeXT does not define the standard macros, but has the equivalent.
00053    WIN32 doesn't define them either, and doesn't have them.
00054    From: Gregor Hoffleit <flight@mathi.uni-heidelberg.de>.  */
00055 #ifndef S_IXUSR
00056 #ifdef WIN32
00057 #define S_IXUSR 0
00058 #define S_IXGRP 0
00059 #define S_IXOTH 0
00060 #else /* not WIN32 */
00061 #define S_IXUSR 0100
00062 #endif /* not WIN32 */
00063 #endif /* not S_IXUSR */
00064 #ifndef S_IXGRP
00065 #define S_IXGRP 0010
00066 #endif
00067 #ifndef S_IXOTH
00068 #define S_IXOTH 0001
00069 #endif
00070 
00071 #ifndef HAVE_PROGRAM_INVOCATION_NAME
00072 /* Don't redefine the variables if glibc already has.  */
00073 string program_invocation_name = NULL;
00074 string program_invocation_short_name = NULL;
00075 #endif
00076 /* And the variable for the program we pretend to be. */
00077 string kpse_program_name = NULL;
00078 
00079 #ifndef WIN32
00080 /* From a standalone program `ll' to expand symlinks written by Kimbo Mundy.
00081    Don't bother to compile if we don't have symlinks; thus we can assume
00082    / as the separator.  Also don't try to use basename, etc., or
00083    handle arbitrary filename length.  Mixed case function names because
00084    that's what kimbo liked.  */
00085 
00086 #ifdef S_ISLNK
00087 static int ll_verbose = 0;
00088 static int ll_loop = 0;
00089 
00090 #undef BSIZE
00091 #define BSIZE 2048 /* sorry */
00092 
00093 
00094 /* Read link FN into SYM.  */
00095 
00096 static void
00097 ReadSymLink P2C(char *, fn, char *, sym)
00098 {
00099   register int n = readlink (fn, sym, BSIZE);
00100   if (n < 0) {
00101     perror (fn);
00102     exit (1);
00103   }
00104   sym[n] = 0;
00105 }
00106 
00107 
00108 /* Strip first component from S, and also return it in a static buffer.  */
00109 
00110 static char *
00111 StripFirst P1C(register char *, s)
00112 {
00113   static char buf[BSIZE];
00114   register char *s1;
00115 
00116   /* Find the end of the first path element */
00117   for (s1 = s; *s1 && (*s1 != '/' || s1 == s); s1++)
00118     ;
00119 
00120   /* Copy it into buf and null-terminate it. */
00121   strncpy (buf, s, s1 - s);
00122   buf[s1 - s] = 0;
00123 
00124   /* Skip over the leading / (if any) */
00125   if (*s1 == '/')
00126     ++s1;
00127 
00128   /* Squeeze out the element */
00129   while ((*s++ = *s1++) != 0)
00130     ;
00131 
00132   return buf;
00133 }
00134 
00135 
00136 /* Strip last component from S, and also return it in a static buffer.  */
00137 
00138 static char *
00139 StripLast P1C(register char *, s)
00140 {
00141   static char buf[BSIZE];
00142   register char *s1;
00143 
00144   for (s1 = s + strlen (s); s1 > s && *s1 != '/'; s1--)
00145     ;
00146   strcpy (buf, s1 + (*s1 == '/'));
00147   *s1 = 0;
00148 
00149   return buf;
00150 }
00151 
00152 
00153 /* Copy first path element from B to A, removing it from B.  */
00154 
00155 static void 
00156 CopyFirst P2C(register char *, a, char *, b)
00157 {
00158   register int length = strlen (a);
00159 
00160    if (length > 0 && a[length - 1] != '/') {
00161    a[length] = '/';
00162     a[length + 1] = 0;
00163   }
00164   strcat (a, StripFirst (b));
00165 }
00166 
00167 /* Returns NULL on error.  Prints intermediate results if global
00168    `ll_verbose' is nonzero.  */
00169 
00170 #define EX(s)        (strlen (s) && strcmp (s, "/") ? "/" : "")
00171 #define EXPOS        EX(post)
00172 #define EXPRE        EX(pre)
00173 
00174 static char *
00175 expand_symlinks P1C(char *, s)
00176 {
00177   static char pre[BSIZE];   /* return value */
00178   char post[BSIZE], sym[BSIZE], tmp[BSIZE], before[BSIZE];
00179   char *cp;
00180   char a;
00181   struct stat st;
00182   int done;
00183 
00184   /* Check for symlink loops.  It's difficult to check for all the
00185      possibilities ourselves, so let the kernel do it.  And make it
00186      conditional so that people can see where the infinite loop is
00187      being caused (see engtools#1536).  */
00188   if (!ll_loop) {
00189     FILE *f = fopen (s, "r");
00190     if (!f && errno == ELOOP) {
00191       /* Not worried about other errors, we'll get to them in due course.  */
00192       perror (s);
00193       return NULL;
00194     }
00195     if (f) fclose (f);
00196   }
00197 
00198   strcpy (post, s);
00199   strcpy (pre, "");
00200 
00201   while (strlen (post) != 0) {
00202     CopyFirst (pre, post);
00203 
00204     if (lstat (pre, &st) != 0) {
00205       fprintf (stderr, "lstat(%s) failed ...\n", pre);
00206       perror (pre);
00207       return NULL;
00208     }
00209 
00210     if (S_ISLNK (st.st_mode)) {
00211       ReadSymLink (pre, sym);
00212 
00213       if (!strncmp (sym, "/", 1)) {
00214         if (ll_verbose)
00215           printf ("[%s]%s%s -> [%s]%s%s\n", pre, EXPOS, post, sym, EXPOS,post);
00216         strcpy (pre, "");
00217 
00218       } else {
00219         a = pre[0];  /* handle links through the root */
00220         strcpy (tmp, StripLast (pre));
00221         if (!strlen (pre) && a == '/')
00222           strcpy (pre, "/");
00223 
00224         if (ll_verbose) {
00225           sprintf (before, "%s%s[%s]%s%s", pre, EXPRE, tmp, EXPOS, post);
00226           printf ("%s -> %s%s[%s]%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
00227         }
00228 
00229         /* Strip "../" path elements from the front of sym; print
00230            new result if there were any such elements.  */
00231         done = 0;
00232         a = pre[0];  /* handle links through the root */
00233         while (!strncmp (sym, "..", 2)
00234                && (sym[2] == 0 || sym[2] == '/')
00235                && strlen (pre) != 0
00236                && strcmp (pre, ".")
00237                && strcmp (pre, "..")
00238                && (strlen (pre) < 3
00239                    || strcmp (pre + strlen (pre) - 3, "/.."))) {
00240           done = 1;
00241           StripFirst (sym);
00242           StripLast (pre);
00243         }
00244 
00245         if (done && ll_verbose) {
00246           for (cp = before; *cp;)
00247             *cp++ = ' ';
00248           if (strlen (sym))
00249             printf ("%s == %s%s%s%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
00250           else
00251             printf ("%s == %s%s%s\n", before, pre, EXPOS, post);
00252         }
00253         if (!strlen (pre) && a == '/')
00254           strcpy (pre, "/");
00255       }
00256 
00257       if (strlen (post) != 0 && strlen (sym) != 0)
00258         strcat (sym, "/");
00259 
00260       strcat (sym, post);
00261       strcpy (post, sym);
00262     }
00263   }
00264 
00265   return pre;
00266 }
00267 #else /* not S_ISLNK */
00268 #define expand_symlinks(s) (s)
00269 #endif /* not S_ISLNK */
00270 
00271 /* Remove .'s and ..'s in DIR, to avoid problems with relative symlinks
00272    as the program name, etc.  This does not canonicalize symlinks.  */
00273 
00274 static string
00275 remove_dots P1C(string, dir)
00276 {
00277 #ifdef AMIGA
00278   return dir;
00279 #else
00280   string c;
00281   unsigned len;
00282   string ret = (string) ""; /* We always reassign.  */
00283   
00284   for (c = kpse_filename_component (dir); c;
00285        c = kpse_filename_component (NULL)) {
00286     if (STREQ (c, ".")) {
00287       /* If leading ., replace with cwd.  Else ignore.  */
00288       if (*ret == 0) {
00289         ret = xgetcwd ();
00290       }
00291 
00292     } else if (STREQ (c, "..")) {
00293       /* If leading .., start with xdirname (cwd).  Else remove last
00294          component from ret, if any.  */
00295       if (*ret == 0) {
00296         string dot = xgetcwd ();
00297         ret = xdirname (dot);
00298         free (dot);
00299       } else {
00300         unsigned last;
00301         for (last = strlen (ret);
00302              last > (NAME_BEGINS_WITH_DEVICE (ret) ? 2 : 0);
00303              last--) {
00304           if (IS_DIR_SEP (ret[last - 1])) {
00305             /* If we have `/../', that's the same as `/'.  */
00306             if (last > 1) {
00307               ret[last - 1] = 0;
00308             }
00309             break;
00310           }
00311         }
00312       }
00313 
00314     } else {
00315       /* Not . or ..; just append.  Include a directory separator unless
00316          our string already ends with one.  This also changes all directory
00317          separators into the canonical DIR_SEP_STRING.  */
00318       string temp;
00319       len = strlen (ret);
00320       temp = concat3 (ret, ((len > 0 && ret[len - 1] == DIR_SEP)
00321                             || (NAME_BEGINS_WITH_DEVICE (c) && *ret == 0))
00322                            ? "" : DIR_SEP_STRING,
00323                       c);
00324       if (*ret)
00325         free (ret);
00326       ret = temp;
00327     }
00328   }
00329   
00330   /* Remove a trailing /, just in case it snuck in.  */
00331   len = strlen (ret);
00332   if (len > 0 && ret[len - 1] == DIR_SEP) {
00333     ret[len - 1] = 0;
00334   }
00335 
00336   return ret;
00337 #endif /* not AMIGA */
00338 }
00339 
00340 /* Return directory ARGV0 comes from.  Check PATH if ARGV0 is not
00341    absolute.  */
00342 
00343 static string
00344 selfdir P1C(const_string, argv0)
00345 {
00346   string ret = NULL;
00347   string self = NULL;
00348   
00349   if (kpse_absolute_p (argv0, true)) {
00350     self = xstrdup (argv0);
00351   } else {
00352 #ifdef AMIGA
00353 #include <dos.h>
00354 #include <proto/dos.h>
00355 #include <proto/exec.h>
00356     BPTR lock;
00357     struct DosLibrary *DOSBase
00358       = (struct DosLibrary *) OpenLibrary ("dos.library", 0L);
00359     assert (DOSBase);
00360 
00361     self = xmalloc (BUFSIZ);
00362     lock = findpath (argv0);
00363     if (lock != ((BPTR) -1)) {
00364       if (getpath (lock, self) == -1) {
00365         *self = '\0';
00366       } else {
00367         strcat (self,DIR_SEP_STRING);
00368         strcat (self,argv0); 
00369       }
00370       UnLock (lock);
00371     }
00372     CloseLibrary((struct Library *) DOSBase);
00373 #else /* not AMIGA */
00374     string elt;
00375     struct stat s;
00376 
00377     /* Have to check PATH.  But don't call kpse_path_search since we don't
00378        want to search any ls-R's or do anything special with //'s.  */
00379     for (elt = kpse_path_element (getenv ("PATH")); !self && elt;
00380          elt = kpse_path_element (NULL)) {
00381       string name;
00382       
00383       /* UNIX tradition interprets the empty path element as "." */
00384       if (*elt == 0) elt = ".";
00385       
00386       name = concat3 (elt, DIR_SEP_STRING, argv0);
00387 
00388       /* In order to do this perfectly, we'd have to check the owner bits only
00389          if we are the file owner, and the group bits only if we belong
00390          to the file group.  That's a lot of work, though, and it's not
00391          likely that kpathsea will ever be used with a program that's
00392          only executable by some classes and not others.  See the
00393          `file_status' function in execute_cmd.c in bash for what's
00394          necessary if we were to do it right.  */
00395       if (stat (name, &s) == 0 && s.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
00396         /* Do not stop at directories. */
00397         if (!S_ISDIR(s.st_mode)) 
00398           self = name;
00399       }
00400     }
00401 #endif /* not AMIGA */
00402   }
00403   
00404   /* If argv0 is somehow dir/exename, `self' will still be NULL.  */
00405   if (!self)
00406     self = concat3 (".", DIR_SEP_STRING, argv0);
00407     
00408   ret = xdirname (remove_dots (expand_symlinks (self)));
00409 
00410   free (self);
00411   
00412   return ret;
00413 }
00414 #endif /* not WIN32 */
00415 
00416 void
00417 kpse_set_program_name P2C(const_string, argv0, const_string, progname)
00418 {
00419   string ext, sdir, sdir_parent, sdir_grandparent;
00420   string s = getenv ("KPATHSEA_DEBUG");
00421 #ifdef WIN32
00422   string debug_output = getenv("KPATHSEA_DEBUG_OUTPUT");
00423   string append_debug_output = getenv("KPATHSEA_DEBUG_APPEND");
00424   int err, olderr;
00425 #endif
00426   
00427   /* Set debugging stuff first, in case we end up doing debuggable stuff
00428      during this initialization.  */
00429   if (s) {
00430     kpathsea_debug |= atoi (s);
00431   }
00432 
00433 #ifndef HAVE_PROGRAM_INVOCATION_NAME
00434 #if defined(WIN32)
00435   /* Set various info about user. Among many things,
00436      ensure that HOME is set. If debug_paths is on, 
00437      turn on some message if $HOME is not found. */
00438   if (KPSE_DEBUG_P(KPSE_DEBUG_PATHS)) {
00439     set_home_warning();
00440   }
00441   init_user_info();
00442 
00443   /* redirect stderr to debug_output. Easier to send logfiles. */
00444   if (debug_output) {
00445     int flags =  _O_CREAT | _O_TRUNC | _O_RDWR;
00446     err = -1;
00447     if (_stricmp(debug_output, "con") == 0
00448        || _stricmp(debug_output, "con:") == 0) {
00449       err = _fileno(stdout);
00450     } else {
00451       if (append_debug_output) {
00452         flags =  _O_CREAT | _O_APPEND | _O_WRONLY;
00453       } else {
00454         flags =  _O_CREAT | _O_TRUNC | _O_WRONLY;
00455         xputenv("KPATHSEA_DEBUG_APPEND", "yes");
00456       }
00457     }
00458 
00459     if ((err < 0)
00460         && (err = _open(debug_output, flags, _S_IREAD | _S_IWRITE)) == -1)
00461     {
00462       WARNING1("Can't open %s for stderr redirection!\n", debug_output);
00463       perror(debug_output);
00464     } else if ((olderr = _dup(fileno(stderr))) == -1) {
00465       WARNING("Can't dup() stderr!\n");
00466       close(err);
00467     } else if (_dup2(err, fileno(stderr)) == -1) {
00468       WARNING1("Can't redirect stderr to %s!\n", debug_output);
00469       close(olderr);
00470       close(err);
00471     } else {
00472       close(err);
00473     }
00474   }
00475   /* Win95 always gives the short filename for argv0, not the long one.
00476      There is only this way to catch it. It makes all the selfdir stuff
00477      useless for win32. */
00478   {
00479     char short_path[PATH_MAX], path[PATH_MAX], *fp;
00480       
00481     /* SearchPath() always gives back an absolute directory */
00482     if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0)
00483         FATAL1("Can't determine where the executable %s is.\n", argv0);
00484     if (!win32_get_long_filename(short_path, path, sizeof(path))) {
00485         FATAL1("This path points to an invalid file : %s\n", short_path);
00486     }
00487     /* slashify the dirname */
00488     for (fp = path; fp && *fp; fp++)
00489         if (IS_DIR_SEP(*fp)) *fp = DIR_SEP;
00490     /* sdir will be the directory of the executable, ie: c:/TeX/bin */
00491     sdir = xdirname(path);
00492     program_invocation_name = xstrdup(xbasename(path));
00493   }
00494 
00495 #elif defined(__DJGPP__)
00496 
00497   /* DJGPP programs support long filenames on Windows 95, but ARGV0 there
00498      is always made with the short 8+3 aliases of all the pathname elements.
00499      If long names are supported, we need to convert that to a long name.
00500 
00501      All we really need is to call `_truename', but most of the code
00502      below is required to deal with the special case of networked drives.  */
00503   if (pathconf (argv0, _PC_NAME_MAX) > 12) {
00504     char long_progname[PATH_MAX];
00505 
00506     if (_truename (argv0, long_progname)) {
00507       char *fp;
00508 
00509       if (long_progname[1] != ':') {
00510        /* A complication: `_truename' returns network-specific string at
00511           the beginning of `long_progname' when the program resides on a
00512           networked drive, and DOS calls cannot grok such pathnames.  We
00513           need to convert the filesystem name back to a drive letter.  */
00514        char rootname[PATH_MAX], rootdir[4];
00515 
00516        if (argv0[0] && argv0[1] == ':')
00517          rootdir[0] = argv0[0]; /* explicit drive in `argv0' */
00518        else
00519          rootdir[0] = getdisk () + 'A';
00520        rootdir[1] = ':';
00521        rootdir[2] = '\\';
00522        rootdir[3] = '\0';
00523        if (_truename (rootdir, rootname)) {
00524          /* Find out where `rootname' ends in `long_progname' and replace
00525             it with the drive letter.  */
00526          int root_len = strlen (rootname);
00527 
00528          if (IS_DIR_SEP (rootname[root_len - 1]))
00529             root_len--;     /* keep the trailing slash */
00530          long_progname[0] = rootdir[0];
00531          long_progname[1] = ':';
00532          memmove (long_progname + 2, long_progname + root_len,
00533                  strlen (long_progname + root_len) + 1);
00534        }
00535       }
00536 
00537       /* Convert everything to canonical form.  */
00538       if (long_progname[0] >= 'A' && long_progname[0] <= 'Z')
00539        long_progname[0] += 'a' - 'A'; /* make drive lower case, for beauty */
00540       for (fp = long_progname; *fp; fp++)
00541        if (IS_DIR_SEP (*fp))
00542          *fp = DIR_SEP;
00543 
00544       program_invocation_name = xstrdup (long_progname);
00545     }
00546     else
00547       /* If `_truename' failed, God help them, because we won't...  */
00548       program_invocation_name = xstrdup (argv0);
00549   }
00550   else
00551     program_invocation_name = xstrdup (argv0);
00552 
00553 #else /* !WIN32 && !__DJGPP__ */
00554 
00555   program_invocation_name = xstrdup (argv0);
00556 
00557 #endif
00558 #endif /* not HAVE_PROGRAM_INVOCATION_NAME */
00559 
00560   /* We need to find SELFAUTOLOC *before* removing the ".exe" suffix from
00561      the program_name, otherwise the PATH search inside selfdir will fail,
00562      since `prog' doesn't exists as a file, there's `prog.exe' instead.  */
00563 #ifndef WIN32
00564   sdir = selfdir (program_invocation_name);
00565 #endif
00566   /* SELFAUTODIR is actually the parent of the invocation directory,
00567      and SELFAUTOPARENT the grandparent.  This is how teTeX did it.  */
00568   xputenv ("SELFAUTOLOC", sdir);
00569   sdir_parent = xdirname (sdir);
00570   xputenv ("SELFAUTODIR", sdir_parent);
00571   sdir_grandparent = xdirname (sdir_parent);
00572   xputenv ("SELFAUTOPARENT", sdir_grandparent);
00573 
00574   free (sdir);
00575   free (sdir_parent);
00576   free (sdir_grandparent);
00577 
00578 #ifndef HAVE_PROGRAM_INVOCATION_NAME
00579   program_invocation_short_name = (string)xbasename (program_invocation_name);
00580 #endif
00581 
00582   if (progname) {
00583     kpse_program_name = xstrdup (progname);
00584   } else {
00585     /* If configured --enable-shared and running from the build directory
00586        with the wrapper scripts (e.g., for make check), the binaries will
00587        be named foo.exe instead of foo.  Or possibly if we're running on a
00588        DOSISH system.  */
00589     ext = find_suffix (program_invocation_short_name);
00590     if (ext && FILESTRCASEEQ (ext, "exe")) {
00591       kpse_program_name = remove_suffix (program_invocation_short_name);
00592     } else {
00593       kpse_program_name = xstrdup (program_invocation_short_name);
00594     }
00595   }
00596 
00597   xputenv("progname", kpse_program_name);
00598 }
00599 
00600 /* This function is deprecated, because when we pretend to have a different
00601    name it will look for _that_ name in the PATH if program_invocation_name
00602    is not defined.  */
00603 void
00604 kpse_set_progname P1C(const_string, argv0)
00605 {
00606   kpse_set_program_name (argv0, NULL);
00607 }
00608 
00609 #ifdef TEST
00610 void
00611 main (int argc, char **argv)
00612 {
00613   puts (remove_dots ("/w/kpathsea"));
00614   puts (remove_dots ("/w//kpathsea"));
00615   puts (remove_dots ("/w/./kpathsea"));
00616   puts (remove_dots ("."));
00617   puts (remove_dots ("./"));
00618   puts (remove_dots ("./."));
00619   puts (remove_dots ("../kpathsea"));
00620   puts (remove_dots ("/kpathsea/../foo"));
00621   puts (remove_dots ("/../w/kpathsea"));
00622   puts (remove_dots ("/../w/kpathsea/."));
00623   puts (remove_dots ("/te/share/texmf/../../../../bin/gnu"));
00624 }
00625 /*
00626 Local variables:
00627 standalone-compile-command: "gcc -g -I. -I.. -DTEST progname.c STATIC/libkpathsea.a"
00628 End:
00629 */
00630 #endif /* TEST */