diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
commit | 487ca4806cc046076293cf6cc5fbba0db282bac7 (patch) | |
tree | 847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/contrib/fileutils.c | |
parent | a3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff) |
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/contrib/fileutils.c')
-rw-r--r-- | Build/source/texk/contrib/fileutils.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/Build/source/texk/contrib/fileutils.c b/Build/source/texk/contrib/fileutils.c new file mode 100644 index 00000000000..cfd9e3b0db2 --- /dev/null +++ b/Build/source/texk/contrib/fileutils.c @@ -0,0 +1,561 @@ +/* stackenv.c: routines that help to deal with files and directories. + +Copyright (C) 1997 Fabrice POPINEAU. + +Adapted to DJGPP by Eli Zaretskii. + +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. */ + +#include "fileutils.h" + +/* A few utility functions shared by both mktex and makempx. */ + +int mt_append_mask = 0; + +/* Execute a command, much like system() but using popen() */ +int +execute_command(string cmd) +{ + int retcode = 0; + FILE *fcmd = NULL; + if ((fcmd = popen(cmd, "r")) == NULL) + return -1; + retcode = pclose(fcmd); + if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { + fprintf(stderr, "executing %s => %d\n", cmd, retcode); + } + return retcode; +} + +/* Copy a file, appending if required. */ + +int catfile (const char *from, const char *to, int append) +{ + FILE *finp, *fout; + unsigned char buf[16*1024]; + int rbytes; + + finp = fopen (from, "rb"); + if (!finp) + return FALSE; + fout = fopen (to, append ? "ab" : "wb"); + if (!fout) { + fclose (finp); + return FALSE; + } + + while ((rbytes = fread (buf, 1, sizeof buf, finp)) > 0) { + int wbytes = fwrite (buf, 1, rbytes, fout); + + if (wbytes < rbytes) { + if (wbytes >= 0) + fprintf (stderr, "%s: disk full\n", to); + fclose (finp); + fclose (fout); + unlink (to); + return FALSE; + } + } + + fclose (finp); + fclose (fout); + return TRUE; +} + +/* Move a file using rename if possible, copy if necessary. */ + +int mvfile (const char *from, const char *to) +{ + int e = errno; + int retval = TRUE; + char errstring[PATH_MAX*4]; + + errno = 0; + if (rename (from, to) == 0) { + errno = e; + return TRUE; + } + +#ifndef DOSISH + /* I don't want to rely on this on DOSISH systems. First, not all + of them know about EXDEV. And second, DOS will fail `rename' + with EACCES when the target is more than 8 directory levels deep. */ + if (errno != EXDEV) + retval = FALSE; + else +#endif + + /* `rename' failed on cross-filesystem move. Copy, then delete. */ + if (catfile (from, to, 0) == FALSE) + retval = FALSE; + else + unlink (from); /* ignore possible errors */ + + /* Simulate error message from `mv'. */ + if (retval == FALSE) { + sprintf(errstring, "%s: can't move %s to %s", kpse_program_name, from, to); + perror(errstring); + } + + if (!errno) + errno = e; + return retval; +} + +/* Like the test function from Unix */ + +boolean test_file(int c, string path) +{ + struct stat stat_buf; + boolean file_exists; + + if (c == 'z') + return (path == NULL || *path == '\0'); + + if (c == 'n') + return (path != NULL && *path != '\0'); + +#ifdef WIN32 + /* Bug under W95 */ + if (c == 'd') + return dir_p(path); +#endif + + file_exists = (stat(path, &stat_buf) != -1); + +#ifdef DOSISH + /* When they say "test -x foo", make so we find "foo.exe" etc. */ + if (c == 'x' && !file_exists) + { + static string try_ext[] = { + ".exe", + ".bat", + ".cmd", + ".btm", + 0 + }; + string *p = try_ext; + + while (p) + { + string try = concat(path, *p++); + if (stat(try, &stat_buf) != -1) + { + file_exists = 1; + free(try); + break; + } + free(try); + } + } +#endif + + if (!file_exists) return FALSE; + switch (c) { + case 'd': + return ((stat_buf.st_mode & S_IFMT) == S_IFDIR); + case 'e': + case 'r': + return TRUE; + case 'f': + return ((stat_buf.st_mode & S_IFMT) == S_IFREG); + case 's': + return (stat_buf.st_size > 0); + case 'x': + return ((stat_buf.st_mode & S_IFMT) == S_IFREG + && (stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))); + case 'w': + { + return ( +#ifdef DOSISH + /* DOSISH systems allow to create files in read-only + directories, and `is_writable' knows about that. So we + artificially look at the write bit returned by `stat' + and refuse to write in read-only directories. */ + (stat_buf.st_mode & S_IWUSR) != 0 && +#endif + is_writable(path)); + } + default: + return FALSE; + } +} + +#ifdef _WIN32 + +boolean is_writable(string path) +{ + DWORD dw; + char tmppath[PATH_MAX*2]; + int writable; + + /* Test 2 cases: directory or file */ + if ((dw = GetFileAttributes(path)) == 0xFFFFFFFF) { + /* Invalid Path, not writable */ + writable = false; + errno = ENOENT; /* like `access' does */ + } + else if (dw & FILE_ATTRIBUTE_READONLY) { + writable = false; + } + else if (dw & FILE_ATTRIBUTE_DIRECTORY) { + /* Trying to open a file */ + if (GetTempFileName(path, "foo", 0, tmppath) == 0) { + writable = false; + } + else { + writable = true; + } + DeleteFile(tmppath); + } + else { + writable = true; + } + return writable; +} + +#else /* !_WIN32 */ + +boolean is_writable(string path) +{ + if (!path || !*path) /* like `access' called with empty variable as arg */ + return false; +#ifdef MSDOS + /* Directories always return as writable, even on read-only filesystems + such as CD-ROMs or write-protected floppies. Need to try harder... */ + if (access(path, D_OK) == 0) { + /* Use a unique file name. */ + string template = concat3(path, "/", "wrXXXXXX"); + int fd; + + if (mktemp(template) && (fd = creat(template, S_IRUSR | S_IWUSR)) >= 0) { + close(fd); + unlink(template); + free(template); + return true; + } + + free(template); + + /* Simulate errno from `access'. */ +#ifdef EROFS + errno = EROFS; /* POSIX platforms should have this */ +#else + errno = EACCES; +#endif + return false; + } + else +#endif /* MSDOS */ + return access(path, W_OK) == 0 ? true : false; +} + +#endif /* !_WIN32 */ + +/* + Recursive walk through the directory tree. Depth-first order. +*/ +void recurse_dir(string path, execfn before, execfn after) +{ + /* In depth traversal of the subdir tree */ +#if defined(_WIN32) + WIN32_FIND_DATA find_file_data; + HANDLE hnd; +#else + DIR *dp; + struct dirent *ep; +#endif + struct stat stat_buf; /* We have to have one local because + of after */ + + int path_len = strlen(path); + + /* current node */ + if (stat(path, &stat_buf)) + perror(path); + + /* execute before for the current node */ + if (before) + (*before)(path, &stat_buf); + + /* if it is a directory, recurse through all sons */ + if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) { +#if defined(_WIN32) + strcat(path, "/*"); + hnd = FindFirstFile(path, &find_file_data); + while (hnd != INVALID_HANDLE_VALUE && + FindNextFile(hnd, &find_file_data) != FALSE) { + if(!strcmp(find_file_data.cFileName, ".") + || !strcmp(find_file_data.cFileName, "..")) + continue; + path[path_len+1] = '\0'; + strcat(path, find_file_data.cFileName); + recurse_dir(path, before, after); + } + path[path_len] = '\0'; + FindClose(hnd); +#else + if ((dp = opendir(path))) { + while ((ep = readdir(dp))) + if (strcmp(ep->d_name, ".") && + strcmp(ep->d_name, "..")) { + path[path_len] = '/'; + strcpy(path+path_len+1, ep->d_name); + recurse_dir(path, before, after); + } + path[path_len] = '\0'; + closedir(dp); + } + else + perror(path); +#endif + } + /* execute after for the current node */ + if (after) + (*after)(path, &stat_buf); +} + +void do_rmdir(string path) +{ + static char name[PATH_MAX]; + strcpy(name,path); + + if (test_file('d', path)) { + recurse_dir(name, NULL, remove_path); + } + else + unlink(path); +} + +/* Recursively destructs files & directories from path. */ +void rec_rmdir(string path) +{ + string p; + + for(p=path+strlen(path)-1; IS_DIR_SEP(*p) && (p > path) ; p--) { + *p = '\0'; + } + do_rmdir(path); +} + +/* Creates path, and all intermediate directories if necessary. */ + +int do_makedir(string path) +{ + struct stat stat_buf; + string p = path; + mode_t dir_perms = 0777 & ~umask (0); /* default permissions for `mkdir' */ + mode_t parent_perms = 0; + + /* We can't authorize multiple slashes here */ + normalize(path); + + if (KPSE_DEBUG_P(MKTEX_DEBUG)) { + fprintf(stderr, "Creating directory: %s\n", path); + } + + if (stat(path, &stat_buf) == 0 && + (stat_buf.st_mode & S_IFMT) == S_IFDIR) + return 0; /* The path already exists */ + +#ifdef WIN32 + /* Handling of UNC names */ + if (IS_UNC_NAME(path)) { + /* UNC pathname : \\server\dir\... + We must begin at the third component. + Name is normalized, so only '/' as dir sep. + */ + p = strchr(path+3, '/'); + if (!p) { + /* invalid pathname */ + return ENOTDIR; + } + p = strchr(p+1, '/'); + if (!p) { + /* invalid pathname */ + return ENOTDIR; + } + p++; + } + else { + if (NAME_BEGINS_WITH_DEVICE(path)) { + p = path+2; + } + if (IS_DIR_SEP(*p)) + p++; + } +#else + /* Finding the [drive letter][:] */ + if (IS_DEVICE_SEP(*(p+1))) + p +=2; + + if (IS_DIR_SEP(*p)) + p++; +#endif + for ( ; *p ; p++) { + if (IS_DIR_SEP(*p)) { + *p = '\0'; + errno = 0; + if (!test_file('d', path)) { + if ((mkdir(path, dir_perms) == -1)) { + return errno; + } else if (parent_perms == 0 && errno == 0) { + /* The first time we actually create a directory, remember the + permission bits of its parent. */ + string parent = concat(path, "/.."); + + if (stat(parent, &stat_buf) == 0) { + parent_perms = stat_buf.st_mode; + /* Use the correct bits from now on. */ + dir_perms = parent_perms | mt_append_mask; + } + free(parent); + } + /* chmod the directory we just created. */ + chmod(path, dir_perms); + } + *p = DIR_SEP; + } + } + /* and for the whole path */ + if (mkdir(path, dir_perms) == -1) { + return errno; + } + chmod(path, dir_perms); + + return 0; +} + +/* remove something which already exists */ +void remove_path(string path, struct stat* st) +{ + int e = errno; + + if ((st->st_mode & S_IFMT) == S_IFDIR) { + errno = 0; + if (rmdir(path) == -1 && errno == EBUSY) { + /* `rmdir' will fail (at least on MS-DOS) if PATH is the current + working directory on another drive, or the parent of the + working directory. To work around that, we chdir to the root + of that drive, then try to rmdir again. */ + char drv_root[4], *p = drv_root; + + if (path[0] && path[1] == ':') { + *p++ = path[0]; + *p++ = path[1]; + } + *p++ = '/'; + *p = '\0'; + pushd(drv_root); + if (rmdir(path) == 0) { + errno = e; + popd(); + return; + } + } + else if (errno) + perror(path); + else + errno = e; + } + else { + if (unlink(path) == -1) + perror(path); + } +} + +/* + Rewrite path with the same DIR_SEP and deleting multiple ones + FIXME: does this eliminates the trailing ones ? W95 doesn't grok + stat() on pathes that end up in PATH_SEP. + */ +char *normalize(string path) +{ + string p, q; + int offset = 0; + + /* Be safe */ + if (path == NULL) return path; + + /* Delete any multiple slashes, except leading ones on WIN32 (UNC names) */ +#ifdef WIN32 + if (IS_UNC_NAME(path)) { + path[0] = path[1] = DIR_SEP; + offset = 2; + } +#endif + for ( p = q = path+offset; *p != '\0'; p++, q++) { + *q = (IS_DIR_SEP(*p) ? DIR_SEP : *p); + if (IS_DIR_SEP(*p)) + while (*(p+1) && IS_DIR_SEP(*(p+1))) p++; + } + *q = '\0'; + + return path; +} + + +/* Smart concatenation of 2 pathes: add the separator only + when needed. Allocates a new path. */ +string concat_pathes(const_string p1, const_string p2) +{ + string res; + if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { + fprintf(stderr, "Concat pathes %s and %s\n", p1, p2); + } + + assert (p1 && p2); + + res = normalize(concat3(p1, DIR_SEP_STRING, p2)); + + if (KPSE_DEBUG_P(MKTEX_FINE_DEBUG)) { + fprintf(stderr, "\t=> %s\n", res); + } + + return res; +} + +/* + Kpathsea's basename does not strip suffixes + */ +string my_basename(string name, string suffix) +{ + string res = (string)xbasename(name); + if (suffix) { + int suffix_len = strlen(suffix); + if (FILESTRCASEEQ(res+strlen(res)-suffix_len, suffix)) + *(res+strlen(res)-suffix_len) = '\0'; + } + return xstrdup(res); +} + +#if 0 +/* + Kpathsea's kpse_find_file() is not parametrized by '--progname'. + This is an attempt to do that. + */ + +/* The path spec we are defining, one element of the global array. */ +#define FMT_INFO kpse_format_info[format] +/* Call add_suffixes. */ +#define SUFFIXES(args) add_suffixes(&FMT_INFO.suffix, args, NULL) +#define ALT_SUFFIXES(args) add_suffixes (&FMT_INFO.alt_suffix, args, NULL) + +/* Call `init_path', including appending the trailing NULL to the envvar + list. Also initialize the fields not needed in setting the path. */ +#define INIT_FORMAT(text, default_path, envs) \ + FMT_INFO.type = text; \ + init_path (&FMT_INFO, default_path, envs, NULL) +#endif |