Exemple #1
0
static string
kpse_expand_kpse_dot P1C(string, path)
{
  string ret, elt;
  string kpse_dot = getenv("KPSE_DOT");
#ifdef MSDOS
  boolean malloced_kpse_dot = false;
#endif
  
  if (kpse_dot == NULL)
    return path;
  ret = (string)xmalloc(1);
  *ret = 0;

#ifdef MSDOS
  /* Some setups of ported Bash force $KPSE_DOT to have the //d/foo/bar
     form (when `pwd' is used), which is not understood by libc and the OS.
     Convert them back to the usual d:/foo/bar form.  */
  if (kpse_dot[0] == '/' && kpse_dot[1] == '/'
      && kpse_dot[2] >= 'A' && kpse_dot[2] <= 'z' && kpse_dot[3] == '/') {
    kpse_dot++;
    kpse_dot = xstrdup (kpse_dot);
    kpse_dot[0] = kpse_dot[1];  /* drive letter */
    kpse_dot[1] = ':';
    malloced_kpse_dot = true;
  }
#endif

  for (elt = kpse_path_element (path); elt; elt = kpse_path_element (NULL)) {
    string save_ret = ret;
    boolean ret_copied = true;
    /* We assume that the !! magic is only used on absolute components.
       Single "." gets special treatment, as does "./" or its equivalent. */
    if (kpse_absolute_p (elt, false) || (elt[0] == '!' && elt[1] == '!')) {
      ret = concat3(ret, elt, ENV_SEP_STRING);
    } else if (elt[0] == '.' && elt[1] == 0) {
      ret = concat3 (ret, kpse_dot, ENV_SEP_STRING);
#ifndef VMS
    } else if (elt[0] == '.' && IS_DIR_SEP(elt[1])) {
      ret = concatn (ret, kpse_dot, elt + 1, ENV_SEP_STRING, NULL);
    } else if (*elt) {
      ret = concatn (ret, kpse_dot, DIR_SEP_STRING, elt, ENV_SEP_STRING, NULL);
#endif
    } else {
      /* omit empty path elements from TEXMFCNF.
         See http://bugs.debian.org/358330.  */
      ret_copied = false;
    }
    if (ret_copied)
      free (save_ret);
  }

#ifdef MSDOS
  if (malloced_kpse_dot) free (kpse_dot);
#endif

  ret[strlen (ret) - 1] = 0;
  return ret;
}
Exemple #2
0
static str_list_type
dir_list_search_list P3C(str_llist_type *, dirs,  const_string*, names,
                         boolean, search_all)
{
  str_llist_elt_type *elt;
  str_list_type ret;
  unsigned allocated = INIT_ALLOC;
  string potential = XTALLOC(allocated, char);

  ret = str_list_init ();

  for (elt = *dirs; elt; elt = STR_LLIST_NEXT(*elt)) {
      const_string dir = STR_LLIST (*elt);
      unsigned dir_len = strlen (dir);
      int i;
      
      for (i = 0; names[i]; i++) {
          const_string name = names[i];
          unsigned name_len;

          /* Don't bother with absolute & explicit relative. */
          if (kpse_absolute_p(name, true))
              continue;
          
          name_len = strlen(name);

          while (dir_len + name_len + 1 > allocated) {
              allocated += allocated;
              XRETALLOC (potential, allocated, char);
          }

          strcpy (potential, dir);
          strcat (potential+dir_len, name);

          if (kpse_readable_file (potential)) {
              str_list_add (&ret, potential);

              /* Move this element towards the top of the list.  */
              str_llist_float (dirs, elt);

              /* If caller only wanted one file returned, no need to
                 terminate the list with NULL; the caller knows to only look
                 at the first element.  */
              if (!search_all)
                  return ret;

              /* Start new filename. */
              allocated = INIT_ALLOC;
              potential = XTALLOC(allocated, char);
          }
      }
  }

  /* If we get here, either we didn't find any files, or we were finding
     all the files.  But we're done with the last filename, anyway.  */
  free (potential);
  
  return ret;
}
Exemple #3
0
int main()
{
  char **name;
  char *t[] = { "./foo", "\\\\server\\foo\\bar", "ftp://localhost/foo" };

  for (name = t; name - t < sizeof(t)/sizeof(char*); name++) {
    printf ("Path `%s' %s absolute.\n", *name,
            kpse_absolute_p(*name, true) ? "is" : "is not");
  }
}
Exemple #4
0
/*
 * Derived from BSD basename
 */
