/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2010 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Phong Vo
* Glenn Fowler
* AT&T Research
*
* fts implementation unwound from the kpv ftwalk() of 1988-10-30
*/
#include <ast.h>
#include <ast_dir.h>
#include <error.h>
#include <fs3d.h>
#include <ls.h>
struct Ftsent;
#define _FTS_PRIVATE_ \
int cd; /* chdir status */ \
int cpname; \
int flags; /* fts_open() flags */ \
int nd; \
unsigned char children; \
unsigned char fs3d; \
unsigned char nostat; \
unsigned char state; /* fts_read() state */ \
char* base; /* basename in path */ \
char* name; \
char* path; /* path workspace */ \
char* endbase; /* space to build paths */ \
char* endbuf; /* space to build paths */ \
/*
* NOTE: <ftwalk.h> relies on status and statb being the first two elements
*/
#define _FTSENT_PRIVATE_ \
int nd; /* popdir() count */ \
long nlink; /* FTS_D link count */ \
unsigned char must; /* must stat */ \
unsigned char type; /* DT_* type */ \
unsigned char symlink; /* originally a symlink */ \
char name[sizeof(int)]; /* fts_name data */
#include <fts.h>
#ifndef ENOSYS
#endif
#if MAXNAMLEN > 16
#else
#endif
#define drop(p,f) (((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free))
#ifdef D_TYPE
#define SKIP(p,f) ((f)->fts_parent->must == 0 && (((f)->type == DT_UNKNOWN) ? SKIPLINK(p,f) : ((f)->type != DT_DIR && ((f)->type != DT_LNK || ((p)->flags & FTS_PHYSICAL)))))
#else
#define DT_UNKNOWN 0
#define TYPE(f,d)
#endif
#ifndef D_FILENO
#endif
/*
* NOTE: a malicious dir rename() could change .. underfoot so we
* must always verify; undef verify to enable the unsafe code
*/
/*
* FTS_NOSTAT requires a dir with
* D_TYPE(&dirent_t)!=DT_UNKNOWN
* OR
* st_nlink>=2
*/
typedef struct Notify_s
{
void* context;
} Notify_t;
/*
* allocate an FTSENT node
*/
static FTSENT*
{
register FTSENT* f;
register size_t n;
{
}
else
{
{
return 0;
}
}
TYPE(f, DT_UNKNOWN);
f->status = 0;
f->symlink = 0;
#if __OBSOLETE__ < 20140101
f->_fts_level = (short)f->fts_level;
#endif
f->fts_link = 0;
f->fts_pointer = 0;
f->fts_number = 0;
f->fts_errno = 0;
f->fts_namelen = namelen;
#if __OBSOLETE__ < 20140101
f->_fts_namelen = (unsigned short)f->fts_namelen;
#endif
return f;
}
/*
*/
static int
{
return -1;
return 1;
return -1;
return 1;
/*
* hack for NFS where <dev,ino> may not uniquely identify objects
*/
return -1;
return 1;
return 0;
}
/*
* search trees with top-down splaying (a la Tarjan and Sleator)
* when used for insertion sort, this implements a stable sort
*/
static FTSENT*
{
register int cmp;
register FTSENT* t;
while (root)
{
break;
if (cmp < 0)
{
/*
* this is the left zig-zig case
*/
{
break;
}
/*
* stick all things > e to the right tree
*/
if (right)
else
}
else
{
/*
* this is the right zig-zig case
*/
{
break;
}
/*
* stick all things <= e to the left tree
*/
if (left)
else
}
}
if (!root)
root = e;
else
{
if (right)
else
if (left)
else
}
return root;
}
/*
* delete the root element from the tree
*/
static FTSENT*
{
register FTSENT* t;
else
{
}
return root;
}
/*
* generate ordered fts_link list from binary tree at root
* FTSENT.stack instead of recursion to avoid blowing the real
* stack on big directories
*/
static void
{
for (;;)
{
{
}
else
{
for (;;)
{
if (*top)
else
{
break;
}
{
return;
}
}
}
}
}
/*
* set directory when curdir is lost in space
*/
static int
{
register int cdrv;
if (path[0] == '/')
else
{
/*
* note that path and home are in the same buffer
*/
path[-1] = 0;
}
if (cdrv < 0)
return cdrv;
}
/*
* set to parent dir
*/
static int
{
register int c;
register int cdrv;
{
c = base[0];
base[0] = 0;
base[0] = c;
}
else
return cdrv;
}
/*
* pop a set of directories
*/
static int
{
register FTSENT*f;
register char* s;
register char* e;
#ifndef verify
register int verify;
#endif
return -1;
#ifndef verify
verify = 0;
#endif
{
{
{
#ifndef verify
#endif
}
*s++ = '.';
*s++ = '.';
*s++ = '/';
}
*s = 0;
return -1;
}
}
/*
* initialize st from path and fts_info from st
*/
static int
{
if (path)
{
#ifdef S_ISLNK
{
goto bad;
}
else
#endif
goto bad;
}
#ifdef S_ISLNK
#endif
{
{
f->fts_parent->nlink--;
#ifdef D_TYPE
{
f->must = 2;
f->nlink = 2;
}
else
f->must = 0;
#else
f->must = 1;
else
f->must = 2;
#endif
}
else
f->must = 2;
}
#ifdef S_ISLNK
{
f->symlink = 1;
{
goto again;
}
}
#endif
else
{
}
return 0;
bad:
TYPE(f, DT_UNKNOWN);
return -1;
}
/*
* get top list of elements to process
* ordering delayed until first fts_read()
* to give caller a chance to set fts->handle
*/
static FTSENT*
{
register char* path;
register FTSENT* f;
int physical;
int metaphysical;
char* s;
{
/*
* make elements
*/
break;
if (!physical)
else if (*path != '.')
{
}
else
{
{
s = path;
while (*s++ == '.' && *s++ == '/')
{
while (*s == '/')
s++;
if (!*s)
break;
while (*path++ = *s++);
}
}
else
*s = 0;
f->fts_namelen = s - path;
}
#if __OBSOLETE__ < 20140101
f->_fts_namelen = (unsigned short)f->fts_namelen;
#endif
if (!*path)
{
}
else
#ifdef S_ISLNK
/*
* don't let any standards committee get
* away with calling your idea a hack
*/
{
}
#endif
if (bot)
{
bot = f;
}
else
}
return top;
}
/*
* order fts->todo if fts->comparf != 0
*/
static void
{
register FTSENT* f;
}
/*
* resize the path buffer
* note that free() is not used because we may need to chdir(fts->home)
* if there isn't enough space to continue
*/
static int
{
register char* old;
register char* newp;
/*
* add space for "/." used in testing FTS_DNX
*/
{
return -1;
}
return 0;
}
/*
* open a new fts stream on pathnames
*/
FTS*
{
return 0;
/*
* set up the path work buffer
*/
for (;;)
{
{
return 0;
}
break;
else
}
/*
* initialize the tippity-top
*/
memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2);
#if __OBSOLETE__ < 20140101
#endif
/*
* make the list of top elements
*/
{
char* v[2];
v[1] = 0;
}
else
{
return 0;
}
return fts;
}
/*
* return the next FTS entry
*/
{
register char* s;
register int n;
register FTSENT* f;
struct dirent* d;
size_t i;
FTSENT* t;
Notify_t* p;
#ifdef verify
#endif
for (;;)
{
case FTS_top_return:
t = 0;
while (f)
{
if (t)
{
f = t->fts_link;
}
else
{
}
}
else
{
t = f;
f = f->fts_link;
}
/*FALLTHROUGH*/
case 0:
return 0;
/*FALLTHROUGH*/
case FTS_todo:
/*
* process the top object on the stack
*/
/*
* initialize the top level
*/
if (f->fts_level == 0)
{
}
/*
* chdir to parent if asked for
*/
{
}
/*
* add object's name to the path
*/
return 0;
/*FALLTHROUGH*/
case FTS_preorder:
/*
* check for cycle and open dir
*/
{
if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0)
{
}
else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev))
{
/*
* buffer is known to be large enough here!
*/
}
}
{
f->fts_link = 0;
goto note;
}
/*FALLTHROUGH*/
case FTS_preorder_resume:
/*
* prune
*/
{
{
}
continue;
}
/*
* FTS_D or FTS_DNX, about to read children
*/
{
{
}
}
/*FALLTHROUGH*/
case FTS_readdir:
{
s = d->d_name;
if (s[0] == '.')
{
if (s[1] == 0)
{
continue;
n = 1;
}
else if (s[1] == '.' && s[2] == 0)
{
continue;
n = 2;
}
else
n = 0;
}
else
n = 0;
/*
* make a new entry
*/
i = D_NAMLEN(d);
return 0;
/*
* check for space
*/
{
return 0;
}
{
}
if (n)
{
/*
* don't recurse on . and ..
*/
if (n == 1)
else
{
{
}
else
}
}
else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags))
{
else
}
else
{
/*
* terminal node
*/
goto note;
}
}
/*
* done with the directory
*/
{
/*
* try moving back to parent dir
*/
{
#ifdef verify
#endif
)
}
goto note;
}
/*FALLTHROUGH*/
case FTS_children_resume:
{
}
/*FALLTHROUGH*/
case FTS_popstack:
/*
* pop objects completely processed
*/
/*FALLTHROUGH*/
case FTS_popstack_resume:
{
t = f->fts_parent;
{
/*
* delete from <dev,ino> tree
*/
{
}
/*
* perform post-order processing
*/
f->status != FTS_NOPOSTORDER)
{
/*
* move to parent dir
*/
/*
*/
f->fts_link = 0;
goto note;
}
}
/*
* reset base
*/
/*
* try again or delete from top of stack
*/
{
f->status = 0;
}
else
{
}
f = t;
}
/*
* reset current directory
*/
{
}
{
continue;
}
return 0;
case FTS_children_return:
/*
* chdir down again
*/
{
{
}
}
/*
* prune
*/
{
else
}
else
{
{
{
s = f->fts_name;
{
}
}
}
f = f->fts_link;
}
continue;
case FTS_popstack_return:
continue;
case FTS_preorder_return:
/*
* follow symlink if asked to
*/
if (f->status == FTS_FOLLOW)
{
f->status = 0;
{
{
continue;
}
}
}
/*
* about to prune this f and already at home
*/
continue;
case FTS_terminal:
if (f->status == FTS_FOLLOW)
{
f->status = 0;
{
{
continue;
}
}
}
f = f->fts_parent;
continue;
case FTS_error:
return 0;
default:
return 0;
}
note:
#if __OBSOLETE__ < 20140101
f->_fts_pathlen = (unsigned short)f->fts_pathlen;
#endif
break;
else if (n < 0)
{
return 0;
}
return f;
}
/*
* set stream or entry flags
*/
int
{
return -1;
switch (status)
{
case FTS_AGAIN:
break;
case FTS_FOLLOW:
return -1;
break;
case FTS_NOPOSTORDER:
break;
case FTS_SKIP:
return -1;
break;
default:
return -1;
}
return 0;
}
/*
* return the list of child entries
*/
{
register FTSENT* f;
{
case 0:
case FTS_preorder_return:
f = f->fts_link;
return f;
}
return 0;
}
/*
* return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags
* conditioned by astconf()
*/
int
fts_flags(void)
{
register char* s;
if (streq(s, "logical"))
return FTS_LOGICAL;
if (streq(s, "physical"))
return FTS_PHYSICAL|FTS_SEEDOTDIR;
}
/*
* return 1 if ent is mounted on a local filesystem
*/
int
{
#ifdef ST_LOCAL
#else
#endif
}
/*
* close an open fts stream
*/
int
{
register FTSENT* f;
register FTSENT* x;
{
}
{
x = f->fts_link;
free(f);
}
{
x = f->fts_link;
free(f);
}
return 0;
}
/*
* register function to be called for each fts_read() entry
* context==0 => unregister notifyf
*/
int
{
if (context)
{
return -1;
}
else
{
{
if (pp)
else
return 0;
}
return -1;
}
return 0;
}