fts/
ffi.rs

1//! Bindings for libc fts.
2
3use libc::*;
4
5/// struct FTS in fts.h ( opaque struct )
6pub enum FTS {}
7
8/// struct FTSENT in fts.h
9#[repr(C)]
10#[derive(Debug)]
11pub struct FTSENT {
12    /// cycle node
13    pub fts_cycle: *const FTSENT,
14    /// parent directory
15    pub fts_parent: *const FTSENT,
16    /// next file in directory
17    pub fts_link: *const FTSENT,
18    /// local numeric value
19    pub fts_number: c_long,
20    /// local address value
21    pub fts_pointer: *const c_void,
22    /// access path
23    pub fts_accpath: *const c_char,
24    /// root path
25    pub fts_path: *const c_char,
26    /// errno for this node
27    pub fts_errno: c_int,
28    /// fd for symlink
29    pub fts_symfd: c_int,
30    /// strlen(fts_path)
31    pub fts_pathlen: c_ushort,
32    /// strlen(fts_name)
33    pub fts_namelen: c_ushort,
34    /// inode
35    pub fts_ino: ino_t,
36    /// device
37    pub fts_dev: dev_t,
38    /// link count
39    pub fts_nlink: nlink_t,
40    /// depth (-1 to N)
41    pub fts_level: c_short,
42    /// user flags for FTSENT structure
43    pub fts_info: c_ushort,
44    /// private flags for FTSENT structure
45    pub fts_flags: c_ushort,
46    /// fts_set() instructions
47    pub fts_instr: c_ushort,
48    /// stat(2) information
49    pub fts_statp: *const stat,
50    /// file name
51    pub fts_name: [u8; 0],
52}
53
54/// fts_level: level of root parent
55pub const FTS_ROOTPARENTLEVEL: c_short = -1;
56/// fts_level: level of root
57pub const FTS_ROOTLEVEL: c_short = 0;
58/// fts_info: preorder directory
59pub const FTS_D: c_ushort = 1;
60/// fts_info: directory that causes cycles
61pub const FTS_DC: c_ushort = 2;
62/// fts_info: none of the above
63pub const FTS_DEFAULT: c_ushort = 3;
64/// fts_info: unreadable directory
65pub const FTS_DNR: c_ushort = 4;
66/// fts_info: dot or dot-dot
67pub const FTS_DOT: c_ushort = 5;
68/// fts_info: postorder directory
69pub const FTS_DP: c_ushort = 6;
70/// fts_info: error; errno is set
71pub const FTS_ERR: c_ushort = 7;
72/// fts_info: regular file
73pub const FTS_F: c_ushort = 8;
74/// fts_info: initialized only
75pub const FTS_INIT: c_ushort = 9;
76/// fts_info: stat(2) failed
77pub const FTS_NS: c_ushort = 10;
78/// fts_info: no stat(2) requested
79pub const FTS_NSOK: c_ushort = 11;
80/// fts_info: symbolic link
81pub const FTS_SL: c_ushort = 12;
82/// fts_info: symbolic link without target
83pub const FTS_SLNONE: c_ushort = 13;
84/// fts_info: whiteout object
85pub const FTS_W: c_ushort = 14;
86/// fts_flags: don't chdir .. to the parent
87pub const FTS_DONTCHDIR: c_ushort = 0x01;
88/// fts_flags: followed a symlink to get here
89pub const FTS_SYMFOLLOW: c_ushort = 0x02;
90/// fts_instr: read node again
91pub const FTS_AGAIN: c_int = 1;
92/// fts_instr: follow symbolic link
93pub const FTS_FOLLOW: c_int = 2;
94/// fts_instr: no instructions
95pub const FTS_NOINSTR: c_int = 3;
96/// fts_instr: discard node
97pub const FTS_SKIP: c_int = 4;
98/// fts_open options: follow command line symlinks
99pub const FTS_COMFOLLOW: c_int = 0x0001;
100/// fts_open options: logical walk
101pub const FTS_LOGICAL: c_int = 0x0002;
102/// fts_open options: don't change directories
103pub const FTS_NOCHDIR: c_int = 0x0004;
104/// fts_open options: don't get stat info
105pub const FTS_NOSTAT: c_int = 0x0008;
106/// fts_open options: physical walk
107pub const FTS_PHYSICAL: c_int = 0x0010;
108/// fts_open options: return dot and dot-dot
109pub const FTS_SEEDOT: c_int = 0x0020;
110/// fts_open options: don't cross devices
111pub const FTS_XDEV: c_int = 0x0040;
112/// fts_open options: return whiteout information
113pub const FTS_WHITEOUT: c_int = 0x0080;
114/// fts_open options: valid user option mask
115pub const FTS_OPTIONMASK: c_int = 0x00ff;
116/// fts_open options: (private) child names only
117pub const FTS_NAMEONLY: c_int = 0x0100;
118/// fts_open options: (private) unrecoverable error
119pub const FTS_STOP: c_int = 0x0200;
120
121extern "C" {
122    /// fts_open() in fts.h
123    ///
124    /// # C function
125    /// ```c
126    /// FTS *fts_open(char * const *path_argv, int options,
127    ///               int (*compar)(const FTSENT **, const FTSENT **));
128    /// ```
129    ///
130    /// # Safety
131    /// `path_argv` must be a pointer of a null-terminated array of C strings( null-terminated ).
132    ///
133    /// `options` must contain either `FTS_LOGICAL` or `FTS_PHYSICAL`.
134    ///
135    /// # Examples
136    /// ```
137    /// let path  = std::ffi::CString::new( "." ).unwrap();
138    /// let paths = vec![path.as_ptr(), std::ptr::null()];
139    /// let _fts  = unsafe { fts::ffi::fts_open( paths.as_ptr(), fts::ffi::FTS_LOGICAL, None ) };
140    /// ```
141    #[cfg_attr(target_os = "macos", link_name = "fts_open$INODE64")]
142    pub fn fts_open(
143        path_argv: *const *const c_char,
144        options: c_int,
145        compar: Option<extern "C" fn(*const *const FTSENT, *const *const FTSENT) -> c_int>,
146    ) -> *mut FTS;
147
148    /// fts_read() in fts.h
149    ///
150    /// # C function
151    /// ```c
152    /// FTSENT *fts_read(FTS *ftsp);
153    /// ```
154    ///
155    /// # Safety
156    /// `ftsp` must be a valid pointer of struct FTS.
157    ///
158    /// # Examples
159    /// ```
160    /// let path    = std::ffi::CString::new( "." ).unwrap();
161    /// let paths   = vec![path.as_ptr(), std::ptr::null()];
162    /// let fts     = unsafe { fts::ffi::fts_open( paths.as_ptr(), fts::ffi::FTS_LOGICAL, None ) };
163    /// let _ftsent = unsafe { fts::ffi::fts_read( fts ) };
164    /// ```
165    #[cfg_attr(target_os = "macos", link_name = "fts_read$INODE64")]
166    pub fn fts_read(ftsp: *mut FTS) -> *const FTSENT;
167
168    /// fts_children() in fts.h
169    ///
170    /// # C function
171    /// ```c
172    /// FTSENT *fts_children(FTS *ftsp, int options);
173    /// ```
174    ///
175    /// # Safety
176    /// `ftsp` must be a valid pointer of struct FTS.
177    ///
178    /// `options` must be either 0 or `FTS_NAMEONLY`.
179    ///
180    /// # Examples
181    /// ```
182    /// let path    = std::ffi::CString::new( "." ).unwrap();
183    /// let paths   = vec![path.as_ptr(), std::ptr::null()];
184    /// let fts     = unsafe { fts::ffi::fts_open( paths.as_ptr(), fts::ffi::FTS_LOGICAL, None ) };
185    /// let _ftsent = unsafe { fts::ffi::fts_children( fts, 0 ) };
186    /// ```
187    #[cfg_attr(target_os = "macos", link_name = "fts_children$INODE64")]
188    pub fn fts_children(ftsp: *mut FTS, options: c_int) -> *const FTSENT;
189
190    /// fts_set() in fts.h
191    ///
192    /// # C function
193    /// ```c
194    /// int fts_set(FTS *ftsp, FTSENT *f, int options);
195    /// ```
196    ///
197    /// # Safety
198    /// `ftsp` must be a valid pointer of struct FTS.
199    ///
200    /// `f` must be a valid pointer of struct FTSENT.
201    ///
202    /// `options` must be `FTS_AGAIN`, `FTS_FOLLOW` or `FTS_SKIP`.
203    ///
204    /// # Examples
205    /// ```
206    /// let path   = std::ffi::CString::new( "." ).unwrap();
207    /// let paths  = vec![path.as_ptr(), std::ptr::null()];
208    /// let fts    = unsafe { fts::ffi::fts_open( paths.as_ptr(), fts::ffi::FTS_LOGICAL, None ) };
209    /// let ftsent = unsafe { fts::ffi::fts_read( fts ) };
210    /// let _      = unsafe { fts::ffi::fts_set( fts, ftsent, fts::ffi::FTS_AGAIN ) };
211    /// ```
212    #[cfg_attr(target_os = "macos", link_name = "fts_set$INODE64")]
213    pub fn fts_set(ftsp: *mut FTS, f: *const FTSENT, options: c_int) -> c_int;
214
215    /// fts_close() in fts.h
216    ///
217    /// # C function
218    /// ```c
219    /// int fts_close(FTS *ftsp);
220    /// ```
221    ///
222    /// # Safety
223    /// `ftsp` must be a valid pointer of struct FTS.
224    ///
225    /// # Examples
226    /// ```
227    /// let path   = std::ffi::CString::new( "." ).unwrap();
228    /// let paths  = vec![path.as_ptr(), std::ptr::null()];
229    /// let fts    = unsafe { fts::ffi::fts_open( paths.as_ptr(), fts::ffi::FTS_LOGICAL, None ) };
230    /// let _      = unsafe { fts::ffi::fts_close( fts ) };
231    /// ```
232    #[cfg_attr(target_os = "macos", link_name = "fts_close$INODE64")]
233    pub fn fts_close(ftsp: *mut FTS) -> c_int;
234}
235
236#[cfg(test)]
237mod test {
238    use super::*;
239    use std::ffi::CString;
240    use std::ptr;
241
242    fn ftsent_valid(ftsent: *const FTSENT) {
243        unsafe {
244            assert!(!ftsent.is_null());
245            assert!(!(*ftsent).fts_accpath.is_null());
246            assert!(!(*ftsent).fts_path.is_null());
247            assert!((*ftsent).fts_pathlen != 0);
248            assert!((*ftsent).fts_namelen != 0);
249            assert!((*ftsent).fts_level >= -1);
250            assert!((*ftsent).fts_number == 0);
251            assert!((*ftsent).fts_pointer.is_null());
252            assert!(!(*ftsent).fts_parent.is_null());
253            assert!(!(*ftsent).fts_statp.is_null());
254        }
255    }
256
257    #[test]
258    fn normal() {
259        unsafe {
260            let path = CString::new(".").unwrap();
261            let paths = vec![path.as_ptr(), ptr::null()];
262            let fts = fts_open(paths.as_ptr(), 0, None);
263            assert!(!fts.is_null());
264
265            let mut ftsent = fts_read(fts);
266            while !ftsent.is_null() {
267                ftsent_valid(ftsent);
268                ftsent = fts_read(fts);
269            }
270
271            let ret = fts_close(fts);
272            assert!(ret == 0);
273        }
274    }
275
276    #[test]
277    fn children() {
278        unsafe {
279            let path = CString::new(".").unwrap();
280            let paths = vec![path.as_ptr(), ptr::null()];
281            let fts = fts_open(paths.as_ptr(), FTS_LOGICAL, None);
282            assert!(!fts.is_null());
283
284            let _ = fts_read(fts);
285            let mut ftsent = fts_children(fts, 0);
286            while !ftsent.is_null() {
287                ftsent_valid(ftsent);
288                ftsent = (*ftsent).fts_link;
289            }
290
291            let ret = fts_close(fts);
292            assert!(ret == 0);
293        }
294    }
295}