Fcitx
standardpath.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_UTILS_STANDARDPATH_H_
8 #define _FCITX_UTILS_STANDARDPATH_H_
9 
10 /// \addtogroup FcitxUtils
11 /// \{
12 /// \file
13 /// \brief Utility classes to handle XDG file path.
14 ///
15 /// Example:
16 /// \code{.cpp}
17 /// auto files = path.multiOpenAll(StandardPath::Type::PkgData, "inputmethod",
18 /// O_RDONLY, filter::Suffix(".conf"));
19 /// \endcode
20 /// Open all files under $XDG_CONFIG_{HOME,DIRS}/fcitx5/inputmethod/*.conf.
21 
22 #include <cstdint>
23 #include <functional>
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29 #include <fcitx-utils/fcitxutils_export.h>
30 #include <fcitx-utils/log.h>
31 #include <fcitx-utils/macros.h>
33 #include <fcitx-utils/unixfd.h>
34 
35 namespace fcitx {
36 
37 namespace filter {
38 
39 /// \brief Filter class to chain sub filters together.
40 template <typename... Types>
41 class Chainer;
42 
43 template <>
44 class Chainer<> {
45 public:
46  bool operator()(const std::string & /*unused*/,
47  const std::string & /*unused*/, bool /*unused*/) {
48  return true;
49  }
50 };
51 
52 template <typename First, typename... Rest>
53 class Chainer<First, Rest...> : Chainer<Rest...> {
54  using super_class = Chainer<Rest...>;
55 
56 public:
57  Chainer(First first, Rest... rest)
58  : super_class(std::move(rest)...), filter(std::move(first)) {}
59 
60  bool operator()(const std::string &path, const std::string &dir,
61  bool user) {
62  if (!filter(path, dir, user)) {
63  return false;
64  }
65  return super_class::operator()(path, dir, user);
66  }
67 
68 private:
69  First filter;
70 };
71 
72 /// \brief Filter class that revert the sub filter result.
73 template <typename T>
74 struct NotFilter {
75  NotFilter(T filter_) : filter(filter_) {}
76 
77  bool operator()(const std::string &path, const std::string &dir,
78  bool isUser) {
79  return !filter(path, dir, isUser);
80  }
81 
82 private:
83  T filter;
84 };
85 
86 template <typename T>
87 NotFilter<T> Not(T t) {
88  return {t};
89 }
90 
91 /// \brief Filter class that filters based on user file.
92 struct FCITXUTILS_DEPRECATED_EXPORT User {
93  bool operator()(const std::string & /*unused*/,
94  const std::string & /*unused*/, bool isUser) {
95  return isUser;
96  }
97 };
98 
99 /// \brief Filter class that filters file based on prefix
100 struct FCITXUTILS_DEPRECATED_EXPORT Prefix {
101  Prefix(const std::string &prefix_) : prefix(prefix_) {}
102 
103  bool operator()(const std::string &path, const std::string & /*unused*/,
104  bool /*unused*/) const {
105  return path.starts_with(prefix);
106  }
107 
108  std::string prefix;
109 };
110 
111 /// \brief Filter class that filters file based on suffix
112 struct FCITXUTILS_DEPRECATED_EXPORT Suffix {
113  Suffix(const std::string &suffix_) : suffix(suffix_) {}
114 
115  bool operator()(const std::string &path, const std::string & /*unused*/,
116  bool /*unused*/) const {
117  return path.ends_with(suffix);
118  }
119 
120  std::string suffix;
121 };
122 } // namespace filter
123 
124 /// \brief File descriptor wrapper that handles file descriptor and rename
125 /// automatically.
126 class FCITXUTILS_DEPRECATED_EXPORT StandardPathTempFile {
127 public:
128  StandardPathTempFile(int fd = -1, const std::string &realFile = {},
129  const std::string &tempPath = {})
130  : fd_(UnixFD::own(fd)), path_(realFile), tempPath_(tempPath) {}
131  StandardPathTempFile(StandardPathTempFile &&other) = default;
132  virtual ~StandardPathTempFile();
133 
134  int fd() const { return fd_.fd(); }
135  bool isValid() const { return fd_.isValid(); }
136 
137  const std::string &path() const { return path_; }
138  const std::string &tempPath() const { return tempPath_; }
139 
140  int release();
141  void close();
142  void removeTemp();
143 
144 private:
145  UnixFD fd_;
146  std::string path_;
147  std::string tempPath_;
148 };
149 
150 /// \brief Utility class that wraps around UnixFD. It also contains the actual
151 /// file name information.
152 class FCITXUTILS_DEPRECATED_EXPORT StandardPathFile {
153 public:
154  StandardPathFile(int fd = -1, const std::string &path = {})
155  : fd_(UnixFD::own(fd)), path_(path) {}
156  StandardPathFile(StandardPathFile &&other) = default;
157  virtual ~StandardPathFile();
158 
159  StandardPathFile &operator=(StandardPathFile &&other) = default;
160 
161  int fd() const { return fd_.fd(); }
162  bool isValid() const { return fd_.isValid(); }
163 
164  const std::string &path() const { return path_; }
165 
166  int release();
167 
168 private:
169  UnixFD fd_;
170  std::string path_;
171 };
172 
173 class StandardPathPrivate;
174 
175 using StandardPathFileMap = std::map<std::string, StandardPathFile>;
176 using StandardPathFilesMap =
177  std::map<std::string, std::vector<StandardPathFile>>;
178 
179 /// \brief Utility class to open, locate, list files based on XDG standard.
180 class FCITXUTILS_DEPRECATED_EXPORT StandardPath {
181 public:
182  /// \brief Enum for location type.
183  enum class Type {
184  /// Xdg Config dir
185  Config,
186  /// Xdg Config dir/fcitx5
187  PkgConfig,
188  /// Xdg data dir
189  Data,
190  /// Xdg cache dir
191  Cache,
192  /// Xdg runtime dir
193  Runtime,
194  /// addon shared library dir.
195  Addon,
196  /// Xdg data dir/fcitx5
197  PkgData
198  };
199 
200  /**
201  * Allow to construct a StandardPath with customized internal value.
202  *
203  * @param packageName the sub directory under other paths.
204  * @param builtInPath this will override the value from fcitxPath.
205  * @param skipBuiltInPath skip built-in path
206  * @param skipUserPath skip user path, useful when doing readonly-test.
207  * @since 5.1.9
208  */
209  explicit StandardPath(
210  const std::string &packageName,
211  const std::unordered_map<std::string, std::string> &builtInPath,
212  bool skipBuiltInPath, bool skipUserPath);
213 
214  explicit StandardPath(bool skipFcitxPath, bool skipUserPath);
215  explicit StandardPath(bool skipFcitxPath = false);
216  virtual ~StandardPath();
217 
218  /// \brief Return the global instance of StandardPath.
219  ///
220  /// return a global default so we can share it, C++11 static initialization
221  /// is thread-safe
222  static const StandardPath &global();
223 
224  /// \brief Return fcitx specific path defined at compile time.
225  ///
226  /// Currently, available value of fcitxPath are:
227  /// datadir, pkgdatadir, libdir, bindir, localedir, addondir, libdatadir.
228  /// Otherwise it will return nullptr.
229  static const char *fcitxPath(const char *path);
230 
231  /// \brief Return a path under specific fcitxPath directory.
232  /// path is required to be a valid value.
233  static std::string fcitxPath(const char *path, const char *subPath);
234 
235  /// \brief Scan the directories of given type.
236  ///
237  /// Callback returns true to continue the scan.
238  void scanDirectories(Type type,
239  const std::function<bool(const std::string &path,
240  bool user)> &scanner) const;
241 
242  /// \brief Scan the given directories.
243  ///
244  /// Callback returns true to continue the scan.
245  /// @since 5.0.4
246  void scanDirectories(
247  const std::string &userDir, const std::vector<std::string> &directories,
248  const std::function<bool(const std::string &path, bool user)> &scanner)
249  const;
250 
251  /// \brief Scan files scan file under [directory]/[path]
252  /// \param path sub directory name.
253  void scanFiles(Type type, const std::string &path,
254  const std::function<bool(const std::string &path,
255  const std::string &dir, bool user)>
256  &scanner) const;
257 
258  /// \brief Get user writable directory for given type.
259  std::string userDirectory(Type type) const;
260 
261  /// \brief Get all directories in the order of priority.
262  std::vector<std::string> directories(Type type) const;
263 
264  /// \brief Check if a file exists.
265  std::string locate(Type type, const std::string &path) const;
266 
267  /// \brief list all matched files.
268  std::vector<std::string> locateAll(Type type,
269  const std::string &path) const;
270 
271  /// \brief Open the first matched and succeeded file.
272  ///
273  /// This function is preferred over locale if you just want to open the
274  /// file. Then you can avoid the race condition.
275  /// \see openUser()
276  StandardPathFile open(Type type, const std::string &path, int flags) const;
277 
278  /// \brief Open the user file.
279  StandardPathFile openUser(Type type, const std::string &path,
280  int flags) const;
281 
282  /**
283  * \brief Open the non-user file.
284  *
285  * \since 5.0.6
286  */
287  StandardPathFile openSystem(Type type, const std::string &path,
288  int flags) const;
289 
290  /// \brief Open user file, but create file with mktemp.
291  StandardPathTempFile openUserTemp(Type type,
292  const std::string &pathOrig) const;
293 
294  /**
295  * \brief Save the file safely with write and rename to make sure the
296  * operation is atomic.
297  *
298  * Callback shall not close the file descriptor. If the API you are using
299  * does cannot do that, you may use UnixFD to help you dup it first.
300  *
301  * \param callback Callback function that accept a file descriptor and
302  * return whether the save if success or not.
303  */
304  bool safeSave(Type type, const std::string &pathOrig,
305  const std::function<bool(int)> &callback) const;
306 
307  /**
308  * \brief Locate all files match the filter under first [directory]/[path].
309  *
310  * Prefer this function over multiOpenFilter, if there could be too many
311  * files that exceeds the systems file descriptor limit.
312  * @since 5.1.10
313  * @see multiOpenFilter
314  */
315  std::map<std::string, std::string>
316  locateWithFilter(Type type, const std::string &path,
317  std::function<bool(const std::string &path,
318  const std::string &dir, bool user)>
319  filter) const;
320 
321  /**
322  * \brief Locate all files match the filter under first [directory]/[path].
323  *
324  * You may pass multiple filter to it.
325  * Prefer this function over multiOpen, if there could be too many
326  * files that exceeds the systems file descriptor limit.
327  * @since 5.1.10
328  * @see multiOpen
329  */
330  template <typename Arg1, typename... Args>
331  std::map<std::string, std::string>
332  locate(Type type, const std::string &path, Arg1 arg1, Args... args) const {
333  return locateWithFilter(type, path,
335  std::move(arg1), std::move(args)...));
336  }
337 
338  /// \brief Open all files match the first [directory]/[path].
339  std::vector<StandardPathFile> openAll(Type type, const std::string &path,
340  int flags) const;
341 
342  /// \brief Open all files match the filter under first [directory]/[path].
343  StandardPathFileMap
344  multiOpenFilter(Type type, const std::string &path, int flags,
345  std::function<bool(const std::string &path,
346  const std::string &dir, bool user)>
347  filter) const;
348 
349  /// \brief Open all files match the filter under first [directory]/[path].
350  ///
351  /// You may pass multiple filter to it.
352  template <typename... Args>
353  StandardPathFileMap multiOpen(Type type, const std::string &path, int flags,
354  Args... args) const {
355  return multiOpenFilter(type, path, flags,
356  filter::Chainer<Args...>(std::move(args)...));
357  }
358 
359  /// \brief Open all files match the filter under all [directory]/[path].
360  StandardPathFilesMap
361  multiOpenAllFilter(Type type, const std::string &path, int flags,
362  std::function<bool(const std::string &path,
363  const std::string &dir, bool user)>
364  filter) const;
365 
366  /// \brief Open all files match the filter under all [directory]/[path].
367  ///
368  /// You may pass multiple filter to it.
369  template <typename... Args>
370  StandardPathFilesMap multiOpenAll(Type type, const std::string &path,
371  int flags, Args... args) const {
372  return multiOpenAllFilter(type, path, flags,
373  filter::Chainer<Args...>(args...));
374  }
375 
376  int64_t timestamp(Type type, const std::string &path) const;
377 
378  /**
379  * Check if certain executable presents in PATH.
380  *
381  * If the file presents, return the absolute PATH. If name is absolute path,
382  * check the absolute path instead of using PATH.
383  *
384  * @since 5.0.18
385  */
386  static std::string findExecutable(const std::string &name);
387 
388  /**
389  * Check if certain executable presents in PATH.
390  *
391  * @see findExecutable *
392  * @since 5.0.18
393  */
394  static bool hasExecutable(const std::string &name);
395 
396  /**
397  * Sync system umask to internal state. This will affect the file
398  * permission created by safeSave.
399  *
400  * @see safeSave
401  * @since 5.1.2
402  */
403  void syncUmask() const;
404 
405  /**
406  * Whether this StandardPath is configured to Skip built-in path.
407  *
408  * Built-in path is usually configured at build time, hardcoded.
409  * In portable environment (Install prefix is not fixed), this should be
410  * set to false.
411  *
412  * @since 5.1.9
413  */
414  bool skipBuiltInPath() const;
415 
416  /**
417  * Whether this StandardPath is configured to Skip user path.
418  *
419  * @since 5.1.9
420  */
421  bool skipUserPath() const;
422 
423 private:
424  std::unique_ptr<StandardPathPrivate> d_ptr;
425  FCITX_DECLARE_PRIVATE(StandardPath);
426 };
427 
428 static inline LogMessageBuilder &operator<<(LogMessageBuilder &builder,
429  const StandardPathFile &file) {
430  builder << "StandardPathFile(fd=" << file.fd() << ",path=" << file.path()
431  << ")";
432  return builder;
433 }
434 
435 template <>
437 public:
439 
440  static constexpr StandardPathsType convert(StandardPath::Type type) {
441  switch (type) {
443  return StandardPathsType::Config;
445  return StandardPathsType::PkgConfig;
447  return StandardPathsType::Data;
449  return StandardPathsType::Cache;
451  return StandardPathsType::Runtime;
453  return StandardPathsType::Addon;
455  return StandardPathsType::PkgData;
456  default:
457  return StandardPathsType::Config;
458  }
459  }
460 };
461 
462 } // namespace fcitx
463 
464 #endif // _FCITX_UTILS_STANDARDPATH_H_
Class wrap around the unix fd.
Definition: unixfd.h:22
StandardPathsType
Enum for location type.
Definition: standardpaths.h:41
Definition: action.cpp:17
Filter class that filters based on user file.
Definition: standardpath.h:92
Utility class that wraps around UnixFD.
Definition: standardpath.h:152
Filter class to chain sub filters together.
Definition: standardpath.h:41
Filter class that revert the sub filter result.
Definition: standardpath.h:74
Filter class that filters file based on prefix.
Definition: standardpath.h:100
Utility class to handle unix file descriptor.
New Utility classes to handle application specific path.
Filter class that filters file based on suffix.
Definition: standardpath.h:112
static UnixFD own(int fd)
Create a UnixFD by owning the fd.
Definition: unixfd.h:42
std::map< std::string, std::string > locate(Type type, const std::string &path, Arg1 arg1, Args... args) const
Locate all files match the filter under first [directory]/[path].
Definition: standardpath.h:332
Utility class to open, locate, list files based on XDG standard.
Definition: standardpath.h:180
StandardPathFilesMap multiOpenAll(Type type, const std::string &path, int flags, Args... args) const
Open all files match the filter under all [directory]/[path].
Definition: standardpath.h:370
Type
Enum for location type.
Definition: standardpath.h:183
Log utilities.
StandardPathFileMap multiOpen(Type type, const std::string &path, int flags, Args... args) const
Open all files match the filter under first [directory]/[path].
Definition: standardpath.h:353
addon shared library dir.
File descriptor wrapper that handles file descriptor and rename automatically.
Definition: standardpath.h:126