Fcitx
standardpath.cpp
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "standardpath.h"
9 #include <dirent.h>
10 #include <fcntl.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #include <algorithm>
15 #include <atomic>
16 #include <cassert>
17 #include <cstdint>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <exception>
22 #include <functional>
23 #include <map>
24 #include <memory>
25 #include <optional>
26 #include <stdexcept>
27 #include <string>
28 #include <string_view>
29 #include <tuple>
30 #include <unordered_map>
31 #include <unordered_set>
32 #include <utility>
33 #include <vector>
34 #include "config.h" // IWYU pragma: keep
35 #include "environ.h"
36 #include "fs.h"
37 #include "macros.h"
38 #include "misc.h"
39 #include "misc_p.h"
40 #include "stringutils.h"
41 
42 #ifdef _WIN32
43 #include <io.h>
44 #include "utf8.h"
45 #endif
46 
47 #if __has_include(<paths.h>)
48 #include <paths.h>
49 #endif
50 
51 #ifndef O_ACCMODE
52 #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
53 #endif
54 
55 namespace fcitx {
56 
57 namespace {
58 bool isAbsolutePath(const std::string &path) {
59  return !path.empty() && path[0] == '/';
60 }
61 
62 std::string constructPath(const std::string &basepath,
63  const std::string &subpath) {
64  if (basepath.empty()) {
65  return {};
66  }
67  return fs::cleanPath(stringutils::joinPath(basepath, subpath));
68 }
69 
70 } // namespace
71 
72 StandardPathFile::~StandardPathFile() = default;
73 
74 int StandardPathFile::release() { return fd_.release(); }
75 
76 StandardPathTempFile::~StandardPathTempFile() { close(); }
77 
78 int StandardPathTempFile::release() { return fd_.release(); }
79 
80 void StandardPathTempFile::removeTemp() {
81  if (fd_.fd() >= 0) {
82  // close it
83  fd_.reset();
84  unlink(tempPath_.c_str());
85  }
86 }
87 
88 void StandardPathTempFile::close() {
89  if (fd_.fd() >= 0) {
90  // sync first.
91 #ifdef _WIN32
92  _commit(fd_.fd());
93 #else
94  fsync(fd_.fd());
95 #endif
96  fd_.reset();
97  if (rename(tempPath_.c_str(), path_.c_str()) < 0) {
98  unlink(tempPath_.c_str());
99  }
100  }
101 }
102 
104 public:
106  const std::string &packageName,
107  const std::unordered_map<std::string, std::string> &builtInPathMap,
108  bool skipBuiltInPath, bool skipUserPath)
109  : skipBuiltInPath_(skipBuiltInPath), skipUserPath_(skipUserPath) {
110  bool isFcitx = (packageName == "fcitx5");
111  // initialize user directory
112  configHome_ = defaultPath("XDG_CONFIG_HOME", ".config");
113  pkgconfigHome_ =
114  defaultPath((isFcitx ? "FCITX_CONFIG_HOME" : nullptr),
115  constructPath(configHome_, packageName).c_str());
116  configDirs_ = defaultPaths("XDG_CONFIG_DIRS", "/etc/xdg",
117  builtInPathMap, nullptr);
118  auto pkgconfigDirFallback = configDirs_;
119  for (auto &path : pkgconfigDirFallback) {
120  path = constructPath(path, packageName);
121  }
122  pkgconfigDirs_ =
123  defaultPaths((isFcitx ? "FCITX_CONFIG_DIRS" : nullptr),
124  stringutils::join(pkgconfigDirFallback, ":").c_str(),
125  builtInPathMap, nullptr);
126 
127  dataHome_ = defaultPath("XDG_DATA_HOME", ".local/share");
128  pkgdataHome_ =
129  defaultPath((isFcitx ? "FCITX_DATA_HOME" : nullptr),
130  constructPath(dataHome_, packageName).c_str());
131  dataDirs_ = defaultPaths("XDG_DATA_DIRS", "/usr/local/share:/usr/share",
132  builtInPathMap,
133  skipBuiltInPath_ ? nullptr : "datadir");
134  auto pkgdataDirFallback = dataDirs_;
135  for (auto &path : pkgdataDirFallback) {
136  path = constructPath(path, packageName);
137  }
138  pkgdataDirs_ = defaultPaths(
139  (isFcitx ? "FCITX_DATA_DIRS" : nullptr),
140  stringutils::join(pkgdataDirFallback, ":").c_str(), builtInPathMap,
141  skipBuiltInPath_ ? nullptr : "pkgdatadir");
142  cacheHome_ = defaultPath("XDG_CACHE_HOME", ".cache");
143  auto tmpdir = getEnvironment("TMPDIR");
144  runtimeDir_ =
145  defaultPath("XDG_RUNTIME_DIR",
146  (!tmpdir || tmpdir->empty()) ? "/tmp" : tmpdir->data());
147  // Though theoratically, this is also fcitxPath, we just simply don't
148  // use it here.
149  addonDirs_ = defaultPaths("FCITX_ADDON_DIRS", FCITX_INSTALL_ADDONDIR,
150  builtInPathMap, nullptr);
151 
152  syncUmask();
153  }
154 
155  std::string userPath(StandardPath::Type type) const {
156  if (skipUserPath_) {
157  return {};
158  }
159  switch (type) {
161  return configHome_;
163  return pkgconfigHome_;
165  return dataHome_;
167  return pkgdataHome_;
169  return cacheHome_;
171  return runtimeDir_;
172  default:
173  return {};
174  }
175  }
176 
177  std::vector<std::string> directories(StandardPath::Type type) const {
178  switch (type) {
180  return configDirs_;
182  return pkgconfigDirs_;
184  return dataDirs_;
186  return pkgdataDirs_;
188  return addonDirs_;
189  default:
190  return {};
191  }
192  }
193 
194  bool skipUser() const { return skipUserPath_; }
195  bool skipBuiltIn() const { return skipBuiltInPath_; }
196 
197  void syncUmask() {
198  // read umask, use 022 which is likely the default value, so less likely
199  // to mess things up.
200  mode_t old = ::umask(022);
201  // restore
202  ::umask(old);
203  umask_.store(old, std::memory_order_relaxed);
204  }
205 
206  mode_t umask() const { return umask_.load(std::memory_order_relaxed); }
207 
208 private:
209  // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
210  static std::string defaultPath(const char *env, const char *defaultPath) {
211  std::optional<std::string> cdir;
212  if (env) {
213  cdir = getEnvironment(env);
214  }
215  std::string dir;
216  if (cdir && !cdir->empty()) {
217  dir = *cdir;
218  } else {
219  // caller need to ensure HOME is not empty;
220  if (defaultPath[0] != '/') {
221  auto home = getEnvironment("HOME");
222  if (!home) {
223  throw std::runtime_error("Home is not set");
224  }
225  dir = stringutils::joinPath(*home, defaultPath);
226  } else {
227  if (env && strcmp(env, "XDG_RUNTIME_DIR") == 0) {
228 #ifdef _WIN32
229  dir = stringutils::joinPath(
230  defaultPath, stringutils::concat("fcitx-runtime"));
231 #else
232  dir = stringutils::joinPath(
233  defaultPath,
234  stringutils::concat("fcitx-runtime-", geteuid()));
235  if (!fs::isdir(dir)) {
236  if (mkdir(dir.c_str(), 0700) != 0) {
237  return {};
238  }
239  }
240 #endif
241  } else {
242  dir = defaultPath;
243  }
244  }
245  }
246 
247 #ifndef _WIN32
248  if (!dir.empty() && env && strcmp(env, "XDG_RUNTIME_DIR") == 0) {
249  struct stat buf;
250  if (stat(dir.c_str(), &buf) != 0 || buf.st_uid != geteuid() ||
251  (buf.st_mode & 0777) != S_IRWXU) {
252  return {};
253  }
254  }
255 #endif
256  return dir;
257  }
258 
259  static std::vector<std::string> defaultPaths(
260  const char *env, const char *defaultPath,
261  const std::unordered_map<std::string, std::string> &builtInPathMap,
262  const char *builtInPathType) {
263  std::vector<std::string> dirs;
264 
265  std::optional<std::string> dir;
266  if (env) {
267  dir = getEnvironment(env);
268  }
269  if (!dir) {
270  dir = defaultPath;
271  }
272  assert(dir.has_value());
273 
274  auto rawDirs = stringutils::split(*dir, ":");
275  for (auto &rawDir : rawDirs) {
276  rawDir = fs::cleanPath(rawDir);
277  }
278  std::unordered_set<std::string> uniqueDirs(rawDirs.begin(),
279  rawDirs.end());
280 
281  for (auto &s : rawDirs) {
282  auto iter = uniqueDirs.find(s);
283  if (iter != uniqueDirs.end()) {
284  uniqueDirs.erase(iter);
285  dirs.push_back(s);
286  }
287  }
288  if (builtInPathType) {
289  std::string builtInPath;
290  if (const auto *value =
291  findValue(builtInPathMap, builtInPathType)) {
292  builtInPath = *value;
293  } else {
294  builtInPath = StandardPath::fcitxPath(builtInPathType);
295  }
296  std::string path = fs::cleanPath(builtInPath);
297  if (!path.empty() &&
298  std::find(dirs.begin(), dirs.end(), path) == dirs.end()) {
299  dirs.push_back(path);
300  }
301  }
302 
303  return dirs;
304  }
305 
306  bool skipBuiltInPath_;
307  bool skipUserPath_;
308  std::string configHome_;
309  std::vector<std::string> configDirs_;
310  std::string pkgconfigHome_;
311  std::vector<std::string> pkgconfigDirs_;
312  std::string dataHome_;
313  std::vector<std::string> dataDirs_;
314  std::string pkgdataHome_;
315  std::vector<std::string> pkgdataDirs_;
316  std::string cacheHome_;
317  std::string runtimeDir_;
318  std::vector<std::string> addonDirs_;
319  std::atomic<mode_t> umask_;
320 };
321 
323  const std::string &packageName,
324  const std::unordered_map<std::string, std::string> &builtInPath,
325  bool skipBuiltInPath, bool skipUserPath)
326  : d_ptr(std::make_unique<StandardPathPrivate>(
327  packageName, builtInPath, skipBuiltInPath, skipUserPath)) {}
328 
329 StandardPath::StandardPath(bool skipFcitxPath, bool skipUserPath)
330  : StandardPath("fcitx5", {}, skipFcitxPath, skipUserPath) {}
331 
332 StandardPath::StandardPath(bool skipFcitxPath)
333  : StandardPath(skipFcitxPath, false) {}
334 
335 StandardPath::~StandardPath() = default;
336 
338  static bool skipFcitx = checkBoolEnvVar("SKIP_FCITX_PATH");
339  static bool skipUser = checkBoolEnvVar("SKIP_FCITX_USER_PATH");
340  static StandardPath globalPath(skipFcitx, skipUser);
341  return globalPath;
342 }
343 
344 const char *StandardPath::fcitxPath(const char *path) {
345  if (!path) {
346  return nullptr;
347  }
348 
349  static const std::unordered_map<std::string, std::string> pathMap = {
350  std::make_pair<std::string, std::string>("datadir",
351  FCITX_INSTALL_DATADIR),
352  std::make_pair<std::string, std::string>("pkgdatadir",
353  FCITX_INSTALL_PKGDATADIR),
354  std::make_pair<std::string, std::string>("libdir",
355  FCITX_INSTALL_LIBDIR),
356  std::make_pair<std::string, std::string>("bindir",
357  FCITX_INSTALL_BINDIR),
358  std::make_pair<std::string, std::string>("localedir",
359  FCITX_INSTALL_LOCALEDIR),
360  std::make_pair<std::string, std::string>("addondir",
361  FCITX_INSTALL_ADDONDIR),
362  std::make_pair<std::string, std::string>("libdatadir",
363  FCITX_INSTALL_LIBDATADIR),
364  std::make_pair<std::string, std::string>("libexecdir",
365  FCITX_INSTALL_LIBEXECDIR),
366  };
367 
368  auto iter = pathMap.find(path);
369  if (iter != pathMap.end()) {
370  return iter->second.c_str();
371  }
372 
373  return nullptr;
374 }
375 
376 std::string StandardPath::fcitxPath(const char *path, const char *subPath) {
377  return stringutils::joinPath(fcitxPath(path), subPath);
378 }
379 
380 std::string StandardPath::userDirectory(Type type) const {
381  FCITX_D();
382  return d->userPath(type);
383 }
384 
385 std::vector<std::string> StandardPath::directories(Type type) const {
386  FCITX_D();
387  return d->directories(type);
388 }
389 
391  Type type,
392  const std::function<bool(const std::string &path, bool user)> &scanner)
393  const {
394  FCITX_D();
395  std::string userDir = d->userPath(type);
396  std::vector<std::string> list = d->directories(type);
397  if (userDir.empty() && list.empty()) {
398  return;
399  }
400  scanDirectories(userDir, list, scanner);
401 }
402 
404  const std::string &userDir, const std::vector<std::string> &directories,
405  const std::function<bool(const std::string &path, bool user)> &scanner)
406  const {
407  std::string_view userDirView(userDir);
408  FCITX_D();
409  if (d->skipUser()) {
410  userDirView = "";
411  }
412 
413  if (userDirView.empty() && directories.empty()) {
414  return;
415  }
416 
417  size_t len = (!userDirView.empty() ? 1 : 0) + directories.size();
418 
419  for (size_t i = 0; i < len; i++) {
420  bool isUser = false;
421  std::string dirBasePath;
422  if (!userDirView.empty()) {
423  isUser = (i == 0);
424  dirBasePath = isUser ? userDirView : directories[i - 1];
425  } else {
426  dirBasePath = directories[i];
427  }
428 
429  dirBasePath = fs::cleanPath(dirBasePath);
430  if (!scanner(dirBasePath, isUser)) {
431  return;
432  }
433  }
434 }
435 
437  Type type, const std::string &path,
438  const std::function<bool(const std::string &fileName,
439  const std::string &dir, bool user)> &scanner)
440  const {
441  auto scanDir = [scanner](const std::string &fullPath, bool isUser) {
442  UniqueCPtr<DIR, closedir> scopedDir{opendir(fullPath.c_str())};
443  if (auto *dir = scopedDir.get()) {
444  struct dirent *drt;
445  while ((drt = readdir(dir)) != nullptr) {
446  if (strcmp(drt->d_name, ".") == 0 ||
447  strcmp(drt->d_name, "..") == 0) {
448  continue;
449  }
450 
451  if (!scanner(drt->d_name, fullPath, isUser)) {
452  return false;
453  }
454  }
455  }
456  return true;
457  };
458  if (isAbsolutePath(path)) {
459  scanDir(path, false);
460  } else {
462  type, [&path, &scanDir](const std::string &dirPath, bool isUser) {
463  auto fullPath = constructPath(dirPath, path);
464  return scanDir(fullPath, isUser);
465  });
466  }
467 }
468 
469 std::string StandardPath::locate(Type type, const std::string &path) const {
470  std::string retPath;
471  if (isAbsolutePath(path)) {
472  if (fs::isreg(path)) {
473  retPath = path;
474  }
475  } else {
476  scanDirectories(type,
477  [&retPath, &path](const std::string &dirPath, bool) {
478  std::string fullPath = constructPath(dirPath, path);
479  if (!fs::isreg(fullPath)) {
480  return true;
481  }
482  retPath = std::move(fullPath);
483  return false;
484  });
485  }
486  return retPath;
487 }
488 
489 std::vector<std::string>
490 StandardPath::locateAll(Type type, const std::string &path) const {
491  std::vector<std::string> retPaths;
492  if (isAbsolutePath(path)) {
493  if (fs::isreg(path)) {
494  retPaths.push_back(path);
495  }
496  } else {
497  scanDirectories(type,
498  [&retPaths, &path](const std::string &dirPath, bool) {
499  auto fullPath = constructPath(dirPath, path);
500  if (fs::isreg(fullPath)) {
501  retPaths.push_back(fullPath);
502  }
503  return true;
504  });
505  }
506  return retPaths;
507 }
508 
509 StandardPathFile StandardPath::open(Type type, const std::string &path,
510  int flags) const {
511  int retFD = -1;
512  std::string fdPath;
513  if (isAbsolutePath(path)) {
514  int fd = ::open(path.c_str(), flags);
515  if (fd >= 0) {
516  retFD = fd;
517  fdPath = path;
518  }
519  } else {
520  scanDirectories(type, [flags, &retFD, &fdPath,
521  &path](const std::string &dirPath, bool) {
522  auto fullPath = constructPath(dirPath, path);
523  int fd = ::open(fullPath.c_str(), flags);
524  if (fd < 0) {
525  return true;
526  }
527  retFD = fd;
528  fdPath = std::move(fullPath);
529  return false;
530  });
531  }
532  return {retFD, fdPath};
533 }
534 
535 StandardPathFile StandardPath::openUser(Type type, const std::string &path,
536  int flags) const {
537  std::string fullPath;
538  if (isAbsolutePath(path)) {
539  fullPath = path;
540  } else {
541  auto dirPath = userDirectory(type);
542  if (dirPath.empty()) {
543  return {};
544  }
545  fullPath = constructPath(dirPath, path);
546  }
547 
548  // Try ensure directory exists if we want to write.
549  if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
550  if (!fs::makePath(fs::dirName(fullPath))) {
551  return {};
552  }
553  }
554 
555  int fd = ::open(fullPath.c_str(), flags, 0600);
556  if (fd >= 0) {
557  return {fd, fullPath};
558  }
559  return {};
560 }
561 
562 StandardPathFile StandardPath::openSystem(Type type, const std::string &path,
563  int flags) const {
564  int retFD = -1;
565  std::string fdPath;
566  if (isAbsolutePath(path)) {
567  int fd = ::open(path.c_str(), flags);
568  if (fd >= 0) {
569  retFD = fd;
570  fdPath = path;
571  }
572  } else {
573  scanDirectories(type, [flags, &retFD, &fdPath,
574  &path](const std::string &dirPath, bool user) {
575  if (user) {
576  return true;
577  }
578  auto fullPath = constructPath(dirPath, path);
579  int fd = ::open(fullPath.c_str(), flags);
580  if (fd < 0) {
581  return true;
582  }
583  retFD = fd;
584  fdPath = std::move(fullPath);
585  return false;
586  });
587  }
588  return {retFD, fdPath};
589 }
590 
591 std::vector<StandardPathFile> StandardPath::openAll(StandardPath::Type type,
592  const std::string &path,
593  int flags) const {
594  std::vector<StandardPathFile> result;
595  if (isAbsolutePath(path)) {
596  int fd = ::open(path.c_str(), flags);
597  if (fd >= 0) {
598  result.emplace_back(fd, path);
599  }
600  } else {
602  type, [flags, &result, &path](const std::string &dirPath, bool) {
603  auto fullPath = constructPath(dirPath, path);
604  int fd = ::open(fullPath.c_str(), flags);
605  if (fd < 0) {
606  return true;
607  }
608  result.emplace_back(fd, fullPath);
609  return true;
610  });
611  }
612  return result;
613 }
614 
616 StandardPath::openUserTemp(Type type, const std::string &pathOrig) const {
617  std::string path = pathOrig + "_XXXXXX";
618  std::string fullPath;
619  std::string fullPathOrig;
620  if (isAbsolutePath(pathOrig)) {
621  fullPath = std::move(path);
622  fullPathOrig = pathOrig;
623  } else {
624  auto dirPath = userDirectory(type);
625  if (dirPath.empty()) {
626  return {};
627  }
628  fullPath = constructPath(dirPath, path);
629  fullPathOrig = constructPath(dirPath, pathOrig);
630  }
631  if (fs::makePath(fs::dirName(fullPath))) {
632  std::vector<char> cPath(fullPath.data(),
633  fullPath.data() + fullPath.size() + 1);
634  int fd = mkstemp(cPath.data());
635  if (fd >= 0) {
636  return {fd, fullPathOrig, cPath.data()};
637  }
638  }
639  return {};
640 }
641 
642 bool StandardPath::safeSave(Type type, const std::string &pathOrig,
643  const std::function<bool(int)> &callback) const {
644  FCITX_D();
645  auto file = openUserTemp(type, pathOrig);
646  if (!file.isValid()) {
647  return false;
648  }
649  try {
650  if (callback(file.fd())) {
651 #ifdef _WIN32
652  auto wfile = utf8::UTF8ToUTF16(file.tempPath());
653  ::_wchmod(wfile.data(), 0666 & ~(d->umask()));
654 #else
655  // close it
656  fchmod(file.fd(), 0666 & ~(d->umask()));
657 #endif
658  return true;
659  }
660  } catch (const std::exception &) {
661  }
662  file.removeTemp();
663  return false;
664 }
665 
666 std::map<std::string, std::string> StandardPath::locateWithFilter(
667  Type type, const std::string &path,
668  std::function<bool(const std::string &path, const std::string &dir,
669  bool user)>
670  filter) const {
671  std::map<std::string, std::string> result;
672  scanFiles(type, path,
673  [&result, &filter](const std::string &path,
674  const std::string &dir, bool isUser) {
675  if (!result.contains(path) && filter(path, dir, isUser)) {
676  auto fullPath = constructPath(dir, path);
677  if (fs::isreg(fullPath)) {
678  result.emplace(path, std::move(fullPath));
679  }
680  }
681  return true;
682  });
683 
684  return result;
685 }
686 
687 StandardPathFileMap StandardPath::multiOpenFilter(
688  Type type, const std::string &path, int flags,
689  std::function<bool(const std::string &path, const std::string &dir,
690  bool user)>
691  filter) const {
692  StandardPathFileMap result;
693  scanFiles(type, path,
694  [&result, flags, &filter](const std::string &path,
695  const std::string &dir, bool isUser) {
696  if (!result.contains(path) && filter(path, dir, isUser)) {
697  auto fullPath = constructPath(dir, path);
698  int fd = ::open(fullPath.c_str(), flags);
699  if (fd >= 0) {
700  result.emplace(std::piecewise_construct,
701  std::forward_as_tuple(path),
702  std::forward_as_tuple(fd, fullPath));
703  }
704  }
705  return true;
706  });
707 
708  return result;
709 }
710 
711 StandardPathFilesMap StandardPath::multiOpenAllFilter(
712  Type type, const std::string &path, int flags,
713  std::function<bool(const std::string &path, const std::string &dir,
714  bool user)>
715  filter) const {
716  StandardPathFilesMap result;
717  scanFiles(type, path,
718  [&result, flags, &filter](const std::string &path,
719  const std::string &dir, bool isUser) {
720  if (filter(path, dir, isUser)) {
721  auto fullPath = constructPath(dir, path);
722  int fd = ::open(fullPath.c_str(), flags);
723  if (fd >= 0) {
724  result[path].emplace_back(fd, fullPath);
725  }
726  }
727  return true;
728  });
729 
730  return result;
731 }
732 
733 int64_t StandardPath::timestamp(Type type, const std::string &path) const {
734  if (isAbsolutePath(path)) {
735  return fs::modifiedTime(path);
736  }
737 
738  int64_t timestamp = 0;
740  type, [&timestamp, &path](const std::string &dirPath, bool) {
741  auto fullPath = constructPath(dirPath, path);
742  timestamp = std::max(timestamp, fs::modifiedTime(fullPath));
743  return true;
744  });
745  return timestamp;
746 }
747 
748 std::string StandardPath::findExecutable(const std::string &name) {
749  if (name.empty()) {
750  return "";
751  }
752 
753  if (name[0] == '/') {
754  return fs::isexe(name) ? name : "";
755  }
756 
757  std::string sEnv;
758  if (auto pEnv = getEnvironment("PATH")) {
759  sEnv = std::move(*pEnv);
760  } else {
761 #if defined(_PATH_DEFPATH)
762  sEnv = _PATH_DEFPATH;
763 #elif defined(_CS_PATH)
764  size_t n = confstr(_CS_PATH, nullptr, 0);
765  if (n) {
766  std::vector<char> data;
767  data.resize(n + 1);
768  confstr(_CS_PATH, data.data(), data.size());
769  data.push_back('\0');
770  sEnv = data.data();
771  }
772 #endif
773  }
774  auto paths = stringutils::split(sEnv, ":");
775  for (auto &path : paths) {
776  path = fs::cleanPath(path);
777  auto fullPath = constructPath(path, name);
778  if (!fullPath.empty() && fs::isexe(fullPath)) {
779  return fullPath;
780  }
781  }
782  return "";
783 }
784 
785 bool StandardPath::hasExecutable(const std::string &name) {
786  return !findExecutable(name).empty();
787 }
788 
789 void StandardPath::syncUmask() const { d_ptr->syncUmask(); }
790 
792  FCITX_D();
793  return d->skipBuiltIn();
794 }
795 
797  FCITX_D();
798  return d->skipUser();
799 }
800 
801 } // namespace fcitx
StandardPathTempFile openUserTemp(Type type, const std::string &pathOrig) const
Open user file, but create file with mktemp.
static bool hasExecutable(const std::string &name)
Check if certain executable presents in PATH.
StandardPath(const std::string &packageName, const std::unordered_map< std::string, std::string > &builtInPath, bool skipBuiltInPath, bool skipUserPath)
Allow to construct a StandardPath with customized internal value.
void scanFiles(Type type, const std::string &path, const std::function< bool(const std::string &path, const std::string &dir, bool user)> &scanner) const
Scan files scan file under [directory]/[path].
Simple file system related API for checking file status.
bool isexe(const std::string &path)
check whether path is an executable regular file.
Definition: fs.cpp:88
bool isreg(const std::string &path)
check whether path is a regular file.
Definition: fs.cpp:82
Definition: action.cpp:17
std::vector< StandardPathFile > openAll(Type type, const std::string &path, int flags) const
Open all files match the first [directory]/[path].
Definition: matchrule.h:78
bool skipBuiltInPath() const
Whether this StandardPath is configured to Skip built-in path.
bool isdir(const std::string &path)
check whether path is a directory.
Definition: fs.cpp:76
static const StandardPath & global()
Return the global instance of StandardPath.
std::vector< std::string > directories(Type type) const
Get all directories in the order of priority.
StandardPathFile openUser(Type type, const std::string &path, int flags) const
Open the user file.
C++ Utility functions for handling utf8 strings.
StandardPathFile openSystem(Type type, const std::string &path, int flags) const
Open the non-user file.
Utility class that wraps around UnixFD.
Definition: standardpath.h:153
std::vector< std::string > split(std::string_view str, std::string_view delim, SplitBehavior behavior)
Split the string by delim.
std::map< std::string, std::string > locateWithFilter(Type type, const std::string &path, std::function< bool(const std::string &path, const std::string &dir, bool user)> filter) const
Locate all files match the filter under first [directory]/[path].
static std::string findExecutable(const std::string &name)
Check if certain executable presents in PATH.
static const char * fcitxPath(const char *path)
Return fcitx specific path defined at compile time.
std::vector< std::string > locateAll(Type type, const std::string &path) const
list all matched files.
bool safeSave(Type type, const std::string &pathOrig, const std::function< bool(int)> &callback) const
Save the file safely with write and rename to make sure the operation is atomic.
StandardPathFilesMap multiOpenAllFilter(Type type, const std::string &path, int flags, std::function< bool(const std::string &path, const std::string &dir, bool user)> filter) const
Open all files match the filter under all [directory]/[path].
void syncUmask() const
Sync system umask to internal state.
int fd() const noexcept
Get the internal fd.
Definition: unixfd.cpp:41
std::string dirName(const std::string &path)
Get directory name of path.
Definition: fs.cpp:192
bool skipUserPath() const
Whether this StandardPath is configured to Skip user path.
std::string locate(Type type, const std::string &path) const
Check if a file exists.
Utility classes to handle XDG file path.
bool makePath(const std::filesystem::path &path)
Create directory recursively.
Definition: fs.cpp:179
Utility class to open, locate, list files based on XDG standard.
Definition: standardpath.h:181
String handle utilities.
void reset() noexcept
Clear the FD and close it.
Definition: unixfd.cpp:71
int64_t modifiedTime(const std::filesystem::path &path)
Return modified time in seconds of given path.
Definition: fs.cpp:271
std::string join(Iter start, Iter end, T &&delim)
Join a range of string with delim.
Definition: stringutils.h:108
Type
Enum for location type.
Definition: standardpath.h:184
void scanDirectories(Type type, const std::function< bool(const std::string &path, bool user)> &scanner) const
Scan the directories of given type.
StandardPathFile open(Type type, const std::string &path, int flags) const
Open the first matched and succeeded file.
StandardPathFileMap multiOpenFilter(Type type, const std::string &path, int flags, std::function< bool(const std::string &path, const std::string &dir, bool user)> filter) const
Open all files match the filter under first [directory]/[path].
std::string userDirectory(Type type) const
Get user writable directory for given type.
addon shared library dir.
std::string cleanPath(const std::string &path)
Get the clean path by removing . , .. , and duplicate / in the path.
Definition: fs.cpp:104
File descriptor wrapper that handles file descriptor and rename automatically.
Definition: standardpath.h:127
int release() noexcept
Get the internal fd and release the ownership.
Definition: unixfd.cpp:81