static boolean db_build (kpathsea kpse, hash_table_type *table, const_string db_filename) { string line; unsigned dir_count = 0, file_count = 0, ignore_dir_count = 0; unsigned len = strlen (db_filename) - sizeof (DB_NAME) + 1; /* Keep the /. */ string top_dir = (string)xmalloc (len + 1); string cur_dir = NULL; /* First thing in ls-R might be a filename. */ FILE *db_file = fopen (db_filename, FOPEN_R_MODE); #if defined(WIN32) string pp; #endif strncpy (top_dir, db_filename, len); top_dir[len] = 0; if (db_file) { while ((line = read_line (db_file)) != NULL) { len = strlen (line); #if defined(WIN32) for (pp = line; *pp; pp++) { if (IS_KANJI(pp)) pp++; else *pp = TRANSFORM(*pp); } #endif /* A line like `/foo:' = new dir foo. Allow both absolute (/...) and explicitly relative (./...) names here. It's a kludge to pass in the directory name with the trailing : still attached, but it doesn't actually hurt. */ if (len > 0 && line[len - 1] == ':' && kpathsea_absolute_p (kpse, line, true)) { /* New directory line. */ if (!ignore_dir_p (line)) { /* If they gave a relative name, prepend full directory name now. */ line[len - 1] = DIR_SEP; /* Skip over leading `./', it confuses `match' and is just a waste of space, anyway. This will lose on `../', but `match' won't work there, either, so it doesn't matter. */ cur_dir = *line == '.' ? concat (top_dir, line + 2) : xstrdup (line); dir_count++; } else { cur_dir = NULL; ignore_dir_count++; } /* Ignore blank, `.' and `..' lines. */ } else if (*line != 0 && cur_dir /* a file line? */ && !(*line == '.' && (line[1] == 0 || (line[1] == '.' && line[2] == 0)))) { /* Make a new hash table entry with a key of `line' and a data of `cur_dir'. An already-existing identical key is ok, since a file named `foo' can be in more than one directory. Share `cur_dir' among all its files (and hence never free it). Note that we assume that all names in the ls-R file have already been case-smashed to lowercase where appropriate. */ hash_insert_normalized (table, xstrdup (line), cur_dir); file_count++; } /* else ignore blank lines or top-level files or files in ignored directories*/ free (line); } xfclose (db_file, db_filename); if (file_count == 0) { WARNING1 ("kpathsea: %s: No usable entries in ls-R", db_filename); WARNING ("kpathsea: See the manual for how to generate ls-R"); db_file = NULL; } else { str_list_add (&(kpse->db_dir_list), xstrdup (top_dir)); } #ifdef KPSE_DEBUG if (KPATHSEA_DEBUG_P (KPSE_DEBUG_HASH)) { /* Don't make this a debugging bit, since the output is so voluminous, and being able to specify -1 is too useful. Instead, let people who want it run the program under a debugger and change the variable that way. */ boolean hash_summary_only = true; DEBUGF4 ("%s: %u entries in %d directories (%d hidden).\n", db_filename, file_count, dir_count, ignore_dir_count); DEBUGF ("ls-R hash table:"); hash_print (*table, hash_summary_only); fflush (stderr); } #endif /* KPSE_DEBUG */ } free (top_dir); return db_file != NULL; }
boolean kpse_absolute_p (const_string filename, boolean relative_ok) { return kpathsea_absolute_p (kpse_def, filename, relative_ok); }
str_list_type * kpathsea_db_search_list (kpathsea kpse, string* names, const_string path_elt, boolean all) { const_string *db_dirs, *orig_dirs; const_string last_slash, name, path; string temp_str = NULL; boolean done; unsigned e; const_string *aliases, *r; int n; str_list_type *ret = NULL; boolean relevant = false; /* If we failed to build the database (or if this is the recursive call to build the db path), quit. */ if (kpse->db.buckets == NULL) return NULL; /* Don't bother doing any lookups if this `path_elt' isn't covered by any of database directories. We do this not so much because the extra couple of hash lookups matter -- they don't -- but rather because we want to return NULL in this case, so path_search can know to do a disk search. */ for (e = 0; !relevant && e < STR_LIST_LENGTH (kpse->db_dir_list); e++) { relevant = elt_in_db (STR_LIST_ELT (kpse->db_dir_list, e), path_elt); } if (!relevant) return NULL; done = false; ret = XTALLOC1 (str_list_type); *ret = str_list_init (); /* Handle each name. */ for (n = 0; !done && names[n]; n++) { name = names[n]; /* Absolute names should have been caught in our caller. */ if (kpathsea_absolute_p(kpse, name, true)) continue; /* When tex-glyph.c calls us looking for, e.g., dpi600/cmr10.pk, we won't find it unless we change NAME to just `cmr10.pk' and append `/dpi600' to PATH_ELT. We are justified in using a literal `/' here, since that's what tex-glyph.c unconditionally uses in DPI_BITMAP_SPEC. But don't do anything if the / begins NAME; that should never happen. */ last_slash = strrchr (name, '/'); if (last_slash && last_slash != name) { unsigned len = last_slash - name + 1; string dir_part = (string)xmalloc (len); strncpy (dir_part, name, len - 1); dir_part[len - 1] = 0; path = temp_str = concat3 (path_elt, "/", dir_part); name = last_slash + 1; free (dir_part); } else { path = path_elt; } /* If we have aliases for this name, use them. */ if (kpse->alias_db.buckets) aliases = hash_lookup (kpse->alias_db, name); else aliases = NULL; if (!aliases) { aliases = XTALLOC1 (const_string); aliases[0] = NULL; } { /* Push aliases up by one and insert the original name at front. */ unsigned i; unsigned len = 1; /* Have NULL element already allocated. */ for (r = aliases; *r; r++) len++; aliases = (const_string *) xrealloc ((void *) aliases, (len + 1) * sizeof(const_string)); for (i = len; i > 0; i--) { aliases[i] = aliases[i - 1]; } aliases[0] = name; } for (r = aliases; !done && *r; r++) { const_string ctry = *r; /* We have an ls-R db. Look up `try'. */ orig_dirs = db_dirs = hash_lookup (kpse->db, ctry); /* For each filename found, see if it matches the path element. For example, if we have .../cx/cmr10.300pk and .../ricoh/cmr10.300pk, and the path looks like .../cx, we don't want the ricoh file. */ while (!done && db_dirs && *db_dirs) { string db_file = concat (*db_dirs, ctry); boolean matched = match (db_file, path); #ifdef KPSE_DEBUG if (KPATHSEA_DEBUG_P (KPSE_DEBUG_SEARCH)) DEBUGF3 ("db:match(%s,%s) = %d\n", db_file, path, matched); #endif /* We got a hit in the database. Now see if the file actually exists, possibly under an alias. */ if (matched) { string found = NULL; if (kpathsea_readable_file (kpse, db_file)) { found = db_file; } else { const_string *a; free (db_file); /* `db_file' wasn't on disk. */ /* The hit in the DB doesn't exist in disk. Now try all its aliases. For example, suppose we have a hierarchy on CD, thus `mf.bas', but ls-R contains `mf.base'. Find it anyway. Could probably work around this with aliases, but this is pretty easy and shouldn't hurt. The upshot is that if one of the aliases actually exists, we use that. */ for (a = aliases + 1; *a && !found; a++) { string atry = concat (*db_dirs, *a); if (kpathsea_readable_file (kpse, atry)) found = atry; else free (atry); } } /* If we have a real file, add it to the list, maybe done. */ if (found) { str_list_add (ret, found); if (!all && found) done = true; } } else { /* no match in the db */ free (db_file); } /* On to the next directory, if any. */ db_dirs++; } /* This is just the space for the pointers, not the strings. */ if (orig_dirs && *orig_dirs) free (orig_dirs); } free ((void *) aliases); if (temp_str) free (temp_str); } return ret; }
string kpathsea_selfdir (kpathsea kpse, const_string argv0) { string ret = NULL; 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)) { 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 (kpse, expand_symlinks (kpse, self))); free (self); return ret; }