char *basename(char *str, const char *suffix){   
   char *p; int len = 0;
   const char *t;
   char *base;

   printf("basename of %s = ", str);
#ifdef KPATHSEA
   for (p = base = (NAME_BEGINS_WITH_DEVICE(str) ? str+2 : str);
	*p; p++) {
#else
   for (p = base = str; *p; p++) {
#endif
      /*      if (*p++ == DIRSEP) { base = p; len = 0; } */
      if (IS_DIR_SEP(*p)) {
         base = p+1;
         len = 0;
      } else len++;
   }
   printf("%s\n", base);
   if (suffix != NULL) {
      for (t = suffix; *t; ++t);
      do {
         len--; t--; p--;
         if (*t != *p) break;
         if (t == suffix) {
            char *bn;
            if (len == 0)
               return NULL;
            bn = malloc(len+1);
            if (bn == NULL)
               fatal("Out of memory\n");
            strncpy(bn, base, len);
            *(bn+len) = '\0'; /* RA */
            return bn;
         }
      } while (p > base);
   }
   return base;
}

/*
 * Return true if name can be the name of a PostScript resource
 * (no extension and no absolute pathname).
 */
int ps_resource(const char *name) {
   if (strchr(name, '.')) return 0 ;
#ifdef KPATHSEA
   if (kpse_absolute_p(name, true)) return 0;
#else
   if (strchr(name, DIRSEP)) return 0 ;
#endif
   return 1;
}
Exemple #5
0
search P3C(kpse_file_format_type, format, char *, file, char *, mode)
{
  FILE *ret;
  string found_name;

#ifdef SECURE
  /* This change suggested by [email protected] to disallow reading of
     arbitrary files.  */
  if (secure && kpse_absolute_p (file)) return NULL;
#endif

  /* Most file looked for through here must exist -- the exception is
     VF's. Bitmap fonts go through pksearch. */
  found_name = kpse_find_file (file, format, format != vfpath);

  if (found_name) {
    unsigned len = strlen (found_name);
#ifndef AMIGA
    if ((format == figpath || format == headerpath)
        && ((len > 2 && FILESTRCASEEQ (found_name + len - 2, ".Z"))
            || (len > 3 && FILESTRCASEEQ (found_name + len - 3, ".gz")))) {
/* FIXME : use zlib instead of gzip ! */
      char *cmd = concat3 (GUNZIP, " -c ", found_name);
      ret = popen (cmd, "r");
      to_close = USE_PCLOSE ;
    } else {
#endif /* not AMIGA */
      ret = fopen (found_name, mode);
      to_close = USE_FCLOSE ;
#ifndef AMIGA
    }
#endif /* not AMIGA */
    if (!ret)
      FATAL_PERROR (found_name);
    /* Free result of previous search.  */
    if (realnameoffile)
      free (realnameoffile);
    /* Save in `name' and `realnameoffile' because other routines
       access those globals.  Sigh.  */
    realnameoffile = found_name;
    strcpy(name, realnameoffile);
  } else
    ret = NULL;

  return ret;
}               /* end search */
Exemple #6
0
boolean
open_output (FILE **f_ptr, const_string fopen_mode)
{
    string fname;
    boolean absolute = kpse_absolute_p(nameoffile+1, false);

    /* If we have an explicit output directory, use it. */
    if (output_directory && !absolute) {
        fname = concat3(output_directory, DIR_SEP_STRING, nameoffile + 1);
    } else {
        fname = nameoffile + 1;
    }

    /* Is the filename openable as given?  */
    *f_ptr = fopen (fname, fopen_mode);

    if (!*f_ptr) {
        /* Can't open as given.  Try the envvar.  */
        string texmfoutput = kpse_var_value("TEXMFOUTPUT");

        if (texmfoutput && *texmfoutput && !absolute) {
            if (fname != nameoffile + 1)
                free(fname);
            fname = concat3(texmfoutput, DIR_SEP_STRING, nameoffile+1);
            *f_ptr = fopen(fname, fopen_mode);
        }
    }
    /* If this succeeded, change nameoffile accordingly.  */
    if (*f_ptr) {
        if (fname != nameoffile + 1) {
            free (nameoffile);
            namelength = strlen (fname);
            nameoffile = xmalloc (namelength + 2);
            strcpy (nameoffile + 1, fname);
        }
        recorder_record_output (fname);
    }
    if (fname != nameoffile +1)
        free(fname);
    return *f_ptr != NULL;
}
static void
log_search P1C(str_list_type, filenames)
{
  static FILE *log_file = NULL;
  static boolean first_time = true; /* Need to open the log file?  */
  
  if (first_time) {
    /* Get name from either envvar or config file.  */
    string log_name = kpse_var_value ("TEXMFLOG");
    first_time = false;
    if (log_name) {
      log_file = fopen (log_name, FOPEN_A_MODE);
      if (!log_file)
        perror (log_name);
      free (log_name);
    }
  }

  if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file) {
    unsigned e;

    /* FILENAMES should never be null, but safety doesn't hurt.  */
    for (e = 0; e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e);
         e++) {
      string filename = STR_LIST_ELT (filenames, e);

      /* Only record absolute filenames, for privacy.  */
      if (log_file && kpse_absolute_p (filename, false))
        fprintf (log_file, "%lu %s\n", (long unsigned) time (NULL),
                 filename);

      /* And show them online, if debugging.  We've already started
         the debugging line in `search', where this is called, so
         just print the filename here, don't use DEBUGF.  */
      if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
        putc (' ', stderr);
        fputs (filename, stderr);
      }
    }
  }
}
Exemple #8
0
string
kpse_find_glyph P4C(const_string, passed_fontname,  unsigned, dpi,
                    kpse_file_format_type, format,
                    kpse_glyph_file_type *, glyph_file)
{
  string ret;
  kpse_glyph_source_type source;
  string fontname = (string) passed_fontname; /* discard const */
  
  /* Start the search: try the name we're given.  */
  source = kpse_glyph_source_normal;
  xputenv ("KPATHSEA_NAME", fontname);
  ret = try_resolution (fontname, dpi, format, glyph_file);
  
  /* Try all the various possibilities in order of preference.  */
  if (!ret) {
    /* Maybe FONTNAME was an alias.  */
    source = kpse_glyph_source_alias;
    ret = try_fontmap (&fontname, dpi, format, glyph_file);

    /* If not an alias, try creating it on the fly with mktexpk,
       unless FONTNAME is absolute or explicitly relative.  */
    if (!ret && !kpse_absolute_p (fontname, true)) {
      source = kpse_glyph_source_maketex;
      /* `try_resolution' leaves the envvar set randomly.  */
      xputenv_int ("KPATHSEA_DPI", dpi);
      ret = kpse_make_tex (format, fontname);
    }

    /* If mktex... succeeded, set return struct.  Doesn't make sense for
       `kpse_make_tex' to set it, since it can only succeed or fail,
       unlike the other routines.  */
    if (ret) {
      KPSE_GLYPH_FILE_DPI (*glyph_file) = dpi;
      KPSE_GLYPH_FILE_NAME (*glyph_file) = fontname;
    }

    /* If mktex... failed, try any fallback resolutions.  */
    else {
      if (kpse_fallback_resolutions)
        ret = try_fallback_resolutions (fontname, dpi, format, glyph_file);

      /* We're down to the font of last resort.  */
      if (!ret && kpse_fallback_font) {
        const_string name = kpse_fallback_font;
        source = kpse_glyph_source_fallback;
        xputenv ("KPATHSEA_NAME", name);

        /* As before, first try it at the given size.  */
        ret = try_resolution (name, dpi, format, glyph_file);

        /* The fallback font at the fallback resolutions.  */
        if (!ret && kpse_fallback_resolutions)
          ret = try_fallback_resolutions (name, dpi, format, glyph_file);
      }
    }
  }
  
  /* If RET is null, then the caller is not supposed to look at GLYPH_FILE,
     so it doesn't matter if we assign something incorrect.  */
  KPSE_GLYPH_FILE_SOURCE (*glyph_file) = source;

  /* FIXME: fontname may have been allocated, but (worse) it may also
     have been assigned to struct that's passed out of this function.
  if (fontname != passed_fontname)
    free (fontname);
  */
  
  return ret;
}
Exemple #9
0
boolean
open_input (FILE **f_ptr, int filefmt, const_string fopen_mode)
{
    string fname = NULL;
#ifdef FUNNY_CORE_DUMP
    /* This only applies if a preloaded TeX/Metafont is being made;
       it allows automatic creation of the core dump (typing ^\ loses
       since that requires manual intervention).  */
    if ((filefmt == kpse_tex_format || filefmt == kpse_mf_format
            || filefmt == kpse_mp_format)
            && STREQ (nameoffile + 1, "HackyInputFileNameForCoreDump.tex"))
        funny_core_dump ();
#endif

    /* We havent found anything yet. */
    *f_ptr = NULL;
    if (fullnameoffile)
        free(fullnameoffile);
    fullnameoffile = NULL;

    /* Look in -output-directory first, if the filename is not
       absolute.  This is because .aux and other such files will get
       written to the output directory, and we have to be able to read
       them from there.  We only look for the name as-is.  */
    if (output_directory && !kpse_absolute_p (nameoffile+1, false)) {
        fname = concat3 (output_directory, DIR_SEP_STRING, nameoffile + 1);
        *f_ptr = fopen (fname, fopen_mode);
        if (*f_ptr) {
            free (nameoffile);
            namelength = strlen (fname);
            nameoffile = xmalloc (namelength + 2);
            strcpy (nameoffile + 1, fname);
            fullnameoffile = fname;
        } else {
            free (fname);
        }
    }

    /* No file means do the normal search. */
    if (*f_ptr == NULL) {
        /* A negative FILEFMT means don't use a path.  */
        if (filefmt < 0) {
            /* no_file_path, for BibTeX .aux files and MetaPost things.  */
            *f_ptr = fopen(nameoffile + 1, fopen_mode);
            /* FIXME... fullnameoffile = xstrdup(nameoffile + 1); */
        } else {
            /* The only exception to `must_exist' being true is \openin, for
               which we set `tex_input_type' to 0 in the change file.  */
            /* According to the pdfTeX people, pounding the disk for .vf files
               is overkill as well.  A more general solution would be nice. */
            boolean must_exist = (filefmt != kpse_tex_format || texinputtype)
                                 && (filefmt != kpse_vf_format);
            fname = kpse_find_file (nameoffile + 1,
                                    (kpse_file_format_type)filefmt,
                                    must_exist);
            if (fname) {
                fullnameoffile = xstrdup(fname);
                /* If we found the file in the current directory, don't leave
                   the `./' at the beginning of `nameoffile', since it looks
                   dumb when `tex foo' says `(./foo.tex ... )'.  On the other
                   hand, if the user said `tex ./foo', and that's what we
                   opened, then keep it -- the user specified it, so we
                   shouldn't remove it.  */
                if (fname[0] == '.' && IS_DIR_SEP (fname[1])
                        && (nameoffile[1] != '.' || !IS_DIR_SEP (nameoffile[2])))
                {
                    unsigned i = 0;
                    while (fname[i + 2] != 0) {
                        fname[i] = fname[i + 2];
                        i++;
                    }
                    fname[i] = 0;
                }

                /* kpse_find_file always returns a new string. */
                free (nameoffile);
                namelength = strlen (fname);
                nameoffile = xmalloc (namelength + 2);
                strcpy (nameoffile + 1, fname);
                free (fname);

                /* This fopen is not allowed to fail. */
#if defined(PTEX) && !defined(WIN32)
                if (filefmt == kpse_tex_format ||
                        filefmt == kpse_bib_format) {
                    *f_ptr = nkf_open (nameoffile + 1, fopen_mode);
                } else
#endif
                    *f_ptr = xfopen (nameoffile + 1, fopen_mode);
            }
        }
    }

    if (*f_ptr) {
        recorder_record_input (nameoffile + 1);

        /* If we just opened a TFM file, we have to read the first
           byte, to pretend we're Pascal.  See tex.ch and mp.ch.
           Ditto for the ocp/ofm Omega file formats.  */
        if (filefmt == kpse_tfm_format) {
            tfmtemp = getc (*f_ptr);
            /* We intentionally do not check for EOF here, i.e., an
               empty TFM file.  TeX will see the 255 byte and complain
               about a bad TFM file, which is what we want.  */
        } else if (filefmt == kpse_ocp_format) {
            ocptemp = getc (*f_ptr);
        } else if (filefmt == kpse_ofm_format) {
            tfmtemp = getc (*f_ptr);
        }
    }

    return *f_ptr != NULL;
}
Exemple #10
0
search_list P4C(const_string, path,  const_string*, names,
                boolean, must_exist,  boolean, all)
{
  str_list_type ret_list;
  const_string* namep;
  string elt;
  boolean done = false;

#ifdef __DJGPP__
  /* We will use `stat' heavily, so let's request for
     the fastest possible version of `stat', by telling
     it what members of struct stat do we really need.

     We need to set this on each call because this is a
     library function; the caller might need other options
     from `stat'.  Thus save the flags and restore them
     before exit.

     This call tells `stat' that we do NOT need to recognize
     executable files (neither by an extension nor by a magic
     signature); that we do NOT need time stamp of root directories;
     and that we do NOT need the write access bit in st_mode.

     Note that `kpse_set_progname' needs the EXEC bits,
     but it was already called by the time we get here.  */
  unsigned short save_djgpp_flags  = _djstat_flags;

  _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
		  | _STAT_ROOT_TIME | _STAT_WRITEBIT;
#endif

  ret_list = str_list_init();

  if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
    DEBUGF1  ("start search(files=[%s", *names);
    for (namep = names+1; *namep != NULL; namep++) {
      fputc(' ', stderr);
      fputs(*namep, stderr);
    }
    fprintf (stderr, "], must_exist=%d, find_all=%d, path=%s).\n",
             must_exist, all, path);
  }
  
  /* No need to do any expansion on names.  */

  for (namep = names; *namep; namep++) {
      if (kpse_absolute_p(*namep, true) && kpse_readable_file(*namep)) {
          str_list_add(&ret_list, xstrdup(*namep));
          /* I know, I know... */
          goto out;
      }
  }

  /* Look at each path element in turn. */
  for (elt = kpse_path_element (path); !done && elt;
       elt = kpse_path_element (NULL))
  {
    str_list_type *found;
    boolean allow_disk_search = true;
    if (elt[0] == '!' && elt[1] == '!') {
      /* !! magic -> disallow disk searches. */
      allow_disk_search = false;
      elt += 2;
    }

    /* See elt-dirs.c for side effects of this function. */
    kpse_normalize_path(elt);

    /* Try ls-R, unless we're searching for texmf.cnf. */
    found = first_search ? NULL : kpse_db_search_list(names, elt, all);

    /* Search the filesystem if (1) the path spec allows it, and either
         (2a) we are searching for texmf.cnf ; or
         (2b) no db exists; or 
         (2c) no db's are relevant to this elt; or
         (3) MUST_EXIST && NAME was not in the db.
       In (2*), `found' will be NULL.
       In (3),  `found' will be an empty list. */
    if (allow_disk_search && (!found || (must_exist && !STR_LIST(*found)))) {
      str_llist_type *dirs = kpse_element_dirs (elt);
      if (dirs && *dirs) {
        if (!found)
          found = XTALLOC1 (str_list_type);
        *found = dir_list_search_list (dirs, names, all);
      }
    }

    /* Did we find anything? */
    if (found && STR_LIST (*found)) {
      if (all) {
        str_list_concat (&ret_list, *found);
      } else {
        str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
        done = true;
      }
    }
  }

 out:
  if (STR_LIST_LENGTH (ret_list) == 0
      || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
    str_list_add (&ret_list, NULL);

  if (first_search) {
    first_search = false;
  } else {
    /* Record the filenames we found, if desired.  And wrap them in a
       debugging line if we're doing that.  */
    if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
      DEBUGF1 ("search([%s", *names);
      for (namep = names+1; *namep != NULL; namep++) {
        fputc(' ', stderr);
        fputs(*namep, stderr);
      }
      fputs ("]) =>", stderr);
    }
    log_search (ret_list);
    if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
      putc ('\n', stderr);
  }

