mirror of https://github.com/WebAssembly/wasi-libc
Browse Source
Close https://github.com/WebAssembly/wasi-libc/issues/520 Add FTS implementation derived from musl-fts with a few modifications. The compiled fts.o is included in the libc.a archive, and the fts.h header is installed in the sysroot (`include/fts.h`). * fts/musl-fts: Add a copy of the musl-fts sources with modifications. * fts/patches: A set of patches to apply to the musl-fts sources. * Upstream pull request: https://github.com/void-linux/musl-fts/pull/14 * fts/update-musl-fts.sh: A script to update the musl-fts sources with the patches applied. * fts/config.h: A configuration header included by the musl-fts sources. * test/smoke: Add a test suite for wasi-libc specific features that libc-test does not cover.main
Yuta Saito
1 month ago
committed by
GitHub
22 changed files with 1989 additions and 1 deletions
@ -0,0 +1,7 @@ |
|||
#define HAVE_DECL_MAX 1 |
|||
|
|||
#define HAVE_DECL_UINTMAX_MAX 0 |
|||
|
|||
#define HAVE_DIRFD 1 |
|||
|
|||
#define HAVE_FCHDIR 0 |
@ -0,0 +1,26 @@ |
|||
Copyright (c) 1989, 1993 |
|||
The Regents of the University of California. All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
1. Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
3. Neither the name of the University nor the names of its contributors |
|||
may be used to endorse or promote products derived from this software |
|||
without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
SUCH DAMAGE. |
@ -0,0 +1,9 @@ |
|||
This directory contains a copy of the musl-fts library licensed under |
|||
BSD-3-Clause with wasi-libc modifications. See COPYING for the full license text. |
|||
|
|||
The original source code can be found at https://github.com/void-linux/musl-fts/archive/refs/tags/v1.2.7.tar.gz |
|||
|
|||
## How to update musl-fts sources |
|||
|
|||
1. Update the `MUSL_FTS_VERSION` variable in `./fts/update-musl-fts.sh` |
|||
2. Run `./fts/update-musl-fts.sh` |
File diff suppressed because it is too large
@ -0,0 +1,155 @@ |
|||
/* $NetBSD: fts.h,v 1.19 2009/08/16 19:33:38 christos Exp $ */ |
|||
|
|||
/*
|
|||
* Copyright (c) 1989, 1993 |
|||
* The Regents of the University of California. All rights reserved. |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions |
|||
* are met: |
|||
* 1. Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* 2. Redistributions in binary form must reproduce the above copyright |
|||
* notice, this list of conditions and the following disclaimer in the |
|||
* documentation and/or other materials provided with the distribution. |
|||
* 3. Neither the name of the University nor the names of its contributors |
|||
* may be used to endorse or promote products derived from this software |
|||
* without specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
* SUCH DAMAGE. |
|||
* |
|||
* @(#)fts.h 8.3 (Berkeley) 8/14/94 |
|||
*/ |
|||
|
|||
#ifndef _FTS_H_ |
|||
#define _FTS_H_ |
|||
|
|||
#include <stdint.h> |
|||
#include <sys/types.h> |
|||
|
|||
#ifndef __fts_stat_t |
|||
#define __fts_stat_t struct stat |
|||
#endif |
|||
#ifndef __fts_nlink_t |
|||
#define __fts_nlink_t nlink_t |
|||
#endif |
|||
#ifndef __fts_ino_t |
|||
#define __fts_ino_t ino_t |
|||
#endif |
|||
#ifndef __fts_length_t |
|||
#define __fts_length_t unsigned int |
|||
#endif |
|||
#ifndef __fts_number_t |
|||
#define __fts_number_t int64_t |
|||
#endif |
|||
#ifndef __fts_dev_t |
|||
#define __fts_dev_t dev_t |
|||
#endif |
|||
#ifndef __fts_level_t |
|||
#define __fts_level_t int |
|||
#endif |
|||
|
|||
typedef struct { |
|||
struct _ftsent *fts_cur; /* current node */ |
|||
struct _ftsent *fts_child; /* linked list of children */ |
|||
struct _ftsent **fts_array; /* sort array */ |
|||
dev_t fts_dev; /* starting device # */ |
|||
char *fts_path; /* path for this descent */ |
|||
int fts_rfd; /* fd for root */ |
|||
unsigned int fts_pathlen; /* sizeof(path) */ |
|||
unsigned int fts_nitems; /* elements in the sort array */ |
|||
int (*fts_compar) /* compare function */ |
|||
(const struct _ftsent **, const struct _ftsent **); |
|||
|
|||
#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ |
|||
#define FTS_LOGICAL 0x002 /* logical walk */ |
|||
#define FTS_NOCHDIR 0x004 /* don't change directories */ |
|||
#define FTS_NOSTAT 0x008 /* don't get stat info */ |
|||
#define FTS_PHYSICAL 0x010 /* physical walk */ |
|||
#define FTS_SEEDOT 0x020 /* return dot and dot-dot */ |
|||
#define FTS_XDEV 0x040 /* don't cross devices */ |
|||
#define FTS_WHITEOUT 0x080 /* return whiteout information */ |
|||
#define FTS_OPTIONMASK 0x0ff /* valid user option mask */ |
|||
|
|||
#define FTS_NAMEONLY 0x100 /* (private) child names only */ |
|||
#define FTS_STOP 0x200 /* (private) unrecoverable error */ |
|||
int fts_options; /* fts_open options, global flags */ |
|||
} FTS; |
|||
|
|||
typedef struct _ftsent { |
|||
struct _ftsent *fts_cycle; /* cycle node */ |
|||
struct _ftsent *fts_parent; /* parent directory */ |
|||
struct _ftsent *fts_link; /* next file in directory */ |
|||
__fts_number_t fts_number; /* local numeric value */ |
|||
void *fts_pointer; /* local address value */ |
|||
char *fts_accpath; /* access path */ |
|||
char *fts_path; /* root path */ |
|||
int fts_errno; /* errno for this node */ |
|||
int fts_symfd; /* fd for symlink */ |
|||
__fts_length_t fts_pathlen; /* strlen(fts_path) */ |
|||
__fts_length_t fts_namelen; /* strlen(fts_name) */ |
|||
|
|||
__fts_ino_t fts_ino; /* inode */ |
|||
__fts_dev_t fts_dev; /* device */ |
|||
__fts_nlink_t fts_nlink; /* link count */ |
|||
|
|||
#define FTS_ROOTPARENTLEVEL -1 |
|||
#define FTS_ROOTLEVEL 0 |
|||
__fts_level_t fts_level; /* depth (-1 to N) */ |
|||
|
|||
#define FTS_D 1 /* preorder directory */ |
|||
#define FTS_DC 2 /* directory that causes cycles */ |
|||
#define FTS_DEFAULT 3 /* none of the above */ |
|||
#define FTS_DNR 4 /* unreadable directory */ |
|||
#define FTS_DOT 5 /* dot or dot-dot */ |
|||
#define FTS_DP 6 /* postorder directory */ |
|||
#define FTS_ERR 7 /* error; errno is set */ |
|||
#define FTS_F 8 /* regular file */ |
|||
#define FTS_INIT 9 /* initialized only */ |
|||
#define FTS_NS 10 /* stat(2) failed */ |
|||
#define FTS_NSOK 11 /* no stat(2) requested */ |
|||
#define FTS_SL 12 /* symbolic link */ |
|||
#define FTS_SLNONE 13 /* symbolic link without target */ |
|||
#define FTS_W 14 /* whiteout object */ |
|||
unsigned short fts_info; /* user flags for FTSENT structure */ |
|||
|
|||
#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ |
|||
#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ |
|||
#define FTS_ISW 0x04 /* this is a whiteout object */ |
|||
unsigned short fts_flags; /* private flags for FTSENT structure */ |
|||
|
|||
#define FTS_AGAIN 1 /* read node again */ |
|||
#define FTS_FOLLOW 2 /* follow symbolic link */ |
|||
#define FTS_NOINSTR 3 /* no instructions */ |
|||
#define FTS_SKIP 4 /* discard node */ |
|||
unsigned short fts_instr; /* fts_set() instructions */ |
|||
|
|||
__fts_stat_t *fts_statp; /* stat(2) information */ |
|||
char fts_name[1]; /* file name */ |
|||
} FTSENT; |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
FTSENT *fts_children(FTS *, int); |
|||
int fts_close(FTS *); |
|||
FTS *fts_open(char * const *, int, |
|||
int (*)(const FTSENT **, const FTSENT **)); |
|||
FTSENT *fts_read(FTS *); |
|||
int fts_set(FTS *, FTSENT *, int); |
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
|
|||
#endif /* !_FTS_H_ */ |
@ -0,0 +1,102 @@ |
|||
From 878675b2299c23593a9adee25d5c828fb760819e Mon Sep 17 00:00:00 2001 |
|||
From: Yuta Saito <kateinoigakukun@gmail.com> |
|||
Date: Thu, 1 Aug 2024 01:50:49 +0000 |
|||
Subject: [PATCH] Add conditional handling for fchdir and chdir functions |
|||
|
|||
Define preprocessor conditions for `HAVE_FCHDIR` to handle platforms without |
|||
fchdir. On these platforms, pretend that `FTS_NOCHDIR` is always set. |
|||
|
|||
This change is necessary to build the `fts` module on the top of the WASI and |
|||
wasi-libc, which doesn't provide the `fchdir`. |
|||
|
|||
Upstream pull request: https://github.com/void-linux/musl-fts/pull/14 |
|||
---
|
|||
fts.c | 24 ++++++++++++++++++++++++ |
|||
1 file changed, 24 insertions(+) |
|||
|
|||
diff --git a/fts.c b/fts.c
|
|||
index 0f8d05b..da4bc08 100644
|
|||
--- a/fts.c
|
|||
+++ b/fts.c
|
|||
@@ -112,8 +112,15 @@ static int fts_safe_changedir(const FTS *, const FTSENT *, int,
|
|||
#define ISSET(opt) (sp->fts_options & (opt)) |
|||
#define SET(opt) (sp->fts_options |= (opt)) |
|||
|
|||
+#if HAVE_FCHDIR
|
|||
#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) |
|||
#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) |
|||
+#else
|
|||
+/* If we don't have fchdir, pretend that !ISSET(FTS_NOCHDIR) is always false in
|
|||
+ * the above macros, and do not reference chdir or fchdir. */
|
|||
+#define CHDIR(sp, path) 0
|
|||
+#define FCHDIR(sp, fd) 0
|
|||
+#endif
|
|||
|
|||
/* fts_build flags */ |
|||
#define BCHILD 1 /* fts_children */ |
|||
@@ -136,6 +143,11 @@ fts_open(char * const *argv, int options,
|
|||
|
|||
_DIAGASSERT(argv != NULL); |
|||
|
|||
+#if !HAVE_FCHDIR
|
|||
+ /* If we don't have fchdir, pretend that FTS_NOCHDIR is always set. */
|
|||
+ options |= FTS_NOCHDIR;
|
|||
+#endif
|
|||
+
|
|||
/* Options check. */ |
|||
if (options & ~FTS_OPTIONMASK) { |
|||
errno = EINVAL; |
|||
@@ -299,12 +311,14 @@ fts_close(FTS *sp)
|
|||
free(sp->fts_array); |
|||
free(sp->fts_path); |
|||
|
|||
+ #if HAVE_FCHDIR
|
|||
/* Return to original directory, save errno if necessary. */ |
|||
if (!ISSET(FTS_NOCHDIR)) { |
|||
if (fchdir(sp->fts_rfd) == -1) |
|||
saved_errno = errno; |
|||
(void)close(sp->fts_rfd); |
|||
} |
|||
+ #endif
|
|||
|
|||
/* Free up the stream pointer. */ |
|||
free(sp); |
|||
@@ -611,6 +625,7 @@ fts_children(FTS *sp, int instr)
|
|||
} else |
|||
instr = BCHILD; |
|||
|
|||
+ #if HAVE_FCHDIR
|
|||
/* |
|||
* If using chdir on a relative path and called BEFORE fts_read does |
|||
* its chdir to the root of a traversal, we can lose -- we need to |
|||
@@ -631,6 +646,10 @@ fts_children(FTS *sp, int instr)
|
|||
} |
|||
(void)close(fd); |
|||
return (sp->fts_child); |
|||
+ #else
|
|||
+ /* If not using chdir, just build the list. */
|
|||
+ return (sp->fts_child = fts_build(sp, instr));
|
|||
+ #endif
|
|||
} |
|||
|
|||
/* |
|||
@@ -1226,6 +1245,7 @@ fts_maxarglen(char * const *argv)
|
|||
static int |
|||
fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path) |
|||
{ |
|||
+#if HAVE_FCHDIR
|
|||
int oldfd = fd, ret = -1; |
|||
__fts_stat_t sb; |
|||
|
|||
@@ -1252,4 +1272,8 @@ bail:
|
|||
errno = save_errno; |
|||
} |
|||
return ret; |
|||
+#else
|
|||
+ /* If we can't do fchdir, pretend as if ISSET(FTS_NOCHDIR) is set. */
|
|||
+ return 0;
|
|||
+#endif
|
|||
} |
|||
--
|
|||
2.43.2 |
|||
|
@ -0,0 +1,32 @@ |
|||
#!/bin/sh |
|||
|
|||
FTS_DIR="$(dirname "$(realpath "$0")")" |
|||
MUSL_FTS_VERSION=1.2.7 |
|||
MUSL_FTS_TARBALL_URL="https://github.com/void-linux/musl-fts/archive/refs/tags/v$MUSL_FTS_VERSION.tar.gz" |
|||
MUSL_FTS_SRC_DIR="$FTS_DIR/musl-fts" |
|||
|
|||
mkdir -p "$MUSL_FTS_SRC_DIR" |
|||
|
|||
# Download and extract the musl-fts tarball |
|||
echo "Downloading musl-fts $MUSL_FTS_VERSION" |
|||
curl -L "$MUSL_FTS_TARBALL_URL" | tar xzf - --strip-component=1 -C "$MUSL_FTS_SRC_DIR" \ |
|||
musl-fts-$MUSL_FTS_VERSION/fts.c \ |
|||
musl-fts-$MUSL_FTS_VERSION/fts.h \ |
|||
musl-fts-$MUSL_FTS_VERSION/COPYING |
|||
|
|||
for patch in "$FTS_DIR/patches/"*.patch; do |
|||
echo "Applying patch $patch" |
|||
patch -p1 -d "$MUSL_FTS_SRC_DIR" < "$patch" |
|||
done |
|||
|
|||
cat <<EOF > "$MUSL_FTS_SRC_DIR/NOTICE.wasi-libc.md" |
|||
This directory contains a copy of the musl-fts library licensed under |
|||
BSD-3-Clause with wasi-libc modifications. See COPYING for the full license text. |
|||
|
|||
The original source code can be found at $MUSL_FTS_TARBALL_URL |
|||
|
|||
## How to update musl-fts sources |
|||
|
|||
1. Update the \`MUSL_FTS_VERSION\` variable in \`$0\` |
|||
2. Run \`$0\` |
|||
EOF |
@ -1,3 +1,5 @@ |
|||
build |
|||
download |
|||
run |
|||
|
|||
smoke/*.dir |
|||
|
@ -0,0 +1,35 @@ |
|||
# Smoke test suite specific to wasi-libc
|
|||
#
|
|||
# This Makefile is included by the parent Makefile
|
|||
|
|||
SMOKE_TESTS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) |
|||
SMOKE_TESTS := $(wildcard $(SMOKE_TESTS_DIR)/*.c) |
|||
SMOKE_OBJDIR := $(OBJDIR)/smoke |
|||
SMOKE_WASMS := $(SMOKE_TESTS:$(SMOKE_TESTS_DIR)/%.c=$(SMOKE_OBJDIR)/%.core.wasm) |
|||
SMOKE_ERRS := $(SMOKE_WASMS:%.core.wasm=%.wasm.err) |
|||
|
|||
$(SMOKE_OBJDIR): |
|||
mkdir -p $@ |
|||
|
|||
$(SMOKE_OBJDIR)/%.core.wasm: $(SMOKE_TESTS_DIR)/%.c | $(SMOKE_OBJDIR) |
|||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ |
|||
|
|||
ifeq ($(TARGET_TRIPLE), wasm32-wasip2) |
|||
$(SMOKE_OBJDIR)/%.wasm: $(SMOKE_OBJDIR)/%.core.wasm |
|||
$(WASM_TOOLS) component new --adapt $(ADAPTER) $< -o $@ |
|||
endif |
|||
|
|||
ifeq ($(TARGET_TRIPLE), wasm32-wasip2) |
|||
$(SMOKE_OBJDIR)/%.wasm.err: $(SMOKE_OBJDIR)/%.wasm |
|||
rm -rf $(SMOKE_TESTS_DIR)/$*.dir |
|||
mkdir -p $(SMOKE_TESTS_DIR)/$*.dir |
|||
$(ENGINE) --dir $(SMOKE_TESTS_DIR)/$*.dir::/ --wasm component-model $< >$@ |
|||
else |
|||
$(SMOKE_OBJDIR)/%.wasm.err: $(SMOKE_OBJDIR)/%.core.wasm |
|||
rm -rf $(SMOKE_TESTS_DIR)/$*.dir |
|||
mkdir -p $(SMOKE_TESTS_DIR)/$*.dir |
|||
$(ENGINE) --dir $(SMOKE_TESTS_DIR)/$*.dir::/ $< >$@ |
|||
endif |
|||
|
|||
run_smoke: $(SMOKE_ERRS) |
|||
@echo "Smoke tests passed" |
@ -0,0 +1,180 @@ |
|||
/*
|
|||
* We modified musl-fts not to use fchdir() and we made FTS_NOCHDIR |
|||
* the default behavior. This test is to make sure that the modified |
|||
* musl-fts works as expected. |
|||
*/ |
|||
|
|||
#include <fts.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <sys/stat.h> |
|||
#include <errno.h> |
|||
|
|||
void __expect_next_ftsent(FTSENT *p, const char *path, short level, int info, const char *file, int line) { |
|||
if (p == NULL) { |
|||
printf("Error: fts_read() returned NULL at %s:%d\n", file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
if (strcmp(p->fts_path, path) != 0) { |
|||
printf("Error: expected path \"%s\", got \"%s\" at %s:%d\n", path, p->fts_path, file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
if (p->fts_level != level) { |
|||
printf("Error: expected level %d, got %d at %s:%d\n", level, p->fts_level, file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
if (p->fts_info != info) { |
|||
printf("Error: expected info %d, got %d at %s:%d\n", info, p->fts_info, file, line); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
#define expect_next_ftsent(p, path, level, info) __expect_next_ftsent(p, path, level, info, __FILE__, __LINE__) |
|||
|
|||
static int compare_ents (const FTSENT **a, const FTSENT **b) { |
|||
return strcmp((*a)->fts_name, (*b)->fts_name); |
|||
} |
|||
|
|||
void touch(const char *path) { |
|||
FILE *f = fopen(path, "w"); |
|||
if (f == NULL) { |
|||
printf("Error: fopen(%s) failed: %s\n", path, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
fclose(f); |
|||
} |
|||
|
|||
void test_fts_info(int base_options) { |
|||
FTS *ftsp; |
|||
|
|||
mkdir("t1", 0755); |
|||
touch("t1/file1"); |
|||
mkdir("t1/dir1", 0755); |
|||
touch("t1/dir1/file2"); |
|||
mkdir("t1/dir1/dir2", 0755); |
|||
touch("t1/dir1/file3"); |
|||
|
|||
char *paths[] = {"./t1", NULL}; |
|||
ftsp = fts_open(paths, base_options, compare_ents); |
|||
if (ftsp == NULL) { |
|||
printf("Error: fts_open(%s, %d) failed: %s\n", paths[0], base_options, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
|
|||
expect_next_ftsent(fts_read(ftsp), "./t1", 0, FTS_D); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1", 1, FTS_D); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1/dir2", 2, FTS_D); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1/dir2", 2, FTS_DP); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1/file2", 2, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1/file3", 2, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/dir1", 1, FTS_DP); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1/file1", 1, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t1", 0, FTS_DP); |
|||
|
|||
fts_close(ftsp); |
|||
} |
|||
|
|||
void test_symlink_fts_info(int base_options) { |
|||
FTS *ftsp; |
|||
|
|||
mkdir("t2", 0755); |
|||
touch("t2/file1"); |
|||
symlink("file1", "t2/symlink1"); |
|||
symlink("nonexistent", "t2/broken_symlink1"); |
|||
|
|||
char *paths[] = {"./t2", NULL}; |
|||
ftsp = fts_open(paths, base_options, compare_ents); |
|||
if (ftsp == NULL) { |
|||
printf("Error: fts_open(%s, %d) failed: %s\n", paths[0], base_options, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
|
|||
expect_next_ftsent(fts_read(ftsp), "./t2", 0, FTS_D); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2/broken_symlink1", 1, FTS_SL); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2/file1", 1, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2/symlink1", 1, FTS_SL); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2", 0, FTS_DP); |
|||
|
|||
fts_close(ftsp); |
|||
|
|||
ftsp = fts_open(paths, base_options | FTS_LOGICAL, compare_ents); |
|||
if (ftsp == NULL) { |
|||
printf("Error: fts_open(%s, %d | FTS_LOGICAL) failed: %s\n", paths[0], base_options, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
|
|||
expect_next_ftsent(fts_read(ftsp), "./t2", 0, FTS_D); |
|||
// FTS_SLNONE should be returned for broken symlinks in FTS_LOGICAL mode
|
|||
expect_next_ftsent(fts_read(ftsp), "./t2/broken_symlink1", 1, FTS_SLNONE); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2/file1", 1, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2/symlink1", 1, FTS_F); |
|||
expect_next_ftsent(fts_read(ftsp), "./t2", 0, FTS_DP); |
|||
} |
|||
|
|||
void __expect_child_ftsent(FTSENT *p, const char *name, short level, int info, const char *file, int line) { |
|||
if (p == NULL) { |
|||
printf("Error: fts_children() returned NULL at %s:%d\n", file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
// Check fts_name instead of fts_path because fts_children() doesn't set fts_path
|
|||
if (strcmp(p->fts_name, name) != 0) { |
|||
printf("Error: expected name \"%s\", got \"%s\" at %s:%d\n", name, p->fts_name, file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
if (p->fts_level != level) { |
|||
printf("Error: expected level %d, got %d at %s:%d\n", level, p->fts_level, file, line); |
|||
exit(1); |
|||
} |
|||
|
|||
if (p->fts_info != info) { |
|||
printf("Error: expected info %d, got %d at %s:%d\n", info, p->fts_info, file, line); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
#define expect_child_ftsent(p, name, level, info) __expect_child_ftsent(p, name, level, info, __FILE__, __LINE__) |
|||
|
|||
void test_fts_children(int base_options) { |
|||
FTS *ftsp; |
|||
FTSENT *p; |
|||
|
|||
mkdir("t3", 0755); |
|||
touch("t3/file1"); |
|||
mkdir("t3/dir1", 0755); |
|||
touch("t3/dir1/file2"); |
|||
mkdir("t3/dir1/dir2", 0755); |
|||
touch("t3/dir1/file3"); |
|||
|
|||
char *paths[] = {"./t3", NULL}; |
|||
ftsp = fts_open(paths, base_options, compare_ents); |
|||
if (ftsp == NULL) { |
|||
printf("Error: fts_open(%s, %d) failed: %s\n", paths[0], base_options, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
|
|||
FTSENT *t3 = fts_read(ftsp); |
|||
expect_next_ftsent(t3, "./t3", 0, FTS_D); |
|||
|
|||
FTSENT *ents = fts_children(ftsp, 0); |
|||
expect_child_ftsent(ents, "dir1", 1, FTS_D); |
|||
expect_child_ftsent(ents->fts_link, "file1", 1, FTS_F); |
|||
|
|||
fts_close(ftsp); |
|||
} |
|||
|
|||
int main(void) { |
|||
int base_options_set[] = {FTS_PHYSICAL, FTS_NOCHDIR}; |
|||
for (int i = 0; i < sizeof(base_options_set) / sizeof(base_options_set[0]); i++) { |
|||
test_fts_info(base_options_set[i]); |
|||
test_symlink_fts_info(base_options_set[i]); |
|||
test_fts_children(base_options_set[i]); |
|||
} |
|||
return 0; |
|||
} |
Loading…
Reference in new issue