summaryrefslogtreecommitdiffstats
path: root/include/jau/file_util.hpp
blob: 933952ded90eceac391a3ed71ab60b99922362a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/**
 * Author: Sven Gothel <sgothel@jausoft.com>
 * Copyright (c) 2022 Gothel Software e.K.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef JAU_FILE_UTIL_HPP_
#define JAU_FILE_UTIL_HPP_

#include <cstring>
#include <string>
#include <memory>
#include <cstdint>
#include <cstdio>

#include <jau/fraction_type.hpp>
#include <jau/function_def.hpp>

namespace jau {

    namespace fs {

        /** @defgroup FileUtils File Utilities
         *  File types and functionality.
         *
         *  @{
         */

        /**
         * Generic file type and mode bits as used in file_stats
         */
        enum class fmode_bits : uint32_t {
            /** No mode bit set */
            NONE         =        0,
            /** Entity is a file, might be in combination with LINK. */
            FILE         = 1U <<  0,
            /** Entity is a directory, might be in combination with LINK. */
            DIR          = 1U <<  1,
            /** Entity is a symbolic link, might be in combination with FILE or DIR. */
            LINK         = 1U <<  2,
            /** Entity gives no access to user, exclusive bit. */
            NO_ACCESS    = 1U << 30,
            /** Entity does not exist, exclusive bit. */
            NOT_EXISTING = 1U << 31
        };
        constexpr uint32_t number(const fmode_bits rhs) noexcept {
            return static_cast<uint32_t>(rhs);
        }
        constexpr fmode_bits operator ^(const fmode_bits lhs, const fmode_bits rhs) noexcept {
            return static_cast<fmode_bits> ( number(lhs) ^ number(rhs) );
        }
        constexpr fmode_bits operator |(const fmode_bits lhs, const fmode_bits rhs) noexcept {
            return static_cast<fmode_bits> ( number(lhs) | number(rhs) );
        }
        constexpr fmode_bits& operator |=(fmode_bits& lhs, const fmode_bits rhs) noexcept {
            lhs = static_cast<fmode_bits> ( number(lhs) | number(rhs) );
            return lhs;
        }
        constexpr fmode_bits operator &(const fmode_bits lhs, const fmode_bits rhs) noexcept {
            return static_cast<fmode_bits> ( number(lhs) & number(rhs) );
        }
        constexpr bool operator ==(const fmode_bits lhs, const fmode_bits rhs) noexcept {
            return number(lhs) == number(rhs);
        }
        constexpr bool operator !=(const fmode_bits lhs, const fmode_bits rhs) noexcept {
            return !( lhs == rhs );
        }
        constexpr bool has_fmode_bit(const fmode_bits mask, const fmode_bits bit) noexcept {
            return fmode_bits::NONE != ( mask & bit );
        }
        std::string to_string(const fmode_bits mask) noexcept;

        /**
         * Representing a directory element, optionally split into parent_dir() and item(),
         * where item() shall not be empty.
         */
        class dir_item {
            private:
                std::string parent_dir_;
                std::string element_;

            public:
                dir_item() noexcept
                : parent_dir_(), element_() {}

                dir_item(std::string parent_dir__, std::string element__) noexcept
                : parent_dir_(parent_dir__), element_(element__) {}

                dir_item(std::string element__) noexcept
                : parent_dir_(), element_(element__) {}

                /** Returns the parent dir, may be empty. */
                const std::string& parent_dir() const noexcept { return parent_dir_; }

                /** Return the directory element, shall not be empty and may contain full path. */
                const std::string& element() const noexcept { return element_; }

                /**
                 * Returns a unix path representation combining parent_dir() and element().
                 */
                std::string path() const noexcept;
        };

        /**
         * Platform agnostic C++ representation of POSIX ::lstat() and ::stat()
         * for a given pathname.
         *
         * Implementation follows the symbolic link, i.e. first opens
         * the given pathname with ::lstat() and if identifying as a symbolic link
         * opens it via ::stat() to retrieve the actual properties like size, time and ownership.
         */
        class file_stats {
            public:
                typedef uint32_t uid_t;
                typedef uint32_t gid_t;

            private:
                dir_item item_;

                fmode_bits mode_;
                uid_t uid_;
                gid_t gid_;
                size_t size_;
                fraction_timespec atime_;
                fraction_timespec mtime_;
                fraction_timespec ctime_;

                int errno_res_;

            public:
                /** Instantiate an empty file_stats with fmode_bits::NOT_EXISTING set. */
                file_stats() noexcept;

                /**
                 * Instantiates a file_stats for the given `path`.
                 *
                 * The dir_item will be constructed without parent_dir
                 * @param path the path to produce stats for
                 */
                file_stats(const std::string& path) noexcept;

                /**
                 * Instantiates a file_stats for the given dir_item.
                 *
                 * @param item the dir_item to produce stats for
                 */
                file_stats(const dir_item& item) noexcept;

                /** Returns the dir_item. */
                const dir_item& item() const noexcept { return item_; }

                /** Returns a unix path representation from item(). */
                std::string path() const noexcept { return item_.path(); }

                /** Returns the fmode_bits, file type and mode. */
                fmode_bits mode() const noexcept { return mode_; }

                /** Returns the user id, owning the element. */
                uid_t uid() const noexcept { return uid_; }

                /** Returns the group id, owning the element. */
                gid_t gid() const noexcept { return gid_; }

                /** Returns the size in bytes of this element if is_file() including is_link(), otherwise zero. */
                size_t size() const noexcept { return size_; }

                /** Returns the last access time of this element since Unix Epoch. */
                const fraction_timespec& atime() const noexcept { return atime_; }
                /** Returns the last modification time of this element since Unix Epoch. */
                const fraction_timespec& mtime() const noexcept { return mtime_; }
                /** Returns the last status change time of this element since Unix Epoch. */
                const fraction_timespec& ctime() const noexcept { return ctime_; }

                /** Returns the `errno` value occurred to produce this instance, or zero for no error. */
                int errno_res() const noexcept { return errno_res_; }

                /** Returns true if no error occurred */
                bool ok()  const noexcept { return 0 == errno_res_; }

                /** Returns true if entity is a file, might be in combination with is_link().  */
                bool is_file() const noexcept { return has_fmode_bit( mode_, fmode_bits::FILE ); }

                /** Returns true if entity is a directory, might be in combination with is_link().  */
                bool is_dir() const noexcept { return has_fmode_bit( mode_, fmode_bits::DIR ); }

                /** Returns true if entity is a symbolic link, might be in combination with is_file() or is_dir(). */
                bool is_link() const noexcept { return has_fmode_bit( mode_, fmode_bits::LINK ); }

                /** Returns true if entity gives no access to user, exclusive bit. */
                bool has_access() const noexcept { return !has_fmode_bit( mode_, fmode_bits::NO_ACCESS ); }

                /** Returns true if entity does not exist, exclusive bit. */
                bool exists() const noexcept { return !has_fmode_bit( mode_, fmode_bits::NOT_EXISTING ); }

                /**
                 * Returns a comprehensive string representation of this element
                 * @param use_space if true, using space instead for 'T' separator and drop trailing UTC `Z` for readability, otherwise be compliant with ISO 8601 (default)
                 */
                std::string to_string(const bool use_space=false) const noexcept;
        };

        /**
         * Return the current working directory or empty on failure.
         */
        std::string get_cwd() noexcept;

        /**
         * Create directory
         * @param path full path to new directory
         * @param verbose
         * @return true if successful, otherwise false
         */
        bool mkdir(const std::string& path, const bool verbose=false) noexcept;

        /**
         * Touch the file with given atime and mtime and create file if not existing yet.
         * @param path full path to file
         * @param atime new access time
         * @param mtime new modification time
         * @param verbose
         * @return true if successful, otherwise false
         */
        bool touch(const std::string& path, const jau::fraction_timespec& atime, const jau::fraction_timespec& mtime, const bool verbose=false) noexcept;

        /**
         * Touch the file with current time and create file if not existing yet.
         * @param path full path to file
         * @param verbose
         * @return true if successful, otherwise false
         */
        bool touch(const std::string& path, const bool verbose=false) noexcept;

        /**
         * `void consume_dir_item(const dir_item& item)`
         */
        typedef jau::FunctionDef<void, const dir_item&> consume_dir_item;

        /**
         * Returns a list of directory elements excluding `.` and `..` for the given path, non recursive.
         *
         * The custom consume_dir_item `digest` may also be used to filter the element, besides storing it.
         *
         * @param path path to directory
         * @param digest consume_dir_item function to receive each directory item, e.g. `void consume_dir_item(const dir_item& item)`
         * @return true if given path exists, is directory and is readable, otherwise false
         */
        bool get_dir_content(const std::string& path, const consume_dir_item& digest) noexcept;

        /**
         * path_visitor jau::FunctionDef definition
         * - `bool visitor(const file_stats& item_stats)`
         */
        typedef jau::FunctionDef<bool, const file_stats&> path_visitor;

        /**
         * Visit all elements of path in a recursive manner, visiting content first then its parent directory.
         *
         * Any element without access or error otherwise will be skipped.
         *
         * All elements of type fmode_bits::FILE, fmode_bits::DIR and fmode_bits::NO_ACCESS
         * will be visited by the `visitor`
         * and processing ends if it returns `false`.
         *
         * @param path the starting path
         * @param follow_sym_link_dirs pass true to visit directories with property fmode_bits::LINK, otherwise false.
         * @param visitor path_visitor function `bool visitor(const file_stats& item_stats)`.
         * @return true if all visitor invocation returned true and no error occurred, otherwise false
         */
        bool visit(const std::string& path, const bool follow_sym_link_dirs, const path_visitor& visitor) noexcept;

        /**
         * Visit all elements of path in a recursive manner, visiting content first then its parent directory.
         *
         * Any element without access or error otherwise will be skipped.
         *
         * All elements of type fmode_bits::FILE, fmode_bits::DIR and fmode_bits::NO_ACCESS
         * will be visited by the `visitor`
         * and processing ends if it returns `false`.
         *
         * @param item_stats pre-fetched file_stats for a given dir_item, used for efficiency
         * @param follow_sym_link_dirs pass true to visit directories with property fmode_bits::LINK, otherwise false.
         * @param visitor path_visitor function `bool visitor(const file_stats& item_stats)`.
         * @return true if all visitor invocation returned true and no error occurred, otherwise false
         */
        bool visit(const file_stats& item_stats, const bool follow_sym_link_dirs, const path_visitor& visitor) noexcept;

        /**
         * Remove the given path. If path represents a director, `recursive` must be set to true.
         * @param path path to remove
         * @param recursive indicates to remove directories
         * @param follow_sym_link_dirs pass true to remove directories with property fmode_bits::LINK (default), otherwise false.
         * @param verbose pass true for verbose information about deleted files and directories, otherwise false (default)
         * @return true only if the file or the directory with content has been deleted, otherwise false
         */
        bool remove(const std::string& path, const bool recursive, const bool follow_sym_link_dirs=true, const bool verbose=false) noexcept;

        /**@}*/

    } /* namespace fs */

} /* namespace jau */

#endif /* JAU_FILE_UTIL_HPP_ */