summaryrefslogtreecommitdiff
path: root/Build/source/texk/kpathsea/progname.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/kpathsea/progname.c')
-rw-r--r--Build/source/texk/kpathsea/progname.c793
1 files changed, 793 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..a984eabc471
--- /dev/null
+++ b/Build/source/texk/kpathsea/progname.c
@@ -0,0 +1,793 @@
+/* progname.c: the executable name we were invoked as; general initialization.
+
+ Copyright 1994, 1996, 1997, 2008-2013, 2016-2019 Karl Berry.
+ Copyright 1998-2005 Olaf Weber.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#include <kpathsea/config.h>
+#include <kpathsea/absolute.h>
+#include <kpathsea/c-pathch.h>
+#include <kpathsea/c-pathmx.h>
+#include <kpathsea/c-stat.h>
+#include <kpathsea/pathsearch.h>
+/* For kpse_reset_progname */
+#include <kpathsea/tex-file.h>
+
+
+#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 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
+
+#undef BSIZE
+#define BSIZE 2048 /* sorry */
+
+
+/* Read link FN into SYM. */
+
+static void
+ReadSymLink (char *fn, char *sym)
+{
+ 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 (char *s)
+{
+ static char buf[BSIZE];
+ 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 (char *s)
+{
+ static char buf[BSIZE];
+ 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 (char *a, char *b)
+{
+ int length = strlen (a);
+
+ if (length > 0 && a[length - 1] != '/') {
+ a[length] = '/';
+ a[length + 1] = 0;
+ }
+ strcat (a, StripFirst (b));
+}
+
+/* Returns NULL on error, such as an unresolvable symlink. Prints
+ intermediate results if global `ll_verbose' is nonzero. Otherwise,
+ returns a pointer to a static buffer (sorry). */
+
+#define EMPTY_STRING(s) (*(s) == 0)
+#define EX(s) (!EMPTY_STRING (s) && strcmp (s, "/") ? "/" : "")
+#define EXPOS EX(post)
+#define EXPRE EX(pre)
+
+static char *
+expand_symlinks (kpathsea kpse, 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;
+
+ strcpy (post, s);
+ strcpy (pre, "");
+
+ while (!EMPTY_STRING (post)) {
+ CopyFirst (pre, post);
+
+ if (lstat (pre, &st) != 0) {
+ fprintf (stderr, "lstat(%s) failed: ", pre);
+ perror (pre);
+ return NULL;
+ }
+
+ if (S_ISLNK (st.st_mode)) {
+ ReadSymLink (pre, sym);
+
+ if (!strncmp (sym, "/", 1)) {
+ if (kpse->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 (EMPTY_STRING (pre) && a == '/')
+ strcpy (pre, "/");
+
+ if (kpse->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] == '/')
+ && !EMPTY_STRING (pre)
+ && strcmp (pre, ".")
+ && strcmp (pre, "..")
+ && (strlen (pre) < 3
+ || strcmp (pre + strlen (pre) - 3, "/.."))) {
+ done = 1;
+ StripFirst (sym);
+ StripLast (pre);
+ }
+
+ if (done && kpse->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 (EMPTY_STRING (pre) && a == '/')
+ strcpy (pre, "/");
+ }
+
+ if (!EMPTY_STRING (post) && !EMPTY_STRING (sym))
+ strcat (sym, "/");
+
+ strcat (sym, post);
+ strcpy (post, sym);
+ }
+ }
+
+ return pre;
+}
+#else /* not S_ISLNK */
+#define expand_symlinks(k,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 (kpathsea kpse, string dir)
+{
+#ifdef AMIGA
+ return dir;
+#else
+ string c;
+ unsigned len;
+ string ret = NULL;
+
+ for (c = kpathsea_filename_component (kpse, dir); c;
+ c = kpathsea_filename_component (kpse, NULL)) {
+ if (STREQ (c, ".")) {
+ /* If leading ., replace with cwd. Else ignore. */
+ if (!ret) {
+ ret = xgetcwd ();
+ }
+
+ } else if (STREQ (c, "..")) {
+ /* If leading .., start with xdirname (cwd). Else remove last
+ component from ret, if any. */
+ if (!ret) {
+ string dot = xgetcwd ();
+ ret = xdirname (dot);
+ free (dot);
+ } else {
+ unsigned last;
+ string p = NAME_BEGINS_WITH_DEVICE (ret) ? ret + 2 : ret;
+ for (last = strlen (p); last > 0; last--) {
+ if (IS_DIR_SEP_CH (p[last - 1])) {
+ /* If we have `/../', that's the same as `/'. */
+ p[(last > 1 ? last - 1 : 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. */
+ if (!ret) {
+ ret = concat (NAME_BEGINS_WITH_DEVICE (c) ? "" : DIR_SEP_STRING, c);
+ } else {
+ string temp = ret;
+ len = strlen (ret);
+ ret = concat3 (ret, ret[len - 1] == DIR_SEP ? "" : DIR_SEP_STRING, c);
+ free (temp);
+ }
+ }
+ }
+ assert (ret);
+
+ /* 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. If ARGV0 cannot be found (e.g., --progname=nonesuch), quit. */
+
+string
+kpathsea_selfdir (kpathsea kpse, const_string argv0)
+{
+ string name;
+ string ret;
+ string self = NULL;
+
+ if (kpathsea_absolute_p (kpse, 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 */
+ const_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 = kpathsea_path_element (kpse, getenv ("PATH")); !self && elt;
+ elt = kpathsea_path_element (kpse, NULL)) {
+ /* 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. */
+ && !S_ISDIR(s.st_mode))
+ self = name;
+ else
+ free (name);
+ }
+#endif /* not AMIGA */
+ }
+
+ /* If argv0 is somehow dir/exename, `self' will still be NULL. */
+ if (!self)
+ self = concat3 (".", DIR_SEP_STRING, argv0);
+
+ /* If we can't expand symlinks (--progname=nonesuch), give up. */
+ name = expand_symlinks (kpse, self);
+ if (!name) {
+ fprintf (stderr, "kpathsea: Can't get directory of program name: %s\n",
+ self);
+ exit (1);
+ }
+
+ /* If we have something real, we can resolve ./ and ../ elements. */
+ name = remove_dots (kpse, name);
+
+#ifndef AMIGA
+ free (self);
+#endif
+
+ ret = xdirname (name);
+ free (name);
+ return ret;
+}
+
+#if defined (KPSE_COMPAT_API)
+string
+kpse_selfdir (const_string argv0)
+{
+ return kpathsea_selfdir (kpse_def, argv0);
+}
+#endif
+
+#endif /* not WIN32 */
+
+#if defined(WIN32) || defined(__CYGWIN__)
+
+/* Create a list of executable suffixes of files not to be written. */
+#define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw"
+
+static void
+mk_suffixlist (kpathsea kpse)
+{
+ char **p;
+ char *q, *r, *v;
+ int n;
+
+#if defined(__CYGWIN__)
+ v = xstrdup (EXE_SUFFIXES);
+#else
+ v = getenv ("PATHEXT");
+ if (v) /* strlwr() exists also in MingW */
+ v = strlwr (xstrdup (v));
+ else
+ v = xstrdup (EXE_SUFFIXES);
+#endif
+
+ q = v;
+ n = 0;
+
+ while ((r = strchr (q, ';')) != NULL) {
+ n++;
+ r++;
+ q = r;
+ }
+ if (*q)
+ n++;
+ kpse->suffixlist = (char **) xmalloc ((n + 2) * sizeof (char *));
+ p = kpse->suffixlist;
+ *p = xstrdup (".dll");
+ p++;
+ q = v;
+ while ((r = strchr (q, ';')) != NULL) {
+ *r = '\0';
+ *p = xstrdup (q);
+ p++;
+ r++;
+ q = r;
+ }
+ if (*q) {
+ *p = xstrdup (q);
+ p++;
+ }
+ *p = NULL;
+ free (v);
+}
+#endif /* WIN32 || __CYGWIN__ */
+
+/* On win32 SELFAUTO{LOC,DIR,PARENT} must not be just `/', otherwise,
+ e.g., $SELFAUTODIR/texmf/tex would be mistaken as UNC name. */
+static inline string
+fix_selfdir (string dir)
+{
+#if defined(WIN32)
+ if (IS_DIR_SEP_CH (*dir) && dir[1] == 0)
+ dir++;
+#endif
+ return dir;
+}
+
+void
+kpathsea_set_program_name (kpathsea kpse, const_string argv0,
+ const_string progname)
+{
+ const_string ext;
+ string sdir, sdir_parent, sdir_grandparent, sdir_greatgrandparent;
+ 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, cp;
+#endif
+
+ /* Set debugging stuff first, in case we end up doing debuggable stuff
+ during this initialization. */
+ if (s) {
+ kpse->debug |= atoi (s);
+ }
+
+#if defined(WIN32)
+ cp = AreFileApisANSI() ? GetACP() : GetOEMCP();
+ if (!kpse->File_system_codepage)
+ kpse->File_system_codepage = cp;
+ kpse->Win32_codepage = cp;
+ if (cp == 932 || cp == 936 || cp == 950) {
+ kpse->Is_cp932_system = cp;
+ }
+ else
+ kpse->Is_cp932_system = 0;
+
+#if defined(__MINGW32__)
+ /* Set various info about user. Among many things,
+ ensure that HOME is set. */
+ init_user_info();
+#else /* !__MINGW32__ */
+ kpse->the_passwd.pw_name = kpse->the_passwd_name;
+ kpse->the_passwd.pw_passwd = kpse->the_passwd_passwd;
+ kpse->the_passwd.pw_uid = 0;
+ kpse->the_passwd.pw_gid = 0;
+ kpse->the_passwd.pw_quota = 0;
+ kpse->the_passwd.pw_gecos = kpse->the_passwd_gecos;
+ kpse->the_passwd.pw_dir = kpse->the_passwd_dir;
+ kpse->the_passwd.pw_shell = kpse->the_passwd_shell;
+ kpse->__system_allow_multiple_cmds = 0;
+
+ /* Set various info about user. Among many things,
+ ensure that HOME is set. */
+ kpathsea_init_user_info(kpse);
+#endif /* !__MINGW32__ */
+
+ /* 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;
+ kpathsea_xputenv(kpse, "KPATHSEA_DEBUG_APPEND", "yes");
+ }
+ }
+
+ if ((err < 0)
+ && (err = _open(debug_output, flags, _S_IREAD | _S_IWRITE)) == -1)
+ {
+ WARNING1 ("kpathsea: Can't open %s for stderr redirection!\n",
+ debug_output);
+ perror (debug_output);
+ } else if ((olderr = _dup(fileno(stderr))) == -1) {
+ WARNING ("kpathsea: Can't dup() stderr!\n");
+ close (err);
+ } else if (_dup2(err, fileno(stderr)) == -1) {
+ WARNING1 ("kpathsea: 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 kpse_selfdir
+ stuff useless for win32. */
+ {
+ char short_path[PATH_MAX], path[PATH_MAX], *fp;
+#if !defined(__MINGW32__)
+ HANDLE hnd;
+ WIN32_FIND_DATA ffd;
+#endif
+
+ /* SearchPath() always gives back an absolute directory */
+ if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0)
+ LIB_FATAL1("Can't determine where the executable %s is.\n", argv0);
+#if defined(__MINGW32__)
+ if (!win32_get_long_filename(short_path, path, sizeof(path))) {
+ LIB_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;
+#else /* !__MINGW32__ */
+ if (kpathsea_getlongpath(kpse, path, short_path, PATH_MAX) == 0)
+ FATAL1("Can't get long name for %s.\n", short_path);
+ if ((hnd = FindFirstFile(short_path, &ffd)) == INVALID_HANDLE_VALUE)
+ FATAL1("The following path points to an invalid file : %s\n", path);
+ FindClose(hnd);
+ /* dirname aleady slashified in WIN32 */
+#endif /* !__MINGW32__ */
+ /* sdir will be the directory of the executable, ie: c:/TeX/bin */
+ sdir = xdirname(path);
+ kpse->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;
+
+ kpse->invocation_name = xstrdup (long_progname);
+ }
+ else
+ /* If `_truename' failed, God help them, because we won't... */
+ kpse->invocation_name = xstrdup (argv0);
+ }
+ else
+ kpse->invocation_name = xstrdup (argv0);
+
+#else /* !WIN32 && !__DJGPP__ */
+ kpse->invocation_name = xstrdup (argv0);
+#endif
+
+ /* We need to find SELFAUTOLOC *before* removing the ".exe" suffix from
+ the program_name, otherwise the PATH search inside kpse_selfdir will fail,
+ since `prog' doesn't exists as a file, there's `prog.exe' instead. */
+#ifndef WIN32
+ sdir = kpathsea_selfdir (kpse, kpse->invocation_name);
+#endif
+ /* SELFAUTODIR is actually the parent of the invocation directory,
+ and SELFAUTOPARENT the grandparent. This is how teTeX did it. */
+ kpathsea_xputenv (kpse, "SELFAUTOLOC", fix_selfdir (sdir));
+ sdir_parent = xdirname (sdir);
+ kpathsea_xputenv (kpse, "SELFAUTODIR", fix_selfdir (sdir_parent));
+ sdir_grandparent = xdirname (sdir_parent);
+ kpathsea_xputenv (kpse, "SELFAUTOPARENT", fix_selfdir (sdir_grandparent));
+ sdir_greatgrandparent = xdirname (sdir_grandparent);
+ kpathsea_xputenv (kpse, "SELFAUTOGRANDPARENT", fix_selfdir (sdir_greatgrandparent));
+
+#if defined(WIN32) || defined(__CYGWIN__)
+ mk_suffixlist(kpse);
+#endif /* WIN32 || __CYGWIN__ */
+
+ free (sdir);
+ free (sdir_parent);
+ free (sdir_grandparent);
+ free (sdir_greatgrandparent);
+
+ kpse->invocation_short_name
+ = xstrdup (xbasename (kpse->invocation_name));
+
+ 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 (kpse->invocation_short_name);
+ if (ext && FILESTRCASEEQ (ext, "exe")) {
+ kpse->program_name = remove_suffix (kpse->invocation_short_name);
+ } else {
+ kpse->program_name = xstrdup (kpse->invocation_short_name);
+ }
+ }
+
+ /* Runtime check that snprintf always writes a trailing NUL byte. */
+ {
+ char buf[4] = "old";
+ assert (snprintf (buf, 2, "a") == 1 && buf[1] == '\0');
+ assert ((unsigned)snprintf (buf, 2, "ab") >= 2 && buf[1] == '\0');
+ assert ((unsigned)snprintf (buf, 2, "abc") >= 2 && buf[1] == '\0');
+ }
+ /* Some of the utility routines (like atou() and xfopen()) will use
+ FATAL and variations thereof (see lib.h) if there is a problem.
+
+ The next trick makes it possible for that message to report some useful
+ name instead of (NULL), if the backward compatible is compiled in. */
+
+#if defined (KPSE_COMPAT_API)
+ if (kpse!=kpse_def) {
+ kpse_def->invocation_name = xstrdup(kpse->invocation_name);
+ kpse_def->invocation_short_name = xstrdup(kpse->invocation_short_name);
+ }
+#endif
+
+ kpathsea_xputenv (kpse, "progname", kpse->program_name);
+}
+
+
+#if defined (KPSE_COMPAT_API)
+void
+kpse_set_program_name (const_string argv0, const_string progname)
+{
+ kpathsea_set_program_name (kpse_def, argv0, progname);
+}
+#endif
+
+/* Returns ARGV0 with any leading path and on some systems the suffix
+ for executables stripped off. This returns a new string.
+ For example, `kpse_program_basename ("/foo/bar.EXE")' returns "bar"
+ on WIndows or Cygwin and "bar.EXE" otherwise. */
+
+string
+kpse_program_basename (const_string argv0)
+{
+ string base = xstrdup (xbasename (argv0));
+#ifdef EXEEXT
+ string dot = strrchr (base, '.');
+ if (dot && FILESTRCASEEQ (dot, EXEEXT))
+ *dot = 0;
+#endif
+ return base;
+}
+
+
+#ifdef TEST
+static const char *tab[] = {
+/* 'normal' names */
+ "/w/kpathsea",
+ "/w//kpathsea",
+ "/w/./kpathsea",
+ ".",
+ "./",
+ "./.",
+ "../kpathsea",
+ "/kpathsea/../foo",
+ "/../w/kpathsea",
+ "/../w/kpathsea/.",
+ "/te/share/texmf/../../../../bin/gnu",
+ NULL
+};
+
+int
+main (int argc, char **argv)
+{
+ const char **p;
+ kpathsea kpse = xcalloc(1, sizeof(kpathsea_instance));
+
+ kpathsea_set_program_name(kpse, argv[0], NULL);
+
+#if defined(WIN32)
+ printf("\n%s: Nothing to do for WIN32\n",
+ kpse->invocation_short_name);
+#else
+ printf("\n%s: name -> remove_dots(name)\n\n",
+ kpse->invocation_short_name);
+
+ for (p = tab; *p; p++) {
+ char *q = xstrdup(*p);
+ char *s = remove_dots(kpse, q);
+
+ printf("%s -> %s\n", q, s);
+ free (q);
+ free (s);
+ }
+#endif
+
+ return 0;
+}
+#endif /* TEST */