├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── README.md ├── bootstrap.sh ├── configure.ac ├── fts.3 ├── fts.c ├── fts.h └── musl-fts.pc.in /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | INSTALL 3 | Makefile.in 4 | Makefile 5 | aclocal.m4 6 | autom4te.cache/ 7 | compile 8 | config.guess 9 | config.h 10 | config.h.in 11 | config.h.in~ 12 | config.log 13 | config.status 14 | config.sub 15 | configure 16 | depcomp 17 | install-sh 18 | ltmain.sh 19 | libfts.la 20 | libtool 21 | missing 22 | fts.lo 23 | fts.o 24 | musl-fts.pc 25 | stamp-h1 26 | .libs/ 27 | m4/ 28 | 29 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Original code: 2 | The NetBSD developers. 3 | 4 | Packaging: 5 | Jürgen Buchmüller 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 1989, 1993 2 | The Regents of the University of California. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the University nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.0 Initial release. 2 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Makefile.am - procress this file with automake to produce Makefile.in 2 | ACLOCAL_AMFLAGS = -I m4 3 | lib_LTLIBRARIES = libfts.la 4 | libfts_la_SOURCES = fts.c 5 | libfts_la_HEADERS = fts.h 6 | libfts_ladir = $(includedir) 7 | 8 | pkgconfig_DATA = musl-fts.pc 9 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | First implementation of a libfts.a for musl. 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | musl-fts 2 | 3 | The musl-fts package implements the fts(3) functions 4 | fts_open, fts_read, fts_children, fts_set and fts_close, 5 | which are missing in musl libc. 6 | 7 | It uses the NetBSD implementation of fts(3) to build a static 8 | library /usr/lib/libfts.a and the /usr/include/fts.h header file. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### musl-fts 2 | 3 | The musl-fts package implements the `fts(3)` functions 4 | `fts_open`, `fts_read`, `fts_children`, `fts_set` and `fts_close`, 5 | which are missing in musl libc. 6 | 7 | It uses the NetBSD implementation of `fts(3)` to build a static 8 | library `/usr/lib/libfts.a` and the `/usr/include/fts.h` header file. 9 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | libtoolize 3 | aclocal 4 | autoconf 5 | autoheader 6 | automake --add-missing 7 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script 2 | 3 | AC_PREREQ(2.69) 4 | AC_INIT([fts], [1.2.7], [pullmoll@t-online.de]) 5 | AM_INIT_AUTOMAKE([1.15]) 6 | AC_CONFIG_MACRO_DIRS([m4]) 7 | AC_CONFIG_HEADERS([config.h]) 8 | 9 | AC_PROG_CC 10 | LT_INIT 11 | 12 | AC_CHECK_HEADERS(assert.h dirent.h errno.h fcntl.h stdlib.h string.h unistd.h sys/param.h sys/stat.h) 13 | 14 | AC_CHECK_DECLS([MAX], , , [ 15 | #include 16 | ]) 17 | 18 | AC_CHECK_DECLS([UINTMAX_MAX], , , [ 19 | #include 20 | ]) 21 | 22 | AC_CHECK_FUNCS([dirfd]) 23 | 24 | AC_CHECK_MEMBERS([DIR.dd_fd, DIR.d_fd],,, 25 | [#include 26 | #include 27 | ]) 28 | 29 | PKG_INSTALLDIR 30 | 31 | AC_CONFIG_FILES([Makefile musl-fts.pc]) 32 | AC_OUTPUT 33 | -------------------------------------------------------------------------------- /fts.3: -------------------------------------------------------------------------------- 1 | .\" $NetBSD: fts.3,v 1.30 2011/03/30 16:29:26 jruoho Exp $ 2 | .\" 3 | .\" Copyright (c) 1989, 1991, 1993, 1994 4 | .\" The Regents of the University of California. All rights reserved. 5 | .\" 6 | .\" Redistribution and use in source and binary forms, with or without 7 | .\" modification, are permitted provided that the following conditions 8 | .\" are met: 9 | .\" 1. Redistributions of source code must retain the above copyright 10 | .\" notice, this list of conditions and the following disclaimer. 11 | .\" 2. Redistributions in binary form must reproduce the above copyright 12 | .\" notice, this list of conditions and the following disclaimer in the 13 | .\" documentation and/or other materials provided with the distribution. 14 | .\" 3. Neither the name of the University nor the names of its contributors 15 | .\" may be used to endorse or promote products derived from this software 16 | .\" without specific prior written permission. 17 | .\" 18 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | .\" SUCH DAMAGE. 29 | .\" 30 | .\" @(#)fts.3 8.5 (Berkeley) 4/16/94 31 | .\" 32 | .Dd March 30, 2011 33 | .Dt FTS 3 34 | .Os 35 | .Sh NAME 36 | .Nm fts , 37 | .Nm fts_open , 38 | .Nm fts_read , 39 | .Nm fts_children , 40 | .Nm fts_set , 41 | .Nm fts_close 42 | .Nd traverse a file hierarchy 43 | .Sh LIBRARY 44 | .Lb libc 45 | .Sh SYNOPSIS 46 | .In sys/types.h 47 | .In sys/stat.h 48 | .In fts.h 49 | .Ft FTS * 50 | .Fo fts_open 51 | .Fa "char * const *path_argv" 52 | .Fa "int options" 53 | .Fa "int (*compar)(const FTSENT **, const FTSENT **)" 54 | .Fc 55 | .Ft FTSENT * 56 | .Fn fts_read "FTS *ftsp" 57 | .Ft FTSENT * 58 | .Fn fts_children "FTS *ftsp" "int options" 59 | .Ft int 60 | .Fn fts_set "FTS *ftsp" "FTSENT *f" "int options" 61 | .Ft int 62 | .Fn fts_close "FTS *ftsp" 63 | .Sh DESCRIPTION 64 | The 65 | .Nm 66 | functions are provided for traversing 67 | .Ux 68 | file hierarchies. 69 | A simple overview is that the 70 | .Fn fts_open 71 | function returns a 72 | .Dq handle 73 | on a file hierarchy, which is then supplied to 74 | the other 75 | .Nm 76 | functions. 77 | The function 78 | .Fn fts_read 79 | returns a pointer to a structure describing one of the files in the file 80 | hierarchy. 81 | The function 82 | .Fn fts_children 83 | returns a pointer to a linked list of structures, each of which describes 84 | one of the files contained in a directory in the hierarchy. 85 | In general, directories are visited two distinguishable times; in pre-order 86 | (before any of their descendants are visited) and in post-order (after all 87 | of their descendants have been visited). 88 | Files are visited once. 89 | It is possible to walk the hierarchy 90 | .Dq logically 91 | (ignoring symbolic links) 92 | or physically (visiting symbolic links), order the walk of the hierarchy or 93 | prune and/or re-visit portions of the hierarchy. 94 | .Pp 95 | Two structures are defined (and typedef'd) in the include file 96 | .In fts.h . 97 | The first is 98 | .Fa FTS , 99 | the structure that represents the file hierarchy itself. 100 | The second is 101 | .Fa FTSENT , 102 | the structure that represents a file in the file 103 | hierarchy. 104 | Normally, an 105 | .Fa FTSENT 106 | structure is returned for every file in the file 107 | hierarchy. 108 | In this manual page, 109 | .Dq file 110 | and 111 | .Dq Fa FTSENT No structure 112 | are generally 113 | interchangeable. 114 | The 115 | .Fa FTSENT 116 | structure contains at least the following fields, which are 117 | described in greater detail below: 118 | .Bd -literal -offset 2n 119 | typedef struct _ftsent { 120 | u_short fts_info; /* flags for FTSENT structure */ 121 | char *fts_accpath; /* access path */ 122 | char *fts_path; /* root path */ 123 | short fts_pathlen; /* strlen(fts_path) */ 124 | char *fts_name; /* file name */ 125 | short fts_namelen; /* strlen(fts_name) */ 126 | short fts_level; /* depth (\-1 to N) */ 127 | int fts_errno; /* file errno */ 128 | long fts_number; /* local numeric value */ 129 | void *fts_pointer; /* local address value */ 130 | struct ftsent *fts_parent; /* parent directory */ 131 | struct ftsent *fts_link; /* next file structure */ 132 | struct ftsent *fts_cycle; /* cycle structure */ 133 | struct stat *fts_statp; /* stat(2) information */ 134 | } FTSENT; 135 | .Ed 136 | .Pp 137 | These fields are defined as follows: 138 | .Bl -tag -width "fts_namelen" 139 | .It Fa fts_info 140 | One of the following flags describing the returned 141 | .Fa FTSENT 142 | structure and 143 | the file it represents. 144 | With the exception of directories without errors 145 | .Pq Dv FTS_D , 146 | all of these 147 | entries are terminal, that is, they will not be revisited, nor will any 148 | of their descendants be visited. 149 | .Bl -tag -width FTS_DEFAULT 150 | .It Dv FTS_D 151 | A directory being visited in pre-order. 152 | .It Dv FTS_DC 153 | A directory that causes a cycle in the tree. 154 | (The 155 | .Fa fts_cycle 156 | field of the 157 | .Fa FTSENT 158 | structure will be filled in as well). 159 | .It Dv FTS_DEFAULT 160 | Any 161 | .Fa FTSENT 162 | structure that represents a file type not explicitly described 163 | by one of the other 164 | .Fa fts_info 165 | values. 166 | .It Dv FTS_DNR 167 | A directory which cannot be read. 168 | This is an error return, and the 169 | .Fa fts_errno 170 | field will be set to indicate what caused the error. 171 | .It Dv FTS_DOT 172 | A file named 173 | .Ql \&. 174 | or 175 | .Ql .. 176 | which was not specified as a file name to 177 | .Fn fts_open 178 | (see 179 | .Dv FTS_SEEDOT ) . 180 | .It Dv FTS_DP 181 | A directory being visited in post-order. 182 | The contents of the 183 | .Fa FTSENT 184 | structure will be unchanged from when 185 | it was returned in pre-order, i.e., with the 186 | .Fa fts_info 187 | field set to 188 | .Dv FTS_D . 189 | .It Dv FTS_ERR 190 | This is an error return, and the 191 | .Fa fts_errno 192 | field will be set to indicate what caused the error. 193 | .It Dv FTS_F 194 | A regular file. 195 | .It Dv FTS_NS 196 | A file for which no 197 | .Xr stat 2 198 | information was available. 199 | The contents of the 200 | .Fa fts_statp 201 | field are undefined. 202 | This is an error return, and the 203 | .Fa fts_errno 204 | field will be set to indicate what caused the error. 205 | .It Dv FTS_NSOK 206 | A file for which no 207 | .Xr stat 2 208 | information was requested. 209 | The contents of the 210 | .Fa fts_statp 211 | field are undefined. 212 | .It Dv FTS_SL 213 | A symbolic link. 214 | .It Dv FTS_SLNONE 215 | A symbolic link with a non-existent target. 216 | The contents of the 217 | .Fa fts_statp 218 | field reference the file characteristic information for the symbolic link 219 | itself. 220 | .It Dv FTS_W 221 | A whiteout object. 222 | .El 223 | .It Fa fts_accpath 224 | A path for accessing the file from the current directory. 225 | .It Fa fts_path 226 | The path for the file relative to the root of the traversal. 227 | This path contains the path specified to 228 | .Fn fts_open 229 | as a prefix. 230 | .It Fa fts_pathlen 231 | The length of the string referenced by 232 | .Fa fts_path . 233 | .It Fa fts_name 234 | The name of the file. 235 | .It Fa fts_namelen 236 | The length of the string referenced by 237 | .Fa fts_name . 238 | .It Fa fts_level 239 | The depth of the traversal, numbered from \-1 to N, where this file 240 | was found. 241 | The 242 | .Fa FTSENT 243 | structure representing the parent of the starting point (or root) 244 | of the traversal is numbered \-1, and the 245 | .Fa FTSENT 246 | structure for the root 247 | itself is numbered 0. 248 | .It Fa fts_errno 249 | Upon return of a 250 | .Fa FTSENT 251 | structure from the 252 | .Fn fts_children 253 | or 254 | .Fn fts_read 255 | functions, with its 256 | .Fa fts_info 257 | field set to 258 | .Dv FTS_DNR , 259 | .Dv FTS_ERR 260 | or 261 | .Dv FTS_NS , 262 | the 263 | .Fa fts_errno 264 | field contains the value of the external variable 265 | .Va errno 266 | specifying the cause of the error. 267 | Otherwise, the contents of the 268 | .Fa fts_errno 269 | field are undefined. 270 | .It Fa fts_number 271 | This field is provided for the use of the application program and is 272 | not modified by the 273 | .Nm 274 | functions. 275 | It is initialized to 0. 276 | .It Fa fts_pointer 277 | This field is provided for the use of the application program and is 278 | not modified by the 279 | .Nm 280 | functions. 281 | It is initialized to 282 | .Dv NULL . 283 | .It Fa fts_parent 284 | A pointer to the 285 | .Fa FTSENT 286 | structure referencing the file in the hierarchy 287 | immediately above the current file, i.e., the directory of which this 288 | file is a member. 289 | A parent structure for the initial entry point is provided as well, 290 | however, only the 291 | .Fa fts_level , 292 | .Fa fts_number 293 | and 294 | .Fa fts_pointer 295 | fields are guaranteed to be initialized. 296 | .It Fa fts_link 297 | Upon return from the 298 | .Fn fts_children 299 | function, the 300 | .Fa fts_link 301 | field points to the next structure in the 302 | .Dv NULL Ns -terminated 303 | linked list of directory members. 304 | Otherwise, the contents of the 305 | .Fa fts_link 306 | field are undefined. 307 | .It Fa fts_cycle 308 | If a directory causes a cycle in the hierarchy (see 309 | .Dv FTS_DC ) , 310 | either because 311 | of a hard link between two directories, or a symbolic link pointing to a 312 | directory, the 313 | .Fa fts_cycle 314 | field of the structure will point to the 315 | .Fa FTSENT 316 | structure in the hierarchy that references the same file as the current 317 | .Fa FTSENT 318 | structure. 319 | Otherwise, the contents of the 320 | .Fa fts_cycle 321 | field are undefined. 322 | .It Fa fts_statp 323 | A pointer to 324 | .Xr stat 2 325 | information for the file. 326 | .El 327 | .Pp 328 | A single buffer is used for all of the paths of all of the files in the 329 | file hierarchy. 330 | Therefore, the 331 | .Fa fts_path 332 | and 333 | .Fa fts_accpath 334 | fields are guaranteed to be 335 | .Dv NULL Ns -terminated 336 | .Em only 337 | for the file most recently returned by 338 | .Fn fts_read . 339 | To use these fields to reference any files represented by other 340 | .Fa FTSENT 341 | structures will require that the path buffer be modified using the 342 | information contained in that 343 | .Fa FTSENT 344 | structure's 345 | .Fa fts_pathlen 346 | field. 347 | Any such modifications should be undone before further calls to 348 | .Fn fts_read 349 | are attempted. 350 | The 351 | .Fa fts_name 352 | field is always 353 | .Dv NULL Ns -terminated . 354 | .Sh FTS_OPEN 355 | The 356 | .Fn fts_open 357 | function takes a pointer to an array of character pointers naming one 358 | or more paths which make up a logical file hierarchy to be traversed. 359 | The array must be terminated by a 360 | .Dv NULL 361 | pointer. 362 | .Pp 363 | There are 364 | a number of options, at least one of which (either 365 | .Dv FTS_LOGICAL 366 | or 367 | .Dv FTS_PHYSICAL ) 368 | must be specified. 369 | The options are selected by 370 | .Em or Ns 'ing 371 | the following values: 372 | .Bl -tag -width "FTS_COMFOLLOW " 373 | .It Dv FTS_COMFOLLOW 374 | This option causes any symbolic link specified as a root path to be 375 | followed immediately whether or not 376 | .Dv FTS_LOGICAL 377 | is also specified. 378 | .It Dv FTS_LOGICAL 379 | This option causes the 380 | .Nm 381 | routines to return 382 | .Fa FTSENT 383 | structures for the targets of symbolic links 384 | instead of the symbolic links themselves. 385 | If this option is set, the only symbolic links for which 386 | .Fa FTSENT 387 | structures 388 | are returned to the application are those referencing non-existent files. 389 | Either 390 | .Dv FTS_LOGICAL 391 | or 392 | .Dv FTS_PHYSICAL 393 | .Em must 394 | be provided to the 395 | .Fn fts_open 396 | function. 397 | .It Dv FTS_NOCHDIR 398 | As a performance optimization, the 399 | .Nm 400 | functions change directories as they walk the file hierarchy. 401 | This has the side-effect that an application cannot rely on being 402 | in any particular directory during the traversal. 403 | The 404 | .Dv FTS_NOCHDIR 405 | option turns off this optimization, and the 406 | .Nm 407 | functions will not change the current directory. 408 | Note that applications should not themselves change their current directory 409 | and try to access files unless 410 | .Dv FTS_NOCHDIR 411 | is specified and absolute 412 | pathnames were provided as arguments to 413 | .Fn fts_open . 414 | .It Dv FTS_NOSTAT 415 | By default, returned 416 | .Fa FTSENT 417 | structures reference file characteristic information (the 418 | .Fa statp 419 | field) for each file visited. 420 | This option relaxes that requirement as a performance optimization, 421 | allowing the 422 | .Nm 423 | functions to set the 424 | .Fa fts_info 425 | field to 426 | .Dv FTS_NSOK 427 | and leave the contents of the 428 | .Fa statp 429 | field undefined. 430 | .It Dv FTS_PHYSICAL 431 | This option causes the 432 | .Nm 433 | routines to return 434 | .Fa FTSENT 435 | structures for symbolic links themselves instead 436 | of the target files they point to. 437 | If this option is set, 438 | .Fa FTSENT 439 | structures for all symbolic links in the 440 | hierarchy are returned to the application. 441 | Either 442 | .Dv FTS_LOGICAL 443 | or 444 | .Dv FTS_PHYSICAL 445 | .Em must 446 | be provided to the 447 | .Fn fts_open 448 | function. 449 | .It Dv FTS_SEEDOT 450 | By default, unless they are specified as path arguments to 451 | .Fn fts_open , 452 | any files named 453 | .Ql \&. 454 | or 455 | .Ql .. 456 | encountered in the file hierarchy are ignored. 457 | This option causes the 458 | .Nm 459 | routines to return 460 | .Fa FTSENT 461 | structures for them. 462 | .It Dv FTS_WHITEOUT 463 | Return whiteout entries, which are normally hidden. 464 | .It Dv FTS_XDEV 465 | This option prevents 466 | .Nm 467 | from descending into directories that have a different device number 468 | than the file from which the descent began. 469 | .El 470 | .Pp 471 | The argument 472 | .Fn compar 473 | specifies a user-defined function which may be used to order the traversal 474 | of the hierarchy. 475 | It 476 | takes two pointers to pointers to 477 | .Fa FTSENT 478 | structures as arguments and 479 | should return a negative value, zero, or a positive value to indicate 480 | if the file referenced by its first argument comes before, in any order 481 | with respect to, or after, the file referenced by its second argument. 482 | The 483 | .Fa fts_accpath , 484 | .Fa fts_path 485 | and 486 | .Fa fts_pathlen 487 | fields of the 488 | .Fa FTSENT 489 | structures may 490 | .Em never 491 | be used in this comparison. 492 | If the 493 | .Fa fts_info 494 | field is set to 495 | .Dv FTS_NS 496 | or 497 | .Dv FTS_NSOK , 498 | the 499 | .Fa fts_statp 500 | field may not either. 501 | If the 502 | .Fn compar 503 | argument is 504 | .Dv NULL , 505 | the directory traversal order is in the order listed in 506 | .Fa path_argv 507 | for the root paths, and in the order listed in the directory for 508 | everything else. 509 | .Sh FTS_READ 510 | The 511 | .Fn fts_read 512 | function returns a pointer to an 513 | .Fa FTSENT 514 | structure describing a file in 515 | the hierarchy. 516 | Directories (that are readable and do not cause cycles) are visited at 517 | least twice, once in pre-order and once in post-order. 518 | All other files are visited at least once. 519 | (Hard links between directories that do not cause cycles or symbolic 520 | links to symbolic links may cause files to be visited more than once, 521 | or directories more than twice.) 522 | .Pp 523 | If all the members of the hierarchy have been returned, 524 | .Fn fts_read 525 | returns 526 | .Dv NULL 527 | and sets the external variable 528 | .Va errno 529 | to 0. 530 | If an error unrelated to a file in the hierarchy occurs, 531 | .Fn fts_read 532 | returns 533 | .Dv NULL 534 | and sets 535 | .Va errno 536 | appropriately. 537 | If an error related to a returned file occurs, a pointer to an 538 | .Fa FTSENT 539 | structure is returned, and 540 | .Va errno 541 | may or may not have been set (see 542 | .Fa fts_info ) . 543 | .Pp 544 | The 545 | .Fa FTSENT 546 | structures returned by 547 | .Fn fts_read 548 | may be overwritten after a call to 549 | .Fn fts_close 550 | on the same file hierarchy stream, or, after a call to 551 | .Fn fts_read 552 | on the same file hierarchy stream unless they represent a file of type 553 | directory, in which case they will not be overwritten until after a call to 554 | .Fn fts_read 555 | after the 556 | .Fa FTSENT 557 | structure has been returned by the function 558 | .Fn fts_read 559 | in post-order. 560 | .Sh FTS_CHILDREN 561 | The 562 | .Fn fts_children 563 | function returns a pointer to an 564 | .Fa FTSENT 565 | structure describing the first entry in a 566 | .Dv NULL Ns -terminated 567 | linked list of the files in the directory represented by the 568 | .Fa FTSENT 569 | structure most recently returned by 570 | .Fn fts_read . 571 | The list is linked through the 572 | .Fa fts_link 573 | field of the 574 | .Fa FTSENT 575 | structure, and is ordered by the user-specified comparison function, if any. 576 | Repeated calls to 577 | .Fn fts_children 578 | will recreate this linked list. 579 | .Pp 580 | As a special case, if 581 | .Fn fts_read 582 | has not yet been called for a hierarchy, 583 | .Fn fts_children 584 | will return a pointer to the files in the logical directory specified to 585 | .Fn fts_open , 586 | i.e., the arguments specified to 587 | .Fn fts_open . 588 | Otherwise, if the 589 | .Fa FTSENT 590 | structure most recently returned by 591 | .Fn fts_read 592 | is not a directory being visited in pre-order, 593 | or the directory does not contain any files, 594 | .Fn fts_children 595 | returns 596 | .Dv NULL 597 | and sets 598 | .Va errno 599 | to zero. 600 | If an error occurs, 601 | .Fn fts_children 602 | returns 603 | .Dv NULL 604 | and sets 605 | .Va errno 606 | appropriately. 607 | .Pp 608 | The 609 | .Fa FTSENT 610 | structures returned by 611 | .Fn fts_children 612 | may be overwritten after a call to 613 | .Fn fts_children , 614 | .Fn fts_close 615 | or 616 | .Fn fts_read 617 | on the same file hierarchy stream. 618 | .Pp 619 | .Em Option 620 | may be set to the following value: 621 | .Bl -tag -width "FTS_COMFOLLOW " 622 | .It Dv FTS_NAMEONLY 623 | Only the names of the files are needed. 624 | The contents of all the fields in the returned linked list of structures 625 | are undefined with the exception of the 626 | .Fa fts_name 627 | and 628 | .Fa fts_namelen 629 | fields. 630 | .El 631 | .Sh FTS_SET 632 | The function 633 | .Fn fts_set 634 | allows the user application to determine further processing for the 635 | file 636 | .Fa f 637 | of the stream 638 | .Fa ftsp . 639 | The 640 | .Fn fts_set 641 | function 642 | returns 0 on success, and \-1 if an error occurs. 643 | .Em Option 644 | must be set to one of the following values: 645 | .Bl -tag -width "FTS_COMFOLLOW " 646 | .It Dv FTS_AGAIN 647 | Re-visit the file; any file type may be re-visited. 648 | The next call to 649 | .Fn fts_read 650 | will return the referenced file. 651 | The 652 | .Fa fts_stat 653 | and 654 | .Fa fts_info 655 | fields of the structure will be reinitialized at that time, 656 | but no other fields will have been changed. 657 | This option is meaningful only for the most recently returned 658 | file from 659 | .Fn fts_read . 660 | Normal use is for post-order directory visits, where it causes the 661 | directory to be re-visited (in both pre and post-order) as well as all 662 | of its descendants. 663 | .It Dv FTS_FOLLOW 664 | The referenced file must be a symbolic link. 665 | If the referenced file is the one most recently returned by 666 | .Fn fts_read , 667 | the next call to 668 | .Fn fts_read 669 | returns the file with the 670 | .Fa fts_info 671 | and 672 | .Fa fts_statp 673 | fields reinitialized to reflect the target of the symbolic link instead 674 | of the symbolic link itself. 675 | If the file is one of those most recently returned by 676 | .Fn fts_children , 677 | the 678 | .Fa fts_info 679 | and 680 | .Fa fts_statp 681 | fields of the structure, when returned by 682 | .Fn fts_read , 683 | will reflect the target of the symbolic link instead of the symbolic link 684 | itself. 685 | In either case, if the target of the symbolic link does not exist the 686 | fields of the returned structure will be unchanged and the 687 | .Fa fts_info 688 | field will be set to 689 | .Dv FTS_SLNONE . 690 | .Pp 691 | If the target of the link is a directory, the pre-order return, followed 692 | by the return of all of its descendants, followed by a post-order return, 693 | is done. 694 | .It Dv FTS_SKIP 695 | No descendants of this file are visited. 696 | The file may be one of those most recently returned by either 697 | .Fn fts_children 698 | or 699 | .Fn fts_read . 700 | .El 701 | .Sh FTS_CLOSE 702 | The 703 | .Fn fts_close 704 | function closes a file hierarchy stream 705 | .Fa ftsp 706 | and restores the current directory to the directory from which 707 | .Fn fts_open 708 | was called to open 709 | .Fa ftsp . 710 | The 711 | .Fn fts_close 712 | function 713 | returns 0 on success, and \-1 if an error occurs. 714 | .Sh ERRORS 715 | The function 716 | .Fn fts_open 717 | may fail and set 718 | .Va errno 719 | for any of the errors specified for the library functions 720 | .Xr open 2 721 | and 722 | .Xr malloc 3 . 723 | .Pp 724 | The function 725 | .Fn fts_close 726 | may fail and set 727 | .Va errno 728 | for any of the errors specified for the library functions 729 | .Xr chdir 2 730 | and 731 | .Xr close 2 . 732 | .Pp 733 | The functions 734 | .Fn fts_read 735 | and 736 | .Fn fts_children 737 | may fail and set 738 | .Va errno 739 | for any of the errors specified for the library functions 740 | .Xr chdir 2 , 741 | .Xr malloc 3 , 742 | .Xr opendir 3 , 743 | .Xr readdir 3 744 | and 745 | .Xr stat 2 . 746 | .Pp 747 | In addition, 748 | .Fn fts_children , 749 | .Fn fts_open 750 | and 751 | .Fn fts_set 752 | may fail and set 753 | .Va errno 754 | as follows: 755 | .Bl -tag -width Er 756 | .It Bq Er EINVAL 757 | The options were invalid. 758 | .El 759 | .Sh SEE ALSO 760 | .Xr find 1 , 761 | .Xr chdir 2 , 762 | .Xr stat 2 , 763 | .Xr qsort 3 , 764 | .Xr symlink 7 765 | .Sh STANDARDS 766 | The 767 | .Nm 768 | utility was expected to be included in the 769 | .St -p1003.1-88 770 | revision. 771 | But twenty years later, it still was not included in the 772 | .St -p1003.1-2008 773 | revision. 774 | -------------------------------------------------------------------------------- /fts.c: -------------------------------------------------------------------------------- 1 | /* $NetBSD: fts.c,v 1.48 2015/01/29 15:55:21 manu Exp $ */ 2 | 3 | /*- 4 | * Copyright (c) 1990, 1993, 1994 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #if defined(LIBC_SCCS) && !defined(lint) 33 | #if 0 34 | static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; 35 | #else 36 | __RCSID("$NetBSD: fts.c,v 1.48 2015/01/29 15:55:21 manu Exp $"); 37 | #endif 38 | #endif /* LIBC_SCCS and not lint */ 39 | 40 | #include "config.h" 41 | 42 | #include 43 | #include 44 | 45 | #include 46 | #define _DIAGASSERT(e) 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #if !defined(HAVE_DECL_MAX) || (HAVE_DECL_MAX==0) 56 | #define MAX(a,b) ((a)>(b)?(a):(b)) 57 | #endif 58 | 59 | #if !defined(UINT_MAX) && (HAVE_DECL_UINTMAX_MAX==1) 60 | #define UINT_MAX UINTMAX_MAX 61 | #endif 62 | 63 | #if !defined(HAVE_DIRFD) 64 | #if defined(HAVE_DIR_DD_FD) 65 | #define dirfd(dirp) ((dirp)->dd_fd) 66 | #endif 67 | #if defined(HAVE_DIR_D_FD) 68 | #define dirfd(dirp) ((dirp)->d_fd) 69 | #endif 70 | #endif 71 | 72 | static FTSENT *fts_alloc(FTS *, const char *, size_t); 73 | static FTSENT *fts_build(FTS *, int); 74 | static void fts_free(FTSENT *); 75 | static void fts_lfree(FTSENT *); 76 | static void fts_load(FTS *, FTSENT *); 77 | static size_t fts_maxarglen(char * const *); 78 | static size_t fts_pow2(size_t); 79 | static int fts_palloc(FTS *, size_t); 80 | static void fts_padjust(FTS *, FTSENT *); 81 | static FTSENT *fts_sort(FTS *, FTSENT *, size_t); 82 | static unsigned short fts_stat(FTS *, FTSENT *, int); 83 | static int fts_safe_changedir(const FTS *, const FTSENT *, int, 84 | const char *); 85 | 86 | #if defined(ALIGNBYTES) && defined(ALIGN) 87 | #define FTS_ALLOC_ALIGNED 1 88 | #else 89 | #undef FTS_ALLOC_ALIGNED 90 | #endif 91 | 92 | #ifndef ftsent_namelen_truncate 93 | #define ftsent_namelen_truncate(a) \ 94 | ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a)) 95 | #endif 96 | #ifndef ftsent_pathlen_truncate 97 | #define ftsent_pathlen_truncate(a) \ 98 | ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a)) 99 | #endif 100 | #ifndef fts_pathlen_truncate 101 | #define fts_pathlen_truncate(a) \ 102 | ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a)) 103 | #endif 104 | #ifndef fts_nitems_truncate 105 | #define fts_nitems_truncate(a) \ 106 | ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a)) 107 | #endif 108 | 109 | #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) 110 | 111 | #define CLR(opt) (sp->fts_options &= ~(opt)) 112 | #define ISSET(opt) (sp->fts_options & (opt)) 113 | #define SET(opt) (sp->fts_options |= (opt)) 114 | 115 | #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 116 | #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 117 | 118 | /* fts_build flags */ 119 | #define BCHILD 1 /* fts_children */ 120 | #define BNAMES 2 /* fts_children, names only */ 121 | #define BREAD 3 /* fts_read */ 122 | 123 | #ifndef DTF_HIDEW 124 | #undef FTS_WHITEOUT 125 | #endif 126 | 127 | FTS * 128 | fts_open(char * const *argv, int options, 129 | int (*compar)(const FTSENT **, const FTSENT **)) 130 | { 131 | FTS *sp; 132 | FTSENT *p, *root; 133 | size_t nitems; 134 | FTSENT *parent, *tmp = NULL; /* pacify gcc */ 135 | size_t len; 136 | 137 | _DIAGASSERT(argv != NULL); 138 | 139 | /* Options check. */ 140 | if (options & ~FTS_OPTIONMASK) { 141 | errno = EINVAL; 142 | return (NULL); 143 | } 144 | 145 | /* Allocate/initialize the stream */ 146 | if ((sp = malloc(sizeof(FTS))) == NULL) 147 | return (NULL); 148 | memset(sp, 0, sizeof(FTS)); 149 | sp->fts_compar = compar; 150 | sp->fts_options = options; 151 | 152 | /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 153 | if (ISSET(FTS_LOGICAL)) 154 | SET(FTS_NOCHDIR); 155 | 156 | /* 157 | * Start out with 1K of path space, and enough, in any case, 158 | * to hold the user's paths. 159 | */ 160 | if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 161 | goto mem1; 162 | 163 | /* Allocate/initialize root's parent. */ 164 | if ((parent = fts_alloc(sp, "", 0)) == NULL) 165 | goto mem2; 166 | parent->fts_level = FTS_ROOTPARENTLEVEL; 167 | 168 | /* Allocate/initialize root(s). */ 169 | for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 170 | /* Don't allow zero-length paths. */ 171 | if ((len = strlen(*argv)) == 0) { 172 | errno = ENOENT; 173 | goto mem3; 174 | } 175 | 176 | if ((p = fts_alloc(sp, *argv, len)) == NULL) 177 | goto mem3; 178 | p->fts_level = FTS_ROOTLEVEL; 179 | p->fts_parent = parent; 180 | p->fts_accpath = p->fts_name; 181 | p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 182 | 183 | /* Command-line "." and ".." are real directories. */ 184 | if (p->fts_info == FTS_DOT) 185 | p->fts_info = FTS_D; 186 | 187 | /* 188 | * If comparison routine supplied, traverse in sorted 189 | * order; otherwise traverse in the order specified. 190 | */ 191 | if (compar) { 192 | p->fts_link = root; 193 | root = p; 194 | } else { 195 | p->fts_link = NULL; 196 | if (root == NULL) 197 | tmp = root = p; 198 | else { 199 | tmp->fts_link = p; 200 | tmp = p; 201 | } 202 | } 203 | } 204 | if (compar && nitems > 1) 205 | root = fts_sort(sp, root, nitems); 206 | 207 | /* 208 | * Allocate a dummy pointer and make fts_read think that we've just 209 | * finished the node before the root(s); set p->fts_info to FTS_INIT 210 | * so that everything about the "current" node is ignored. 211 | */ 212 | if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 213 | goto mem3; 214 | sp->fts_cur->fts_link = root; 215 | sp->fts_cur->fts_info = FTS_INIT; 216 | 217 | /* 218 | * If using chdir(2), grab a file descriptor pointing to dot to ensure 219 | * that we can get back here; this could be avoided for some paths, 220 | * but almost certainly not worth the effort. Slashes, symbolic links, 221 | * and ".." are all fairly nasty problems. Note, if we can't get the 222 | * descriptor we run anyway, just more slowly. 223 | */ 224 | #ifndef O_CLOEXEC 225 | #define O_CLOEXEC 0 226 | #endif 227 | if (!ISSET(FTS_NOCHDIR)) { 228 | if ((sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) 229 | SET(FTS_NOCHDIR); 230 | } 231 | 232 | if (nitems == 0) 233 | fts_free(parent); 234 | 235 | return (sp); 236 | 237 | mem3: fts_lfree(root); 238 | fts_free(parent); 239 | mem2: free(sp->fts_path); 240 | mem1: free(sp); 241 | return (NULL); 242 | } 243 | 244 | static void 245 | fts_load(FTS *sp, FTSENT *p) 246 | { 247 | size_t len; 248 | char *cp; 249 | 250 | _DIAGASSERT(sp != NULL); 251 | _DIAGASSERT(p != NULL); 252 | 253 | /* 254 | * Load the stream structure for the next traversal. Since we don't 255 | * actually enter the directory until after the preorder visit, set 256 | * the fts_accpath field specially so the chdir gets done to the right 257 | * place and the user can access the first node. From fts_open it's 258 | * known that the path will fit. 259 | */ 260 | len = p->fts_pathlen = p->fts_namelen; 261 | memmove(sp->fts_path, p->fts_name, len + 1); 262 | if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 263 | len = strlen(++cp); 264 | memmove(p->fts_name, cp, len + 1); 265 | p->fts_namelen = ftsent_namelen_truncate(len); 266 | } 267 | p->fts_accpath = p->fts_path = sp->fts_path; 268 | sp->fts_dev = p->fts_dev; 269 | } 270 | 271 | int 272 | fts_close(FTS *sp) 273 | { 274 | FTSENT *freep, *p; 275 | int saved_errno = 0; 276 | 277 | _DIAGASSERT(sp != NULL); 278 | 279 | /* 280 | * This still works if we haven't read anything -- the dummy structure 281 | * points to the root list, so we step through to the end of the root 282 | * list which has a valid parent pointer. 283 | */ 284 | if (sp->fts_cur) { 285 | if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW) 286 | (void)close(sp->fts_cur->fts_symfd); 287 | for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 288 | freep = p; 289 | p = p->fts_link ? p->fts_link : p->fts_parent; 290 | fts_free(freep); 291 | } 292 | fts_free(p); 293 | } 294 | 295 | /* Free up child linked list, sort array, path buffer. */ 296 | if (sp->fts_child) 297 | fts_lfree(sp->fts_child); 298 | if (sp->fts_array) 299 | free(sp->fts_array); 300 | free(sp->fts_path); 301 | 302 | /* Return to original directory, save errno if necessary. */ 303 | if (!ISSET(FTS_NOCHDIR)) { 304 | if (fchdir(sp->fts_rfd) == -1) 305 | saved_errno = errno; 306 | (void)close(sp->fts_rfd); 307 | } 308 | 309 | /* Free up the stream pointer. */ 310 | free(sp); 311 | if (saved_errno) { 312 | errno = saved_errno; 313 | return -1; 314 | } 315 | 316 | return 0; 317 | } 318 | 319 | #if !defined(__FTS_COMPAT_TAILINGSLASH) 320 | 321 | /* 322 | * Special case of "/" at the end of the path so that slashes aren't 323 | * appended which would cause paths to be written as "....//foo". 324 | */ 325 | #define NAPPEND(p) \ 326 | (p->fts_path[p->fts_pathlen - 1] == '/' \ 327 | ? p->fts_pathlen - 1 : p->fts_pathlen) 328 | 329 | #else /* !defined(__FTS_COMPAT_TAILINGSLASH) */ 330 | 331 | /* 332 | * compatibility with the old behaviour. 333 | * 334 | * Special case a root of "/" so that slashes aren't appended which would 335 | * cause paths to be written as "//foo". 336 | */ 337 | 338 | #define NAPPEND(p) \ 339 | (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 340 | p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 341 | 342 | #endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */ 343 | 344 | FTSENT * 345 | fts_read(FTS *sp) 346 | { 347 | FTSENT *p, *tmp; 348 | int instr; 349 | char *t; 350 | int saved_errno; 351 | 352 | _DIAGASSERT(sp != NULL); 353 | 354 | /* If finished or unrecoverable error, return NULL. */ 355 | if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 356 | return (NULL); 357 | 358 | /* Set current node pointer. */ 359 | p = sp->fts_cur; 360 | 361 | /* Save and zero out user instructions. */ 362 | instr = p->fts_instr; 363 | p->fts_instr = FTS_NOINSTR; 364 | 365 | /* Any type of file may be re-visited; re-stat and re-turn. */ 366 | if (instr == FTS_AGAIN) { 367 | p->fts_info = fts_stat(sp, p, 0); 368 | return (p); 369 | } 370 | 371 | /* 372 | * Following a symlink -- SLNONE test allows application to see 373 | * SLNONE and recover. If indirecting through a symlink, have 374 | * keep a pointer to current location. If unable to get that 375 | * pointer, follow fails. 376 | */ 377 | if (instr == FTS_FOLLOW && 378 | (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 379 | p->fts_info = fts_stat(sp, p, 1); 380 | if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 381 | if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, 0)) 382 | == -1) { 383 | p->fts_errno = errno; 384 | p->fts_info = FTS_ERR; 385 | } else 386 | p->fts_flags |= FTS_SYMFOLLOW; 387 | } 388 | return (p); 389 | } 390 | 391 | /* Directory in pre-order. */ 392 | if (p->fts_info == FTS_D) { 393 | /* If skipped or crossed mount point, do post-order visit. */ 394 | if (instr == FTS_SKIP || 395 | (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { 396 | if (p->fts_flags & FTS_SYMFOLLOW) 397 | (void)close(p->fts_symfd); 398 | if (sp->fts_child) { 399 | fts_lfree(sp->fts_child); 400 | sp->fts_child = NULL; 401 | } 402 | p->fts_info = FTS_DP; 403 | return (p); 404 | } 405 | 406 | /* Rebuild if only read the names and now traversing. */ 407 | if (sp->fts_child && ISSET(FTS_NAMEONLY)) { 408 | CLR(FTS_NAMEONLY); 409 | fts_lfree(sp->fts_child); 410 | sp->fts_child = NULL; 411 | } 412 | 413 | /* 414 | * Cd to the subdirectory. 415 | * 416 | * If have already read and now fail to chdir, whack the list 417 | * to make the names come out right, and set the parent errno 418 | * so the application will eventually get an error condition. 419 | * Set the FTS_DONTCHDIR flag so that when we logically change 420 | * directories back to the parent we don't do a chdir. 421 | * 422 | * If haven't read do so. If the read fails, fts_build sets 423 | * FTS_STOP or the fts_info field of the node. 424 | */ 425 | if (sp->fts_child) { 426 | if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { 427 | p->fts_errno = errno; 428 | p->fts_flags |= FTS_DONTCHDIR; 429 | for (p = sp->fts_child; p; p = p->fts_link) 430 | p->fts_accpath = 431 | p->fts_parent->fts_accpath; 432 | } 433 | } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 434 | if (ISSET(FTS_STOP)) 435 | return (NULL); 436 | return (p); 437 | } 438 | p = sp->fts_child; 439 | sp->fts_child = NULL; 440 | goto name; 441 | } 442 | 443 | next: 444 | /* Move to the next node on this level. */ 445 | tmp = p; 446 | 447 | /* 448 | * We are going to free sp->fts_cur, set it to NULL so 449 | * that fts_close() does not attempt to free it again 450 | * if we exit without setting it to a new value because 451 | * FCHDIR() failed below. 452 | */ 453 | assert(tmp == sp->fts_cur); 454 | sp->fts_cur = NULL; 455 | 456 | if ((p = p->fts_link) != NULL) { 457 | fts_free(tmp); 458 | 459 | /* 460 | * If reached the top, return to the original directory, and 461 | * load the paths for the next root. 462 | */ 463 | if (p->fts_level == FTS_ROOTLEVEL) { 464 | if (FCHDIR(sp, sp->fts_rfd)) { 465 | SET(FTS_STOP); 466 | return (NULL); 467 | } 468 | fts_load(sp, p); 469 | return (sp->fts_cur = p); 470 | } 471 | 472 | /* 473 | * User may have called fts_set on the node. If skipped, 474 | * ignore. If followed, get a file descriptor so we can 475 | * get back if necessary. 476 | */ 477 | if (p->fts_instr == FTS_SKIP) 478 | goto next; 479 | if (p->fts_instr == FTS_FOLLOW) { 480 | p->fts_info = fts_stat(sp, p, 1); 481 | if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 482 | if ((p->fts_symfd = 483 | open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) { 484 | p->fts_errno = errno; 485 | p->fts_info = FTS_ERR; 486 | } else 487 | p->fts_flags |= FTS_SYMFOLLOW; 488 | } 489 | p->fts_instr = FTS_NOINSTR; 490 | } 491 | 492 | name: t = sp->fts_path + NAPPEND(p->fts_parent); 493 | *t++ = '/'; 494 | memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1)); 495 | return (sp->fts_cur = p); 496 | } 497 | 498 | /* Move up to the parent node. */ 499 | p = tmp->fts_parent; 500 | fts_free(tmp); 501 | 502 | if (p->fts_level == FTS_ROOTPARENTLEVEL) { 503 | /* 504 | * Done; free everything up and set errno to 0 so the user 505 | * can distinguish between error and EOF. 506 | */ 507 | fts_free(p); 508 | errno = 0; 509 | return (sp->fts_cur = NULL); 510 | } 511 | 512 | /* NUL terminate the pathname. */ 513 | sp->fts_path[p->fts_pathlen] = '\0'; 514 | 515 | /* 516 | * Return to the parent directory. If at a root node or came through 517 | * a symlink, go back through the file descriptor. Otherwise, cd up 518 | * one directory. 519 | */ 520 | if (p->fts_level == FTS_ROOTLEVEL) { 521 | if (FCHDIR(sp, sp->fts_rfd)) { 522 | SET(FTS_STOP); 523 | return (NULL); 524 | } 525 | } else if (p->fts_flags & FTS_SYMFOLLOW) { 526 | if (FCHDIR(sp, p->fts_symfd)) { 527 | saved_errno = errno; 528 | (void)close(p->fts_symfd); 529 | errno = saved_errno; 530 | SET(FTS_STOP); 531 | return (NULL); 532 | } 533 | (void)close(p->fts_symfd); 534 | } else if (!(p->fts_flags & FTS_DONTCHDIR) && 535 | fts_safe_changedir(sp, p->fts_parent, -1, "..")) { 536 | SET(FTS_STOP); 537 | return (NULL); 538 | } 539 | p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 540 | return (sp->fts_cur = p); 541 | } 542 | 543 | /* 544 | * Fts_set takes the stream as an argument although it's not used in this 545 | * implementation; it would be necessary if anyone wanted to add global 546 | * semantics to fts using fts_set. An error return is allowed for similar 547 | * reasons. 548 | */ 549 | /* ARGSUSED */ 550 | int 551 | fts_set(FTS *sp, FTSENT *p, int instr) 552 | { 553 | 554 | _DIAGASSERT(sp != NULL); 555 | _DIAGASSERT(p != NULL); 556 | 557 | if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 558 | instr != FTS_NOINSTR && instr != FTS_SKIP) { 559 | errno = EINVAL; 560 | return (1); 561 | } 562 | p->fts_instr = instr; 563 | return (0); 564 | } 565 | 566 | FTSENT * 567 | fts_children(FTS *sp, int instr) 568 | { 569 | FTSENT *p; 570 | int fd; 571 | 572 | _DIAGASSERT(sp != NULL); 573 | 574 | if (instr && instr != FTS_NAMEONLY) { 575 | errno = EINVAL; 576 | return (NULL); 577 | } 578 | 579 | /* Set current node pointer. */ 580 | p = sp->fts_cur; 581 | 582 | /* 583 | * Errno set to 0 so user can distinguish empty directory from 584 | * an error. 585 | */ 586 | errno = 0; 587 | 588 | /* Fatal errors stop here. */ 589 | if (ISSET(FTS_STOP)) 590 | return (NULL); 591 | 592 | /* Return logical hierarchy of user's arguments. */ 593 | if (p->fts_info == FTS_INIT) 594 | return (p->fts_link); 595 | 596 | /* 597 | * If not a directory being visited in pre-order, stop here. Could 598 | * allow FTS_DNR, assuming the user has fixed the problem, but the 599 | * same effect is available with FTS_AGAIN. 600 | */ 601 | if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 602 | return (NULL); 603 | 604 | /* Free up any previous child list. */ 605 | if (sp->fts_child) 606 | fts_lfree(sp->fts_child); 607 | 608 | if (instr == FTS_NAMEONLY) { 609 | SET(FTS_NAMEONLY); 610 | instr = BNAMES; 611 | } else 612 | instr = BCHILD; 613 | 614 | /* 615 | * If using chdir on a relative path and called BEFORE fts_read does 616 | * its chdir to the root of a traversal, we can lose -- we need to 617 | * chdir into the subdirectory, and we don't know where the current 618 | * directory is, so we can't get back so that the upcoming chdir by 619 | * fts_read will work. 620 | */ 621 | if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 622 | ISSET(FTS_NOCHDIR)) 623 | return (sp->fts_child = fts_build(sp, instr)); 624 | 625 | if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) 626 | return (sp->fts_child = NULL); 627 | sp->fts_child = fts_build(sp, instr); 628 | if (fchdir(fd)) { 629 | (void)close(fd); 630 | return (NULL); 631 | } 632 | (void)close(fd); 633 | return (sp->fts_child); 634 | } 635 | 636 | /* 637 | * This is the tricky part -- do not casually change *anything* in here. The 638 | * idea is to build the linked list of entries that are used by fts_children 639 | * and fts_read. There are lots of special cases. 640 | * 641 | * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 642 | * set and it's a physical walk (so that symbolic links can't be directories), 643 | * we can do things quickly. First, if it's a 4.4BSD file system, the type 644 | * of the file is in the directory entry. Otherwise, we assume that the number 645 | * of subdirectories in a node is equal to the number of links to the parent. 646 | * The former skips all stat calls. The latter skips stat calls in any leaf 647 | * directories and for any files after the subdirectories in the directory have 648 | * been found, cutting the stat calls by about 2/3. 649 | */ 650 | static FTSENT * 651 | fts_build(FTS *sp, int type) 652 | { 653 | struct dirent *dp; 654 | FTSENT *p, *head; 655 | size_t nitems; 656 | FTSENT *cur, *tail; 657 | DIR *dirp; 658 | void *oldaddr; 659 | size_t dnamlen; 660 | int cderrno, descend, level, nlinks, saved_errno, nostat, doadjust; 661 | size_t len, maxlen; 662 | #ifdef FTS_WHITEOUT 663 | int oflag; 664 | #endif 665 | char *cp = NULL; /* pacify gcc */ 666 | 667 | _DIAGASSERT(sp != NULL); 668 | 669 | /* Set current node pointer. */ 670 | cur = sp->fts_cur; 671 | 672 | /* 673 | * Open the directory for reading. If this fails, we're done. 674 | * If being called from fts_read, set the fts_info field. 675 | */ 676 | #ifdef FTS_WHITEOUT 677 | if (ISSET(FTS_WHITEOUT)) 678 | oflag = DTF_NODUP|DTF_REWIND; 679 | else 680 | oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; 681 | #else 682 | #define __opendir2(path, flag) opendir(path) 683 | #endif 684 | if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { 685 | if (type == BREAD) { 686 | cur->fts_info = FTS_DNR; 687 | cur->fts_errno = errno; 688 | } 689 | return (NULL); 690 | } 691 | 692 | /* 693 | * Nlinks is the number of possible entries of type directory in the 694 | * directory if we're cheating on stat calls, 0 if we're not doing 695 | * any stat calls at all, -1 if we're doing stats on everything. 696 | */ 697 | if (type == BNAMES) { 698 | nlinks = 0; 699 | nostat = 1; 700 | } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { 701 | nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 702 | nostat = 1; 703 | } else { 704 | nlinks = -1; 705 | nostat = 0; 706 | } 707 | 708 | #ifdef notdef 709 | (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 710 | (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 711 | ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 712 | #endif 713 | /* 714 | * If we're going to need to stat anything or we want to descend 715 | * and stay in the directory, chdir. If this fails we keep going, 716 | * but set a flag so we don't chdir after the post-order visit. 717 | * We won't be able to stat anything, but we can still return the 718 | * names themselves. Note, that since fts_read won't be able to 719 | * chdir into the directory, it will have to return different path 720 | * names than before, i.e. "a/b" instead of "b". Since the node 721 | * has already been visited in pre-order, have to wait until the 722 | * post-order visit to return the error. There is a special case 723 | * here, if there was nothing to stat then it's not an error to 724 | * not be able to stat. This is all fairly nasty. If a program 725 | * needed sorted entries or stat information, they had better be 726 | * checking FTS_NS on the returned nodes. 727 | */ 728 | cderrno = 0; 729 | if (nlinks || type == BREAD) { 730 | if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { 731 | if (nlinks && type == BREAD) 732 | cur->fts_errno = errno; 733 | cur->fts_flags |= FTS_DONTCHDIR; 734 | descend = 0; 735 | cderrno = errno; 736 | } else 737 | descend = 1; 738 | } else 739 | descend = 0; 740 | 741 | /* 742 | * Figure out the max file name length that can be stored in the 743 | * current path -- the inner loop allocates more path as necessary. 744 | * We really wouldn't have to do the maxlen calculations here, we 745 | * could do them in fts_read before returning the path, but it's a 746 | * lot easier here since the length is part of the dirent structure. 747 | * 748 | * If not changing directories set a pointer so that can just append 749 | * each new name into the path. 750 | */ 751 | len = NAPPEND(cur); 752 | if (ISSET(FTS_NOCHDIR)) { 753 | cp = sp->fts_path + len; 754 | *cp++ = '/'; 755 | } 756 | len++; 757 | maxlen = sp->fts_pathlen - len; 758 | 759 | #if defined(__FTS_COMPAT_LEVEL) 760 | if (cur->fts_level == SHRT_MAX) { 761 | (void)closedir(dirp); 762 | cur->fts_info = FTS_ERR; 763 | SET(FTS_STOP); 764 | errno = ENAMETOOLONG; 765 | return (NULL); 766 | } 767 | #endif 768 | 769 | level = cur->fts_level + 1; 770 | 771 | /* Read the directory, attaching each entry to the `link' pointer. */ 772 | doadjust = 0; 773 | for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { 774 | 775 | if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 776 | continue; 777 | 778 | #if defined(HAVE_STRUCT_DIRENT_D_NAMLEN) 779 | dnamlen = dp->d_namlen; 780 | #else 781 | dnamlen = strlen(dp->d_name); 782 | #endif 783 | if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL) 784 | goto mem1; 785 | if (dnamlen >= maxlen) { /* include space for NUL */ 786 | oldaddr = sp->fts_path; 787 | if (fts_palloc(sp, dnamlen + len + 1)) { 788 | /* 789 | * No more memory for path or structures. Save 790 | * errno, free up the current structure and the 791 | * structures already allocated. 792 | */ 793 | mem1: saved_errno = errno; 794 | if (p) 795 | fts_free(p); 796 | fts_lfree(head); 797 | (void)closedir(dirp); 798 | errno = saved_errno; 799 | cur->fts_info = FTS_ERR; 800 | SET(FTS_STOP); 801 | return (NULL); 802 | } 803 | /* Did realloc() change the pointer? */ 804 | if (oldaddr != sp->fts_path) { 805 | doadjust = 1; 806 | if (ISSET(FTS_NOCHDIR)) 807 | cp = sp->fts_path + len; 808 | } 809 | maxlen = sp->fts_pathlen - len; 810 | } 811 | 812 | #if defined(__FTS_COMPAT_LENGTH) 813 | if (len + dnamlen >= USHRT_MAX) { 814 | /* 815 | * In an FTSENT, fts_pathlen is an unsigned short 816 | * so it is possible to wraparound here. 817 | * If we do, free up the current structure and the 818 | * structures already allocated, then error out 819 | * with ENAMETOOLONG. 820 | */ 821 | fts_free(p); 822 | fts_lfree(head); 823 | (void)closedir(dirp); 824 | cur->fts_info = FTS_ERR; 825 | SET(FTS_STOP); 826 | errno = ENAMETOOLONG; 827 | return (NULL); 828 | } 829 | #endif 830 | p->fts_level = level; 831 | p->fts_pathlen = ftsent_pathlen_truncate(len + dnamlen); 832 | p->fts_parent = sp->fts_cur; 833 | 834 | #ifdef FTS_WHITEOUT 835 | if (dp->d_type == DT_WHT) 836 | p->fts_flags |= FTS_ISW; 837 | #endif 838 | 839 | if (cderrno) { 840 | if (nlinks) { 841 | p->fts_info = FTS_NS; 842 | p->fts_errno = cderrno; 843 | } else 844 | p->fts_info = FTS_NSOK; 845 | p->fts_accpath = cur->fts_accpath; 846 | } else if (nlinks == 0 847 | #ifdef DT_DIR 848 | || (nostat && 849 | dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) 850 | #endif 851 | ) { 852 | p->fts_accpath = 853 | ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 854 | p->fts_info = FTS_NSOK; 855 | } else { 856 | /* Build a file name for fts_stat to stat. */ 857 | if (ISSET(FTS_NOCHDIR)) { 858 | p->fts_accpath = p->fts_path; 859 | memmove(cp, p->fts_name, 860 | (size_t)(p->fts_namelen + 1)); 861 | } else 862 | p->fts_accpath = p->fts_name; 863 | /* Stat it. */ 864 | p->fts_info = fts_stat(sp, p, 0); 865 | 866 | /* Decrement link count if applicable. */ 867 | if (nlinks > 0 && (p->fts_info == FTS_D || 868 | p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 869 | --nlinks; 870 | } 871 | 872 | /* We walk in directory order so "ls -f" doesn't get upset. */ 873 | p->fts_link = NULL; 874 | if (head == NULL) 875 | head = tail = p; 876 | else { 877 | tail->fts_link = p; 878 | tail = p; 879 | } 880 | ++nitems; 881 | } 882 | (void)closedir(dirp); 883 | 884 | /* 885 | * If had to realloc the path, adjust the addresses for the rest 886 | * of the tree. 887 | */ 888 | if (doadjust) 889 | fts_padjust(sp, head); 890 | 891 | /* 892 | * If not changing directories, reset the path back to original 893 | * state. 894 | */ 895 | if (ISSET(FTS_NOCHDIR)) { 896 | if (len == sp->fts_pathlen || nitems == 0) 897 | --cp; 898 | *cp = '\0'; 899 | } 900 | 901 | /* 902 | * If descended after called from fts_children or after called from 903 | * fts_read and nothing found, get back. At the root level we use 904 | * the saved fd; if one of fts_open()'s arguments is a relative path 905 | * to an empty directory, we wind up here with no other way back. If 906 | * can't get back, we're done. 907 | */ 908 | if (descend && (type == BCHILD || !nitems) && 909 | (cur->fts_level == FTS_ROOTLEVEL ? 910 | FCHDIR(sp, sp->fts_rfd) : 911 | fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { 912 | cur->fts_info = FTS_ERR; 913 | SET(FTS_STOP); 914 | return (NULL); 915 | } 916 | 917 | /* If didn't find anything, return NULL. */ 918 | if (!nitems) { 919 | if (type == BREAD) 920 | cur->fts_info = FTS_DP; 921 | return (NULL); 922 | } 923 | 924 | /* Sort the entries. */ 925 | if (sp->fts_compar && nitems > 1) 926 | head = fts_sort(sp, head, nitems); 927 | return (head); 928 | } 929 | 930 | static unsigned short 931 | fts_stat(FTS *sp, FTSENT *p, int follow) 932 | { 933 | FTSENT *t; 934 | dev_t dev; 935 | __fts_ino_t ino; 936 | __fts_stat_t *sbp, sb; 937 | int saved_errno; 938 | 939 | _DIAGASSERT(sp != NULL); 940 | _DIAGASSERT(p != NULL); 941 | 942 | /* If user needs stat info, stat buffer already allocated. */ 943 | sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 944 | 945 | #ifdef FTS_WHITEOUT 946 | /* check for whiteout */ 947 | if (p->fts_flags & FTS_ISW) { 948 | if (sbp != &sb) { 949 | memset(sbp, '\0', sizeof (*sbp)); 950 | sbp->st_mode = S_IFWHT; 951 | } 952 | return (FTS_W); 953 | } 954 | #endif 955 | 956 | /* 957 | * If doing a logical walk, or application requested FTS_FOLLOW, do 958 | * a stat(2). If that fails, check for a non-existent symlink. If 959 | * fail, set the errno from the stat call. 960 | */ 961 | if (ISSET(FTS_LOGICAL) || follow) { 962 | if (stat(p->fts_accpath, sbp)) { 963 | saved_errno = errno; 964 | if (!lstat(p->fts_accpath, sbp)) { 965 | errno = 0; 966 | return (FTS_SLNONE); 967 | } 968 | p->fts_errno = saved_errno; 969 | goto err; 970 | } 971 | } else if (lstat(p->fts_accpath, sbp)) { 972 | p->fts_errno = errno; 973 | err: memset(sbp, 0, sizeof(*sbp)); 974 | return (FTS_NS); 975 | } 976 | 977 | if (S_ISDIR(sbp->st_mode)) { 978 | /* 979 | * Set the device/inode. Used to find cycles and check for 980 | * crossing mount points. Also remember the link count, used 981 | * in fts_build to limit the number of stat calls. It is 982 | * understood that these fields are only referenced if fts_info 983 | * is set to FTS_D. 984 | */ 985 | dev = p->fts_dev = sbp->st_dev; 986 | ino = p->fts_ino = sbp->st_ino; 987 | p->fts_nlink = sbp->st_nlink; 988 | 989 | if (ISDOT(p->fts_name)) 990 | return (FTS_DOT); 991 | 992 | /* 993 | * Cycle detection is done by brute force when the directory 994 | * is first encountered. If the tree gets deep enough or the 995 | * number of symbolic links to directories is high enough, 996 | * something faster might be worthwhile. 997 | */ 998 | for (t = p->fts_parent; 999 | t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 1000 | if (ino == t->fts_ino && dev == t->fts_dev) { 1001 | p->fts_cycle = t; 1002 | return (FTS_DC); 1003 | } 1004 | return (FTS_D); 1005 | } 1006 | if (S_ISLNK(sbp->st_mode)) 1007 | return (FTS_SL); 1008 | if (S_ISREG(sbp->st_mode)) 1009 | return (FTS_F); 1010 | return (FTS_DEFAULT); 1011 | } 1012 | 1013 | static FTSENT * 1014 | fts_sort(FTS *sp, FTSENT *head, size_t nitems) 1015 | { 1016 | FTSENT **ap, *p; 1017 | 1018 | _DIAGASSERT(sp != NULL); 1019 | _DIAGASSERT(head != NULL); 1020 | 1021 | /* 1022 | * Construct an array of pointers to the structures and call qsort(3). 1023 | * Reassemble the array in the order returned by qsort. If unable to 1024 | * sort for memory reasons, return the directory entries in their 1025 | * current order. Allocate enough space for the current needs plus 1026 | * 40 so don't realloc one entry at a time. 1027 | */ 1028 | if (nitems > sp->fts_nitems) { 1029 | FTSENT **new; 1030 | 1031 | new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40)); 1032 | if (new == 0) 1033 | return (head); 1034 | sp->fts_array = new; 1035 | sp->fts_nitems = fts_nitems_truncate(nitems + 40); 1036 | } 1037 | for (ap = sp->fts_array, p = head; p; p = p->fts_link) 1038 | *ap++ = p; 1039 | qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), 1040 | (int (*)(const void *, const void *))sp->fts_compar); 1041 | for (head = *(ap = sp->fts_array); --nitems; ++ap) 1042 | ap[0]->fts_link = ap[1]; 1043 | ap[0]->fts_link = NULL; 1044 | return (head); 1045 | } 1046 | 1047 | static FTSENT * 1048 | fts_alloc(FTS *sp, const char *name, size_t namelen) 1049 | { 1050 | FTSENT *p; 1051 | #if defined(FTS_ALLOC_ALIGNED) 1052 | size_t len; 1053 | #endif 1054 | 1055 | _DIAGASSERT(sp != NULL); 1056 | _DIAGASSERT(name != NULL); 1057 | 1058 | #if defined(FTS_ALLOC_ALIGNED) 1059 | /* 1060 | * The file name is a variable length array and no stat structure is 1061 | * necessary if the user has set the nostat bit. Allocate the FTSENT 1062 | * structure, the file name and the stat structure in one chunk, but 1063 | * be careful that the stat structure is reasonably aligned. Since the 1064 | * fts_name field is declared to be of size 1, the fts_name pointer is 1065 | * namelen + 2 before the first possible address of the stat structure. 1066 | */ 1067 | len = sizeof(FTSENT) + namelen; 1068 | if (!ISSET(FTS_NOSTAT)) 1069 | len += sizeof(*(p->fts_statp)) + ALIGNBYTES; 1070 | if ((p = malloc(len)) == NULL) 1071 | return (NULL); 1072 | 1073 | if (!ISSET(FTS_NOSTAT)) 1074 | p->fts_statp = (__fts_stat_t *)ALIGN( 1075 | (unsigned long)(p->fts_name + namelen + 2)); 1076 | #else 1077 | if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) 1078 | return (NULL); 1079 | 1080 | if (!ISSET(FTS_NOSTAT)) 1081 | if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) { 1082 | free(p); 1083 | return (NULL); 1084 | } 1085 | #endif 1086 | 1087 | if (ISSET(FTS_NOSTAT)) 1088 | p->fts_statp = NULL; 1089 | 1090 | /* Copy the name plus the trailing NULL. */ 1091 | memmove(p->fts_name, name, namelen + 1); 1092 | 1093 | p->fts_namelen = ftsent_namelen_truncate(namelen); 1094 | p->fts_path = sp->fts_path; 1095 | p->fts_errno = 0; 1096 | p->fts_flags = 0; 1097 | p->fts_instr = FTS_NOINSTR; 1098 | p->fts_number = 0; 1099 | p->fts_pointer = NULL; 1100 | return (p); 1101 | } 1102 | 1103 | static void 1104 | fts_free(FTSENT *p) 1105 | { 1106 | #if !defined(FTS_ALLOC_ALIGNED) 1107 | if (p->fts_statp) 1108 | free(p->fts_statp); 1109 | #endif 1110 | free(p); 1111 | } 1112 | 1113 | static void 1114 | fts_lfree(FTSENT *head) 1115 | { 1116 | FTSENT *p; 1117 | 1118 | /* XXX: head may be NULL ? */ 1119 | 1120 | /* Free a linked list of structures. */ 1121 | while ((p = head) != NULL) { 1122 | head = head->fts_link; 1123 | fts_free(p); 1124 | } 1125 | } 1126 | 1127 | static size_t 1128 | fts_pow2(size_t x) 1129 | { 1130 | 1131 | x--; 1132 | x |= x>>1; 1133 | x |= x>>2; 1134 | x |= x>>4; 1135 | x |= x>>8; 1136 | x |= x>>16; 1137 | #if LONG_BIT > 32 1138 | x |= x>>32; 1139 | #endif 1140 | #if LONG_BIT > 64 1141 | x |= x>>64; 1142 | #endif 1143 | x++; 1144 | return (x); 1145 | } 1146 | 1147 | /* 1148 | * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 1149 | * Most systems will allow creation of paths much longer than MAXPATHLEN, even 1150 | * though the kernel won't resolve them. Round up the new size to a power of 2, 1151 | * so we don't realloc the path 2 bytes at a time. 1152 | */ 1153 | static int 1154 | fts_palloc(FTS *sp, size_t size) 1155 | { 1156 | char *new; 1157 | 1158 | _DIAGASSERT(sp != NULL); 1159 | 1160 | #ifdef __FTS_COMPAT_LENGTH 1161 | /* Protect against fts_pathlen overflow. */ 1162 | if (size > USHRT_MAX + 1) { 1163 | errno = ENAMETOOLONG; 1164 | return (1); 1165 | } 1166 | #endif 1167 | size = fts_pow2(size); 1168 | new = realloc(sp->fts_path, size); 1169 | if (new == 0) 1170 | return (1); 1171 | sp->fts_path = new; 1172 | sp->fts_pathlen = fts_pathlen_truncate(size); 1173 | return (0); 1174 | } 1175 | 1176 | /* 1177 | * When the path is realloc'd, have to fix all of the pointers in structures 1178 | * already returned. 1179 | */ 1180 | static void 1181 | fts_padjust(FTS *sp, FTSENT *head) 1182 | { 1183 | FTSENT *p; 1184 | char *addr; 1185 | 1186 | _DIAGASSERT(sp != NULL); 1187 | 1188 | #define ADJUST(p) do { \ 1189 | if ((p)->fts_accpath != (p)->fts_name) \ 1190 | (p)->fts_accpath = \ 1191 | addr + ((p)->fts_accpath - (p)->fts_path); \ 1192 | (p)->fts_path = addr; \ 1193 | } while (/*CONSTCOND*/0) 1194 | 1195 | addr = sp->fts_path; 1196 | 1197 | /* Adjust the current set of children. */ 1198 | for (p = sp->fts_child; p; p = p->fts_link) 1199 | ADJUST(p); 1200 | 1201 | /* Adjust the rest of the tree, including the current level. */ 1202 | for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { 1203 | ADJUST(p); 1204 | p = p->fts_link ? p->fts_link : p->fts_parent; 1205 | } 1206 | } 1207 | 1208 | static size_t 1209 | fts_maxarglen(char * const *argv) 1210 | { 1211 | size_t len, max; 1212 | 1213 | _DIAGASSERT(argv != NULL); 1214 | 1215 | for (max = 0; *argv; ++argv) 1216 | if ((len = strlen(*argv)) > max) 1217 | max = len; 1218 | return (max + 1); 1219 | } 1220 | 1221 | /* 1222 | * Change to dir specified by fd or p->fts_accpath without getting 1223 | * tricked by someone changing the world out from underneath us. 1224 | * Assumes p->fts_dev and p->fts_ino are filled in. 1225 | */ 1226 | static int 1227 | fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path) 1228 | { 1229 | int oldfd = fd, ret = -1; 1230 | __fts_stat_t sb; 1231 | 1232 | if (ISSET(FTS_NOCHDIR)) 1233 | return 0; 1234 | 1235 | if (oldfd < 0 && (fd = open(path, O_RDONLY | O_CLOEXEC)) == -1) 1236 | return -1; 1237 | 1238 | if (fstat(fd, &sb) == -1) 1239 | goto bail; 1240 | 1241 | if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) { 1242 | errno = ENOENT; 1243 | goto bail; 1244 | } 1245 | 1246 | ret = fchdir(fd); 1247 | 1248 | bail: 1249 | if (oldfd < 0) { 1250 | int save_errno = errno; 1251 | (void)close(fd); 1252 | errno = save_errno; 1253 | } 1254 | return ret; 1255 | } 1256 | -------------------------------------------------------------------------------- /fts.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: fts.h,v 1.19 2009/08/16 19:33:38 christos Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1989, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)fts.h 8.3 (Berkeley) 8/14/94 32 | */ 33 | 34 | #ifndef _FTS_H_ 35 | #define _FTS_H_ 36 | 37 | #include 38 | #include 39 | 40 | #ifndef __fts_stat_t 41 | #define __fts_stat_t struct stat 42 | #endif 43 | #ifndef __fts_nlink_t 44 | #define __fts_nlink_t nlink_t 45 | #endif 46 | #ifndef __fts_ino_t 47 | #define __fts_ino_t ino_t 48 | #endif 49 | #ifndef __fts_length_t 50 | #define __fts_length_t unsigned int 51 | #endif 52 | #ifndef __fts_number_t 53 | #define __fts_number_t int64_t 54 | #endif 55 | #ifndef __fts_dev_t 56 | #define __fts_dev_t dev_t 57 | #endif 58 | #ifndef __fts_level_t 59 | #define __fts_level_t int 60 | #endif 61 | 62 | typedef struct { 63 | struct _ftsent *fts_cur; /* current node */ 64 | struct _ftsent *fts_child; /* linked list of children */ 65 | struct _ftsent **fts_array; /* sort array */ 66 | dev_t fts_dev; /* starting device # */ 67 | char *fts_path; /* path for this descent */ 68 | int fts_rfd; /* fd for root */ 69 | unsigned int fts_pathlen; /* sizeof(path) */ 70 | unsigned int fts_nitems; /* elements in the sort array */ 71 | int (*fts_compar) /* compare function */ 72 | (const struct _ftsent **, const struct _ftsent **); 73 | 74 | #define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ 75 | #define FTS_LOGICAL 0x002 /* logical walk */ 76 | #define FTS_NOCHDIR 0x004 /* don't change directories */ 77 | #define FTS_NOSTAT 0x008 /* don't get stat info */ 78 | #define FTS_PHYSICAL 0x010 /* physical walk */ 79 | #define FTS_SEEDOT 0x020 /* return dot and dot-dot */ 80 | #define FTS_XDEV 0x040 /* don't cross devices */ 81 | #define FTS_WHITEOUT 0x080 /* return whiteout information */ 82 | #define FTS_OPTIONMASK 0x0ff /* valid user option mask */ 83 | 84 | #define FTS_NAMEONLY 0x100 /* (private) child names only */ 85 | #define FTS_STOP 0x200 /* (private) unrecoverable error */ 86 | int fts_options; /* fts_open options, global flags */ 87 | } FTS; 88 | 89 | typedef struct _ftsent { 90 | struct _ftsent *fts_cycle; /* cycle node */ 91 | struct _ftsent *fts_parent; /* parent directory */ 92 | struct _ftsent *fts_link; /* next file in directory */ 93 | __fts_number_t fts_number; /* local numeric value */ 94 | void *fts_pointer; /* local address value */ 95 | char *fts_accpath; /* access path */ 96 | char *fts_path; /* root path */ 97 | int fts_errno; /* errno for this node */ 98 | int fts_symfd; /* fd for symlink */ 99 | __fts_length_t fts_pathlen; /* strlen(fts_path) */ 100 | __fts_length_t fts_namelen; /* strlen(fts_name) */ 101 | 102 | __fts_ino_t fts_ino; /* inode */ 103 | __fts_dev_t fts_dev; /* device */ 104 | __fts_nlink_t fts_nlink; /* link count */ 105 | 106 | #define FTS_ROOTPARENTLEVEL -1 107 | #define FTS_ROOTLEVEL 0 108 | __fts_level_t fts_level; /* depth (-1 to N) */ 109 | 110 | #define FTS_D 1 /* preorder directory */ 111 | #define FTS_DC 2 /* directory that causes cycles */ 112 | #define FTS_DEFAULT 3 /* none of the above */ 113 | #define FTS_DNR 4 /* unreadable directory */ 114 | #define FTS_DOT 5 /* dot or dot-dot */ 115 | #define FTS_DP 6 /* postorder directory */ 116 | #define FTS_ERR 7 /* error; errno is set */ 117 | #define FTS_F 8 /* regular file */ 118 | #define FTS_INIT 9 /* initialized only */ 119 | #define FTS_NS 10 /* stat(2) failed */ 120 | #define FTS_NSOK 11 /* no stat(2) requested */ 121 | #define FTS_SL 12 /* symbolic link */ 122 | #define FTS_SLNONE 13 /* symbolic link without target */ 123 | #define FTS_W 14 /* whiteout object */ 124 | unsigned short fts_info; /* user flags for FTSENT structure */ 125 | 126 | #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ 127 | #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ 128 | #define FTS_ISW 0x04 /* this is a whiteout object */ 129 | unsigned short fts_flags; /* private flags for FTSENT structure */ 130 | 131 | #define FTS_AGAIN 1 /* read node again */ 132 | #define FTS_FOLLOW 2 /* follow symbolic link */ 133 | #define FTS_NOINSTR 3 /* no instructions */ 134 | #define FTS_SKIP 4 /* discard node */ 135 | unsigned short fts_instr; /* fts_set() instructions */ 136 | 137 | __fts_stat_t *fts_statp; /* stat(2) information */ 138 | char fts_name[1]; /* file name */ 139 | } FTSENT; 140 | 141 | #ifdef __cplusplus 142 | extern "C" { 143 | #endif 144 | FTSENT *fts_children(FTS *, int); 145 | int fts_close(FTS *); 146 | FTS *fts_open(char * const *, int, 147 | int (*)(const FTSENT **, const FTSENT **)); 148 | FTSENT *fts_read(FTS *); 149 | int fts_set(FTS *, FTSENT *, int); 150 | #ifdef __cplusplus 151 | } 152 | #endif 153 | 154 | 155 | #endif /* !_FTS_H_ */ 156 | -------------------------------------------------------------------------------- /musl-fts.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: musl-fts 7 | Description: Implementation of fts(3) functions for musl libc 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lfts 10 | Cflags: -I${includedir} 11 | --------------------------------------------------------------------------------