static string kpse_brace_expand_element P1C(const_string, elt) { unsigned i; str_list_type expansions = brace_expand (&elt); string ret = (string)xmalloc (1); *ret = 0; for (i = 0; i != STR_LIST_LENGTH(expansions); i++) { /* Do $ and ~ expansion on each element. */ string x = kpse_expand (STR_LIST_ELT(expansions,i)); string save_ret = ret; if (!STREQ (x, STR_LIST_ELT(expansions,i))) { /* If we did any expansions, do brace expansion again. Since recursive variable definitions are not allowed, this recursion must terminate. (In practice, it's unlikely there will ever be more than one level of recursion.) */ string save_x = x; x = kpse_brace_expand_element (x); free (save_x); } ret = concat3 (ret, x, ENV_SEP_STRING); free (save_ret); free (x); } for (i = 0; i != STR_LIST_LENGTH(expansions); ++i) { free(STR_LIST_ELT(expansions,i)); } str_list_free(&expansions); ret[strlen (ret) - 1] = 0; /* waste the trailing null */ return ret; }
void str_list_add P2C(str_list_type *, l, string, s) { STR_LIST_LENGTH (*l)++; XRETALLOC (STR_LIST (*l), STR_LIST_LENGTH (*l), string); STR_LIST_LAST_ELT (*l) = s; }
void str_list_concat P2C(str_list_type *, target, str_list_type, more) { unsigned e; unsigned prev_len = STR_LIST_LENGTH (*target); STR_LIST_LENGTH (*target) += STR_LIST_LENGTH (more); XRETALLOC (STR_LIST (*target), STR_LIST_LENGTH (*target), string); for (e = 0; e < STR_LIST_LENGTH (more); e++) STR_LIST_ELT (*target, prev_len + e) = STR_LIST_ELT (more, e); }
static string *subdir_match(str_list_type subdirs, string * matches) { string *ret = XTALLOC1(string); unsigned len = 1; unsigned m; for (m = 0; matches[m]; m++) { size_t loc; unsigned e; string s = xstrdup(matches[m]); for (loc = strlen(s); loc > 0 && !IS_DIR_SEP(s[loc - 1]); loc--); while (loc > 0 && IS_DIR_SEP(s[loc - 1])) { loc--; } s[loc] = 0; /* wipe out basename */ for (e = 0; e < STR_LIST_LENGTH(subdirs); e++) { string subdir = STR_LIST_ELT(subdirs, e); size_t subdir_len = strlen(subdir); while (subdir_len > 0 && IS_DIR_SEP(subdir[subdir_len - 1])) { subdir_len--; subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */ } if (FILESTRCASEEQ(subdir, s + loc - subdir_len)) { /* matched, save this one. */ XRETALLOC(ret, len + 1, string); ret[len - 1] = matches[m]; len++; } } free(s); } ret[len - 1] = NULL; return ret; }
str_list_type str_list_init P1H(void) { str_list_type ret; STR_LIST_LENGTH (ret) = 0; STR_LIST (ret) = NULL; return ret; }
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); } } } }
/* kpse:lookup("plain.tex", {}) */ static int do_lua_kpathsea_lookup(lua_State * L, kpathsea kpse, int idx) { int i; string ret = NULL; string *ret_list = NULL; const_string name = NULL; string user_path = NULL; boolean show_all = false; boolean must_exist = false; kpse_file_format_type user_format = kpse_last_format; int dpi = 600; str_list_type subdir_paths = { 0, NULL }; unsigned saved_debug = kpse->debug; int saved_mktexpk = kpse->format_info[kpse_pk_format].program_enabled_p; int saved_mktexmf = kpse->format_info[kpse_mf_format].program_enabled_p; int saved_mktextex = kpse->format_info[kpse_tex_format].program_enabled_p; int saved_mktextfm = kpse->format_info[kpse_tfm_format].program_enabled_p; name = luaL_checkstring(L, idx); /* todo: fetch parameter values */ if (lua_type(L, idx + 1) == LUA_TTABLE) { lua_pushstring(L, "format"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TSTRING) { int op = luaL_checkoption(L, -1, NULL, filetypenames); user_format = filetypes[op]; } lua_pop(L, 1); lua_pushstring(L, "dpi"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TNUMBER) { dpi = (int) lua_tointeger(L, -1); } lua_pop(L, 1); lua_pushstring(L, "debug"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TNUMBER) { int d = 0; d = (int) lua_tointeger(L, -1); kpse->debug |= d; } lua_pop(L, 1); lua_pushstring(L, "path"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TSTRING) { user_path = xstrdup(lua_tostring(L, -1)); } lua_pop(L, 1); lua_pushstring(L, "all"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { show_all = lua_toboolean(L, -1); } lua_pop(L, 1); lua_pushstring(L, "mktexpk"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { kpathsea_maketex_option(kpse, "pk", lua_toboolean(L, -1)); } lua_pop(L, 1); lua_pushstring(L, "mktextex"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { kpathsea_maketex_option(kpse, "tex", lua_toboolean(L, -1)); } lua_pop(L, 1); lua_pushstring(L, "mktexmf"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { kpathsea_maketex_option(kpse, "mf", lua_toboolean(L, -1)); } lua_pop(L, 1); lua_pushstring(L, "mktextfm"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { kpathsea_maketex_option(kpse, "tfm", lua_toboolean(L, -1)); } lua_pop(L, 1); lua_pushstring(L, "mustexist"); lua_gettable(L, idx + 1); if (lua_type(L, -1) == LUA_TBOOLEAN) { must_exist = lua_toboolean(L, -1); } lua_pop(L, 1); lua_pushstring(L, "subdir"); lua_gettable(L, idx + 1); if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { /* numeric value */ if (lua_type(L, -1) == LUA_TSTRING) { char *s = xstrdup(lua_tostring(L, -1)); str_list_add(&subdir_paths, s); xfree(s); } lua_pop(L, 1); } } else if (lua_type(L, -1) == LUA_TSTRING) { char *s = xstrdup(lua_tostring(L, -1)); str_list_add(&subdir_paths, s); xfree(s); } lua_pop(L, 1); if (STR_LIST_LENGTH(subdir_paths) > 0) { show_all = 1; } } if (user_path) { /* Translate ; to : if that's our ENV_SEP. See cnf.c. */ if (IS_ENV_SEP(':')) { string loc; for (loc = user_path; *loc; loc++) { if (*loc == ';') *loc = ':'; } } user_path = kpathsea_path_expand(kpse, user_path); if (show_all) { ret_list = kpathsea_all_path_search(kpse, user_path, name); } else { ret = kpathsea_path_search(kpse, user_path, name, must_exist); } free(user_path); } else { /* No user-specified search path, check user format or guess from NAME. */ kpse_file_format_type fmt; if (user_format != kpse_last_format) fmt = user_format; else fmt = find_format(kpse, name, true); switch (fmt) { case kpse_pk_format: case kpse_gf_format: case kpse_any_glyph_format: { kpse_glyph_file_type glyph_ret; string temp = remove_suffix (name); /* Try to extract the resolution from the name. */ unsigned local_dpi = find_dpi(name); if (!local_dpi) local_dpi = (unsigned) dpi; ret = kpathsea_find_glyph(kpse, temp, local_dpi, fmt, &glyph_ret); if (temp != name) free (temp); } break; case kpse_last_format: /* If the suffix isn't recognized, assume it's a tex file. */ fmt = kpse_tex_format; /* fall through */ default: if (show_all) { ret_list = kpathsea_find_file_generic(kpse, name, fmt, must_exist, true); } else { ret = kpathsea_find_file(kpse, name, fmt, must_exist); } } } /* Turn single return into a null-terminated list for uniform treatment. */ if (ret) { ret_list = XTALLOC(2, string); ret_list[0] = ret; ret_list[1] = NULL; } /* Filter by subdirectories, if specified. */ if (STR_LIST_LENGTH(subdir_paths) > 0) { string *new_list = subdir_match(subdir_paths, ret_list); free(ret_list); ret_list = new_list; } kpse->debug = saved_debug; kpse->format_info[kpse_pk_format].program_enabled_p = saved_mktexpk; kpse->format_info[kpse_mf_format].program_enabled_p = saved_mktexmf; kpse->format_info[kpse_tex_format].program_enabled_p = saved_mktextex; kpse->format_info[kpse_tfm_format].program_enabled_p = saved_mktextfm; /* Print output. */ i = 0; if (ret_list) { for (; ret_list[i]; i++) { lua_pushstring(L, ret_list[i]); } free(ret_list); } if (i == 0) { i++; lua_pushnil(L); } return i; }
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); }
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); }
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; }
void str_list_concat_elements P2C(str_list_type *, target, str_list_type, more) { if (STR_LIST_LENGTH(more) == 0) { return; } else if (STR_LIST_LENGTH(*target) == 0) { unsigned int i; STR_LIST_LENGTH(*target) = STR_LIST_LENGTH(more); STR_LIST(*target) = xmalloc(STR_LIST_LENGTH(more)*sizeof(char*)); for (i=0;i!=STR_LIST_LENGTH(more);++i) { STR_LIST_ELT(*target,i)=xstrdup(STR_LIST_ELT(more,i)); } return; } else { unsigned new_len; char ** new_list; unsigned int i,j; new_list = xmalloc(STR_LIST_LENGTH (*target) * STR_LIST_LENGTH (more) * sizeof(char*)); new_len = 0; for (j = 0; j != STR_LIST_LENGTH(more); ++j) { for (i = 0; i != STR_LIST_LENGTH(*target); ++i) { new_list[new_len] = concat(STR_LIST_ELT(*target,i), STR_LIST_ELT(more,j)); ++new_len; } } for (i = 0; i != STR_LIST_LENGTH(*target); ++i) free(STR_LIST_ELT(*target, i)); free(STR_LIST(*target)); STR_LIST_LENGTH(*target) = new_len; STR_LIST(*target) = new_list; } }