Update libarchive, tar and cpio to version 2.8.5
The following additional vendor revisions are applied: Revision 3740: Use archive_clear_error() to clear the error markers. Obtained from: http://code.google.com/p/libarchive MFC after: 2 weeks
This commit is contained in:
parent
f873e4d1bd
commit
f6ccfb42a2
@ -52,7 +52,7 @@
|
||||
/* These should match the types used in 'struct stat' */
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#define __LA_INT64_T __int64
|
||||
# if defined(_SSIZE_T_DEFINED)
|
||||
# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
|
||||
# define __LA_SSIZE_T ssize_t
|
||||
# elif defined(_WIN64)
|
||||
# define __LA_SSIZE_T __int64
|
||||
@ -98,6 +98,13 @@
|
||||
# define __LA_DECL
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
#define __LA_PRINTF(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
||||
#else
|
||||
#define __LA_PRINTF(fmtarg, firstvararg) /* nothing */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -129,13 +136,13 @@ extern "C" {
|
||||
* (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
|
||||
* #endif
|
||||
*/
|
||||
#define ARCHIVE_VERSION_NUMBER 2008004
|
||||
#define ARCHIVE_VERSION_NUMBER 2008005
|
||||
__LA_DECL int archive_version_number(void);
|
||||
|
||||
/*
|
||||
* Textual name/version of the library, useful for version displays.
|
||||
*/
|
||||
#define ARCHIVE_VERSION_STRING "libarchive 2.8.4"
|
||||
#define ARCHIVE_VERSION_STRING "libarchive 2.8.5"
|
||||
__LA_DECL const char * archive_version_string(void);
|
||||
|
||||
#if ARCHIVE_VERSION_NUMBER < 3000000
|
||||
@ -717,7 +724,7 @@ __LA_DECL const char *archive_format_name(struct archive *);
|
||||
__LA_DECL int archive_format(struct archive *);
|
||||
__LA_DECL void archive_clear_error(struct archive *);
|
||||
__LA_DECL void archive_set_error(struct archive *, int _err,
|
||||
const char *fmt, ...);
|
||||
const char *fmt, ...) __LA_PRINTF(3, 4);
|
||||
__LA_DECL void archive_copy_error(struct archive *dest,
|
||||
struct archive *src);
|
||||
__LA_DECL int archive_file_count(struct archive *);
|
||||
|
@ -715,7 +715,7 @@ archive_read_data_block(struct archive *_a,
|
||||
/*
|
||||
* Close the file and release most resources.
|
||||
*
|
||||
* Be careful: client might just call read_new and then read_finish.
|
||||
* Be careful: client might just call read_new and then read_free.
|
||||
* Don't assume we actually read anything or performed any non-trivial
|
||||
* initialization.
|
||||
*/
|
||||
|
@ -350,4 +350,4 @@ bzip2_filter_close(struct archive_read_filter *self)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* HAVE_BZLIB_H */
|
||||
#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
|
||||
|
@ -488,9 +488,9 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff)
|
||||
switch (uudecode->state) {
|
||||
default:
|
||||
case ST_FIND_HEAD:
|
||||
if (len - nl > 13 && memcmp(b, "begin ", 6) == 0)
|
||||
if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
|
||||
l = 6;
|
||||
else if (len - nl > 18 &&
|
||||
else if (len - nl >= 18 &&
|
||||
memcmp(b, "begin-base64 ", 13) == 0)
|
||||
l = 13;
|
||||
else
|
||||
|
@ -264,9 +264,9 @@ archive_read_format_cpio_read_header(struct archive_read *a,
|
||||
|
||||
/* Compare name to "TRAILER!!!" to test for end-of-archive. */
|
||||
if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
|
||||
/* TODO: Store file location of start of block. */
|
||||
archive_set_error(&a->archive, 0, NULL);
|
||||
return (ARCHIVE_EOF);
|
||||
/* TODO: Store file location of start of block. */
|
||||
archive_clear_error(&a->archive);
|
||||
return (ARCHIVE_EOF);
|
||||
}
|
||||
|
||||
/* Detect and record hardlinks to previously-extracted entries. */
|
||||
|
@ -934,14 +934,14 @@ read_children(struct archive_read *a, struct file_info *parent)
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Ignoring out-of-order directory (%s) %jd > %jd",
|
||||
parent->name.s,
|
||||
iso9660->current_position,
|
||||
parent->offset);
|
||||
(intmax_t)iso9660->current_position,
|
||||
(intmax_t)parent->offset);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if (parent->offset + parent->size > iso9660->volume_size) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Directory is beyond end-of-media: %s",
|
||||
parent->name);
|
||||
parent->name.s);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
if (iso9660->current_position < parent->offset) {
|
||||
@ -1139,7 +1139,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
|
||||
|
||||
if (file->offset + file->size > iso9660->volume_size) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"File is beyond end-of-media: %s", file->name);
|
||||
"File is beyond end-of-media: %s", file->name.s);
|
||||
iso9660->entry_bytes_remaining = 0;
|
||||
iso9660->entry_sparse_offset = 0;
|
||||
return (ARCHIVE_WARN);
|
||||
@ -1198,10 +1198,10 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
|
||||
if ((file->mode & AE_IFMT) != AE_IFDIR &&
|
||||
file->offset < iso9660->current_position) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Ignoring out-of-order file @%x (%s) %jd < %jd",
|
||||
file,
|
||||
"Ignoring out-of-order file (%s) %jd < %jd",
|
||||
iso9660->pathname.s,
|
||||
file->offset, iso9660->current_position);
|
||||
(intmax_t)file->offset,
|
||||
(intmax_t)iso9660->current_position);
|
||||
iso9660->entry_bytes_remaining = 0;
|
||||
iso9660->entry_sparse_offset = 0;
|
||||
return (ARCHIVE_WARN);
|
||||
@ -1524,8 +1524,8 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Ignoring out-of-order file (%s) %jd < %jd",
|
||||
iso9660->pathname.s,
|
||||
iso9660->entry_content->offset,
|
||||
iso9660->current_position);
|
||||
(intmax_t)iso9660->entry_content->offset,
|
||||
(intmax_t)iso9660->current_position);
|
||||
*buff = NULL;
|
||||
*size = 0;
|
||||
*offset = iso9660->entry_sparse_offset;
|
||||
@ -1626,8 +1626,8 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
|
||||
*/
|
||||
if (location > 0 &&
|
||||
(location + ((fsize + iso9660->logical_block_size -1)
|
||||
/ iso9660->logical_block_size)) >
|
||||
(unsigned int)iso9660->volume_block) {
|
||||
/ iso9660->logical_block_size))
|
||||
> (uint32_t)iso9660->volume_block) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Invalid location of extent of file");
|
||||
return (NULL);
|
||||
|
@ -576,7 +576,7 @@ tar_read_header(struct archive_read *a, struct tar *tar,
|
||||
h = __archive_read_ahead(a, 512, NULL);
|
||||
if (h != NULL)
|
||||
__archive_read_consume(a, 512);
|
||||
archive_set_error(&a->archive, 0, NULL);
|
||||
archive_clear_error(&a->archive);
|
||||
if (a->archive.archive_format_name == NULL) {
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_TAR;
|
||||
a->archive.archive_format_name = "tar";
|
||||
|
@ -211,7 +211,7 @@ archive_read_format_zip_bid(struct archive_read *a)
|
||||
/* Get 4k of data beyond where we stopped. */
|
||||
buff = __archive_read_ahead(a, offset + 4096,
|
||||
&bytes_avail);
|
||||
if (bytes_avail < offset + 1)
|
||||
if (buff == NULL)
|
||||
break;
|
||||
p = (const char *)buff + offset;
|
||||
while (p + 9 < (const char *)buff + bytes_avail) {
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
/*
|
||||
* Basic resizable/reusable string support a la Java's "StringBuffer."
|
||||
*
|
||||
@ -134,10 +136,11 @@ void __archive_string_free(struct archive_string *);
|
||||
|
||||
/* Like 'vsprintf', but resizes the underlying string as necessary. */
|
||||
void __archive_string_vsprintf(struct archive_string *, const char *,
|
||||
va_list);
|
||||
va_list) __LA_PRINTF(2, 0);
|
||||
#define archive_string_vsprintf __archive_string_vsprintf
|
||||
|
||||
void __archive_string_sprintf(struct archive_string *, const char *, ...);
|
||||
void __archive_string_sprintf(struct archive_string *, const char *, ...)
|
||||
__LA_PRINTF(2, 3);
|
||||
#define archive_string_sprintf __archive_string_sprintf
|
||||
|
||||
/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
|
||||
|
@ -1730,7 +1730,7 @@ create_dir(struct archive_write_disk *a, char *path)
|
||||
if (unlink(path) != 0) {
|
||||
archive_set_error(&a->archive, errno,
|
||||
"Can't create directory '%s': "
|
||||
"Conflicting file cannot be removed");
|
||||
"Conflicting file cannot be removed", path);
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
} else if (errno != ENOENT && errno != ENOTDIR) {
|
||||
|
@ -132,9 +132,10 @@ static int
|
||||
archive_compressor_xz_init_stream(struct archive_write *a,
|
||||
struct private_data *state)
|
||||
{
|
||||
static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
|
||||
int ret;
|
||||
|
||||
state->stream = (lzma_stream)LZMA_STREAM_INIT;
|
||||
state->stream = lzma_stream_init_data;
|
||||
state->stream.next_out = state->compressed;
|
||||
state->stream.avail_out = state->compressed_buffer_size;
|
||||
if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ)
|
||||
|
@ -440,7 +440,7 @@ archive_write_ar_finish_entry(struct archive_write *a)
|
||||
if (ar->entry_padding != 1) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Padding wrong size: %d should be 1 or 0",
|
||||
ar->entry_padding);
|
||||
(int)ar->entry_padding);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
|
@ -537,8 +537,7 @@ archive_write_shar_finish_entry(struct archive_write *a)
|
||||
}
|
||||
|
||||
if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
|
||||
archive_string_sprintf(&shar->work, "chflags %s ",
|
||||
p, archive_entry_pathname(shar->entry));
|
||||
archive_string_sprintf(&shar->work, "chflags %s ", p);
|
||||
shar_quote(&shar->work,
|
||||
archive_entry_pathname(shar->entry), 1);
|
||||
archive_strcat(&shar->work, "\n");
|
||||
|
@ -168,7 +168,7 @@ archive_write_set_format_ustar(struct archive *_a)
|
||||
|
||||
/* Basic internal sanity test. */
|
||||
if (sizeof(template_header) != 512) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header));
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", (int)sizeof(template_header));
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@
|
||||
#endif
|
||||
|
||||
#define HAVE_BSDXML_H 1
|
||||
#define HAVE_BZLIB_H 1
|
||||
#define HAVE_CHFLAGS 1
|
||||
#define HAVE_CHOWN 1
|
||||
#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1
|
||||
|
@ -65,6 +65,7 @@ Later variants have extended this by either appropriating undefined
|
||||
areas of the header record, extending the header to multiple records,
|
||||
or by storing special entries that modify the interpretation of
|
||||
subsequent entries.
|
||||
.Pp
|
||||
.Bl -tag -width indent
|
||||
.It Cm gnutar
|
||||
The
|
||||
|
@ -177,6 +177,8 @@ which provides a slightly more efficient interface.
|
||||
You may prefer to use the higher-level
|
||||
.Fn archive_read_data_skip ,
|
||||
which reads and discards the data for this entry,
|
||||
.Fn archive_read_data_to_buffer ,
|
||||
which reads the data into an in-memory buffer,
|
||||
.Fn archive_read_data_to_file ,
|
||||
which copies the data to the provided file descriptor, or
|
||||
.Fn archive_read_extract ,
|
||||
|
@ -52,7 +52,7 @@ TESTS= \
|
||||
test_read_format_gtar_gz.c \
|
||||
test_read_format_gtar_lzma.c \
|
||||
test_read_format_gtar_sparse.c \
|
||||
test_read_format_iso_gz.c \
|
||||
test_read_format_iso_Z.c \
|
||||
test_read_format_iso_multi_extent.c \
|
||||
test_read_format_isorr_rr_moved.c \
|
||||
test_read_format_isojoliet_bz2.c \
|
||||
|
@ -67,7 +67,7 @@ test_compat_zip_1(void)
|
||||
#if ARCHIVE_VERSION_NUMBER < 2000000
|
||||
archive_read_finish(a);
|
||||
#else
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ test1(void)
|
||||
ARCHIVE_COMPRESSION_COMPRESS);
|
||||
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
}
|
||||
|
||||
static
|
||||
@ -87,10 +87,10 @@ void test2(void)
|
||||
ARCHIVE_COMPRESSION_COMPRESS);
|
||||
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_ISO9660);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
|
||||
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_read_format_iso_gz)
|
||||
DEFINE_TEST(test_read_format_iso_Z)
|
||||
{
|
||||
test1();
|
||||
test2();
|
@ -3,7 +3,7 @@
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= bsdcpio
|
||||
BSDCPIO_VERSION_STRING=2.8.4
|
||||
BSDCPIO_VERSION_STRING=2.8.5
|
||||
|
||||
SRCS= cpio.c cmdline.c
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= bsdtar
|
||||
BSDTAR_VERSION_STRING=2.8.4
|
||||
BSDTAR_VERSION_STRING=2.8.5
|
||||
SRCS= bsdtar.c \
|
||||
cmdline.c \
|
||||
getdate.c \
|
||||
|
@ -48,6 +48,9 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_DIRECT_H
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
@ -66,6 +69,9 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "tree.h"
|
||||
|
||||
@ -76,27 +82,38 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
|
||||
struct tree_entry {
|
||||
int depth;
|
||||
struct tree_entry *next;
|
||||
struct tree_entry *parent;
|
||||
char *name;
|
||||
size_t dirname_length;
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
int flags;
|
||||
/* How to return back to the parent of a symlink. */
|
||||
#ifdef HAVE_FCHDIR
|
||||
int fd;
|
||||
int symlink_parent_fd;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
char *fullpath;
|
||||
char *symlink_parent_path;
|
||||
#else
|
||||
#error fchdir function required.
|
||||
#endif
|
||||
int flags;
|
||||
};
|
||||
|
||||
/* Definitions for tree_entry.flags bitmap. */
|
||||
#define isDir 1 /* This entry is a regular directory. */
|
||||
#define isDirLink 2 /* This entry is a symbolic link to a directory. */
|
||||
#define needsPreVisit 4 /* This entry needs to be previsited. */
|
||||
#define needsPostVisit 8 /* This entry needs to be postvisited. */
|
||||
#define needsFirstVisit 4 /* This is an initial entry. */
|
||||
#define needsDescent 8 /* This entry needs to be previsited. */
|
||||
#define needsOpen 16 /* This is a directory that needs to be opened. */
|
||||
#define needsAscent 32 /* This entry needs to be postvisited. */
|
||||
|
||||
/*
|
||||
* On Windows, "first visit" is handled as a pattern to be handed to
|
||||
* _findfirst(). This is consistent with Windows conventions that
|
||||
* file patterns are handled within the application. On Posix,
|
||||
* "first visit" is just returned to the client.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Local data for this package.
|
||||
@ -104,21 +121,28 @@ struct tree_entry {
|
||||
struct tree {
|
||||
struct tree_entry *stack;
|
||||
struct tree_entry *current;
|
||||
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
|
||||
HANDLE d;
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||
#define INVALID_DIR_HANDLE INVALID_HANDLE_VALUE
|
||||
WIN32_FIND_DATA _findData;
|
||||
WIN32_FIND_DATA *findData;
|
||||
#else
|
||||
DIR *d;
|
||||
#ifdef HAVE_FCHDIR
|
||||
int initialDirFd;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
char *initialDir;
|
||||
#define INVALID_DIR_HANDLE NULL
|
||||
struct dirent *de;
|
||||
#endif
|
||||
int flags;
|
||||
int visit_type;
|
||||
int tree_errno; /* Error code from last failed operation. */
|
||||
|
||||
/* Dynamically-sized buffer for holding path */
|
||||
char *buff;
|
||||
const char *basename;
|
||||
size_t buff_length;
|
||||
size_t path_length;
|
||||
size_t dirname_length;
|
||||
|
||||
const char *basename; /* Last path element */
|
||||
size_t dirname_length; /* Leading dir length */
|
||||
size_t path_length; /* Total path length */
|
||||
|
||||
int depth;
|
||||
int openCount;
|
||||
@ -129,10 +153,17 @@ struct tree {
|
||||
};
|
||||
|
||||
/* Definitions for tree.flags bitmap. */
|
||||
#define needsReturn 8 /* Marks first entry as not having been returned yet. */
|
||||
#define hasStat 16 /* The st entry is set. */
|
||||
#define hasLstat 32 /* The lst entry is set. */
|
||||
#define hasStat 16 /* The st entry is valid. */
|
||||
#define hasLstat 32 /* The lst entry is valid. */
|
||||
#define hasFileInfo 64 /* The Windows fileInfo entry is valid. */
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
static int
|
||||
tree_dir_next_windows(struct tree *t, const char *pattern);
|
||||
#else
|
||||
static int
|
||||
tree_dir_next_posix(struct tree *t);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DIRENT_D_NAMLEN
|
||||
/* BSD extension; avoids need for a strlen() call. */
|
||||
@ -141,25 +172,32 @@ struct tree {
|
||||
#define D_NAMELEN(dp) (strlen((dp)->d_name))
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#include <stdio.h>
|
||||
void
|
||||
tree_dump(struct tree *t, FILE *out)
|
||||
{
|
||||
char buff[300];
|
||||
struct tree_entry *te;
|
||||
|
||||
fprintf(out, "\tdepth: %d\n", t->depth);
|
||||
fprintf(out, "\tbuff: %s\n", t->buff);
|
||||
fprintf(out, "\tpwd: "); fflush(stdout); system("pwd");
|
||||
fprintf(out, "\taccess: %s\n", t->basename);
|
||||
fprintf(out, "\tpwd: %s\n", getcwd(buff, sizeof(buff)));
|
||||
fprintf(out, "\tbasename: %s\n", t->basename);
|
||||
fprintf(out, "\tstack:\n");
|
||||
for (te = t->stack; te != NULL; te = te->next) {
|
||||
fprintf(out, "\t\tte->name: %s%s%s\n", te->name,
|
||||
te->flags & needsPreVisit ? "" : " *",
|
||||
t->current == te ? " (current)" : "");
|
||||
fprintf(out, "\t\t%s%d:\"%s\" %s%s%s%s%s%s\n",
|
||||
t->current == te ? "*" : " ",
|
||||
te->depth,
|
||||
te->name,
|
||||
te->flags & needsFirstVisit ? "V" : "",
|
||||
te->flags & needsDescent ? "D" : "",
|
||||
te->flags & needsOpen ? "O" : "",
|
||||
te->flags & needsAscent ? "A" : "",
|
||||
te->flags & isDirLink ? "L" : "",
|
||||
(t->current == te && t->d) ? "+" : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add a directory path to the current stack.
|
||||
@ -172,24 +210,29 @@ tree_push(struct tree *t, const char *path)
|
||||
te = malloc(sizeof(*te));
|
||||
memset(te, 0, sizeof(*te));
|
||||
te->next = t->stack;
|
||||
te->parent = t->current;
|
||||
if (te->parent)
|
||||
te->depth = te->parent->depth + 1;
|
||||
t->stack = te;
|
||||
#ifdef HAVE_FCHDIR
|
||||
te->fd = -1;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
te->fullpath = NULL;
|
||||
#endif
|
||||
te->symlink_parent_fd = -1;
|
||||
te->name = strdup(path);
|
||||
te->flags = needsPreVisit | needsPostVisit;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
te->symlink_parent_path = NULL;
|
||||
te->name = strdup(path);
|
||||
#endif
|
||||
te->flags = needsDescent | needsOpen | needsAscent;
|
||||
te->dirname_length = t->dirname_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append a name to the current path.
|
||||
* Append a name to the current dir path.
|
||||
*/
|
||||
static void
|
||||
tree_append(struct tree *t, const char *name, size_t name_length)
|
||||
{
|
||||
char *p;
|
||||
size_t size_needed;
|
||||
|
||||
if (t->buff != NULL)
|
||||
t->buff[t->dirname_length] = '\0';
|
||||
@ -198,12 +241,16 @@ tree_append(struct tree *t, const char *name, size_t name_length)
|
||||
name_length--;
|
||||
|
||||
/* Resize pathname buffer as needed. */
|
||||
while (name_length + 1 + t->dirname_length >= t->buff_length) {
|
||||
t->buff_length *= 2;
|
||||
size_needed = name_length + 1 + t->dirname_length;
|
||||
if (t->buff_length < size_needed) {
|
||||
if (t->buff_length < 1024)
|
||||
t->buff_length = 1024;
|
||||
while (t->buff_length < size_needed)
|
||||
t->buff_length *= 2;
|
||||
t->buff = realloc(t->buff, t->buff_length);
|
||||
}
|
||||
if (t->buff == NULL)
|
||||
abort();
|
||||
p = t->buff + t->dirname_length;
|
||||
t->path_length = t->dirname_length + name_length;
|
||||
/* Add a separating '/' if it's needed. */
|
||||
@ -211,7 +258,11 @@ tree_append(struct tree *t, const char *name, size_t name_length)
|
||||
*p++ = '/';
|
||||
t->path_length ++;
|
||||
}
|
||||
#if HAVE_STRNCPY_S
|
||||
strncpy_s(p, t->buff_length - (p - t->buff), name, name_length);
|
||||
#else
|
||||
strncpy(p, name, name_length);
|
||||
#endif
|
||||
p[name_length] = '\0';
|
||||
t->basename = p;
|
||||
}
|
||||
@ -222,24 +273,55 @@ tree_append(struct tree *t, const char *name, size_t name_length)
|
||||
struct tree *
|
||||
tree_open(const char *path)
|
||||
{
|
||||
#ifdef HAVE_FCHDIR
|
||||
struct tree *t;
|
||||
|
||||
t = malloc(sizeof(*t));
|
||||
memset(t, 0, sizeof(*t));
|
||||
tree_append(t, path, strlen(path));
|
||||
#ifdef HAVE_FCHDIR
|
||||
t->initialDirFd = open(".", O_RDONLY);
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
t->initialDir = getcwd(NULL, 0);
|
||||
#endif
|
||||
/*
|
||||
* During most of the traversal, items are set up and then
|
||||
* returned immediately from tree_next(). That doesn't work
|
||||
* for the very first entry, so we set a flag for this special
|
||||
* case.
|
||||
*/
|
||||
t->flags = needsReturn;
|
||||
/* First item is set up a lot like a symlink traversal. */
|
||||
tree_push(t, path);
|
||||
t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
|
||||
t->stack->symlink_parent_fd = open(".", O_RDONLY);
|
||||
t->openCount++;
|
||||
t->d = INVALID_DIR_HANDLE;
|
||||
return (t);
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
struct tree *t;
|
||||
char *cwd = _getcwd(NULL, 0);
|
||||
char *pathname = strdup(path), *p, *base;
|
||||
|
||||
if (pathname == NULL)
|
||||
abort();
|
||||
for (p = pathname; *p != '\0'; ++p) {
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
}
|
||||
base = pathname;
|
||||
|
||||
t = malloc(sizeof(*t));
|
||||
memset(t, 0, sizeof(*t));
|
||||
/* First item is set up a lot like a symlink traversal. */
|
||||
/* printf("Looking for wildcard in %s\n", path); */
|
||||
/* TODO: wildcard detection here screws up on \\?\c:\ UNC names */
|
||||
if (strchr(base, '*') || strchr(base, '?')) {
|
||||
// It has a wildcard in it...
|
||||
// Separate the last element.
|
||||
p = strrchr(base, '/');
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
chdir(base);
|
||||
tree_append(t, base, p - base);
|
||||
t->dirname_length = t->path_length;
|
||||
base = p + 1;
|
||||
}
|
||||
}
|
||||
tree_push(t, base);
|
||||
free(pathname);
|
||||
t->stack->flags = needsFirstVisit | isDirLink | needsAscent;
|
||||
t->stack->symlink_parent_path = cwd;
|
||||
t->d = INVALID_DIR_HANDLE;
|
||||
return (t);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -255,22 +337,26 @@ tree_ascend(struct tree *t)
|
||||
t->depth--;
|
||||
if (te->flags & isDirLink) {
|
||||
#ifdef HAVE_FCHDIR
|
||||
if (fchdir(te->fd) != 0) {
|
||||
if (fchdir(te->symlink_parent_fd) != 0) {
|
||||
t->tree_errno = errno;
|
||||
r = TREE_ERROR_FATAL;
|
||||
}
|
||||
close(te->fd);
|
||||
close(te->symlink_parent_fd);
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (chdir(te->fullpath) != 0) {
|
||||
if (SetCurrentDirectory(te->symlink_parent_path) == 0) {
|
||||
t->tree_errno = errno;
|
||||
r = TREE_ERROR_FATAL;
|
||||
}
|
||||
free(te->fullpath);
|
||||
te->fullpath = NULL;
|
||||
free(te->symlink_parent_path);
|
||||
te->symlink_parent_path = NULL;
|
||||
#endif
|
||||
t->openCount--;
|
||||
} else {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (SetCurrentDirectory("..") == 0) {
|
||||
#else
|
||||
if (chdir("..") != 0) {
|
||||
#endif
|
||||
t->tree_errno = errno;
|
||||
r = TREE_ERROR_FATAL;
|
||||
}
|
||||
@ -286,16 +372,18 @@ tree_pop(struct tree *t)
|
||||
{
|
||||
struct tree_entry *te;
|
||||
|
||||
t->buff[t->dirname_length] = '\0';
|
||||
if (t->buff)
|
||||
t->buff[t->dirname_length] = '\0';
|
||||
if (t->stack == t->current && t->current != NULL)
|
||||
t->current = t->current->parent;
|
||||
te = t->stack;
|
||||
t->stack = te->next;
|
||||
t->dirname_length = te->dirname_length;
|
||||
t->basename = t->buff + t->dirname_length;
|
||||
/* Special case: starting dir doesn't skip leading '/'. */
|
||||
if (t->dirname_length > 0)
|
||||
t->basename++;
|
||||
if (t->buff) {
|
||||
t->basename = t->buff + t->dirname_length;
|
||||
while (t->basename[0] == '/')
|
||||
t->basename++;
|
||||
}
|
||||
free(te->name);
|
||||
free(te);
|
||||
}
|
||||
@ -306,101 +394,179 @@ tree_pop(struct tree *t)
|
||||
int
|
||||
tree_next(struct tree *t)
|
||||
{
|
||||
struct dirent *de = NULL;
|
||||
int r;
|
||||
|
||||
/* If we're called again after a fatal error, that's an API
|
||||
* violation. Just crash now. */
|
||||
if (t->visit_type == TREE_ERROR_FATAL) {
|
||||
const char *msg = "Unable to continue traversing"
|
||||
" directory hierarchy after a fatal error.";
|
||||
write(2, msg, strlen(msg));
|
||||
*(volatile int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */
|
||||
exit(1); /* In case the SEGV didn't work. */
|
||||
}
|
||||
|
||||
/* Handle the startup case by returning the initial entry. */
|
||||
if (t->flags & needsReturn) {
|
||||
t->flags &= ~needsReturn;
|
||||
return (t->visit_type = TREE_REGULAR);
|
||||
fprintf(stderr, "Unable to continue traversing"
|
||||
" directory hierarchy after a fatal error.");
|
||||
abort();
|
||||
}
|
||||
|
||||
while (t->stack != NULL) {
|
||||
/* If there's an open dir, get the next entry from there. */
|
||||
while (t->d != NULL) {
|
||||
de = readdir(t->d);
|
||||
if (de == NULL) {
|
||||
closedir(t->d);
|
||||
t->d = NULL;
|
||||
} else if (de->d_name[0] == '.'
|
||||
&& de->d_name[1] == '\0') {
|
||||
/* Skip '.' */
|
||||
} else if (de->d_name[0] == '.'
|
||||
&& de->d_name[1] == '.'
|
||||
&& de->d_name[2] == '\0') {
|
||||
/* Skip '..' */
|
||||
} else {
|
||||
/*
|
||||
* Append the path to the current path
|
||||
* and return it.
|
||||
*/
|
||||
tree_append(t, de->d_name, D_NAMELEN(de));
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
return (t->visit_type = TREE_REGULAR);
|
||||
}
|
||||
if (t->d != INVALID_DIR_HANDLE) {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
r = tree_dir_next_windows(t, NULL);
|
||||
#else
|
||||
r = tree_dir_next_posix(t);
|
||||
#endif
|
||||
if (r == 0)
|
||||
continue;
|
||||
return (r);
|
||||
}
|
||||
|
||||
/* If the current dir needs to be visited, set it up. */
|
||||
if (t->stack->flags & needsPreVisit) {
|
||||
if (t->stack->flags & needsFirstVisit) {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
char *d = t->stack->name;
|
||||
t->stack->flags &= ~needsFirstVisit;
|
||||
if (strchr(d, '*') || strchr(d, '?')) {
|
||||
r = tree_dir_next_windows(t, d);
|
||||
if (r == 0)
|
||||
continue;
|
||||
return (r);
|
||||
}
|
||||
// Not a pattern, handle it as-is...
|
||||
#endif
|
||||
/* Top stack item needs a regular visit. */
|
||||
t->current = t->stack;
|
||||
tree_append(t, t->stack->name, strlen(t->stack->name));
|
||||
t->stack->flags &= ~needsPreVisit;
|
||||
//t->dirname_length = t->path_length;
|
||||
//tree_pop(t);
|
||||
t->stack->flags &= ~needsFirstVisit;
|
||||
return (t->visit_type = TREE_REGULAR);
|
||||
} else if (t->stack->flags & needsDescent) {
|
||||
/* Top stack item is dir to descend into. */
|
||||
t->current = t->stack;
|
||||
tree_append(t, t->stack->name, strlen(t->stack->name));
|
||||
t->stack->flags &= ~needsDescent;
|
||||
/* If it is a link, set up fd for the ascent. */
|
||||
if (t->stack->flags & isDirLink) {
|
||||
#ifdef HAVE_FCHDIR
|
||||
t->stack->fd = open(".", O_RDONLY);
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
t->stack->fullpath = getcwd(NULL, 0);
|
||||
#endif
|
||||
t->stack->symlink_parent_fd = open(".", O_RDONLY);
|
||||
t->openCount++;
|
||||
if (t->openCount > t->maxOpenCount)
|
||||
t->maxOpenCount = t->openCount;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
t->stack->symlink_parent_path = _getcwd(NULL, 0);
|
||||
#endif
|
||||
}
|
||||
t->dirname_length = t->path_length;
|
||||
if (chdir(t->stack->name) != 0) {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (t->path_length == 259 || !SetCurrentDirectory(t->stack->name) != 0)
|
||||
#else
|
||||
if (chdir(t->stack->name) != 0)
|
||||
#endif
|
||||
{
|
||||
/* chdir() failed; return error */
|
||||
tree_pop(t);
|
||||
t->tree_errno = errno;
|
||||
return (t->visit_type = TREE_ERROR_DIR);
|
||||
}
|
||||
t->depth++;
|
||||
t->d = opendir(".");
|
||||
if (t->d == NULL) {
|
||||
return (t->visit_type = TREE_POSTDESCENT);
|
||||
} else if (t->stack->flags & needsOpen) {
|
||||
t->stack->flags &= ~needsOpen;
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
r = tree_dir_next_windows(t, "*");
|
||||
#else
|
||||
r = tree_dir_next_posix(t);
|
||||
#endif
|
||||
if (r == 0)
|
||||
continue;
|
||||
return (r);
|
||||
} else if (t->stack->flags & needsAscent) {
|
||||
/* Top stack item is dir and we're done with it. */
|
||||
r = tree_ascend(t);
|
||||
tree_pop(t);
|
||||
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
|
||||
return (t->visit_type);
|
||||
} else {
|
||||
/* Top item on stack is dead. */
|
||||
tree_pop(t);
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
}
|
||||
}
|
||||
return (t->visit_type = 0);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
static int
|
||||
tree_dir_next_windows(struct tree *t, const char *pattern)
|
||||
{
|
||||
const char *name;
|
||||
size_t namelen;
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
if (pattern != NULL) {
|
||||
t->d = FindFirstFile(pattern, &t->_findData);
|
||||
if (t->d == INVALID_DIR_HANDLE) {
|
||||
r = tree_ascend(t); /* Undo "chdir" */
|
||||
tree_pop(t);
|
||||
t->tree_errno = errno;
|
||||
t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
|
||||
return (t->visit_type);
|
||||
}
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
t->basename = ".";
|
||||
return (t->visit_type = TREE_POSTDESCENT);
|
||||
t->findData = &t->_findData;
|
||||
pattern = NULL;
|
||||
} else if (!FindNextFile(t->d, &t->_findData)) {
|
||||
FindClose(t->d);
|
||||
t->d = INVALID_DIR_HANDLE;
|
||||
t->findData = NULL;
|
||||
return (0);
|
||||
}
|
||||
name = t->findData->cFileName;
|
||||
namelen = strlen(name);
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
if (name[0] == '.' && name[1] == '\0')
|
||||
continue;
|
||||
if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
|
||||
continue;
|
||||
tree_append(t, name, namelen);
|
||||
return (t->visit_type = TREE_REGULAR);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int
|
||||
tree_dir_next_posix(struct tree *t)
|
||||
{
|
||||
int r;
|
||||
const char *name;
|
||||
size_t namelen;
|
||||
|
||||
/* We've done everything necessary for the top stack entry. */
|
||||
if (t->stack->flags & needsPostVisit) {
|
||||
r = tree_ascend(t);
|
||||
if (t->d == NULL) {
|
||||
if ((t->d = opendir(".")) == NULL) {
|
||||
r = tree_ascend(t); /* Undo "chdir" */
|
||||
tree_pop(t);
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
|
||||
t->tree_errno = errno;
|
||||
t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
|
||||
return (t->visit_type);
|
||||
}
|
||||
}
|
||||
return (t->visit_type = 0);
|
||||
for (;;) {
|
||||
t->de = readdir(t->d);
|
||||
if (t->de == NULL) {
|
||||
closedir(t->d);
|
||||
t->d = INVALID_DIR_HANDLE;
|
||||
return (0);
|
||||
}
|
||||
name = t->de->d_name;
|
||||
namelen = D_NAMELEN(t->de);
|
||||
t->flags &= ~hasLstat;
|
||||
t->flags &= ~hasStat;
|
||||
if (name[0] == '.' && name[1] == '\0')
|
||||
continue;
|
||||
if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
|
||||
continue;
|
||||
tree_append(t, name, namelen);
|
||||
return (t->visit_type = TREE_REGULAR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return error code.
|
||||
@ -437,25 +603,51 @@ const struct stat *
|
||||
tree_current_stat(struct tree *t)
|
||||
{
|
||||
if (!(t->flags & hasStat)) {
|
||||
if (stat(t->basename, &t->st) != 0)
|
||||
if (stat(tree_current_access_path(t), &t->st) != 0)
|
||||
return NULL;
|
||||
t->flags |= hasStat;
|
||||
}
|
||||
return (&t->st);
|
||||
}
|
||||
|
||||
#if defined(HAVE_WINDOWS_H) && !defined(__CYGWIN__)
|
||||
const BY_HANDLE_FILE_INFORMATION *
|
||||
tree_current_file_information(struct tree *t)
|
||||
{
|
||||
if (!(t->flags & hasFileInfo)) {
|
||||
HANDLE h = CreateFile(tree_current_access_path(t),
|
||||
0, 0, NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
if (!GetFileInformationByHandle(h, &t->fileInfo)) {
|
||||
CloseHandle(h);
|
||||
return NULL;
|
||||
}
|
||||
CloseHandle(h);
|
||||
t->flags |= hasFileInfo;
|
||||
}
|
||||
return (&t->fileInfo);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Get the lstat() data for the entry just returned from tree_next().
|
||||
*/
|
||||
const struct stat *
|
||||
tree_current_lstat(struct tree *t)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
return (tree_current_stat(t));
|
||||
#else
|
||||
if (!(t->flags & hasLstat)) {
|
||||
if (lstat(t->basename, &t->lst) != 0)
|
||||
if (lstat(tree_current_access_path(t), &t->lst) != 0)
|
||||
return NULL;
|
||||
t->flags |= hasLstat;
|
||||
}
|
||||
return (&t->lst);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -464,8 +656,14 @@ tree_current_lstat(struct tree *t)
|
||||
int
|
||||
tree_current_is_dir(struct tree *t)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (t->findData)
|
||||
return (t->findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
if (tree_current_file_information(t))
|
||||
return (t->fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
return (0);
|
||||
#else
|
||||
const struct stat *st;
|
||||
|
||||
/*
|
||||
* If we already have lstat() info, then try some
|
||||
* cheap tests to determine if this is a dir.
|
||||
@ -490,6 +688,7 @@ tree_current_is_dir(struct tree *t)
|
||||
return 0;
|
||||
/* Use the definitive test. Hopefully this is cached. */
|
||||
return (S_ISDIR(st->st_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -500,6 +699,11 @@ tree_current_is_dir(struct tree *t)
|
||||
int
|
||||
tree_current_is_physical_dir(struct tree *t)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (tree_current_is_physical_link(t))
|
||||
return (0);
|
||||
return (tree_current_is_dir(t));
|
||||
#else
|
||||
const struct stat *st;
|
||||
|
||||
/*
|
||||
@ -523,6 +727,7 @@ tree_current_is_physical_dir(struct tree *t)
|
||||
return 0;
|
||||
/* Use the definitive test. Hopefully this is cached. */
|
||||
return (S_ISDIR(st->st_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -531,10 +736,21 @@ tree_current_is_physical_dir(struct tree *t)
|
||||
int
|
||||
tree_current_is_physical_link(struct tree *t)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#ifndef IO_REPARSE_TAG_SYMLINK
|
||||
/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
|
||||
#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
|
||||
#endif
|
||||
if (t->findData)
|
||||
return ((t->findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
&& (t->findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK));
|
||||
return (0);
|
||||
#else
|
||||
const struct stat *st = tree_current_lstat(t);
|
||||
if (st == NULL)
|
||||
return 0;
|
||||
return (S_ISLNK(st->st_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -582,21 +798,23 @@ tree_close(struct tree *t)
|
||||
/* Release anything remaining in the stack. */
|
||||
while (t->stack != NULL)
|
||||
tree_pop(t);
|
||||
if (t->buff)
|
||||
free(t->buff);
|
||||
/* chdir() back to where we started. */
|
||||
free(t->buff);
|
||||
/* TODO: Ensure that premature close() resets cwd */
|
||||
#if 0
|
||||
#ifdef HAVE_FCHDIR
|
||||
if (t->initialDirFd >= 0) {
|
||||
fchdir(t->initialDirFd);
|
||||
int s = fchdir(t->initialDirFd);
|
||||
(void)s; /* UNUSED */
|
||||
close(t->initialDirFd);
|
||||
t->initialDirFd = -1;
|
||||
}
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (t->initialDir != NULL) {
|
||||
chdir(t->initialDir);
|
||||
SetCurrentDir(t->initialDir);
|
||||
free(t->initialDir);
|
||||
t->initialDir = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
free(t);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user