Mercurial > octave-nkf
diff liboctave/kpse-xfns.c @ 4378:7d48a8fba1d4
[project @ 2003-04-19 00:03:47 by jwe]
author | jwe |
---|---|
date | Sat, 19 Apr 2003 00:03:50 +0000 |
parents | |
children | 0cbcb9d8b4ff |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/liboctave/kpse-xfns.c Sat Apr 19 00:03:50 2003 +0000 @@ -0,0 +1,1623 @@ +/* xfns.c: All the x* functions from kpathsearch in one file. + +Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +Copyright (C) 1993, 94, 95, 96, 97, 98 Karl Berry. +Copyright (C) 1994, 95, 96, 97 Karl Berry & Olaf Weber. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if defined (HAVE_CONFIG_H) +#include <config.h> +#endif + +#ifdef HAVE_UNISTD_H +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <unistd.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#include "kpse-lib.h" + +/* xfopen.c: fopen and fclose with error checking. */ + +/* These routines just check the return status from standard library + routines and abort if an error happens. */ + +FILE * +xfopen (const char *filename, const char *mode) +{ + FILE *f; + + assert (filename && mode); + + f = fopen (filename, mode); + if (f == NULL) + FATAL_PERROR (filename); + + return f; +} + +void +xfclose (FILE *f, const char *filename) +{ + assert (f); + + if (fclose (f) == EOF) + FATAL_PERROR (filename); +} + +/* xfseek.c: fseek with error checking. */ + +void +xfseek (FILE *f, long offset, int wherefrom, char *filename) +{ + if (fseek (f, offset, wherefrom) < 0) + FATAL_PERROR (filename); +} + +/* xftell.c: ftell with error checking. */ + +unsigned long +xftell (FILE *f, char *filename) +{ + long where = ftell (f); + + if (where < 0) + FATAL_PERROR (filename); + + return where; +} + +/* xopendir.c: opendir and closedir with error checking. */ + +#ifndef WIN32 +DIR * +xopendir (char *dirname) +{ + DIR *d = opendir (dirname); + + if (d == NULL) + FATAL_PERROR (dirname); + + return d; +} +#endif /* not WIN32 */ + +void +xclosedir (DIR *d) +{ +#ifdef CLOSEDIR_VOID + closedir (d); +#else + int ret = closedir (d); + + if (ret != 0) + FATAL ("closedir failed"); +#endif +} + +/* xstat.c: stat and (maybe) lstat with error checking. */ + +struct stat +xstat (const char *path) +{ + struct stat s; + + if (stat (path, &s) != 0) + FATAL_PERROR (path); + + return s; +} + +/* If we don't have symbolic links, lstat is the same as stat, and + a #define is made in the include file. */ + +#ifdef S_ISLNK +struct stat +xlstat (const char *path) +{ + struct stat s; + + if (lstat (path, &s) != 0) + FATAL_PERROR (path); + + return s; +} +#endif + +/* xgetcwd.c: a from-scratch version of getwd. Ideas from the tcsh 5.20 + source, apparently uncopyrighted. */ + +#if ! (defined (HAVE_GETCWD) || defined (HAVE_GETWD)) + +static void +xchdir (char *dirname) +{ + if (chdir (dirname) != 0) + FATAL_PERROR (dirname); +} + +#endif /* not HAVE_GETCWD && not HAVE_GETWD */ + +/* Return the pathname of the current directory, or give a fatal error. */ + +char * +xgetcwd (void) +{ + /* If the system provides getcwd, use it. If not, use getwd if + available. But provide a way not to use getcwd: on some systems + getcwd forks, which is expensive and may in fact be impossible for + large programs like tex. If your system needs this define and it + is not detected by configure, let me know. + -- Olaf Weber <infovore@xs4all.nl */ +#if defined (HAVE_GETCWD) && !defined (GETCWD_FORKS) + char *path = (char *) xmalloc (PATH_MAX + 1); + + if (getcwd (path, PATH_MAX + 1) == 0) + { + fprintf (stderr, "getcwd: %s", path); + exit (1); + } + + return path; +#elif defined (HAVE_GETWD) + char *path = (char *) xmalloc (PATH_MAX + 1); + + if (getwd (path) == 0) + { + fprintf (stderr, "getwd: %s", path); + exit (1); + } + + return path; +#else /* not HAVE_GETCWD && not HAVE_GETWD */ + struct stat root_stat, cwd_stat; + char *cwd_path = (char *) xmalloc (2); /* In case we assign "/" below. */ + + *cwd_path = 0; + + /* Find the inodes of the root and current directories. */ + root_stat = xstat ("/"); + cwd_stat = xstat ("."); + + /* Go up the directory hierarchy until we get to root, prepending each + directory we pass through to `cwd_path'. */ + while (!SAME_FILE_P (root_stat, cwd_stat)) + { + struct dirent *e; + DIR *parent_dir; + int found = 0; + + xchdir (".."); + parent_dir = xopendir ("."); + + /* Look through the parent directory for the entry with the same + inode, so we can get its name. */ + while ((e = readdir (parent_dir)) != NULL && !found) + { + struct stat test_stat; + test_stat = xlstat (e->d_name); + + if (SAME_FILE_P (test_stat, cwd_stat)) + { + /* We've found it. Prepend the pathname. */ + char *temp = cwd_path; + cwd_path = concat3 ("/", e->d_name, cwd_path); + free (temp); + + /* Set up to test the next parent. */ + cwd_stat = xstat ("."); + + /* Stop reading this directory. */ + found = 1; + } + } + if (!found) + FATAL2 ("No inode %d/device %d in parent directory", + cwd_stat.st_ino, cwd_stat.st_dev); + + xclosedir (parent_dir); + } + + /* If the current directory is the root, cwd_path will be the empty + string, and we will have not gone through the loop. */ + if (*cwd_path == 0) + strcpy (cwd_path, "/"); + else + /* Go back to where we were. */ + xchdir (cwd_path); + +#ifdef DOSISH + /* Prepend the drive letter to CWD_PATH, since this technique + never tells us what the drive is. + + Note that on MS-DOS/MS-Windows, the branch that works around + missing `getwd' will probably only work for DJGPP (which does + have `getwd'), because only DJGPP reports meaningful + st_ino numbers. But someday, somebody might need this... */ + { + char drive[3]; + char *temp = cwd_path; + + /* Make the drive letter lower-case, unless it is beyond Z: (yes, + there ARE such drives, in case of Novell Netware on MS-DOS). */ + drive[0] = root_stat.st_dev + (root_stat.st_dev < 26 ? 'a' : 'A'); + drive[1] = ':'; + drive[2] = '\0'; + + cwd_path = concat (drive, cwd_path); + free (temp); + } +#endif + + return cwd_path; +#endif /* not HAVE_GETCWD && not HAVE_GETWD */ +} + +/* xmalloc.c: malloc with error checking. */ + +void * +xmalloc (unsigned size) +{ + void *new_mem = (void *) malloc (size); + + if (new_mem == NULL) + { + fprintf (stderr, "fatal: memory exhausted (xmalloc of %u bytes).\n", + size); + /* 1 means success on VMS, so pick a random number (ASCII `K'). */ + exit (75); + } + + return new_mem; +} + +/* xrealloc.c: realloc with error checking. */ + +extern void *xmalloc (unsigned); + +void * +xrealloc (void *old_ptr, unsigned size) +{ + void *new_mem; + + if (old_ptr == NULL) + new_mem = xmalloc (size); + else + { + new_mem = (void *) realloc (old_ptr, size); + if (new_mem == NULL) + { + /* We used to print OLD_PTR here using %x, and casting its + value to unsigned, but that lost on the Alpha, where + pointers and unsigned had different sizes. Since the info + is of little or no value anyway, just don't print it. */ + fprintf (stderr, "fatal: memory exhausted (realloc of %u bytes).\n", + size); + /* 1 means success on VMS, so pick a random number (ASCII `B'). */ + exit (66); + } + } + + return new_mem; +} + +/* xstrdup.c: strdup with error checking. */ + +/* Return a copy of S in new storage. */ + +char * +xstrdup (const char *s) +{ + char *new_string = (char *) xmalloc (strlen (s) + 1); + return strcpy (new_string, s); +} + +/* basename.c: return the last element in a path. */ + +#ifndef HAVE_BASENAME + +/* Return NAME with any leading path stripped off. This returns a + pointer into NAME. For example, `basename ("/foo/bar.baz")' + returns "bar.baz". */ + +const char * +basename (const char *name) +{ + const char *base = NULL; + unsigned len = strlen (name); + + for (len = strlen (name); len > 0; len--) { + if (IS_DIR_SEP (name[len - 1]) || IS_DEVICE_SEP (name[len - 1])) { + base = name + len; + break; + } + } + + if (!base) + base = name; + + return base; +} + +#endif + +char * +xbasename (const char *name) +{ + return (char *) basename (name); +} + +/* file-p.c: file predicates. */ + +/* Test whether FILENAME1 and FILENAME2 are actually the same file. If + stat fails on either of the names, we return false, without error. */ + +int +same_file_p (const char *filename1, const char *filename2) +{ + struct stat sb1, sb2; + /* These are put in variables only so the results can be inspected + under gdb. */ + int r1 = stat (filename1, &sb1); + int r2 = stat (filename2, &sb2); + + return r1 == 0 && r2 == 0 ? SAME_FILE_P (sb1, sb2) : 0; +} + +/* dir.c: directory operations. */ + +/* Return true if FN is a directory or a symlink to a directory, + false if not. */ + +int +dir_p (const char *fn) +{ +#ifdef WIN32 + int fa = GetFileAttributes(fn); + return (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat stats; + return stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode); +#endif +} + +#ifndef WIN32 + +/* 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. */ + +int +dir_links (const char *fn) +{ + static hash_table_type link_table; + char **hash_ret; + long 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 = 1; +#endif + + hash_ret = hash_lookup (link_table, fn); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + kpse_debug_hash_lookup_int = 0; +#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) + ret = (long) *hash_ret; + else + { + struct stat stats; + ret = stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode) + ? stats.st_nlink : (unsigned) -1; + + /* It's up to us to copy the value. */ + hash_insert (&link_table, xstrdup (fn), (const char *) ret); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_STAT)) + DEBUGF2 ("dir_links(%s) => %ld\n", fn, ret); +#endif + } + + return ret; +} + +#endif /* !WIN32 */ + +/* hash.c: hash table operations. */ + +/* The hash function. We go for simplicity here. */ + +/* All our hash tables are related to filenames. */ +#ifdef MONOCASE_FILENAMES +#define TRANSFORM(x) toupper (x) +#else +#define TRANSFORM(x) (x) +#endif + +static unsigned +hash (hash_table_type table, const char *key) +{ + unsigned n = 0; + + /* Our keys aren't often anagrams of each other, so no point in + weighting the characters. */ + while (*key != 0) + n = (n + n + TRANSFORM (*key++)) % table.size; + + return n; +} + +hash_table_type +hash_create (unsigned size) +{ + /* hash_table_type ret; changed into "static ..." to work around gcc + optimizer bug for Alpha. */ + static hash_table_type ret; + unsigned b; + ret.buckets = XTALLOC (size, hash_element_type *); + ret.size = size; + + /* calloc's zeroes aren't necessarily NULL, so be safe. */ + for (b = 0; b <ret.size; b++) + ret.buckets[b] = NULL; + + return ret; +} + +/* Whether or not KEY is already in MAP, insert it and VALUE. Do not + duplicate the strings, in case they're being purposefully shared. */ + +void +hash_insert (hash_table_type *table, const char *key, const char *value) +{ + unsigned n = hash (*table, key); + hash_element_type *new_elt = XTALLOC1 (hash_element_type); + + new_elt->key = key; + new_elt->value = value; + new_elt->next = NULL; + + /* Insert the new element at the end of the list. */ + if (!table->buckets[n]) + /* first element in bucket is a special case. */ + table->buckets[n] = new_elt; + else + { + hash_element_type *loc = table->buckets[n]; + while (loc->next) /* Find the last element. */ + loc = loc->next; + loc->next = new_elt; /* Insert the new one after. */ + } +} + +/* Remove a (KEY, VALUE) pair. */ + +void +hash_remove (hash_table_type *table, const char *key, const char *value) +{ + hash_element_type *p; + hash_element_type *q; + unsigned n = hash (*table, key); + + /* Find pair. */ + for (q = NULL, p = table->buckets[n]; p != NULL; q = p, p = p->next) + if (FILESTRCASEEQ (key, p->key) && STREQ (value, p->value)) + break; + if (p) { + /* We found something, remove it from the chain. */ + if (q) q->next = p->next; else table->buckets[n] = p->next; + /* We cannot dispose of the contents. */ + free (p); + } +} + +/* Look up STR in MAP. Return a (dynamically-allocated) list of the + corresponding strings or NULL if no match. */ + +#ifdef KPSE_DEBUG +/* Print the hash values as integers if this is nonzero. */ +int kpse_debug_hash_lookup_int = 0; +#endif + +char ** +hash_lookup (hash_table_type table, const char *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, (char *) 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 + { + char **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); +} + +/* We only print nonempty buckets, to decrease output volume. */ + +void +hash_print (hash_table_type table, int summary_only) +{ + unsigned b; + unsigned total_elements = 0, total_buckets = 0; + + for (b = 0; b < table.size; b++) { + hash_element_type *bucket = table.buckets[b]; + + if (bucket) { + unsigned len = 1; + hash_element_type *tb; + + total_buckets++; + if (!summary_only) fprintf (stderr, "%4d ", b); + + for (tb = bucket->next; tb != NULL; tb = tb->next) + len++; + if (!summary_only) fprintf (stderr, ":%-5d", len); + total_elements += len; + + if (!summary_only) { + for (tb = bucket; tb != NULL; tb = tb->next) + fprintf (stderr, " %s=>%s", tb->key, tb->value); + putc ('\n', stderr); + } + } + } + + fprintf (stderr, + "%u buckets, %u nonempty (%u%%); %u entries, average chain %.1f.\n", + table.size, + total_buckets, + 100 * total_buckets / table.size, + total_elements, + total_buckets ? total_elements / (double) total_buckets : 0.0); +} + +/* concat.c: dynamic string concatenation. */ + +/* Return the concatenation of S1 and S2. See `concatn.c' for a + `concatn', which takes a variable number of arguments. */ + +char * +concat (const char *s1, const char *s2) +{ + char *answer = (char *) xmalloc (strlen (s1) + strlen (s2) + 1); + strcpy (answer, s1); + strcat (answer, s2); + + return answer; +} + +/* concat3.c: concatenate three strings. */ + +char * +concat3 (const char *s1, const char *s2, const char *s3) +{ + char *answer + = (char *) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1); + strcpy (answer, s1); + strcat (answer, s2); + strcat (answer, s3); + + return answer; +} + +/* concatn.c: Concatenate an arbitrary number of strings. */ + +/* OK, it would be epsilon more efficient to compute the total length + and then do the copying ourselves, but I doubt it matters in reality. */ + +char * +concatn (const char *str1, ...) +{ + char *arg; + char *ret; + va_list ap; + + va_start (ap, str1); + + if (!str1) + return NULL; + + ret = xstrdup (str1); + + while ((arg = va_arg (ap, char *)) != NULL) + { + char *temp = concat (ret, arg); + free (ret); + ret = temp; + } + va_end (ap); + + return ret; +} + +/* debug.c: Help the user discover what's going on. */ + +#ifdef KPSE_DEBUG + +unsigned kpathsea_debug = 0; + +/* If the real definitions of fopen or fclose are macros, we lose -- the + #undef won't restore them. */ + +FILE * +fopen (const char *filename, const char *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 (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; +} + +#endif + +/* libc replacement functions for win32. */ + +/* + This does make sense only under WIN32. + Functions: + - popen() rewritten + - pclose() rewritten + - stat() wrapper for _stat(), removing trailing slashes + */ + +#ifdef WIN32 + +#include <fcntl.h> + +/* The X library (among other things) defines `FALSE' and `TRUE', and so + we only want to define them if necessary, for use by application code. */ +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif /* FALSE */ + +struct _popen_elt { + FILE *f; /* File stream returned */ + HANDLE hp; /* Handle of associated process */ + struct _popen_elt *next; /* Next list element */ +}; + +static struct _popen_elt _z = { NULL, 0, &_z }; +static struct _popen_elt *_popen_list = &_z; + +FILE *popen (const char *cmd, const char *mode) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + FILE *f = NULL; + int fno, i; + HANDLE child_in, child_out; + HANDLE father_in, father_out; + HANDLE father_in_dup, father_out_dup; + HANDLE current_in, current_out; + HANDLE current_pid; + int binary_mode; + char *new_cmd, *app_name = NULL; + char *p, *q; + struct _popen_elt *new_process; + char pname[PATH_MAX], *fp; + char *suffixes[] = { ".bat", ".cmd", ".com", ".exe", NULL }; + char **s; + int go_on; + + /* We should look for the application name along the PATH, + and decide to prepend "%COMSPEC% /c " or not to the command line. + Do nothing for the moment. */ + + /* Another way to do that would be to try CreateProcess first without + invoking cmd, and look at the error code. If it fails because of + command not found, try to prepend "cmd /c" to the cmd line. + */ + + /* Look for the application name */ + for (p = cmd; *p && isspace(*p); p++); + if (*p == '"') { + q = ++p; + while(*p && *p != '"') p++; + if (*p != '\0') { + fprintf(stderr, "popen: malformed command (\" not terminated)\n"); + return NULL; + } + } + else + for (q = p; *p && !isspace(*p); p++); + /* q points to the beginning of appname, p to the last + 1 char */ + if ((app_name = malloc(p - q + 1)) == NULL) { + fprintf(stderr, "xpopen: malloc(app_name) failed.\n"); + return NULL; + } + strncpy(app_name, q, p - q ); + app_name[p - q] = '\0'; + pname[0] = '\0'; +#ifdef TRACE + fprintf(stderr, "popen: app_name = %s\n", app_name); +#endif + + /* Looking for appname on the path */ + for (s = suffixes, go_on = 1; go_on; *s++) { + if (SearchPath(NULL, /* Address of search path */ + app_name, /* Address of filename */ + *s, /* Address of extension */ + PATH_MAX, /* Size of destination buffer */ + pname, /* Address of destination buffer */ + &fp) /* File part of app_name */ + != 0) { +#ifdef TRACE + fprintf(stderr, "%s found with suffix %s\n", app_name, *s); +#endif + new_cmd = xstrdup(cmd); + free(app_name); + app_name = xstrdup(pname); + break; + } + go_on = (*s != NULL); + } + if (go_on == 0) { + /* the app_name was not found */ +#ifdef TRACE + fprintf(stderr, "%s not found, concatenating comspec\n", app_name); +#endif + new_cmd = concatn(getenv("COMSPEC"), " /c ", cmd, NULL); + free(app_name); + app_name = NULL; + } + else { + } +#ifdef TRACE + fprintf(stderr, "popen: app_name = %s\n", app_name); + fprintf(stderr, "popen: cmd_line = %s\n", new_cmd); +#endif + + current_in = GetStdHandle(STD_INPUT_HANDLE); + current_out = GetStdHandle(STD_OUTPUT_HANDLE); + current_pid = GetCurrentProcess(); + ZeroMemory( &si, sizeof(STARTUPINFO) ); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + if (strchr(mode, 'b')) + binary_mode = _O_BINARY; + else + binary_mode = _O_TEXT; + + /* Opening the pipe for writing */ + if (strchr(mode, 'w')) { + binary_mode |= _O_WRONLY; + if (CreatePipe(&child_in, &father_out, &sa, 0) == FALSE) { + fprintf(stderr, "popen: error CreatePipe\n"); + return NULL; + } +#if 0 + if (SetStdHandle(STD_INPUT_HANDLE, child_in) == FALSE) { + fprintf(stderr, "popen: error SetStdHandle child_in\n"); + return NULL; + } +#endif + si.hStdInput = child_in; + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + if (DuplicateHandle(current_pid, father_out, + current_pid, &father_out_dup, + 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { + fprintf(stderr, "popen: error DuplicateHandle father_out\n"); + return NULL; + } + CloseHandle(father_out); + fno = _open_osfhandle((long)father_out_dup, binary_mode); + f = _fdopen(fno, mode); + i = setvbuf( f, NULL, _IONBF, 0 ); + } + /* Opening the pipe for reading */ + else if (strchr(mode, 'r')) { + binary_mode |= _O_RDONLY; + if (CreatePipe(&father_in, &child_out, &sa, 0) == FALSE) { + fprintf(stderr, "popen: error CreatePipe\n"); + return NULL; + } +#if 0 + if (SetStdHandle(STD_OUTPUT_HANDLE, child_out) == FALSE) { + fprintf(stderr, "popen: error SetStdHandle child_out\n"); + return NULL; + } +#endif + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = child_out; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + if (DuplicateHandle(current_pid, father_in, + current_pid, &father_in_dup, + 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { + fprintf(stderr, "popen: error DuplicateHandle father_in\n"); + return NULL; + } + CloseHandle(father_in); + fno = _open_osfhandle((long)father_in_dup, binary_mode); + f = _fdopen(fno, mode); + i = setvbuf( f, NULL, _IONBF, 0 ); + } + else { + fprintf(stderr, "popen: invalid mode %s\n", mode); + return NULL; + } + + /* creating child process */ + if (CreateProcess(app_name, /* pointer to name of executable module */ + new_cmd, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + TRUE, /* handle inheritance flag */ + CREATE_NEW_CONSOLE, /* creation flags */ + NULL, /* pointer to environment */ + NULL, /* pointer to current directory */ + &si, /* pointer to STARTUPINFO */ + &pi /* pointer to PROCESS_INFORMATION */ + ) == FALSE) { + fprintf(stderr, "popen: CreateProcess %x\n", GetLastError()); + return NULL; + } + +#if 0 + /* Restoring saved values for stdin/stdout */ + if (SetStdHandle(STD_INPUT_HANDLE, current_in) == FALSE) + fprintf(stderr, "popen: error re-redirecting Stdin\n"); + if (SetStdHandle(STD_OUTPUT_HANDLE, current_out) == FALSE) + fprintf(stderr, "popen: error re-redirecting Stdout\n"); +#endif + /* Only the process handle is needed */ + if (CloseHandle(pi.hThread) == FALSE) { + fprintf(stderr, "popen: error closing thread handle\n"); + return NULL; + } + + if (new_cmd) free(new_cmd); + if (app_name) free(app_name); + +#if 0 + /* This does not seem to make sense for console apps */ + while (1) { + i = WaitForInputIdle(pi.hProcess, 5); /* Wait 5ms */ + if (i == 0xFFFFFFFF) { + fprintf(stderr, "popen: process can't initialize\n"); + return NULL; + } + else if (i == WAIT_TIMEOUT) + fprintf(stderr, "popen: warning, process still not initialized\n"); + else + break; + } +#endif + + /* Add the pair (f, pi.hProcess) to the list */ + if ((new_process = malloc(sizeof(struct _popen_elt))) == NULL) { + fprintf (stderr, "popen: malloc(new_process) error\n"); + return NULL; + } + /* Saving the FILE * pointer, access key for retrieving the process + handle later on */ + new_process->f = f; + /* Closing the unnecessary part of the pipe */ + if (strchr(mode, 'r')) { + CloseHandle(child_out); + } + else if (strchr(mode, 'w')) { + CloseHandle(child_in); + } + /* Saving the process handle */ + new_process->hp = pi.hProcess; + /* Linking it to the list of popen() processes */ + new_process->next = _popen_list; + _popen_list = new_process; + + return f; + +} + +int pclose (FILE *f) +{ + struct _popen_elt *p, *q; + int exit_code; + + /* Look for f is the access key in the linked list */ + for (q = NULL, p = _popen_list; + p != &_z && p->f != f; + q = p, p = p->next); + + if (p == &_z) { + fprintf(stderr, "pclose: error, file not found."); + return -1; + } + + /* Closing the FILE pointer */ + fclose(f); + + /* Waiting for the process to terminate */ + if (WaitForSingleObject(p->hp, INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "pclose: error, process still active\n"); + return -1; + } + + /* retrieving the exit code */ + if (GetExitCodeProcess(p->hp, &exit_code) == 0) { + fprintf(stderr, "pclose: can't get process exit code\n"); + return -1; + } + + /* Closing the process handle, this will cause the system to + remove the process from memory */ + if (CloseHandle(p->hp) == FALSE) { + fprintf(stderr, "pclose: error closing process handle\n"); + return -1; + } + + /* remove the elt from the list */ + if (q != NULL) + q->next = p->next; + else + _popen_list = p->next; + free(p); + + return exit_code; +} + +#endif + +/* find-suffix.c: return the stuff after a dot. */ + +/* Return pointer to first character after `.' in last directory element + of NAME. If the name is `foo' or `/foo.bar/baz', we have no extension. */ + +char * +find_suffix (const char *name) +{ + const char *slash_pos; + char *dot_pos = strrchr (name, '.'); + + if (dot_pos == NULL) + return NULL; + + for (slash_pos = name + strlen (name); + slash_pos > dot_pos && !IS_DIR_SEP (*slash_pos); + slash_pos--) + ; + + return slash_pos > dot_pos ? NULL : dot_pos + 1; +} + +/* rm-suffix.c: remove any suffix. */ + +/* Generic const warning -- see extend-fname.c. */ + +char * +remove_suffix (const char *s) +{ + char *ret; + const char *suffix = find_suffix (s); + + if (suffix) + { + /* Back up to before the dot. */ + suffix--; + ret = (char *) xmalloc (suffix - s + 1); + strncpy (ret, s, suffix - s); + ret[suffix - s] = 0; + } + else + ret = (char *) s; + + return ret; +} + +/* make-suffix.c: unconditionally add a filename suffix. */ + +/* Return a new string: S suffixed with SUFFIX, regardless of what it + was before. This returns a newly allocated string. */ + +char * +make_suffix (const char *s, const char *suffix) +{ + char *new_s; + const char *dot_pos = strrchr (s, '.'); + const char *slash_pos; + + for (slash_pos = s + strlen (s) - 1; slash_pos > dot_pos && slash_pos > s; + slash_pos--) { + if (IS_DIR_SEP (*slash_pos)) + break; + } + + if (dot_pos == NULL || slash_pos > dot_pos ) + new_s = concat3 (s, ".", suffix); + else + { + unsigned past_dot_index = dot_pos + 1 - s; + + new_s = (char *) xmalloc (past_dot_index + strlen (suffix) + 1); + strncpy (new_s, s, dot_pos + 1 - s); + strcpy (new_s + past_dot_index, suffix); + } + + return new_s; +} + +/* readable.c: check if a filename is a readable non-directory file. */ + +/* Truncate any too-long components in NAME, returning the result. It's + too bad this is necessary. See comments in readable.c for why. */ + +static char * +kpse_truncate_filename (const char *name) +{ + unsigned c_len = 0; /* Length of current component. */ + unsigned ret_len = 0; /* Length of constructed result. */ + + /* Allocate enough space. */ + char *ret = (char *) xmalloc (strlen (name) + 1); + + for (; *name; name++) + { + if (IS_DIR_SEP (*name) || IS_DEVICE_SEP (*name)) + { /* At a directory delimiter, reset component length. */ + c_len = 0; + } + else if (c_len > NAME_MAX) + { /* If past the max for a component, ignore this character. */ + continue; + } + + /* Copy this character. */ + ret[ret_len++] = *name; + c_len++; + } + ret[ret_len] = 0; + + return ret; +} + +/* If access can read FN, run stat (assigning to stat buffer ST) and + check that fn is not a directory. Don't check for just being a + regular file, as it is potentially useful to read fifo's or some + kinds of devices. */ + +#ifdef __DJGPP__ +/* `stat' is way too expensive for such a simple job. */ +#define READABLE(fn, st) \ + (access (fn, R_OK) == 0 && access (fn, D_OK) == -1) +#elif WIN32 +#define READABLE(fn, st) \ + (GetFileAttributes(fn) != 0xFFFFFFFF && \ + !(GetFileAttributes(fn) & FILE_ATTRIBUTE_DIRECTORY)) +#else +#define READABLE(fn, st) \ + (access (fn, R_OK) == 0 && stat (fn, &(st)) == 0 && !S_ISDIR (st.st_mode)) +#endif + +/* POSIX invented the brain-damage of not necessarily truncating + filename components; the system's behavior is defined by the value of + the symbol _POSIX_NO_TRUNC, but you can't change it dynamically! + + Generic const return warning. See extend-fname.c. */ + +char * +kpse_readable_file (const char *name) +{ + struct stat st; + char *ret; + + if (READABLE (name, st)) { + ret = (char *) name; + +#ifdef ENAMETOOLONG + } else if (errno == ENAMETOOLONG) { + ret = kpse_truncate_filename (name); + + /* Perhaps some other error will occur with the truncated name, so + let's call access again. */ + if (!READABLE (ret, st)) + { /* Failed. */ + if (ret != name) free (ret); + ret = NULL; + } +#endif /* ENAMETOOLONG */ + + } else { /* Some other error. */ + if (errno == EACCES) { /* Maybe warn them if permissions are bad. */ + perror (name); + } + ret = NULL; + } + + return ret; +} + +/* absolute.c: Test if a filename is absolute or explicitly relative. */ + +/* Sorry this is such a system-dependent mess, but I can't see any way + to usefully generalize. */ + +int +kpse_absolute_p (const char *filename, int relative_ok) +{ + int absolute = IS_DIR_SEP (*filename) +#ifdef DOSISH + /* Novell allows non-alphanumeric drive letters. */ + || (*filename && IS_DEVICE_SEP (filename[1])) +#endif /* DOSISH */ +#ifdef WIN32 + /* UNC names */ + || (*filename == '\\' && filename[1] == '\\') +#endif + ; + int explicit_relative + = relative_ok + && (*filename == '.' && (IS_DIR_SEP (filename[1]) + || (filename[1] == '.' && IS_DIR_SEP (filename[2])))); + + return absolute || explicit_relative; +} + +/* str-list.c: define routines for string lists. */ + +/* See the lib.h file for comments. */ + +str_list_type +str_list_init (void) +{ + str_list_type ret; + + STR_LIST_LENGTH (ret) = 0; + STR_LIST (ret) = NULL; + + return ret; +} + +void +str_list_add (str_list_type *l, char *s) +{ + STR_LIST_LENGTH (*l)++; + XRETALLOC (STR_LIST (*l), STR_LIST_LENGTH (*l), char *); + STR_LIST_LAST_ELT (*l) = s; +} + +/* May as well save some reallocations and do everything in a chunk + instead of calling str_list_add on each element. */ + +void +str_list_concat (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), char *); + + for (e = 0; e < STR_LIST_LENGTH (more); e++) + STR_LIST_ELT (*target, prev_len + e) = STR_LIST_ELT (more, e); +} + +/* Free the list (but not the elements within it). */ + +void +str_list_free (str_list_type *l) +{ + if (STR_LIST (*l)) + { + free (STR_LIST (*l)); + STR_LIST (*l) = NULL; + } +} + +/* str-llist.c: Implementation of a linked list of strings. */ + +/* Add the new string STR to the end of the list L. */ + +void +str_llist_add (str_llist_type *l, char *str) +{ + str_llist_elt_type *e; + str_llist_elt_type *new_elt = XTALLOC1 (str_llist_elt_type); + + /* The new element will be at the end of the list. */ + STR_LLIST (*new_elt) = str; + STR_LLIST_MOVED (*new_elt) = 0; + STR_LLIST_NEXT (*new_elt) = NULL; + + /* Find the current end of the list. */ + for (e = *l; e && STR_LLIST_NEXT (*e); e = STR_LLIST_NEXT (*e)) + ; + + if (!e) + *l = new_elt; + else + STR_LLIST_NEXT (*e) = new_elt; +} + +/* Move an element towards the top. The idea is that when a file is + found in a given directory, later files will likely be in that same + directory, and looking for the file in all the directories in between + is thus a waste. */ + +void +str_llist_float (str_llist_type *l, str_llist_elt_type *mover) +{ + str_llist_elt_type *last_moved, *unmoved; + + /* If we've already moved this element, never mind. */ + if (STR_LLIST_MOVED (*mover)) + return; + + /* Find the first unmoved element (to insert before). We're + guaranteed this will terminate, since MOVER itself is currently + unmoved, and it must be in L (by hypothesis). */ + for (last_moved = NULL, unmoved = *l; STR_LLIST_MOVED (*unmoved); + last_moved = unmoved, unmoved = STR_LLIST_NEXT (*unmoved)) + ; + + /* If we are the first unmoved element, nothing to relink. */ + if (unmoved != mover) + { /* Remember `mover's current successor, so we can relink `mover's + predecessor to it. */ + str_llist_elt_type *before_mover; + str_llist_elt_type *after_mover = STR_LLIST_NEXT (*mover); + + /* Find `mover's predecessor. */ + for (before_mover = unmoved; STR_LLIST_NEXT (*before_mover) != mover; + before_mover = STR_LLIST_NEXT (*before_mover)) + ; + + /* `before_mover' now links to `after_mover'. */ + STR_LLIST_NEXT (*before_mover) = after_mover; + + /* Insert `mover' before `unmoved' and after `last_moved' (or at + the head of the list). */ + STR_LLIST_NEXT (*mover) = unmoved; + if (!last_moved) + *l = mover; + else + STR_LLIST_NEXT (*last_moved) = mover; + } + + /* We've moved it. */ + STR_LLIST_MOVED (*mover) = 1; +} + +/* fn.c: arbitrarily long filenames (or just strings). */ + +/* /usr/local/lib/texmf/fonts/public/cm/pk/ljfour/cmr10.300pk is 58 + chars, so ASCII `K' seems a good choice. */ +#define CHUNK_SIZE 75 + +fn_type +fn_init (void) +{ + fn_type ret; + + FN_ALLOCATED (ret) = FN_LENGTH (ret) = 0; + FN_STRING (ret) = NULL; + + return ret; +} + +fn_type +fn_copy0 (const char *s, unsigned len) +{ + fn_type ret; + + FN_ALLOCATED (ret) = CHUNK_SIZE > len ? CHUNK_SIZE : len + 1; + FN_STRING (ret) = (char *) xmalloc (FN_ALLOCATED (ret)); + + strncpy (FN_STRING (ret), s, len); + FN_STRING (ret)[len] = 0; + FN_LENGTH (ret) = len + 1; + + return ret; +} + +/* Don't think we ever try to free something that might usefully be + empty, so give fatal error if nothing allocated. */ + +void +fn_free (fn_type *f) +{ + assert (FN_STRING (*f) != NULL); + free (FN_STRING (*f)); + FN_STRING (*f) = NULL; + FN_ALLOCATED (*f) = 0; + FN_LENGTH (*f) = 0; +} + +/* An arithmetic increase seems more reasonable than geometric. We + don't increase the length member since it may be more convenient for + the caller to add than subtract when appending the stuff that will + presumably follow. */ + +static void +grow (fn_type *f, unsigned len) +{ + while (FN_LENGTH (*f) + len > FN_ALLOCATED (*f)) + { + FN_ALLOCATED (*f) += CHUNK_SIZE; + XRETALLOC (FN_STRING (*f), FN_ALLOCATED (*f), char); + } +} + +void +fn_1grow (fn_type *f, char c) +{ + grow (f, 1); + FN_STRING (*f)[FN_LENGTH (*f)] = c; + FN_LENGTH (*f)++; +} + +void +fn_grow (fn_type *f, void *source, unsigned len) +{ + grow (f, len); + strncpy (FN_STRING (*f) + FN_LENGTH (*f), (char *) source, len); + FN_LENGTH (*f) += len; +} + +void +fn_str_grow (fn_type *f, const char *s) +{ + unsigned more_len = strlen (s); + grow (f, more_len); + strcat (FN_STRING (*f), s); + FN_LENGTH (*f) += more_len; +} + +void +fn_shrink_to (fn_type *f, unsigned loc) +{ + assert (FN_LENGTH (*f) > loc); + FN_STRING (*f)[loc] = 0; + FN_LENGTH (*f) = loc + 1; +} + +/* variable.c: variable expansion. */ + +/* Here's the simple one, when a program just wants a value. */ + +char * +kpse_var_value (const char *var) +{ + char *ret = getenv (var); + + if (ret) + ret = kpse_var_expand (ret); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_VARS)) + DEBUGF2("variable: %s = %s\n", var, ret ? ret : "(nil)"); +#endif + + return ret; +} + +/* We have to keep track of variables being expanded, otherwise + constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop. + (Or indirectly recursive variables, etc.) Our simple solution is to + add to a list each time an expansion is started, and check the list + before expanding. */ + +typedef struct { + const char *var; + int expanding; +} expansion_type; +static expansion_type *expansions; /* The sole variable of this type. */ +static unsigned expansion_len = 0; + +static void +expanding (const char *var, int xp) +{ + unsigned e; + for (e = 0; e < expansion_len; e++) { + if (STREQ (expansions[e].var, var)) { + expansions[e].expanding = xp; + return; + } + } + + /* New variable, add it to the list. */ + expansion_len++; + XRETALLOC (expansions, expansion_len, expansion_type); + expansions[expansion_len - 1].var = xstrdup (var); + expansions[expansion_len - 1].expanding = xp; +} + + +/* Return whether VAR is currently being expanding. */ + +static int +expanding_p (const char *var) +{ + unsigned e; + for (e = 0; e < expansion_len; e++) { + if (STREQ (expansions[e].var, var)) + return expansions[e].expanding; + } + + return 0; +} + +/* Append the result of value of `var' to EXPANSION, where `var' begins + at START and ends at END. If `var' is not set, do not complain. + This is a subroutine for the more complicated expansion function. */ + +static void +expand (fn_type *expansion, const char *start, const char *end) +{ + char *value; + unsigned len = end - start + 1; + char *var = (char *) xmalloc (len + 1); + strncpy (var, start, len); + var[len] = 0; + + if (expanding_p (var)) { + WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var); + } else { + /* Check for an environment variable. */ + value = getenv (var); + + if (value) { + expanding (var, 1); + value = kpse_var_expand (value); + expanding (var, 0); + fn_grow (expansion, value, strlen (value)); + free (value); + } + + free (var); + } +} + +/* Can't think of when it would be useful to change these (and the + diagnostic messages assume them), but ... */ +#ifndef IS_VAR_START /* starts all variable references */ +#define IS_VAR_START(c) ((c) == '$') +#endif +#ifndef IS_VAR_CHAR /* variable name constituent */ +#define IS_VAR_CHAR(c) (ISALNUM (c) || (c) == '_') +#endif +#ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */ +#define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{') +#endif +#ifndef IS_VAR_END_DELIMITER +#define IS_VAR_END_DELIMITER(c) ((c) == '}') +#endif + + +/* Maybe we should support some or all of the various shell ${...} + constructs, especially ${var-value}. */ + +char * +kpse_var_expand (const char *src) +{ + const char *s; + char *ret; + fn_type expansion; + expansion = fn_init (); + + /* Copy everything but variable constructs. */ + for (s = src; *s; s++) { + if (IS_VAR_START (*s)) { + s++; + + /* Three cases: `$VAR', `${VAR}', `$<anything-else>'. */ + if (IS_VAR_CHAR (*s)) { + /* $V: collect name constituents, then expand. */ + const char *var_end = s; + + do { + var_end++; + } while (IS_VAR_CHAR (*var_end)); + + var_end--; /* had to go one past */ + expand (&expansion, s, var_end); + s = var_end; + + } else if (IS_VAR_BEGIN_DELIMITER (*s)) { + /* ${: scan ahead for matching delimiter, then expand. */ + const char *var_end = ++s; + + while (*var_end && !IS_VAR_END_DELIMITER (*var_end)) + var_end++; + + if (! *var_end) { + WARNING1 ("%s: No matching } for ${", src); + s = var_end - 1; /* will incr to null at top of loop */ + } else { + expand (&expansion, s, var_end - 1); + s = var_end; /* will incr past } at top of loop*/ + } + + } else { + /* $<something-else>: error. */ + WARNING2 ("%s: Unrecognized variable construct `$%c'", src, *s); + /* Just ignore those chars and keep going. */ + } + } else + fn_1grow (&expansion, *s); + } + fn_1grow (&expansion, 0); + + ret = FN_STRING (expansion); + return ret; +}