#ifdef __DJGPP__
  /* Undo any side effects.  */
  _djstat_flags = save_djgpp_flags;
#endif
  
  return STR_LIST (ret_list);
}
Exemple #11
0
search P4C(const_string, path,  const_string, original_name,
           boolean, must_exist,  boolean, all)
{
  str_list_type ret_list;
  string name;
  boolean absolute_p;

#ifdef __DJGPP__
  /* We will use `stat' heavily, so let's request for
     the fastest possible version of `stat', by telling
     it what members of struct stat do we really need.

     We need to set this on each call because this is a
     library function; the caller might need other options
     from `stat'.  Thus save the flags and restore them
     before exit.

     This call tells `stat' that we do NOT need to recognize
     executable files (neither by an extension nor by a magic
     signature); that we do NOT need time stamp of root directories;
     and that we do NOT need the write access bit in st_mode.

     Note that `kpse_set_progname' needs the EXEC bits,
     but it was already called by the time we get here.  */
  unsigned short save_djgpp_flags  = _djstat_flags;

  _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
		  | _STAT_ROOT_TIME | _STAT_WRITEBIT;
#endif

  /* Make a leading ~ count as an absolute filename, and expand $FOO's.  */
  name = kpse_expand (original_name);
  
  /* If the first name is absolute or explicitly relative, no need to
     consider PATH at all.  */
  absolute_p = kpse_absolute_p (name, true);
  
  if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
    DEBUGF4 ("start search(file=%s, must_exist=%d, find_all=%d, path=%s).\n",
             name, must_exist, all, path);

  /* Find the file(s). */
  ret_list = absolute_p ? absolute_search (name)
                        : path_search (path, name, must_exist, all);
  
  /* Append NULL terminator if we didn't find anything at all, or we're
     supposed to find ALL and the list doesn't end in NULL now.  */
  if (STR_LIST_LENGTH (ret_list) == 0
      || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
    str_list_add (&ret_list, NULL);

  /* The very first search is for texmf.cnf.  We can't log that, since
     we want to allow setting TEXMFLOG in texmf.cnf.  */
  if (first_search) {
    first_search = false;
  } else {
    /* Record the filenames we found, if desired.  And wrap them in a
       debugging line if we're doing that.  */
    if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
      DEBUGF1 ("search(%s) =>", original_name);
    log_search (ret_list);
    if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
      putc ('\n', stderr);
  }  

#ifdef __DJGPP__
  /* Undo any side effects.  */
  _djstat_flags = save_djgpp_flags;
#endif

  return STR_LIST (ret_list);
}
Exemple #12
0
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;
}