OXYGENHUB
◆ main openmsx / src / file / FileOperations.hh
11.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef FILEOPERATIONS_HH
2
#define FILEOPERATIONS_HH
3
4
#include "Date.hh"
5
#include "statp.hh"
6
#include "unistdp.hh" // needed for mode_t definition when building with VC++
7
#include "zstring_view.hh"
8
9
#include <fstream>
10
#include <memory>
11
#include <optional>
12
#include <string_view>
13
#include <sys/types.h>
14
15
namespace openmsx::FileOperations {
16
17
	struct FClose {
18
		void operator()(FILE* f) const { fclose(f); }
19
	};
20
	using FILE_t = std::unique_ptr<FILE, FClose>;
21
22
	const char nativePathSeparator =
23
#ifdef _WIN32
24
		'\\';
25
#else
26
		'/';
27
#endif
28
29
	/**
30
	 * Expand the '~' character to the users home directory
31
	 * @param path Pathname, with or without '~' character
32
	 * @result The expanded pathname
33
	 */
34
	[[nodiscard]] std::string expandTilde(std::string path);
35
36
	/** Returns true iff expandTilde(s) would have an effect.
37
	  * In other words: expandTilde(s) != s
38
	  * Mostly useful to assert that tilde expansion has already been done.
39
	  */
40
	[[nodiscard]] bool needsTildeExpansion(std::string_view path);
41
42
	/**
43
	 * Create the specified directory. Does some sanity checks so that
44
	 * it behaves the same on all platforms. The mode parameter is ignored
45
	 * on windows. For compatibility with *nix creating the root dir (or a
46
	 * drive name) is not an error instead the operation is silently
47
	 * ignored. This function can only create one directory at-a-time. You
48
	 * probably want to use the mkdirp function (see below).
49
	 * @param path The path of the directory to create
50
	 * @param mode The permission bits (*nix only)
51
	 * @throw FileException
52
	 */
53
	void mkdir(zstring_view path, mode_t mode);
54
55
	/**
56
	 * Acts like the unix command "mkdir -p". Creates the
57
	 * specified directory, including the parent directories.
58
	 * @param path The path of the directory to create
59
	 * @throw FileException
60
	 */
61
	void mkdirp(std::string path);
62
63
	/**
64
	 * Call unlink() in a platform-independent manner
65
	 */
66
	int unlink(zstring_view path);
67
68
	/**
69
	 * Call rmdir() in a platform-independent manner
70
	 */
71
	int rmdir(zstring_view path);
72
73
	/** Recursively delete a file or directory and (in case of a directory)
74
	  * all its sub-components.
75
	  */
76
	int deleteRecursive(zstring_view path);
77
78
	/** Call fopen() in a platform-independent manner
79
	  * @param filename the file path
80
	  * @param mode the mode parameter, same as fopen
81
	  * @result A pointer to the opened file, or nullptr on error
82
	  *         On error the global variable 'errno' is filled in (see
83
	  *         man fopen for details). */
84
	[[nodiscard]] FILE_t openFile(zstring_view filename, zstring_view mode);
85
86
	/**
87
	 * Open an ofstream in a platform-independent manner
88
	 * @param stream an ofstream
89
	 * @param filename the file path
90
	 */
91
	void openOfStream(std::ofstream& stream, zstring_view filename);
92
93
	/**
94
	 * Open an ofstream in a platform-independent manner
95
	 * @param stream an ofstream
96
	 * @param filename the file path
97
	 * @param mode the open mode
98
	 */
99
        void openOfStream(std::ofstream& stream, zstring_view filename,
100
	                  std::ios_base::openmode mode);
101
102
        /**
103
	 * Returns the file portion of a path name.
104
	 * @param path The pathname
105
	 * @result The file portion
106
	 */
107
	[[nodiscard]] std::string_view getFilename(std::string_view path);
108
	[[nodiscard]] inline zstring_view getFilename(zstring_view path) {
109
		auto v = getFilename(path.view());
110
		return {v.data(), v.size()};
111
	}
112
	[[nodiscard]] inline zstring_view getFilename(const std::string& path) {
113
		return getFilename(zstring_view(path));
114
	}
115
116
	/**
117
	 * Returns the directory portion of a path.
118
	 * @param path The pathname
119
	 * @result The directory portion. This includes the ending '/'.
120
	 *         If path doesn't have a directory portion the result
121
	 *         is an empty string.
122
	 */
123
	[[nodiscard]] std::string_view getDirName(std::string_view path);
124
125
	/**
126
	 * Returns the extension portion of a path.
127
	 * @param path The pathname
128
	 * @result The extension portion. This excludes the '.'.
129
	 *         If path doesn't have an extension portion the result
130
	 *         is an empty string.
131
	 */
132
	[[nodiscard]] std::string_view getExtension(std::string_view path);
133
134
	/**
135
	 * Returns the path without extension.
136
	 * @param path The pathname
137
	 * @result The path without extension. This excludes the '.'.
138
	 *         If path doesn't have an extension portion the result
139
	 *         remains unchanged.
140
	 */
141
	[[nodiscard]] std::string_view stripExtension(std::string_view path);
142
143
	/**
144
	 * Return the stem of the path, i.e. the filename without extension.
145
	 * This is equivalent to stripExtension(getFilename(path)), but more
146
	 * efficient: only does a single pass.
147
	 * @param path The pathname
148
	 * @result The stem of the path. This excludes the '.'.
149
	 *         If path doesn't have an extension portion the result
150
	 *         is the filename.
151
	 */
152
	 [[nodiscard]] std::string_view stem(std::string_view path);
153
154
	/**
155
	 * Replace characters that are not allowed (or not desirable) in a
156
	 * filename by '_'. The result is a single path component: path
157
	 * separators are replaced as well, so this is not suitable to clean
158
	 * a whole path.
159
	 * Mostly useful to turn free-form text (e.g. an MSX machine display
160
	 * name or a guessed software title) into something that can be used
161
	 * as a filename.
162
	 * This is the C++ equivalent of the Tcl proc 'utils::filename_clean'
163
	 * (see share/scripts/_utils.tcl), keep the two in sync.
164
	 * @param filename The filename to clean
165
	 * @result The cleaned filename, always the same length as the input.
166
	 */
167
	[[nodiscard]] std::string cleanFilename(std::string_view filename);
168
169
	/** Join two paths.
170
	 * Returns the equivalent of 'path1 + '/' + path2'. If 'part2' is an
171
	 * absolute path, that path is returned ('part1' is ignored). If
172
	 * 'part1' is empty or if it already ends with '/', there will be no
173
	 * extra '/' added between 'part1' and 'part2'.
174
	 */
175
	[[nodiscard]] std::string join(std::string_view part1, std::string_view part2);
176
	[[nodiscard]] std::string join(std::string_view part1, std::string_view part2, std::string_view part3);
177
	[[nodiscard]] std::string join(std::string_view part1, std::string_view part2,
178
	                               std::string_view part3, std::string_view part4);
179
180
	/**
181
	 * Returns the path in conventional path-delimiter.
182
	 * @param path The pathname.
183
	 * @result The path in conventional path-delimiter.
184
	 *    On UNI*Y systems, it will have no effect indeed.
185
	 *    Just for portability issue. (Especially for Win32)
186
	 */
187
#ifdef _WIN32
188
	[[nodiscard]]              std::string  getConventionalPath(      std::string   path);
189
#else
190
	[[nodiscard]] inline const std::string& getConventionalPath(const std::string&  path) { return           path;  }
191
	[[nodiscard]] inline       std::string  getConventionalPath(      std::string&& path) { return std::move(path); }
192
#endif
193
194
	/**
195
	 * Returns the path in native path-delimiter.
196
	 * @param path The pathname.
197
	 * @result The path in native path-delimiter.
198
	 *    On UNI*Y systems, it will have no effect indeed.
199
	 *    Just for portability issue. (Especially for Win32)
200
	 */
201
#ifdef _WIN32
202
	[[nodiscard]] std::string getNativePath(zstring_view path);
203
#else
204
	[[nodiscard]] inline zstring_view getNativePath(zstring_view path) { return path; }
205
#endif
206
207
	/**
208
	* Get the current directory of the specified drive
209
	* Linux: return the given string unchanged
210
	*/
211
#ifdef _WIN32
212
	[[nodiscard]]              std::string  expandCurrentDirFromDrive(std::string path);
213
#else
214
	[[nodiscard]] inline const std::string& expandCurrentDirFromDrive(const std::string&  path) { return           path;  }
215
	[[nodiscard]] inline       std::string  expandCurrentDirFromDrive(      std::string&& path) { return std::move(path); }
216
#endif
217
218
	/** Returns the current working directory.
219
	 * @throw FileException (for example when directory has been deleted).
220
	 */
221
	[[nodiscard]] std::string getCurrentWorkingDirectory();
222
223
	/** Transform given path into an absolute path
224
	 * @throw FileException
225
	 */
226
	[[nodiscard]] std::string getAbsolutePath(std::string_view path);
227
228
	/**
229
	 * Checks whether it's a absolute path or not.
230
	 * @param path The pathname.
231
	 */
232
	[[nodiscard]] bool isAbsolutePath(std::string_view path);
233
234
	/**
235
	 * Get user's home directory.
236
	 * @param username The name of the user
237
	 * @result Home directory of the user or empty string in case of error
238
	 * UNI*Y: get from env var "HOME" or from /etc/passwd
239
	 *        empty string means current user
240
	 * Win32: Currently use "My Documents" as home directory.
241
	 *        Not "Documents and Settings".
242
	 *        This is because to support Win9x.
243
	 *        Ignores the username parameter
244
	 */
245
	[[nodiscard]] std::string getUserHomeDir(std::string_view username);
246
247
	/**
248
	 * Get the openMSX dir in the user's home directory.
249
	 * Default value is "~/.openMSX" (UNIX) or "~/openMSX" (win)
250
	 */
251
	[[nodiscard]] const std::string& getUserOpenMSXDir();
252
	[[nodiscard]] std::string getUserOpenMSXDir(std::string_view subdir);
253
254
	/**
255
	 * Get the openMSX data dir in the user's home directory.
256
	 * Default value is "~/.openMSX/share" (UNIX) or "~/openMSX/share" (win)
257
	 */
258
	[[nodiscard]] const std::string& getUserDataDir();
259
260
	/**
261
	 * Get system directory.
262
	 * UNI*Y: statically defined as "/opt/openMSX/share".
263
	 * Win32: use "same directory as .exe" + "/share".
264
	 */
265
	[[nodiscard]] const std::string& getSystemDataDir();
266
267
	/**
268
	 * Get system doc directory.
269
	 * Win32: use "same directory as .exe" + "/doc".
270
	 */
271
	[[nodiscard]] const std::string& getSystemDocDir();
272
273
#ifdef _WIN32
274
	// using Stat = struct _stat64; // doesn't work for some reason
275
	typedef struct _stat64 Stat; // See cc file for why we use _stat64
276
#else
277
	using Stat = struct stat;
278
#endif
279
	/**
280
	 * Call stat() and return the stat structure
281
	 * @param filename the file path
282
	 */
283
	[[nodiscard]] std::optional<Stat> getStat(zstring_view filename);
284
285
	/**
286
	 * Is this a regular file (no directory, device, ..)?
287
	 */
288
	[[nodiscard]] bool isRegularFile(zstring_view filename);
289
	[[nodiscard]] bool isRegularFile(const Stat& st);
290
291
	/**
292
	 * Is this a directory?
293
	 */
294
	[[nodiscard]] bool isDirectory(zstring_view directory);
295
	[[nodiscard]] bool isDirectory(const Stat& st);
296
297
	/**
298
	 * Does this file (directory) exists?
299
	 */
300
	[[nodiscard]] bool exists(zstring_view filename);
301
302
	/** Get the date/time of last modification.
303
	  * Is guaranteed to not be INVALID_TIME_T.
304
	  */
305
	[[nodiscard]] inline time_t getModificationDate(const Stat& st) {
306
		return Date::adjustTimeT(st.st_mtime);
307
	}
308
309
	/**
310
	 * Gets the next numbered file name with the specified prefix in the
311
	 * specified directory, with the specified extension. Examples:
312
	 * automatic numbering of filenames for new screenshots or sound logs.
313
	 * @param directory Name of the directory in the openMSX user dir in
314
	 * which should be searched for the next filename
315
	 * @param prefix Prefix of the filename with numbers
316
	 * @param extension Extension of the filename with numbers
317
	 * @param addSeparator Add separator character between 'prefix' and 'number'.
318
	 */
319
	[[nodiscard]] std::string getNextNumberedFileName(
320
		std::string_view directory, std::string_view prefix, std::string_view extension,
321
		bool addSeparator = false);
322
323
	/** Helper function for parsing filename arguments in Tcl commands.
324
	 * - If argument is empty then getNextNumberedFileName() is used
325
	 *   with given directory, prefix and extension.
326
	 * - If argument doesn't already end with the given extension that
327
	 *   extension is appended.
328
	 * - If argument doesn't already include a directory, the given
329
	 *   directory is used (and created if required).
330
	 */
331
	[[nodiscard]] std::string parseCommandFileArgument(
332
		std::string_view argument, std::string_view directory,
333
		std::string_view prefix,   std::string_view extension);
334
335
	/**
336
	 * Get the name of the temp directory on the system.
337
	 * Typically /tmp on *nix and C:/WINDOWS/TEMP on windows
338
	 */
339
	[[nodiscard]] std::string getTempDir();
340
341
	/**
342
	 * Open a new file with a unique name in the provided directory
343
	 * @param directory directory in which to open the temp file
344
	 * @param filename [output param] the name of the resulting file
345
	 * @result pointer to the opened file
346
	 */
347
	[[nodiscard]] FILE_t openUniqueFile(const std::string& directory, std::string& filename);
348
349
} // namespace openmsx::FileOperations
350
351
#endif