diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:23:27 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:23:27 +0300 |
| commit | ccef0af007fdeea4d0bcc3c182629d580f05892b (patch) | |
| tree | 398228e55fbc4eed7e5dc62562bb4e53cc09d431 /corebinutils/chflags/fts.c | |
| parent | d857ba85ce6b1c2b1309df6f39a5a2a57aa335de (diff) | |
| parent | e9b78def2f086a356ec092a7f9af1e1613749efe (diff) | |
| download | Project-Tick-ccef0af007fdeea4d0bcc3c182629d580f05892b.tar.gz Project-Tick-ccef0af007fdeea4d0bcc3c182629d580f05892b.zip | |
Add 'corebinutils/chflags/' from commit 'e9b78def2f086a356ec092a7f9af1e1613749efe'
git-subtree-dir: corebinutils/chflags
git-subtree-mainline: d857ba85ce6b1c2b1309df6f39a5a2a57aa335de
git-subtree-split: e9b78def2f086a356ec092a7f9af1e1613749efe
Diffstat (limited to 'corebinutils/chflags/fts.c')
| -rw-r--r-- | corebinutils/chflags/fts.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/corebinutils/chflags/fts.c b/corebinutils/chflags/fts.c new file mode 100644 index 0000000000..2396ff2a14 --- /dev/null +++ b/corebinutils/chflags/fts.c @@ -0,0 +1,458 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2026 + * Project Tick. 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. + */ + +#include "fts.h" + +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +enum { + FTS_STATE_PRE = 0, + FTS_STATE_CHILDREN, + FTS_STATE_DNR, + FTS_STATE_POST, + FTS_STATE_DONE, +}; + +static FTSENT *fts_make_entry(FTS *ftsp, FTSENT *parent, const char *path, + int level, int is_root); +static int fts_prepare_children(FTS *ftsp, FTSENT *parent); +static FTSENT *fts_advance(FTS *ftsp, FTSENT *ent); +static void fts_free_entry(FTSENT *ent); +static const char *fts_basename(const char *path); +static char *fts_join_path(const char *parent, const char *name); +static int fts_should_follow(const FTS *ftsp, int is_root); +static int fts_is_cycle(const FTSENT *parent, const struct stat *st); +static const FTSENT *fts_root(const FTSENT *ent); + +static const char * +fts_basename(const char *path) +{ + const char *end; + const char *base; + + end = path + strlen(path); + while (end > path + 1 && end[-1] == '/') + end--; + base = end; + while (base > path && base[-1] != '/') + base--; + if (base == end) + return ("/"); + return (base); +} + +static char * +fts_join_path(const char *parent, const char *name) +{ + size_t parent_len, name_len; + int need_sep; + char *path; + + parent_len = strlen(parent); + name_len = strlen(name); + need_sep = parent_len > 0 && parent[parent_len - 1] != '/'; + path = malloc(parent_len + need_sep + name_len + 1); + if (path == NULL) + return (NULL); + memcpy(path, parent, parent_len); + if (need_sep) + path[parent_len++] = '/'; + memcpy(path + parent_len, name, name_len + 1); + return (path); +} + +static int +fts_should_follow(const FTS *ftsp, int is_root) +{ + if (ftsp->fts_options & FTS_LOGICAL) + return (1); + if (is_root && (ftsp->fts_options & FTS_COMFOLLOW)) + return (1); + return (0); +} + +static const FTSENT * +fts_root(const FTSENT *ent) +{ + while (ent != NULL && ent->fts_parent != NULL) + ent = ent->fts_parent; + return (ent); +} + +static int +fts_is_cycle(const FTSENT *parent, const struct stat *st) +{ + const FTSENT *cur; + + for (cur = parent; cur != NULL; cur = cur->fts_parent) { + if (cur->fts_statp != NULL && + cur->fts_statp->st_dev == st->st_dev && + cur->fts_statp->st_ino == st->st_ino) + return (1); + } + return (0); +} + +static FTSENT * +fts_make_entry(FTS *ftsp, FTSENT *parent, const char *path, int level, int is_root) +{ + FTSENT *ent; + struct stat st; + int follow; + + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) + return (NULL); + ent->fts_path = strdup(path); + ent->fts_accpath = ent->fts_path; + ent->fts_name = strdup(fts_basename(path)); + ent->fts_parent = parent; + ent->fts_level = level; + ent->fts_statp = malloc(sizeof(*ent->fts_statp)); + if (ent->fts_path == NULL || ent->fts_name == NULL || ent->fts_statp == NULL) { + fts_free_entry(ent); + return (NULL); + } + + follow = fts_should_follow(ftsp, is_root); + if ((follow ? stat(path, &st) : lstat(path, &st)) != 0) { + if (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)) { + ent->fts_info = FTS_SL; + *ent->fts_statp = st; + } else { + ent->fts_info = FTS_NS; + ent->fts_errno = errno; + memset(ent->fts_statp, 0, sizeof(*ent->fts_statp)); + } + return (ent); + } + + *ent->fts_statp = st; + if (S_ISDIR(st.st_mode)) { + if (fts_is_cycle(parent, &st)) { + ent->fts_info = FTS_DC; + } else { + const FTSENT *root; + + ent->fts_info = FTS_D; + root = fts_root(ent); + if ((ftsp->fts_options & FTS_XDEV) != 0 && root != NULL && + root->fts_statp != NULL && root != ent && + root->fts_statp->st_dev != st.st_dev) + ent->fts_instr = FTS_SKIP; + } + } else if (S_ISLNK(st.st_mode) && !follow) { + ent->fts_info = FTS_SL; + } else { + ent->fts_info = FTS_F; + } + + return (ent); +} + +static int +fts_prepare_children(FTS *ftsp, FTSENT *parent) +{ + struct dirent *dp; + DIR *dirp; + char **names; + char *child_path; + FTSENT *child; + size_t cap, count, i; + + dirp = opendir(parent->fts_accpath); + if (dirp == NULL) { + parent->fts_errno = errno; + parent->fts_info = FTS_DNR; + parent->_state = FTS_STATE_DNR; + return (-1); + } + + names = NULL; + cap = count = 0; + while ((dp = readdir(dirp)) != NULL) { + char *name; + + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + if (count == cap) { + size_t new_cap; + char **new_names; + + new_cap = cap == 0 ? 16 : cap * 2; + new_names = realloc(names, new_cap * sizeof(*names)); + if (new_names == NULL) + goto fail; + names = new_names; + cap = new_cap; + } + name = strdup(dp->d_name); + if (name == NULL) + goto fail; + names[count++] = name; + } + closedir(dirp); + + if (count == 0) { + free(names); + parent->_children = NULL; + parent->_child_count = 0; + parent->_child_index = 0; + return (0); + } + + if (ftsp->fts_compar != NULL) { + FTSENT **cmp_entries; + + cmp_entries = calloc(count, sizeof(*cmp_entries)); + if (cmp_entries == NULL) + goto fail_names; + for (i = 0; i < count; i++) { + child_path = fts_join_path(parent->fts_path, names[i]); + if (child_path == NULL) { + while (i > 0) + fts_free_entry(cmp_entries[--i]); + free(cmp_entries); + goto fail_names; + } + cmp_entries[i] = fts_make_entry(ftsp, parent, child_path, + parent->fts_level + 1, 0); + free(child_path); + if (cmp_entries[i] == NULL) { + while (i > 0) + fts_free_entry(cmp_entries[--i]); + free(cmp_entries); + goto fail_names; + } + } + qsort(cmp_entries, count, sizeof(*cmp_entries), + (int (*)(const void *, const void *))ftsp->fts_compar); + parent->_children = cmp_entries; + parent->_child_count = count; + parent->_child_index = 0; + for (i = 0; i < count; i++) + free(names[i]); + free(names); + return (0); + } + + parent->_children = calloc(count, sizeof(*parent->_children)); + if (parent->_children == NULL) + goto fail_names; + for (i = 0; i < count; i++) { + child_path = fts_join_path(parent->fts_path, names[i]); + if (child_path == NULL) + goto fail_children; + child = fts_make_entry(ftsp, parent, child_path, parent->fts_level + 1, 0); + free(child_path); + if (child == NULL) + goto fail_children; + parent->_children[i] = child; + } + parent->_child_count = count; + parent->_child_index = 0; + for (i = 0; i < count; i++) + free(names[i]); + free(names); + return (0); + +fail_children: + while (i > 0) + fts_free_entry(parent->_children[--i]); + free(parent->_children); + parent->_children = NULL; +fail_names: + for (i = 0; i < count; i++) + free(names[i]); + free(names); + parent->fts_errno = ENOMEM; + parent->fts_info = FTS_ERR; + parent->_state = FTS_STATE_DONE; + return (-1); +fail: + closedir(dirp); + free(names); + parent->fts_errno = ENOMEM; + parent->fts_info = FTS_ERR; + parent->_state = FTS_STATE_DONE; + return (-1); +} + +static FTSENT * +fts_advance(FTS *ftsp, FTSENT *ent) +{ + FTSENT *parent; + + for (;;) { + if (ent == NULL) + break; + + if (ent->fts_info == FTS_D && ent->_state == FTS_STATE_PRE) { + if (ent->fts_instr == FTS_SKIP || ent->fts_info == FTS_DC) { + ent->fts_info = FTS_DP; + ent->_state = FTS_STATE_POST; + return (ent); + } + if (fts_prepare_children(ftsp, ent) != 0) { + if (ent->fts_info == FTS_DNR || ent->fts_info == FTS_ERR) + return (ent); + } + ent->_state = FTS_STATE_CHILDREN; + if (ent->_child_count > 0) + return (ent->_children[ent->_child_index++]); + ent->fts_info = FTS_DP; + ent->_state = FTS_STATE_POST; + return (ent); + } + + if (ent->_state == FTS_STATE_DNR) { + ent->fts_info = FTS_DP; + ent->_state = FTS_STATE_POST; + return (ent); + } + + if (ent->_state == FTS_STATE_CHILDREN) { + if (ent->_child_index < ent->_child_count) + return (ent->_children[ent->_child_index++]); + ent->fts_info = FTS_DP; + ent->_state = FTS_STATE_POST; + return (ent); + } + + ent->_state = FTS_STATE_DONE; + parent = ent->fts_parent; + if (parent != NULL) { + ent = parent; + continue; + } + if (++ftsp->_root_index < ftsp->_root_count) + return (ftsp->_roots[ftsp->_root_index]); + break; + } + + return (NULL); +} + +FTS * +fts_open(char * const *paths, int options, + int (*compar)(const FTSENT * const *, const FTSENT * const *)) +{ + FTS *ftsp; + size_t count, i; + + count = 0; + while (paths[count] != NULL) + count++; + + ftsp = calloc(1, sizeof(*ftsp)); + if (ftsp == NULL) + return (NULL); + ftsp->_roots = calloc(count, sizeof(*ftsp->_roots)); + if (ftsp->_roots == NULL) { + free(ftsp); + return (NULL); + } + ftsp->_root_count = count; + ftsp->fts_options = options; + ftsp->fts_compar = compar; + + for (i = 0; i < count; i++) { + ftsp->_roots[i] = fts_make_entry(ftsp, NULL, paths[i], FTS_ROOTLEVEL, 1); + if (ftsp->_roots[i] == NULL) { + while (i > 0) + fts_free_entry(ftsp->_roots[--i]); + free(ftsp->_roots); + free(ftsp); + return (NULL); + } + } + + return (ftsp); +} + +FTSENT * +fts_read(FTS *ftsp) +{ + if (ftsp == NULL || ftsp->_root_count == 0) + return (NULL); + if (ftsp->_current == NULL) { + ftsp->_root_index = 0; + ftsp->_current = ftsp->_roots[0]; + return (ftsp->_current); + } + ftsp->_current = fts_advance(ftsp, ftsp->_current); + if (ftsp->_current == NULL) + errno = 0; + return (ftsp->_current); +} + +int +fts_set(FTS *ftsp, FTSENT *f, int instr) +{ + (void)ftsp; + if (f == NULL) + return (-1); + f->fts_instr = instr; + return (0); +} + +static void +fts_free_entry(FTSENT *ent) +{ + size_t i; + + if (ent == NULL) + return; + for (i = 0; i < ent->_child_count; i++) + fts_free_entry(ent->_children[i]); + free(ent->_children); + free(ent->fts_statp); + free(ent->fts_name); + free(ent->fts_path); + free(ent); +} + +int +fts_close(FTS *ftsp) +{ + size_t i; + + if (ftsp == NULL) + return (0); + for (i = 0; i < ftsp->_root_count; i++) + fts_free_entry(ftsp->_roots[i]); + free(ftsp->_roots); + free(ftsp); + return (0); +} |
