summaryrefslogtreecommitdiff
path: root/Build/source/texk/kpathsea/progname.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/kpathsea/progname.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/kpathsea/progname.c')
-rw-r--r--Build/source/texk/kpathsea/progname.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/Build/source/texk/kpathsea/progname.c b/Build/source/texk/kpathsea/progname.c
new file mode 100644
index 00000000000..0c40a5e11e7
--- /dev/null
+++ b/Build/source/texk/kpathsea/progname.c
@@ -0,0 +1,633 @@
+/* progname.c: the executable name we were invoked as; general initialization.
+
+ Copyright 1998-2005 Olaf Weber.
+ Copyright 1994, 96, 97 Karl Berry.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#include <kpathsea/config.h>
+#include <kpathsea/absolute.h>
+#include <kpathsea/c-pathch.h>
+#include <kpathsea/c-stat.h>
+#include <kpathsea/pathsearch.h>
+/* For kpse_reset_progname */
+#include <kpathsea/tex-file.h>
+
+#if defined (WIN32) || defined (DJGPP)
+#include <kpathsea/c-pathmx.h>
+#endif
+
+#if defined(__i386_pc_gnu__)
+#ifndef _S_ISUID
+#define _S_ISUID 04000 /* Set user ID on execution. */
+#endif
+#ifndef _S_ISGID
+#define _S_ISGID 02000 /* Set group ID on execution. */
+#endif
+#ifndef _S_ISVTX
+#define _S_ISVTX 01000 /* Save swapped text after use (sticky). */
+#endif
+#ifndef _S_IREAD
+#define _S_IREAD 0400 /* Read by owner. */
+#endif
+#ifndef _S_IWRITE
+#define _S_IWRITE 0200 /* Write by owner. */
+#endif
+#ifndef _S_IEXEC
+#define _S_IEXEC 0100 /* Execute by owner. */
+#endif
+#endif
+
+/* NeXT does not define the standard macros, but has the equivalent.
+ WIN32 doesn't define them either, and doesn't have them.
+ From: Gregor Hoffleit <flight@mathi.uni-heidelberg.de>. */
+#ifndef S_IXUSR
+#ifdef WIN32
+#define S_IXUSR 0
+#define S_IXGRP 0
+#define S_IXOTH 0
+#else /* not WIN32 */
+#define S_IXUSR 0100
+#endif /* not WIN32 */
+#endif /* not S_IXUSR */
+#ifndef S_IXGRP
+#define S_IXGRP 0010
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001
+#endif
+
+#ifndef HAVE_PROGRAM_INVOCATION_NAME
+/* Don't redefine the variables if glibc already has. */
+string program_invocation_name = NULL;
+string program_invocation_short_name = NULL;
+#endif
+/* And the variable for the program we pretend to be. */
+string kpse_program_name = NULL;
+
+#ifndef WIN32
+/* From a standalone program `ll' to expand symlinks written by Kimbo Mundy.
+ Don't bother to compile if we don't have symlinks; thus we can assume
+ / as the separator. Also don't try to use basename, etc., or
+ handle arbitrary filename length. Mixed case function names because
+ that's what kimbo liked. */
+
+#ifdef S_ISLNK
+static int ll_verbose = 0;
+static int ll_loop = 0;
+
+#undef BSIZE
+#define BSIZE 2048 /* sorry */
+
+
+/* Read link FN into SYM. */
+
+static void
+ReadSymLink P2C(char *, fn, char *, sym)
+{
+ register int n = readlink (fn, sym, BSIZE);
+ if (n < 0) {
+ perror (fn);
+ exit (1);
+ }
+ sym[n] = 0;
+}
+
+
+/* Strip first component from S, and also return it in a static buffer. */
+
+static char *
+StripFirst P1C(register char *, s)
+{
+ static char buf[BSIZE];
+ register char *s1;
+
+ /* Find the end of the first path element */
+ for (s1 = s; *s1 && (*s1 != '/' || s1 == s); s1++)
+ ;
+
+ /* Copy it into buf and null-terminate it. */
+ strncpy (buf, s, s1 - s);
+ buf[s1 - s] = 0;
+
+ /* Skip over the leading / (if any) */
+ if (*s1 == '/')
+ ++s1;
+
+ /* Squeeze out the element */
+ while ((*s++ = *s1++) != 0)
+ ;
+
+ return buf;
+}
+
+
+/* Strip last component from S, and also return it in a static buffer. */
+
+static char *
+StripLast P1C(register char *, s)
+{
+ static char buf[BSIZE];
+ register char *s1;
+
+ for (s1 = s + strlen (s); s1 > s && *s1 != '/'; s1--)
+ ;
+ strcpy (buf, s1 + (*s1 == '/'));
+ *s1 = 0;
+
+ return buf;
+}
+
+
+/* Copy first path element from B to A, removing it from B. */
+
+static void
+CopyFirst P2C(register char *, a, char *, b)
+{
+ register int length = strlen (a);
+
+ if (length > 0 && a[length - 1] != '/') {
+ a[length] = '/';
+ a[length + 1] = 0;
+ }
+ strcat (a, StripFirst (b));
+}
+
+/* Returns NULL on error. Prints intermediate results if global
+ `ll_verbose' is nonzero. */
+
+#define EX(s) (strlen (s) && strcmp (s, "/") ? "/" : "")
+#define EXPOS EX(post)
+#define EXPRE EX(pre)
+
+static char *
+expand_symlinks P1C(char *, s)
+{
+ static char pre[BSIZE]; /* return value */
+ char post[BSIZE], sym[BSIZE], tmp[BSIZE], before[BSIZE];
+ char *cp;
+ char a;
+ struct stat st;
+ int done;
+
+ /* Check for symlink loops. It's difficult to check for all the
+ possibilities ourselves, so let the kernel do it. And make it
+ conditional so that people can see where the infinite loop is
+ being caused (see engtools#1536). */
+ if (!ll_loop) {
+ FILE *f = fopen (s, "r");
+ if (!f && errno == ELOOP) {
+ /* Not worried about other errors, we'll get to them in due course. */
+ perror (s);
+ return NULL;
+ }
+ if (f) fclose (f);
+ }
+
+ strcpy (post, s);
+ strcpy (pre, "");
+
+ while (strlen (post) != 0) {
+ CopyFirst (pre, post);
+
+ if (lstat (pre, &st) != 0) {
+ fprintf (stderr, "lstat(%s) failed ...\n", pre);
+ perror (pre);
+ return NULL;
+ }
+
+ if (S_ISLNK (st.st_mode)) {
+ ReadSymLink (pre, sym);
+
+ if (!strncmp (sym, "/", 1)) {
+ if (ll_verbose)
+ printf ("[%s]%s%s -> [%s]%s%s\n", pre, EXPOS, post, sym, EXPOS,post);
+ strcpy (pre, "");
+
+ } else {
+ a = pre[0]; /* handle links through the root */
+ strcpy (tmp, StripLast (pre));
+ if (!strlen (pre) && a == '/')
+ strcpy (pre, "/");
+
+ if (ll_verbose) {
+ sprintf (before, "%s%s[%s]%s%s", pre, EXPRE, tmp, EXPOS, post);
+ printf ("%s -> %s%s[%s]%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
+ }
+
+ /* Strip "../" path elements from the front of sym; print
+ new result if there were any such elements. */
+ done = 0;
+ a = pre[0]; /* handle links through the root */
+ while (!strncmp (sym, "..", 2)
+ && (sym[2] == 0 || sym[2] == '/')
+ && strlen (pre) != 0
+ && strcmp (pre, ".")
+ && strcmp (pre, "..")
+ && (strlen (pre) < 3
+ || strcmp (pre + strlen (pre) - 3, "/.."))) {
+ done = 1;
+ StripFirst (sym);
+ StripLast (pre);
+ }
+
+ if (done && ll_verbose) {
+ for (cp = before; *cp;)
+ *cp++ = ' ';
+ if (strlen (sym))
+ printf ("%s == %s%s%s%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
+ else
+ printf ("%s == %s%s%s\n", before, pre, EXPOS, post);
+ }
+ if (!strlen (pre) && a == '/')
+ strcpy (pre, "/");
+ }
+
+ if (strlen (post) != 0 && strlen (sym) != 0)
+ strcat (sym, "/");
+
+ strcat (sym, post);
+ strcpy (post, sym);
+ }
+ }
+
+ return pre;
+}
+#else /* not S_ISLNK */
+#define expand_symlinks(s) (s)
+#endif /* not S_ISLNK */
+
+/* Remove .'s and ..'s in DIR, to avoid problems with relative symlinks
+ as the program name, etc. This does not canonicalize symlinks. */
+
+static string
+remove_dots P1C(string, dir)
+{
+#ifdef AMIGA
+ return dir;
+#else
+ string c;
+ unsigned len;
+ string ret = (string) ""; /* We always reassign. */
+
+ for (c = kpse_filename_component (dir); c;
+ c = kpse_filename_component (NULL)) {
+ if (STREQ (c, ".")) {
+ /* If leading ., replace with cwd. Else ignore. */
+ if (*ret == 0) {
+ ret = xgetcwd ();
+ }
+
+ } else if (STREQ (c, "..")) {
+ /* If leading .., start with xdirname (cwd). Else remove last
+ component from ret, if any. */
+ if (*ret == 0) {
+ string dot = xgetcwd ();
+ ret = xdirname (dot);
+ free (dot);
+ } else {
+ unsigned last;
+ for (last = strlen (ret);
+ last > (NAME_BEGINS_WITH_DEVICE (ret) ? 2 : 0);
+ last--) {
+ if (IS_DIR_SEP (ret[last - 1])) {
+ /* If we have `/../', that's the same as `/'. */
+ if (last > 1) {
+ ret[last - 1] = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ } else {
+ /* Not . or ..; just append. Include a directory separator unless
+ our string already ends with one. This also changes all directory
+ separators into the canonical DIR_SEP_STRING. */
+ string temp;
+ len = strlen (ret);
+ temp = concat3 (ret, ((len > 0 && ret[len - 1] == DIR_SEP)
+ || (NAME_BEGINS_WITH_DEVICE (c) && *ret == 0))
+ ? "" : DIR_SEP_STRING,
+ c);
+ if (*ret)
+ free (ret);
+ ret = temp;
+ }
+ }
+
+ /* Remove a trailing /, just in case it snuck in. */
+ len = strlen (ret);
+ if (len > 0 && ret[len - 1] == DIR_SEP) {
+ ret[len - 1] = 0;
+ }
+
+ return ret;
+#endif /* not AMIGA */
+}
+
+/* Return directory ARGV0 comes from. Check PATH if ARGV0 is not
+ absolute. */
+
+static string
+selfdir P1C(const_string, argv0)
+{
+ string ret = NULL;
+ string self = NULL;
+
+ if (kpse_absolute_p (argv0, true)) {
+ self = xstrdup (argv0);
+ } else {
+#ifdef AMIGA
+#include <dos.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+ BPTR lock;
+ struct DosLibrary *DOSBase
+ = (struct DosLibrary *) OpenLibrary ("dos.library", 0L);
+ assert (DOSBase);
+
+ self = xmalloc (BUFSIZ);
+ lock = findpath (argv0);
+ if (lock != ((BPTR) -1)) {
+ if (getpath (lock, self) == -1) {
+ *self = '\0';
+ } else {
+ strcat (self,DIR_SEP_STRING);
+ strcat (self,argv0);
+ }
+ UnLock (lock);
+ }
+ CloseLibrary((struct Library *) DOSBase);
+#else /* not AMIGA */
+ string elt;
+ struct stat s;
+
+ /* Have to check PATH. But don't call kpse_path_search since we don't
+ want to search any ls-R's or do anything special with //'s. */
+ for (elt = kpse_path_element (getenv ("PATH")); !self && elt;
+ elt = kpse_path_element (NULL)) {
+ string name;
+
+ /* UNIX tradition interprets the empty path element as "." */
+ if (*elt == 0) elt = ".";
+
+ name = concat3 (elt, DIR_SEP_STRING, argv0);
+
+ /* In order to do this perfectly, we'd have to check the owner bits only
+ if we are the file owner, and the group bits only if we belong
+ to the file group. That's a lot of work, though, and it's not
+ likely that kpathsea will ever be used with a program that's
+ only executable by some classes and not others. See the
+ `file_status' function in execute_cmd.c in bash for what's
+ necessary if we were to do it right. */
+ if (stat (name, &s) == 0 && s.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
+ /* Do not stop at directories. */
+ if (!S_ISDIR(s.st_mode))
+ self = name;
+ }
+ }
+#endif /* not AMIGA */
+ }
+
+ /* If argv0 is somehow dir/exename, `self' will still be NULL. */
+ if (!self)
+ self = concat3 (".", DIR_SEP_STRING, argv0);
+
+ ret = xdirname (remove_dots (expand_symlinks (self)));
+
+ free (self);
+
+ return ret;
+}
+#endif /* not WIN32 */
+
+void
+kpse_set_program_name P2C(const_string, argv0, const_string, progname)
+{
+ string ext, sdir, sdir_parent, sdir_grandparent;
+ string s = getenv ("KPATHSEA_DEBUG");
+#ifdef WIN32
+ string debug_output = getenv("KPATHSEA_DEBUG_OUTPUT");
+ string append_debug_output = getenv("KPATHSEA_DEBUG_APPEND");
+ int err, olderr;
+#endif
+
+ /* Set debugging stuff first, in case we end up doing debuggable stuff
+ during this initialization. */
+ if (s) {
+ kpathsea_debug |= atoi (s);
+ }
+
+#ifndef HAVE_PROGRAM_INVOCATION_NAME
+#if defined(WIN32)
+ /* Set various info about user. Among many things,
+ ensure that HOME is set. If debug_paths is on,
+ turn on some message if $HOME is not found. */
+ if (KPSE_DEBUG_P(KPSE_DEBUG_PATHS)) {
+ set_home_warning();
+ }
+ init_user_info();
+
+ /* redirect stderr to debug_output. Easier to send logfiles. */
+ if (debug_output) {
+ int flags = _O_CREAT | _O_TRUNC | _O_RDWR;
+ err = -1;
+ if (_stricmp(debug_output, "con") == 0
+ || _stricmp(debug_output, "con:") == 0) {
+ err = _fileno(stdout);
+ } else {
+ if (append_debug_output) {
+ flags = _O_CREAT | _O_APPEND | _O_WRONLY;
+ } else {
+ flags = _O_CREAT | _O_TRUNC | _O_WRONLY;
+ xputenv("KPATHSEA_DEBUG_APPEND", "yes");
+ }
+ }
+
+ if ((err < 0)
+ && (err = _open(debug_output, flags, _S_IREAD | _S_IWRITE)) == -1)
+ {
+ WARNING1("Can't open %s for stderr redirection!\n", debug_output);
+ perror(debug_output);
+ } else if ((olderr = _dup(fileno(stderr))) == -1) {
+ WARNING("Can't dup() stderr!\n");
+ close(err);
+ } else if (_dup2(err, fileno(stderr)) == -1) {
+ WARNING1("Can't redirect stderr to %s!\n", debug_output);
+ close(olderr);
+ close(err);
+ } else {
+ close(err);
+ }
+ }
+ /* Win95 always gives the short filename for argv0, not the long one.
+ There is only this way to catch it. It makes all the selfdir stuff
+ useless for win32. */
+ {
+ char short_path[PATH_MAX], path[PATH_MAX], *fp;
+
+ /* SearchPath() always gives back an absolute directory */
+ if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0)
+ FATAL1("Can't determine where the executable %s is.\n", argv0);
+ if (!win32_get_long_filename(short_path, path, sizeof(path))) {
+ FATAL1("This path points to an invalid file : %s\n", short_path);
+ }
+ /* slashify the dirname */
+ for (fp = path; fp && *fp; fp++)
+ if (IS_DIR_SEP(*fp)) *fp = DIR_SEP;
+ /* sdir will be the directory of the executable, ie: c:/TeX/bin */
+ sdir = xdirname(path);
+ program_invocation_name = xstrdup(xbasename(path));
+ }
+
+#elif defined(__DJGPP__)
+
+ /* DJGPP programs support long filenames on Windows 95, but ARGV0 there
+ is always made with the short 8+3 aliases of all the pathname elements.
+ If long names are supported, we need to convert that to a long name.
+
+ All we really need is to call `_truename', but most of the code
+ below is required to deal with the special case of networked drives. */
+ if (pathconf (argv0, _PC_NAME_MAX) > 12) {
+ char long_progname[PATH_MAX];
+
+ if (_truename (argv0, long_progname)) {
+ char *fp;
+
+ if (long_progname[1] != ':') {
+ /* A complication: `_truename' returns network-specific string at
+ the beginning of `long_progname' when the program resides on a
+ networked drive, and DOS calls cannot grok such pathnames. We
+ need to convert the filesystem name back to a drive letter. */
+ char rootname[PATH_MAX], rootdir[4];
+
+ if (argv0[0] && argv0[1] == ':')
+ rootdir[0] = argv0[0]; /* explicit drive in `argv0' */
+ else
+ rootdir[0] = getdisk () + 'A';
+ rootdir[1] = ':';
+ rootdir[2] = '\\';
+ rootdir[3] = '\0';
+ if (_truename (rootdir, rootname)) {
+ /* Find out where `rootname' ends in `long_progname' and replace
+ it with the drive letter. */
+ int root_len = strlen (rootname);
+
+ if (IS_DIR_SEP (rootname[root_len - 1]))
+ root_len--; /* keep the trailing slash */
+ long_progname[0] = rootdir[0];
+ long_progname[1] = ':';
+ memmove (long_progname + 2, long_progname + root_len,
+ strlen (long_progname + root_len) + 1);
+ }
+ }
+
+ /* Convert everything to canonical form. */
+ if (long_progname[0] >= 'A' && long_progname[0] <= 'Z')
+ long_progname[0] += 'a' - 'A'; /* make drive lower case, for beauty */
+ for (fp = long_progname; *fp; fp++)
+ if (IS_DIR_SEP (*fp))
+ *fp = DIR_SEP;
+
+ program_invocation_name = xstrdup (long_progname);
+ }
+ else
+ /* If `_truename' failed, God help them, because we won't... */
+ program_invocation_name = xstrdup (argv0);
+ }
+ else
+ program_invocation_name = xstrdup (argv0);
+
+#else /* !WIN32 && !__DJGPP__ */
+
+ program_invocation_name = xstrdup (argv0);
+
+#endif
+#endif /* not HAVE_PROGRAM_INVOCATION_NAME */
+
+ /* We need to find SELFAUTOLOC *before* removing the ".exe" suffix from
+ the program_name, otherwise the PATH search inside selfdir will fail,
+ since `prog' doesn't exists as a file, there's `prog.exe' instead. */
+#ifndef WIN32
+ sdir = selfdir (program_invocation_name);
+#endif
+ /* SELFAUTODIR is actually the parent of the invocation directory,
+ and SELFAUTOPARENT the grandparent. This is how teTeX did it. */
+ xputenv ("SELFAUTOLOC", sdir);
+ sdir_parent = xdirname (sdir);
+ xputenv ("SELFAUTODIR", sdir_parent);
+ sdir_grandparent = xdirname (sdir_parent);
+ xputenv ("SELFAUTOPARENT", sdir_grandparent);
+
+ free (sdir);
+ free (sdir_parent);
+ free (sdir_grandparent);
+
+#ifndef HAVE_PROGRAM_INVOCATION_NAME
+ program_invocation_short_name = (string)xbasename (program_invocation_name);
+#endif
+
+ if (progname) {
+ kpse_program_name = xstrdup (progname);
+ } else {
+ /* If configured --enable-shared and running from the build directory
+ with the wrapper scripts (e.g., for make check), the binaries will
+ be named foo.exe instead of foo. Or possibly if we're running on a
+ DOSISH system. */
+ ext = find_suffix (program_invocation_short_name);
+ if (ext && FILESTRCASEEQ (ext, "exe")) {
+ kpse_program_name = remove_suffix (program_invocation_short_name);
+ } else {
+ kpse_program_name = xstrdup (program_invocation_short_name);
+ }
+ }
+
+ xputenv("progname", kpse_program_name);
+}
+
+/* This function is deprecated, because when we pretend to have a different
+ name it will look for _that_ name in the PATH if program_invocation_name
+ is not defined. */
+void
+kpse_set_progname P1C(const_string, argv0)
+{
+ kpse_set_program_name (argv0, NULL);
+}
+
+#ifdef TEST
+void
+main (int argc, char **argv)
+{
+ puts (remove_dots ("/w/kpathsea"));
+ puts (remove_dots ("/w//kpathsea"));
+ puts (remove_dots ("/w/./kpathsea"));
+ puts (remove_dots ("."));
+ puts (remove_dots ("./"));
+ puts (remove_dots ("./."));
+ puts (remove_dots ("../kpathsea"));
+ puts (remove_dots ("/kpathsea/../foo"));
+ puts (remove_dots ("/../w/kpathsea"));
+ puts (remove_dots ("/../w/kpathsea/."));
+ puts (remove_dots ("/te/share/texmf/../../../../bin/gnu"));
+}
+/*
+Local variables:
+standalone-compile-command: "gcc -g -I. -I.. -DTEST progname.c STATIC/libkpathsea.a"
+End:
+*/
+#endif /* TEST */