diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 5a7834e7c1f3..10072094b3f0 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -92,7 +92,7 @@ static int Rflag, rflag; enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; static int copy(char *[], enum op, int); -static int mastercmp(const FTSENT **, const FTSENT **); +static int mastercmp(const FTSENT * const *, const FTSENT * const *); int main(int argc, char *argv[]) @@ -484,7 +484,7 @@ copy(char *argv[], enum op type, int fts_options) * files first reduces seeking. */ int -mastercmp(const FTSENT **a, const FTSENT **b) +mastercmp(const FTSENT * const *a, const FTSENT * const *b) { int a_info, b_info; diff --git a/bin/ls/ls.c b/bin/ls/ls.c index 71158deead22..be19f3634a82 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -82,7 +82,7 @@ __FBSDID("$FreeBSD$"); static void display(FTSENT *, FTSENT *); static u_quad_t makenines(u_long); -static int mastercmp(const FTSENT **, const FTSENT **); +static int mastercmp(const FTSENT * const *, const FTSENT * const *); static void traverse(int, char **, int); static void (*printfcn)(DISPLAY *); @@ -759,7 +759,7 @@ display(FTSENT *p, FTSENT *list) * All other levels use the sort function. Error entries remain unsorted. */ static int -mastercmp(const FTSENT **a, const FTSENT **b) +mastercmp(const FTSENT * const *a, const FTSENT * const *b) { int a_info, b_info; diff --git a/include/fts.h b/include/fts.h index 8dd2c4160c10..09c4600a1072 100644 --- a/include/fts.h +++ b/include/fts.h @@ -47,7 +47,7 @@ typedef struct { int fts_pathlen; /* sizeof(path) */ int fts_nitems; /* elements in the sort array */ int (*fts_compar) /* compare function */ - (const struct _ftsent **, const struct _ftsent **); + (const struct _ftsent * const *, const struct _ftsent * const *); #define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ #define FTS_LOGICAL 0x002 /* logical walk */ @@ -62,6 +62,7 @@ typedef struct { #define FTS_NAMEONLY 0x100 /* (private) child names only */ #define FTS_STOP 0x200 /* (private) unrecoverable error */ int fts_options; /* fts_open options, global flags */ + void *fts_clientptr; /* thunk for sort function */ } FTS; typedef struct _ftsent { @@ -113,7 +114,8 @@ typedef struct _ftsent { u_short fts_instr; /* fts_set() instructions */ struct stat *fts_statp; /* stat(2) information */ - char fts_name[1]; /* file name */ + char *fts_name; /* file name */ + FTS *fts_fts; /* back pointer to main FTS */ } FTSENT; #include @@ -121,10 +123,15 @@ typedef struct _ftsent { __BEGIN_DECLS FTSENT *fts_children(FTS *, int); int fts_close(FTS *); +void *fts_get_clientptr(FTS *); +#define fts_get_clientptr(fts) ((fts)->fts_clientptr) +FTS *fts_get_stream(FTSENT *); +#define fts_get_stream(ftsent) ((ftsent)->fts_fts) FTS *fts_open(char * const *, int, - int (*)(const FTSENT **, const FTSENT **)); + int (*)(const FTSENT * const *, const FTSENT * const *)); FTSENT *fts_read(FTS *); int fts_set(FTS *, FTSENT *, int); +void fts_set_clientptr(FTS *, void *); __END_DECLS #endif /* !_FTS_H_ */ diff --git a/lib/libc/gen/fts-compat.c b/lib/libc/gen/fts-compat.c index 6220dc6f130c..7a0b068f6b57 100644 --- a/lib/libc/gen/fts-compat.c +++ b/lib/libc/gen/fts-compat.c @@ -81,7 +81,7 @@ FTS * fts_open(argv, options, compar) char * const *argv; int options; - int (*compar)(const FTSENT **, const FTSENT **); + int (*compar)(const FTSENT * const *, const FTSENT * const *); { FTS *sp; FTSENT *p, *root; @@ -96,7 +96,7 @@ fts_open(argv, options, compar) } /* Allocate/initialize the stream */ - if ((sp = malloc((u_int)sizeof(FTS))) == NULL) + if ((sp = malloc(sizeof(FTS))) == NULL) return (NULL); memset(sp, 0, sizeof(FTS)); sp->fts_compar = compar; @@ -547,6 +547,34 @@ fts_children(sp, instr) return (sp->fts_child); } +#ifndef fts_get_clientptr +#error "fts_get_clientptr not defined" +#endif + +void * +(fts_get_clientptr)(FTS *sp) +{ + + return (fts_get_clientptr(sp)); +} + +#ifndef fts_get_stream +#error "fts_get_stream not defined" +#endif + +FTS * +(fts_get_stream)(FTSENT *p) +{ + return (fts_get_stream(p)); +} + +void +fts_set_clientptr(FTS *sp, void *clientptr) +{ + + sp->fts_clientptr = clientptr; +} + /* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children @@ -907,6 +935,21 @@ err: memset(sbp, 0, sizeof(struct stat)); return (FTS_DEFAULT); } +/* + * The comparison function takes pointers to pointers to FTSENT structures. + * Qsort wants a comparison function that takes pointers to void. + * (Both with appropriate levels of const-poisoning, of course!) + * Use a trampoline function to deal with the difference. + */ +static int +fts_compar(const void *a, const void *b) +{ + FTS *parent; + + parent = (*(const FTSENT * const *)a)->fts_fts; + return (*parent->fts_compar)(a, b); +} + static FTSENT * fts_sort(sp, head, nitems) FTS *sp; @@ -932,7 +975,7 @@ fts_sort(sp, head, nitems) } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); + qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; @@ -948,26 +991,36 @@ fts_alloc(sp, name, namelen) FTSENT *p; size_t len; + struct ftsent_withstat { + FTSENT ent; + struct stat statbuf; + }; + /* * The file name is a variable length array and no stat structure is * necessary if the user has set the nostat bit. Allocate the FTSENT * structure, the file name and the stat structure in one chunk, but - * be careful that the stat structure is reasonably aligned. Since the - * fts_name field is declared to be of size 1, the fts_name pointer is - * namelen + 2 before the first possible address of the stat structure. + * be careful that the stat structure is reasonably aligned. */ - len = sizeof(FTSENT) + namelen; - if (!ISSET(FTS_NOSTAT)) - len += sizeof(struct stat) + ALIGNBYTES; + if (ISSET(FTS_NOSTAT)) + len = sizeof(FTSENT) + namelen + 1; + else + len = sizeof(struct ftsent_withstat) + namelen + 1; + if ((p = malloc(len)) == NULL) return (NULL); - /* Copy the name and guarantee NUL termination. */ - memmove(p->fts_name, name, namelen); - p->fts_name[namelen] = '\0'; + if (ISSET(FTS_NOSTAT)) { + p->fts_name = (char *)(p + 1); + p->fts_statp = NULL; + } else { + p->fts_name = (char *)((struct ftsent_withstat *)p + 1); + p->fts_statp = &((struct ftsent_withstat *)p)->statbuf; + } - if (!ISSET(FTS_NOSTAT)) - p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); + /* Copy the name and guarantee NUL termination. */ + memcpy(p->fts_name, name, namelen); + p->fts_name[namelen] = '\0'; p->fts_namelen = namelen; p->fts_path = sp->fts_path; p->fts_errno = 0; @@ -975,6 +1028,7 @@ fts_alloc(sp, name, namelen) p->fts_instr = FTS_NOINSTR; p->fts_number = 0; p->fts_pointer = NULL; + p->fts_fts = sp; return (p); } diff --git a/lib/libc/gen/fts-compat.h b/lib/libc/gen/fts-compat.h index 8dd2c4160c10..09c4600a1072 100644 --- a/lib/libc/gen/fts-compat.h +++ b/lib/libc/gen/fts-compat.h @@ -47,7 +47,7 @@ typedef struct { int fts_pathlen; /* sizeof(path) */ int fts_nitems; /* elements in the sort array */ int (*fts_compar) /* compare function */ - (const struct _ftsent **, const struct _ftsent **); + (const struct _ftsent * const *, const struct _ftsent * const *); #define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ #define FTS_LOGICAL 0x002 /* logical walk */ @@ -62,6 +62,7 @@ typedef struct { #define FTS_NAMEONLY 0x100 /* (private) child names only */ #define FTS_STOP 0x200 /* (private) unrecoverable error */ int fts_options; /* fts_open options, global flags */ + void *fts_clientptr; /* thunk for sort function */ } FTS; typedef struct _ftsent { @@ -113,7 +114,8 @@ typedef struct _ftsent { u_short fts_instr; /* fts_set() instructions */ struct stat *fts_statp; /* stat(2) information */ - char fts_name[1]; /* file name */ + char *fts_name; /* file name */ + FTS *fts_fts; /* back pointer to main FTS */ } FTSENT; #include @@ -121,10 +123,15 @@ typedef struct _ftsent { __BEGIN_DECLS FTSENT *fts_children(FTS *, int); int fts_close(FTS *); +void *fts_get_clientptr(FTS *); +#define fts_get_clientptr(fts) ((fts)->fts_clientptr) +FTS *fts_get_stream(FTSENT *); +#define fts_get_stream(ftsent) ((ftsent)->fts_fts) FTS *fts_open(char * const *, int, - int (*)(const FTSENT **, const FTSENT **)); + int (*)(const FTSENT * const *, const FTSENT * const *)); FTSENT *fts_read(FTS *); int fts_set(FTS *, FTSENT *, int); +void fts_set_clientptr(FTS *, void *); __END_DECLS #endif /* !_FTS_H_ */ diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 1e40a81f924a..452626654db3 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -32,7 +32,7 @@ .\" @(#)fts.3 8.5 (Berkeley) 4/16/94 .\" $FreeBSD$ .\" -.Dd April 16, 1994 +.Dd September 15, 2002 .Dt FTS 3 .Os .Sh NAME @@ -52,6 +52,12 @@ .Fn fts_children "FTS *ftsp" "int options" .Ft int .Fn fts_set "FTS *ftsp" "FTSENT *f" "int options" +.Ft void +.Fn fts_set_clientptr "FTS *ftsp" "void *clientdata" +.Ft void * +.Fn fts_get_clientptr "FTS *ftsp" +.Ft FTS * +.Fn fts_get_stream "FTSENT *f" .Ft int .Fn fts_close "FTS *ftsp" .Sh DESCRIPTION @@ -105,6 +111,26 @@ and .Dq Fa FTSENT No structure are generally interchangeable. +.Pp +The +.Fa FTS +structure contains space for a single pointer, which may be used to +store application data or per-hierarchy state. +The +.Fn fts_set_clientptr +and +.Fn fts_get_clientptr +functions may be used to set and retrieve this pointer. +This is likely to be useful only when accessed from the sort +comparison function, which can determine the original +.Fa FTS +stream of its arguments using the +.Fn fts_get_stream +function. +The two +.Li get +functions are also available as macros of the same name. +.Pp The .Fa FTSENT structure contains at least the following fields, which are @@ -753,9 +779,18 @@ The options were invalid. .Xr chdir 2 , .Xr stat 2 , .Xr qsort 3 -.Sh STANDARDS +.Sh HISTORY The .Nm -utility is expected to be included in a future -.St -p1003.1-88 -revision. +interface was first introduced in +.Bx 4.4 . +The +.Fn fts_get_clientptr , +.Fn fts_get_stream , +and +.Fn fts_set_clientptr +functions were introduced in +.Fx 5.0 , +principally to provide for alternative interfaces to the +.Nm +functionality using different data structures. diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index 6220dc6f130c..7a0b068f6b57 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -81,7 +81,7 @@ FTS * fts_open(argv, options, compar) char * const *argv; int options; - int (*compar)(const FTSENT **, const FTSENT **); + int (*compar)(const FTSENT * const *, const FTSENT * const *); { FTS *sp; FTSENT *p, *root; @@ -96,7 +96,7 @@ fts_open(argv, options, compar) } /* Allocate/initialize the stream */ - if ((sp = malloc((u_int)sizeof(FTS))) == NULL) + if ((sp = malloc(sizeof(FTS))) == NULL) return (NULL); memset(sp, 0, sizeof(FTS)); sp->fts_compar = compar; @@ -547,6 +547,34 @@ fts_children(sp, instr) return (sp->fts_child); } +#ifndef fts_get_clientptr +#error "fts_get_clientptr not defined" +#endif + +void * +(fts_get_clientptr)(FTS *sp) +{ + + return (fts_get_clientptr(sp)); +} + +#ifndef fts_get_stream +#error "fts_get_stream not defined" +#endif + +FTS * +(fts_get_stream)(FTSENT *p) +{ + return (fts_get_stream(p)); +} + +void +fts_set_clientptr(FTS *sp, void *clientptr) +{ + + sp->fts_clientptr = clientptr; +} + /* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children @@ -907,6 +935,21 @@ err: memset(sbp, 0, sizeof(struct stat)); return (FTS_DEFAULT); } +/* + * The comparison function takes pointers to pointers to FTSENT structures. + * Qsort wants a comparison function that takes pointers to void. + * (Both with appropriate levels of const-poisoning, of course!) + * Use a trampoline function to deal with the difference. + */ +static int +fts_compar(const void *a, const void *b) +{ + FTS *parent; + + parent = (*(const FTSENT * const *)a)->fts_fts; + return (*parent->fts_compar)(a, b); +} + static FTSENT * fts_sort(sp, head, nitems) FTS *sp; @@ -932,7 +975,7 @@ fts_sort(sp, head, nitems) } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); + qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; @@ -948,26 +991,36 @@ fts_alloc(sp, name, namelen) FTSENT *p; size_t len; + struct ftsent_withstat { + FTSENT ent; + struct stat statbuf; + }; + /* * The file name is a variable length array and no stat structure is * necessary if the user has set the nostat bit. Allocate the FTSENT * structure, the file name and the stat structure in one chunk, but - * be careful that the stat structure is reasonably aligned. Since the - * fts_name field is declared to be of size 1, the fts_name pointer is - * namelen + 2 before the first possible address of the stat structure. + * be careful that the stat structure is reasonably aligned. */ - len = sizeof(FTSENT) + namelen; - if (!ISSET(FTS_NOSTAT)) - len += sizeof(struct stat) + ALIGNBYTES; + if (ISSET(FTS_NOSTAT)) + len = sizeof(FTSENT) + namelen + 1; + else + len = sizeof(struct ftsent_withstat) + namelen + 1; + if ((p = malloc(len)) == NULL) return (NULL); - /* Copy the name and guarantee NUL termination. */ - memmove(p->fts_name, name, namelen); - p->fts_name[namelen] = '\0'; + if (ISSET(FTS_NOSTAT)) { + p->fts_name = (char *)(p + 1); + p->fts_statp = NULL; + } else { + p->fts_name = (char *)((struct ftsent_withstat *)p + 1); + p->fts_statp = &((struct ftsent_withstat *)p)->statbuf; + } - if (!ISSET(FTS_NOSTAT)) - p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); + /* Copy the name and guarantee NUL termination. */ + memcpy(p->fts_name, name, namelen); + p->fts_name[namelen] = '\0'; p->fts_namelen = namelen; p->fts_path = sp->fts_path; p->fts_errno = 0; @@ -975,6 +1028,7 @@ fts_alloc(sp, name, namelen) p->fts_instr = FTS_NOINSTR; p->fts_number = 0; p->fts_pointer = NULL; + p->fts_fts = sp; return (p); } diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index 5006e9ec7e32..4ef37e880807 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -56,7 +56,7 @@ __FBSDID("$FreeBSD$"); #include "find.h" -static int find_compare(const FTSENT **s1, const FTSENT **s2); +static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2); /* * find_compare -- @@ -66,7 +66,7 @@ static int find_compare(const FTSENT **s1, const FTSENT **s2); */ static int find_compare(s1, s2) - const FTSENT **s1, **s2; + const FTSENT * const *s1, * const *s2; { return (strcoll((*s1)->fts_name, (*s2)->fts_name)); diff --git a/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c index b5a1411adeb4..783fd3f4a1a6 100644 --- a/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c +++ b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c @@ -53,7 +53,7 @@ #define DEFAULT_NUM 1 /* Default number of pieces mailed per run. */ -int fts_sort(const FTSENT **, const FTSENT **); +int fts_sort(const FTSENT * const *, const FTSENT * const *); int run_sendmail(int ifd); int @@ -156,7 +156,7 @@ main(int argc, char **argv) } int -fts_sort(const FTSENT ** a, const FTSENT ** b) +fts_sort(const FTSENT * const * a, const FTSENT * const * b) { if ((*a)->fts_info != FTS_F) return(0); diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c index 3ab160299624..ae82966fe10e 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -80,7 +80,7 @@ static uid_t uid; static mode_t mode; static u_long flags = 0xffffffff; -static int dsort(const FTSENT **, const FTSENT **); +static int dsort(const FTSENT * const *, const FTSENT * const *); static void output(int, int *, const char *, ...) __printflike(3, 4); static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *); static void statf(int, FTSENT *); @@ -398,7 +398,7 @@ statd(t, parent, puid, pgid, pmode, pflags) static int dsort(a, b) - const FTSENT **a, **b; + const FTSENT * const *a, * const *b; { if (S_ISDIR((*a)->fts_statp->st_mode)) { if (!S_ISDIR((*b)->fts_statp->st_mode)) diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c index cf745c32105c..5312c27c2041 100644 --- a/usr.sbin/pkg_install/lib/match.c +++ b/usr.sbin/pkg_install/lib/match.c @@ -40,7 +40,7 @@ struct store { static int rex_match(const char *, const char *); struct store *storecreate(struct store *); static int storeappend(struct store *, const char *); -static int fname_cmp(const FTSENT **, const FTSENT **); +static int fname_cmp(const FTSENT * const *, const FTSENT * const *); /* * Function to query names of installed packages. @@ -337,7 +337,7 @@ storeappend(struct store *store, const char *item) } static int -fname_cmp(const FTSENT **a, const FTSENT **b) +fname_cmp(const FTSENT * const *a, const FTSENT * const *b) { return strcmp((*a)->fts_name, (*b)->fts_name); }