OSDN Git Service

branch: yandytex with kpathsea.
[putex/putex.git] / src / texsourc / kpathsea / kpathsea / progname.c
1 /* progname.c: the executable name we were invoked as; general initialization.
2
3    Copyright 1994, 1996, 1997, 2008-2013 Karl Berry.
4    Copyright 1998-2005 Olaf Weber.
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public License
17    along with this library; if not, see <http://www.gnu.org/licenses/>.  */
18
19 #include <kpathsea/config.h>
20 #include <kpathsea/absolute.h>
21 #include <kpathsea/c-pathch.h>
22 #include <kpathsea/c-pathmx.h>
23 #include <kpathsea/c-stat.h>
24 #include <kpathsea/pathsearch.h>
25 /* For kpse_reset_progname */
26 #include <kpathsea/tex-file.h>
27
28
29 #if defined(__i386_pc_gnu__)
30 #ifndef _S_ISUID
31 #define _S_ISUID    04000  /* Set user ID on execution.  */
32 #endif
33 #ifndef _S_ISGID
34 #define _S_ISGID    02000  /* Set group ID on execution.  */
35 #endif
36 #ifndef _S_ISVTX
37 #define _S_ISVTX    01000  /* Save swapped text after use (sticky).  */
38 #endif
39 #ifndef _S_IREAD
40 #define _S_IREAD    0400   /* Read by owner.  */
41 #endif
42 #ifndef _S_IWRITE
43 #define _S_IWRITE   0200   /* Write by owner.  */
44 #endif
45 #ifndef _S_IEXEC
46 #define _S_IEXEC    0100   /* Execute by owner.  */
47 #endif
48 #endif
49
50 /* NeXT does not define the standard macros, but has the equivalent.
51    WIN32 doesn't define them either, and doesn't have them.
52    From: Gregor Hoffleit <flight@mathi.uni-heidelberg.de>.  */
53 #ifndef S_IXUSR
54 #ifdef WIN32
55 #define S_IXUSR 0
56 #define S_IXGRP 0
57 #define S_IXOTH 0
58 #else /* not WIN32 */
59 #define S_IXUSR 0100
60 #endif /* not WIN32 */
61 #endif /* not S_IXUSR */
62 #ifndef S_IXGRP
63 #define S_IXGRP 0010
64 #endif
65 #ifndef S_IXOTH
66 #define S_IXOTH 0001
67 #endif
68
69 \f
70 #ifndef WIN32
71 /* From a standalone program `ll' to expand symlinks written by Kimbo Mundy.
72    Don't bother to compile if we don't have symlinks; thus we can assume
73    / as the separator.  Also don't try to use basename, etc., or
74    handle arbitrary filename length.  Mixed case function names because
75    that's what kimbo liked.  */
76
77 #ifdef S_ISLNK
78
79 #undef BSIZE
80 #define BSIZE 2048 /* sorry */
81
82
83 /* Read link FN into SYM.  */
84
85 static void
86 ReadSymLink (char *fn, char *sym)
87 {
88   register int n = readlink (fn, sym, BSIZE);
89   if (n < 0) {
90     perror (fn);
91     exit (1);
92   }
93   sym[n] = 0;
94 }
95
96
97 /* Strip first component from S, and also return it in a static buffer.  */
98
99 static char *
100 StripFirst (register char *s)
101 {
102   static char buf[BSIZE];
103   register char *s1;
104
105   /* Find the end of the first path element */
106   for (s1 = s; *s1 && (*s1 != '/' || s1 == s); s1++)
107     ;
108
109   /* Copy it into buf and null-terminate it. */
110   strncpy (buf, s, s1 - s);
111   buf[s1 - s] = 0;
112
113   /* Skip over the leading / (if any) */
114   if (*s1 == '/')
115     ++s1;
116
117   /* Squeeze out the element */
118   while ((*s++ = *s1++) != 0)
119     ;
120
121   return buf;
122 }
123
124
125 /* Strip last component from S, and also return it in a static buffer.  */
126
127 static char *
128 StripLast (register char *s)
129 {
130   static char buf[BSIZE];
131   register char *s1;
132
133   for (s1 = s + strlen (s); s1 > s && *s1 != '/'; s1--)
134     ;
135   strcpy (buf, s1 + (*s1 == '/'));
136   *s1 = 0;
137
138   return buf;
139 }
140
141
142 /* Copy first path element from B to A, removing it from B.  */
143
144 static void
145 CopyFirst (register char *a, char *b)
146 {
147   register int length = strlen (a);
148
149    if (length > 0 && a[length - 1] != '/') {
150    a[length] = '/';
151     a[length + 1] = 0;
152   }
153   strcat (a, StripFirst (b));
154 }
155
156 /* Returns NULL on error.  Prints intermediate results if global
157    `ll_verbose' is nonzero.  */
158
159 #define EX(s)           (strlen (s) && strcmp (s, "/") ? "/" : "")
160 #define EXPOS           EX(post)
161 #define EXPRE           EX(pre)
162
163 static char *
164 expand_symlinks (kpathsea kpse, char *s)
165 {
166   static char pre[BSIZE];       /* return value */
167   char post[BSIZE], sym[BSIZE], tmp[BSIZE], before[BSIZE];
168   char *cp;
169   char a;
170   struct stat st;
171   int done;
172
173   /* Check for symlink loops.  It's difficult to check for all the
174      possibilities ourselves, so let the kernel do it.  And make it
175      conditional so that people can see where the infinite loop is
176      being caused (see engtools#1536).  */
177   /* There used to be a test for a variable |ll_loop| here, but
178      it was initialized to zero and never updated */
179   if (0) {
180     FILE *f = fopen (s, "r");
181     if (!f && errno == ELOOP) {
182       /* Not worried about other errors, we'll get to them in due course.  */
183       perror (s);
184       return NULL;
185     }
186     if (f) fclose (f);
187   }
188
189   strcpy (post, s);
190   strcpy (pre, "");
191
192   while (strlen (post) != 0) {
193     CopyFirst (pre, post);
194
195     if (lstat (pre, &st) != 0) {
196       fprintf (stderr, "lstat(%s) failed ...\n", pre);
197       perror (pre);
198       return NULL;
199     }
200
201     if (S_ISLNK (st.st_mode)) {
202       ReadSymLink (pre, sym);
203
204       if (!strncmp (sym, "/", 1)) {
205         if (kpse->ll_verbose)
206           printf ("[%s]%s%s -> [%s]%s%s\n", pre, EXPOS, post, sym, EXPOS,post);
207         strcpy (pre, "");
208
209       } else {
210         a = pre[0];     /* handle links through the root */
211         strcpy (tmp, StripLast (pre));
212         if (!strlen (pre) && a == '/')
213           strcpy (pre, "/");
214
215         if (kpse->ll_verbose) {
216           sprintf (before, "%s%s[%s]%s%s", pre, EXPRE, tmp, EXPOS, post);
217           printf ("%s -> %s%s[%s]%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
218         }
219
220         /* Strip "../" path elements from the front of sym; print
221            new result if there were any such elements.  */
222         done = 0;
223         a = pre[0];     /* handle links through the root */
224         while (!strncmp (sym, "..", 2)
225                && (sym[2] == 0 || sym[2] == '/')
226                && strlen (pre) != 0
227                && strcmp (pre, ".")
228                && strcmp (pre, "..")
229                && (strlen (pre) < 3
230                    || strcmp (pre + strlen (pre) - 3, "/.."))) {
231           done = 1;
232           StripFirst (sym);
233           StripLast (pre);
234         }
235
236         if (done && kpse->ll_verbose) {
237           for (cp = before; *cp;)
238             *cp++ = ' ';
239           if (strlen (sym))
240             printf ("%s == %s%s%s%s%s\n", before, pre, EXPRE, sym, EXPOS,post);
241           else
242             printf ("%s == %s%s%s\n", before, pre, EXPOS, post);
243         }
244         if (!strlen (pre) && a == '/')
245           strcpy (pre, "/");
246       }
247
248       if (strlen (post) != 0 && strlen (sym) != 0)
249         strcat (sym, "/");
250
251       strcat (sym, post);
252       strcpy (post, sym);
253     }
254   }
255
256   return pre;
257 }
258 #else /* not S_ISLNK */
259 #define expand_symlinks(k,s) (s)
260 #endif /* not S_ISLNK */
261 \f
262 /* Remove .'s and ..'s in DIR, to avoid problems with relative symlinks
263    as the program name, etc.  This does not canonicalize symlinks.  */
264
265 static string
266 remove_dots (kpathsea kpse, string dir)
267 {
268 #ifdef AMIGA
269   return dir;
270 #else
271   string c;
272   unsigned len;
273   string ret = NULL;
274
275   for (c = kpathsea_filename_component (kpse, dir); c;
276        c = kpathsea_filename_component (kpse, NULL)) {
277     if (STREQ (c, ".")) {
278       /* If leading ., replace with cwd.  Else ignore.  */
279       if (!ret) {
280         ret = xgetcwd ();
281       }
282
283     } else if (STREQ (c, "..")) {
284       /* If leading .., start with xdirname (cwd).  Else remove last
285          component from ret, if any.  */
286       if (!ret) {
287         string dot = xgetcwd ();
288         ret = xdirname (dot);
289         free (dot);
290       } else {
291         unsigned last;
292         string p = NAME_BEGINS_WITH_DEVICE (ret) ? ret + 2 : ret;
293         for (last = strlen (p); last > 0; last--) {
294           if (IS_DIR_SEP_CH (p[last - 1])) {
295             /* If we have `/../', that's the same as `/'.  */
296             p[(last > 1 ? last - 1 : 1)] = 0;
297             break;
298           }
299         }
300       }
301
302     } else {
303       /* Not . or ..; just append.  Include a directory separator unless
304          our string already ends with one.  This also changes all directory
305          separators into the canonical DIR_SEP_STRING.  */
306       if (!ret) {
307         ret = concat (NAME_BEGINS_WITH_DEVICE (c) ? "" : DIR_SEP_STRING, c);
308       } else {
309         string temp = ret;
310         len = strlen (ret);
311         ret = concat3 (ret, ret[len - 1] == DIR_SEP ? "" : DIR_SEP_STRING, c);
312         free (temp);
313       }
314     }
315   }
316   assert (ret);
317
318   /* Remove a trailing /, just in case it snuck in.  */
319   len = strlen (ret);
320   if (len > 0 && ret[len - 1] == DIR_SEP) {
321     ret[len - 1] = 0;
322   }
323
324   return ret;
325 #endif /* not AMIGA */
326 }
327 \f
328 /* Return directory ARGV0 comes from.  Check PATH if ARGV0 is not
329    absolute.  */
330
331 string
332 kpathsea_selfdir (kpathsea kpse, const_string argv0)
333 {
334   string self = NULL;
335   string name;
336   string ret;
337
338   if (kpathsea_absolute_p (kpse, argv0, true)) {
339     self = xstrdup (argv0);
340   } else {
341 #ifdef AMIGA
342 #include <dos.h>
343 #include <proto/dos.h>
344 #include <proto/exec.h>
345     BPTR lock;
346     struct DosLibrary *DOSBase
347       = (struct DosLibrary *) OpenLibrary ("dos.library", 0L);
348     assert (DOSBase);
349
350     self = xmalloc (BUFSIZ);
351     lock = findpath (argv0);
352     if (lock != ((BPTR) -1)) {
353       if (getpath (lock, self) == -1) {
354         *self = '\0';
355       } else {
356         strcat (self,DIR_SEP_STRING);
357         strcat (self,argv0);
358       }
359       UnLock (lock);
360     }
361     CloseLibrary((struct Library *) DOSBase);
362 #else /* not AMIGA */
363     const_string elt;
364     struct stat s;
365
366     /* Have to check PATH.  But don't call kpse_path_search since we don't
367        want to search any ls-R's or do anything special with //'s.  */
368     for (elt = kpathsea_path_element (kpse, getenv ("PATH")); !self && elt;
369          elt = kpathsea_path_element (kpse, NULL)) {
370       /* UNIX tradition interprets the empty path element as "." */
371       if (*elt == 0) elt = ".";
372
373       name = concat3 (elt, DIR_SEP_STRING, argv0);
374
375       /* In order to do this perfectly, we'd have to check the owner bits only
376          if we are the file owner, and the group bits only if we belong
377          to the file group.  That's a lot of work, though, and it's not
378          likely that kpathsea will ever be used with a program that's
379          only executable by some classes and not others.  See the
380          `file_status' function in execute_cmd.c in bash for what's
381          necessary if we were to do it right.  */
382       if (stat (name, &s) == 0 && s.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)
383                                /* Do not stop at directories. */
384                                && !S_ISDIR(s.st_mode))
385         self = name;
386       else
387         free (name);
388     }
389 #endif /* not AMIGA */
390   }
391
392   /* If argv0 is somehow dir/exename, `self' will still be NULL.  */
393   if (!self)
394     self = concat3 (".", DIR_SEP_STRING, argv0);
395
396   name = remove_dots (kpse, expand_symlinks (kpse, self));
397
398 #ifndef AMIGA
399   free (self);
400 #endif
401
402   ret = xdirname (name);
403
404   free (name);
405
406   return ret;
407 }
408
409 #if defined (KPSE_COMPAT_API)
410 string
411 kpse_selfdir (const_string argv0)
412 {
413     return kpathsea_selfdir (kpse_def, argv0);
414 }
415 #endif
416
417 #endif /* not WIN32 */
418 \f
419 #if defined(WIN32) || defined(__CYGWIN__)
420
421 /* Create a list of executable suffixes of files not to be written.  */
422 #define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw"
423
424 static void
425 mk_suffixlist (kpathsea kpse)
426 {
427     char **p;
428     char *q, *r, *v;
429     int  n;
430
431 #if defined(__CYGWIN__)
432     v = xstrdup (EXE_SUFFIXES);
433 #else
434     v = getenv ("PATHEXT");
435     if (v) /* strlwr() exists also in MingW */
436       v = strlwr (xstrdup (v));
437     else
438       v = xstrdup (EXE_SUFFIXES);
439 #endif
440
441     q = v;
442     n = 0;
443
444     while ((r = strchr (q, ';')) != NULL) {
445       n++;
446       r++;
447       q = r;
448     }
449     if (*q)
450       n++;
451     kpse->suffixlist = (char **) xmalloc ((n + 2) * sizeof (char *));
452     p = kpse->suffixlist;
453     *p = xstrdup (".dll");
454     p++;
455     q = v;
456     while ((r = strchr (q, ';')) != NULL) {
457       *r = '\0';
458       *p = xstrdup (q);
459       p++;
460       r++;
461       q = r;
462     }
463     if (*q) {
464       *p = xstrdup (q);
465       p++;
466     }
467     *p = NULL;
468     free (v);
469 }
470 #endif /* WIN32 || __CYGWIN__ */
471
472 /* On win32 SELFAUTO{LOC,DIR,PARENT} must not be just `/', otherwise,
473    e.g., $SELFAUTODIR/texmf/tex would be mistaken as UNC name.  */
474 static inline string
475 fix_selfdir (string dir)
476 {
477 #if defined(WIN32)
478   if (IS_DIR_SEP_CH (*dir) && dir[1] == 0)
479     dir++;
480 #endif
481   return dir;
482 }
483
484 void
485 kpathsea_set_program_name (kpathsea kpse,  const_string argv0,
486                            const_string progname)
487 {
488   const_string ext;
489   string sdir, sdir_parent, sdir_grandparent, sdir_greatgrandparent;
490   string s = getenv ("KPATHSEA_DEBUG");
491 #ifdef WIN32
492   string debug_output = getenv("KPATHSEA_DEBUG_OUTPUT");
493   string append_debug_output = getenv("KPATHSEA_DEBUG_APPEND");
494   int err, olderr, cp;
495 #endif
496
497   /* Set debugging stuff first, in case we end up doing debuggable stuff
498      during this initialization.  */
499   if (s) {
500     kpse->debug |= atoi (s);
501   }
502
503 #if defined(WIN32)
504   if (!file_system_codepage)
505     file_system_codepage = AreFileApisANSI() ? GetACP() : GetOEMCP();
506   cp = file_system_codepage;
507   if (cp == 932 || cp == 936 || cp == 950) {
508     is_cp932_system = cp;
509   }
510   else
511     is_cp932_system = 0;
512
513 #if defined(__MINGW32__)
514   /* Set various info about user. Among many things,
515      ensure that HOME is set.  */
516   init_user_info();
517 #else /* !__MINGW32__ */
518   kpse->the_passwd.pw_name = kpse->the_passwd_name;
519   kpse->the_passwd.pw_passwd = kpse->the_passwd_passwd;
520   kpse->the_passwd.pw_uid = 0;
521   kpse->the_passwd.pw_gid = 0;
522   kpse->the_passwd.pw_quota = 0;
523   kpse->the_passwd.pw_gecos = kpse->the_passwd_gecos;
524   kpse->the_passwd.pw_dir = kpse->the_passwd_dir;
525   kpse->the_passwd.pw_shell = kpse->the_passwd_shell;
526   kpse->__system_allow_multiple_cmds = 0;
527
528   /* Set various info about user. Among many things,
529      ensure that HOME is set.  */
530   kpathsea_init_user_info(kpse);
531 #endif /* !__MINGW32__ */
532
533   /* redirect stderr to debug_output. Easier to send logfiles. */
534   if (debug_output) {
535     int flags =  _O_CREAT | _O_TRUNC | _O_RDWR;
536     err = -1;
537     if (_stricmp(debug_output, "con") == 0
538        || _stricmp(debug_output, "con:") == 0) {
539       err = _fileno(stdout);
540     } else {
541       if (append_debug_output) {
542         flags =  _O_CREAT | _O_APPEND | _O_WRONLY;
543       } else {
544         flags =  _O_CREAT | _O_TRUNC | _O_WRONLY;
545         kpathsea_xputenv(kpse, "KPATHSEA_DEBUG_APPEND", "yes");
546       }
547     }
548
549     if ((err < 0)
550         && (err = _open(debug_output, flags, _S_IREAD | _S_IWRITE)) == -1)
551     {
552       WARNING1 ("kpathsea: Can't open %s for stderr redirection!\n",
553                 debug_output);
554       perror (debug_output);
555     } else if ((olderr = _dup(fileno(stderr))) == -1) {
556       WARNING ("kpathsea: Can't dup() stderr!\n");
557       close (err);
558     } else if (_dup2(err, fileno(stderr)) == -1) {
559       WARNING1 ("kpathsea: Can't redirect stderr to %s!\n", debug_output);
560       close (olderr);
561       close (err);
562     } else {
563       close (err);
564     }
565   }
566
567   /* Win95 always gives the short filename for argv0, not the long one.
568      There is only this way to catch it. It makes all the kpse_selfdir
569      stuff useless for win32. */
570   {
571     char short_path[PATH_MAX], path[PATH_MAX], *fp;
572 #if !defined(__MINGW32__)
573     HANDLE hnd;
574     WIN32_FIND_DATA ffd;
575 #endif
576
577     /* SearchPath() always gives back an absolute directory */
578     if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0)
579         LIB_FATAL1("Can't determine where the executable %s is.\n", argv0);
580 #if defined(__MINGW32__)
581     if (!win32_get_long_filename(short_path, path, sizeof(path))) {
582         LIB_FATAL1("This path points to an invalid file : %s\n", short_path);
583     }
584     /* slashify the dirname */
585     for (fp = path; fp && *fp; fp++)
586         if (IS_DIR_SEP(*fp)) *fp = DIR_SEP;
587 #else /* !__MINGW32__ */
588     if (getlongpath(path, short_path, PATH_MAX) == 0)
589         FATAL1("Can't get long name for %s.\n", short_path);
590     if ((hnd = FindFirstFile(short_path, &ffd)) == INVALID_HANDLE_VALUE)
591         FATAL1("The following path points to an invalid file : %s\n", path);
592     FindClose(hnd);
593     /* dirname aleady slashified in WIN32 */
594 #endif /* !__MINGW32__ */
595     /* sdir will be the directory of the executable, ie: c:/TeX/bin */
596     sdir = xdirname(path);
597     kpse->invocation_name = xstrdup(xbasename(path));
598   }
599
600 #elif defined(__DJGPP__)
601
602   /* DJGPP programs support long filenames on Windows 95, but ARGV0 there
603      is always made with the short 8+3 aliases of all the pathname elements.
604      If long names are supported, we need to convert that to a long name.
605
606      All we really need is to call `_truename', but most of the code
607      below is required to deal with the special case of networked drives.  */
608   if (pathconf (argv0, _PC_NAME_MAX) > 12) {
609     char long_progname[PATH_MAX];
610
611     if (_truename (argv0, long_progname)) {
612       char *fp;
613
614       if (long_progname[1] != ':') {
615         /* A complication: `_truename' returns network-specific string at
616            the beginning of `long_progname' when the program resides on a
617            networked drive, and DOS calls cannot grok such pathnames.  We
618            need to convert the filesystem name back to a drive letter.  */
619         char rootname[PATH_MAX], rootdir[4];
620
621         if (argv0[0] && argv0[1] == ':')
622           rootdir[0] = argv0[0]; /* explicit drive in `argv0' */
623         else
624           rootdir[0] = getdisk () + 'A';
625         rootdir[1] = ':';
626         rootdir[2] = '\\';
627         rootdir[3] = '\0';
628         if (_truename (rootdir, rootname)) {
629           /* Find out where `rootname' ends in `long_progname' and replace
630              it with the drive letter.  */
631           int root_len = strlen (rootname);
632
633           if (IS_DIR_SEP (rootname[root_len - 1]))
634             root_len--; /* keep the trailing slash */
635           long_progname[0] = rootdir[0];
636           long_progname[1] = ':';
637           memmove (long_progname + 2, long_progname + root_len,
638                    strlen (long_progname + root_len) + 1);
639         }
640       }
641
642       /* Convert everything to canonical form.  */
643       if (long_progname[0] >= 'A' && long_progname[0] <= 'Z')
644         long_progname[0] += 'a' - 'A'; /* make drive lower case, for beauty */
645       for (fp = long_progname; *fp; fp++)
646         if (IS_DIR_SEP (*fp))
647           *fp = DIR_SEP;
648
649       kpse->invocation_name = xstrdup (long_progname);
650     }
651     else
652       /* If `_truename' failed, God help them, because we won't...  */
653       kpse->invocation_name = xstrdup (argv0);
654   }
655   else
656     kpse->invocation_name = xstrdup (argv0);
657
658 #else /* !WIN32 && !__DJGPP__ */
659   kpse->invocation_name = xstrdup (argv0);
660 #endif
661
662   /* We need to find SELFAUTOLOC *before* removing the ".exe" suffix from
663      the program_name, otherwise the PATH search inside kpse_selfdir will fail,
664      since `prog' doesn't exists as a file, there's `prog.exe' instead.  */
665 #ifndef WIN32
666   sdir = kpathsea_selfdir (kpse, kpse->invocation_name);
667 #endif
668   /* SELFAUTODIR is actually the parent of the invocation directory,
669      and SELFAUTOPARENT the grandparent.  This is how teTeX did it.  */
670   kpathsea_xputenv (kpse, "SELFAUTOLOC", fix_selfdir (sdir));
671   sdir_parent = xdirname (sdir);
672   kpathsea_xputenv (kpse, "SELFAUTODIR", fix_selfdir (sdir_parent));
673   sdir_grandparent = xdirname (sdir_parent);
674   kpathsea_xputenv (kpse, "SELFAUTOPARENT", fix_selfdir (sdir_grandparent));
675   sdir_greatgrandparent = xdirname (sdir_grandparent);
676   kpathsea_xputenv (kpse, "SELFAUTOGRANDPARENT", fix_selfdir (sdir_greatgrandparent));
677
678 #if defined(WIN32) || defined(__CYGWIN__)
679   mk_suffixlist(kpse);
680 #endif /* WIN32 || __CYGWIN__ */
681
682   free (sdir);
683   free (sdir_parent);
684   free (sdir_grandparent);
685   free (sdir_greatgrandparent);
686
687   kpse->invocation_short_name
688     = xstrdup (xbasename (kpse->invocation_name));
689
690   if (progname) {
691     kpse->program_name = xstrdup (progname);
692   } else {
693     /* If configured --enable-shared and running from the build directory
694        with the wrapper scripts (e.g., for make check), the binaries will
695        be named foo.exe instead of foo.  Or possibly if we're running on a
696        DOSISH system.  */
697     ext = find_suffix (kpse->invocation_short_name);
698     if (ext && FILESTRCASEEQ (ext, "exe")) {
699       kpse->program_name = remove_suffix (kpse->invocation_short_name);
700     } else {
701       kpse->program_name = xstrdup (kpse->invocation_short_name);
702     }
703   }
704
705   /* Runtime check that snprintf always writes a trailing NUL byte.  */
706   {
707     char buf[4] = "old";
708     assert (snprintf (buf, 2, "a") == 1 && buf[1] == '\0');
709     assert ((unsigned)snprintf (buf, 2, "ab") >= 2 && buf[1] == '\0');
710     assert ((unsigned)snprintf (buf, 2, "abc") >= 2 && buf[1] == '\0');
711   }
712   /* Some of the utility routines (like atou() and xfopen()) will use
713      FATAL and variations thereof (see lib.h) if there is a problem.
714
715      The next trick makes it possible for that message to report some useful
716      name instead of (NULL), if the backward compatible is compiled in. */
717
718 #if defined (KPSE_COMPAT_API)
719   if (kpse!=kpse_def) {
720     kpse_def->invocation_name = xstrdup(kpse->invocation_name);
721     kpse_def->invocation_short_name = xstrdup(kpse->invocation_short_name);
722   }
723 #endif
724
725   kpathsea_xputenv (kpse, "progname", kpse->program_name);
726 }
727
728
729 #if defined (KPSE_COMPAT_API)
730 void
731 kpse_set_program_name (const_string argv0, const_string progname)
732 {
733   kpathsea_set_program_name (kpse_def, argv0, progname);
734 }
735 #endif
736
737 /* Returns ARGV0 with any leading path and on some systems the suffix
738    for executables stripped off.  This returns a new string.
739    For example, `kpse_program_basename ("/foo/bar.EXE")' returns "bar"
740    on WIndows or Cygwin and "bar.EXE" otherwise.  */
741
742 string
743 kpse_program_basename (const_string argv0)
744 {
745   string base = xstrdup (xbasename (argv0));
746 #ifdef EXEEXT
747   string dot = strrchr (base, '.');
748   if (dot && FILESTRCASEEQ (dot, EXEEXT))
749     *dot = 0;
750 #endif
751   return base;
752 }
753
754 \f
755 #ifdef TEST
756 static const char *tab[] = {
757 /* 'normal' names */
758     "/w/kpathsea",
759     "/w//kpathsea",
760     "/w/./kpathsea",
761     ".",
762     "./",
763     "./.",
764     "../kpathsea",
765     "/kpathsea/../foo",
766     "/../w/kpathsea",
767     "/../w/kpathsea/.",
768     "/te/share/texmf/../../../../bin/gnu",
769     NULL
770 };
771
772 int
773 main (int argc, char **argv)
774 {
775     const char **p;
776     kpathsea kpse = xcalloc(1, sizeof(kpathsea_instance));
777
778     kpathsea_set_program_name(kpse, argv[0], NULL);
779
780 #if defined(WIN32)
781     printf("\n%s: Nothing to do for WIN32\n",
782            kpse->invocation_short_name);
783 #else
784     printf("\n%s: name -> remove_dots(name)\n\n",
785            kpse->invocation_short_name);
786
787     for (p = tab; *p; p++) {
788         char *q = xstrdup(*p);
789         char *s = remove_dots(kpse, q);
790
791         printf("%s -> %s\n", q, s);
792         free (q);
793         free (s);
794     }
795 #endif
796
797     return 0;
798 }
799 #endif /* TEST */