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