string setval_default(string name, string value) { string old_value; /* Be safe ... */ if (!value) { if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "setval_default: inserting NULL value for %s!\n", name); } value = ""; } if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "SetVal default %s ... ", name); } /* The mktex... scripts NEVER use ${foo:=bar}, always ${foo=bar}. So we only need to set NAME=VALUE if $NAME is unset. */ if ((old_value = getval(name)) == NULL) { hash_insert(symtab, name, value); if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "SetVal %s <= %s\n", name, value); } } else value = old_value; return value; }
/* kpse_expand_var does not remove leading !!, but we do not need them in mktex* (Parts of code shamelessly stolen from remove_dbonly on tex-file.c.) */ string expand_var(const_string s) { string p, q; string res = NULL; if (s) { boolean new_elt = true; res = mktex_var_expand(s); for (p = q = res; *p; ) { if (new_elt && *p == '!' && p[1] == '!') p += 2; else { new_elt = *p == ENV_SEP; *q++ = *p++; } } *q = '\0'; if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "Expanding variable %s into %s\n", s, res); } } return res; }
boolean dir_p P1C(const_string, fn) { /* FIXME : using the stat() replacement in gnuw32, we could avoid this win32 specific code. However, I wonder if it would be as fast as this one is ? */ #ifdef WIN32 int fa; kpse_normalize_path((string)fn); fa = GetFileAttributes(fn); if (KPSE_DEBUG_P(KPSE_DEBUG_STAT)) { if (fa == 0xFFFFFFFF) { fprintf(stderr, "failed to get file attributes for %s (%d)\n", fn, GetLastError()); } else { fprintf(stderr, "path %s %s a directory\n", fn , (fa & FILE_ATTRIBUTE_DIRECTORY) ? "is" : "is not"); } } return (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); #else struct stat stats; return stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode); #endif }
fopen P2C(const_string, filename, const_string, mode) { #undef fopen FILE *ret = fopen (filename, mode); if (KPSE_DEBUG_P (KPSE_DEBUG_FOPEN)) DEBUGF3 ("fopen(%s, %s) => 0x%lx\n", filename, mode, (unsigned long) ret); return ret; }
int fclose P1C(FILE *, f) { #undef fclose int ret = fclose (f); if (KPSE_DEBUG_P (KPSE_DEBUG_FOPEN)) DEBUGF2 ("fclose(0x%lx) => %d\n", (unsigned long) f, ret); 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); } } } }
string getval(string name) { string *lookup = NULL; string res = NULL; /* Either the name is already in the symbol table ... */ if ((lookup = hash_lookup(symtab, name)) != NULL) { /* FIXME: we return the first value ... */ res = lookup[0]; free(lookup); } #if 0 else if ((lookup = hash_lookup(symtab, vname)) != NULL) { /* FIXME: we return the first value ... */ res = lookup[0]; free(lookup); } else { string dollar_name = concat("$", vname); res = kpse_cnf_get(vname); free(dollar_name); } #endif if (!res) { #if 0 /* ... Either it is known from kpathsea, in which case we put it in the symbol table. */ string dollar_name = concat("$", vname); res = expand_var(dollar_name); if (test_file('n', res)) { setval(vname, res); } else { /* res = "", so return NULL */ res = NULL; } free (dollar_name); #endif res = getenv(name); } if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "GetVal %s => %s\n", name, res); } /* In any case ... */ return res; }
string setval(string name, string value) { /* Be safe ... */ if (!value) value = ""; hash_remove_all(symtab, name); hash_insert(symtab, name, value); if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "SetVal %s <= %s\n", name, value); } return value; }
/* Substitute from by to in line */ string subst(string line, string from, string to) { string before, after, p; if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "Substituting %s by %s in %s\n", from, to, line); } if ((p = strstr(line, from))) { before = xstrdup(line); before[p-line] = '\0'; after = p+strlen(from); p = concat3(before, to , after); free (before); } else p = xstrdup(line); if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "=> gives %s\n", p); } return p; }
hash_lookup P2C(hash_table_type, table, const_string, key) { hash_element_type *p; str_list_type ret; unsigned n = hash (table, key); ret = str_list_init (); /* Look at everything in this bucket. */ for (p = table.buckets[n]; p != NULL; p = p->next) if (FILESTRCASEEQ (key, p->key)) /* Cast because the general str_list_type shouldn't force const data. */ str_list_add (&ret, (string) p->value); /* If we found anything, mark end of list with null. */ if (STR_LIST (ret)) str_list_add (&ret, NULL); #ifdef KPSE_DEBUG if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) { DEBUGF1 ("hash_lookup(%s) =>", key); if (!STR_LIST (ret)) fputs (" (nil)\n", stderr); else { string *r; for (r = STR_LIST (ret); *r; r++) { putc (' ', stderr); if (kpse_debug_hash_lookup_int) fprintf (stderr, "%ld", (long) *r); else fputs (*r, stderr); } putc ('\n', stderr); } fflush (stderr); } #endif return STR_LIST (ret); }
kpse_element_dirs P1C(const_string, elt) { str_llist_type *ret; /* If given nothing, return nothing. */ if (!elt || !*elt) return NULL; /* If we've already cached the answer for ELT, return it. */ ret = cached (elt); if (ret) return ret; /* We're going to have a real directory list to return. */ ret = XTALLOC1 (str_llist_type); *ret = NULL; /* We handle the hard case in a subroutine. */ expand_elt (ret, elt, 0); /* Remember the directory list we just found, in case future calls are made with the same ELT. */ cache (elt, ret); #ifdef KPSE_DEBUG if (KPSE_DEBUG_P (KPSE_DEBUG_EXPAND)) { DEBUGF1 ("path element %s =>", elt); if (ret) { str_llist_elt_type *e; for (e = *ret; e; e = STR_LLIST_NEXT (*e)) fprintf (stderr, " %s", STR_LLIST (*e)); } putc ('\n', stderr); fflush (stderr); } #endif /* KPSE_DEBUG */ return ret; }
string *grep(char *regexp, char *line, int num_vars) { struct re_pattern_buffer *rc; struct re_registers *p; const_string ok; string *vars = NULL; string *lookup; int i; if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "Grep\n\t%s\n\tin\n\t%s\n", regexp, line); } if (test_file('z', line)) return NULL; /* This will retrieve the precompiled regexp or compile it and remember it. vars contains the strings matched, num_vars the number of these strings. */ #if 0 if ((lookup = hash_lookup(symtab, regexp))) rc = (struct re_pattern_buffer *)lookup[0]; else rc = NULL; if (rc == NULL) { #endif /* Compile the regexp and stores the result */ if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "\tCompiling the regexp\n"); } re_syntax_options = RE_SYNTAX_POSIX_EGREP; rc = (struct re_pattern_buffer *) calloc(1, sizeof(struct re_pattern_buffer)); rc->regs_allocated = REGS_UNALLOCATED; if ((ok = re_compile_pattern(regexp, strlen(regexp), rc)) != 0) FATAL1("Can't compile regex %s\n", regexp); #if 0 hash_remove_all(symtab, regexp); hash_insert(symtab, regexp, (char *)rc); } else if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { fprintf(stderr, "\tAlready compiled\n"); } #endif p = (struct re_registers *) calloc(1, sizeof(struct re_registers)); p->num_regs = num_vars; if ((re_match(rc, line, strlen(line), 0, p)) > 0) { vars = (char **) xmalloc ((num_vars+1) * sizeof(char *)); for (i = 0; i <= num_vars; i++) { vars[i] = malloc((p->end[i] - p->start[i] + 1)*sizeof(char)); strncpy(vars[i], line+p->start[i], p->end[i] - p->start[i]); vars[i][p->end[i] - p->start[i]] = '\0'; } } free (p); if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { if (vars) for(i = 0; i <= num_vars; i++) fprintf(stderr, "String %d matches %s\n", i, vars[i]); } return vars; }
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); }
void kpse_set_program_name P2C(const_string, argv0, const_string, progname) { string ext, sdir, sdir_parent, sdir_grandparent; 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; #endif /* Set debugging stuff first, in case we end up doing debuggable stuff during this initialization. */ if (s) { kpathsea_debug |= atoi (s); } #ifndef HAVE_PROGRAM_INVOCATION_NAME #if defined(WIN32) /* Set various info about user. Among many things, ensure that HOME is set. If debug_paths is on, turn on some message if $HOME is not found. */ if (KPSE_DEBUG_P(KPSE_DEBUG_PATHS)) { set_home_warning(); } init_user_info(); /* 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; xputenv("KPATHSEA_DEBUG_APPEND", "yes"); } } if ((err < 0) && (err = _open(debug_output, flags, _S_IREAD | _S_IWRITE)) == -1) { WARNING1("Can't open %s for stderr redirection!\n", debug_output); perror(debug_output); } else if ((olderr = _dup(fileno(stderr))) == -1) { WARNING("Can't dup() stderr!\n"); close(err); } else if (_dup2(err, fileno(stderr)) == -1) { WARNING1("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 selfdir stuff useless for win32. */ { char short_path[PATH_MAX], path[PATH_MAX], *fp; /* SearchPath() always gives back an absolute directory */ if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0) FATAL1("Can't determine where the executable %s is.\n", argv0); if (!win32_get_long_filename(short_path, path, sizeof(path))) { 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; /* sdir will be the directory of the executable, ie: c:/TeX/bin */ sdir = xdirname(path); program_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; program_invocation_name = xstrdup (long_progname); } else /* If `_truename' failed, God help them, because we won't... */ program_invocation_name = xstrdup (argv0); } else program_invocation_name = xstrdup (argv0); #else /* !WIN32 && !__DJGPP__ */ program_invocation_name = xstrdup (argv0); #endif #endif /* not HAVE_PROGRAM_INVOCATION_NAME */ /* We need to find SELFAUTOLOC *before* removing the ".exe" suffix from the program_name, otherwise the PATH search inside selfdir will fail, since `prog' doesn't exists as a file, there's `prog.exe' instead. */ #ifndef WIN32 sdir = selfdir (program_invocation_name); #endif /* SELFAUTODIR is actually the parent of the invocation directory, and SELFAUTOPARENT the grandparent. This is how teTeX did it. */ xputenv ("SELFAUTOLOC", sdir); sdir_parent = xdirname (sdir); xputenv ("SELFAUTODIR", sdir_parent); sdir_grandparent = xdirname (sdir_parent); xputenv ("SELFAUTOPARENT", sdir_grandparent); free (sdir); free (sdir_parent); free (sdir_grandparent); #ifndef HAVE_PROGRAM_INVOCATION_NAME program_invocation_short_name = (string)xbasename (program_invocation_name); #endif 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 (program_invocation_short_name); if (ext && FILESTRCASEEQ (ext, "exe")) { kpse_program_name = remove_suffix (program_invocation_short_name); } else { kpse_program_name = xstrdup (program_invocation_short_name); } } xputenv("progname", kpse_program_name); }
/* Return -1 if FN isn't a directory, else its number of links. Duplicate the call to stat; no need to incur overhead of a function call for that little bit of cleanliness. The process is a bit different under Win32 : the first call memoizes the nlinks value, the following ones retrieve it. */ int dir_links P2C(const_string, fn, long, nlinks) { static hash_table_type link_table; string *hash_ret; if (link_table.size == 0) link_table = hash_create (457); #ifdef KPSE_DEBUG /* This is annoying, but since we're storing integers as pointers, we can't print them as strings. */ if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) kpse_debug_hash_lookup_int = true; #endif hash_ret = hash_lookup (link_table, fn); #ifdef KPSE_DEBUG if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) kpse_debug_hash_lookup_int = false; #endif /* Have to cast the int we need to/from the const_string that the hash table stores for values. Let's hope an int fits in a pointer. */ if (hash_ret) { #ifdef WIN32 memcpy(&nlinks, hash_ret, sizeof(nlinks)); #else nlinks = (long) *hash_ret; #endif } else { #ifdef WIN32 /* Insert it only if we have some informations about it. */ if (nlinks) { char str_nlinks[sizeof(nlinks)+1]; memcpy(str_nlinks, (char *)&nlinks, sizeof(nlinks)); str_nlinks[sizeof(nlinks)] = '\0'; /* It's up to us to copy the value. */ hash_insert(&link_table, xstrdup(fn), (const_string)str_nlinks); } #else struct stat stats; if (stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode)) nlinks = stats.st_nlink; else nlinks = -1; /* It's up to us to copy the value. */ hash_insert(&link_table, xstrdup(fn), (const_string)nlinks); #endif #ifdef KPSE_DEBUG if (KPSE_DEBUG_P (KPSE_DEBUG_STAT)) DEBUGF2 ("dir_links(%s) => %ld\n", fn, nlinks); #endif } /* In any case, return nlinks (either 0, the value inserted or the value retrieved. */ return nlinks; }