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() && std::ranges::find(dirs, path) == dirs.end()) {
298  dirs.push_back(path);
299  }
300  }
301 
302  return dirs;
303  }
304 
305  bool skipBuiltInPath_;
306  bool skipUserPath_;
307  std::string configHome_;
308  std::vector<std::string> configDirs_;
309  std::string pkgconfigHome_;
310  std::vector<std::string> pkgconfigDirs_;
311  std::string dataHome_;
312  std::vector<std::string> dataDirs_;
313  std::string pkgdataHome_;
314  std::vector<std::string> pkgdataDirs_;
315  std::string cacheHome_;
316  std::string runtimeDir_;
317  std::vector<std::string> addonDirs_;
318  std::atomic<mode_t> umask_;
319 };
320 
322  const std::string &packageName,
323  const std::unordered_map<std::string, std::string> &builtInPath,
324  bool skipBuiltInPath, bool skipUserPath)
325  : d_ptr(std::make_unique<StandardPathPrivate>(
326  packageName, builtInPath, skipBuiltInPath, skipUserPath)) {}
327 
328 StandardPath::StandardPath(bool skipFcitxPath, bool skipUserPath)
329  : StandardPath("fcitx5", {}, skipFcitxPath, skipUserPath) {}
330 
331 StandardPath::StandardPath(bool skipFcitxPath)
332  : StandardPath(skipFcitxPath, false) {}
333 
334 StandardPath::~StandardPath() = default;
335 
337  static bool skipFcitx = checkBoolEnvVar("SKIP_FCITX_PATH");
338  static bool skipUser = checkBoolEnvVar("SKIP_FCITX_USER_PATH");
339  static StandardPath globalPath(skipFcitx, skipUser);
340  return globalPath;
341 }
342 
343 const char *StandardPath::fcitxPath(const char *path) {
344  if (!path) {
345  return nullptr;
346  }
347 
348  static const std::unordered_map<std::string, std::string> pathMap = {
349  std::make_pair<std::string, std::string>("datadir",
350  FCITX_INSTALL_DATADIR),
351  std::make_pair<std::string, std::string>("pkgdatadir",
352  FCITX_INSTALL_PKGDATADIR),
353  std::make_pair<std::string, std::string>("libdir",
354  FCITX_INSTALL_LIBDIR),
355  std::make_pair<std::string, std::string>("bindir",
356  FCITX_INSTALL_BINDIR),
357  std::make_pair<std::string, std::string>("localedir",
358  FCITX_INSTALL_LOCALEDIR),
359  std::make_pair<std::string, std::string>("addondir",
360  FCITX_INSTALL_ADDONDIR),
361  std::make_pair<std::string, std::string>("libdatadir",
362  FCITX_INSTALL_LIBDATADIR),
363  std::make_pair<std::string, std::string>("libexecdir",
364  FCITX_INSTALL_LIBEXECDIR),
365  };
366 
367  auto iter = pathMap.find(path);
368  if (iter != pathMap.end()) {
369  return iter->second.c_str();
370  }
371 
372  return nullptr;
373 }
374 
375 std::string StandardPath::fcitxPath(const char *path, const char *subPath) {
376  return stringutils::joinPath(fcitxPath(path), subPath);
377 }
378 
379 std::string StandardPath::userDirectory(Type type) const {
380  FCITX_D();
381  return d->userPath(type);
382 }
383 
384 std::vector<std::string> StandardPath::directories(Type type) const {
385  FCITX_D();
386  return d->directories(type);
387 }
388 
390  Type type,
391  const std::function<bool(const std::string &path, bool user)> &scanner)
392  const {
393  FCITX_D();
394  std::string userDir = d->userPath(type);
395  std::vector<std::string> list = d->directories(type);
396  if (userDir.empty() && list.empty()) {
397  return;
398  }
399  scanDirectories(userDir, list, scanner);
400 }
401 
403  const std::string &userDir, const std::vector<std::string> &directories,
404  const std::function<bool(const std::string &path, bool user)> &scanner)
405  const {
406  std::string_view userDirView(userDir);
407  FCITX_D();
408  if (d->skipUser()) {
409  userDirView = "";
410  }
411 
412  if (userDirView.empty() && directories.empty()) {
413  return;
414  }
415 
416  size_t len = (!userDirView.empty() ? 1 : 0) + directories.size();
417 
418  for (size_t i = 0; i < len; i++) {
419  bool isUser = false;
420  std::string dirBasePath;
421  if (!userDirView.empty()) {
422  isUser = (i == 0);
423  dirBasePath = isUser ? userDirView : directories[i - 1];
424  } else {
425  dirBasePath = directories[i];
426  }
427 
428  dirBasePath = fs::cleanPath(dirBasePath);
429  if (!scanner(dirBasePath, isUser)) {
430  return;
431  }
432  }
433 }
434 
436  Type type, const std::string &path,
437  const std::function<bool(const std::string &fileName,
438  const std::string &dir, bool user)> &scanner)
439  const {
440  auto scanDir = [scanner](const std::string &fullPath, bool isUser) {
441  UniqueCPtr<DIR, closedir> scopedDir{opendir(fullPath.c_str())};
442  if (auto *dir = scopedDir.get()) {
443  struct dirent *drt;
444  while ((drt = readdir(dir)) != nullptr) {
445  if (strcmp(drt->d_name, ".") == 0 ||
446  strcmp(drt->d_name, "..") == 0) {
447  continue;
448  }
449 
450  if (!scanner(drt->d_name, fullPath, isUser)) {
451  return false;
452  }
453  }
454  }
455  return true;
456  };
457  if (isAbsolutePath(path)) {
458  scanDir(path, false);
459  } else {
461  type, [&path, &scanDir](const std::string &dirPath, bool isUser) {
462  auto fullPath = constructPath(dirPath, path);
463  return scanDir(fullPath, isUser);
464  });
465  }
466 }
467 
468 std::string StandardPath::locate(Type type, const std::string &path) const {
469  std::string retPath;
470  if (isAbsolutePath(path)) {
471  if (fs::isreg(path)) {
472  retPath = path;
473  }
474  } else {
475  scanDirectories(type,
476  [&retPath, &path](const std::string &dirPath, bool) {
477  std::string fullPath = constructPath(dirPath, path);
478  if (!fs::isreg(fullPath)) {
479  return true;
480  }
481  retPath = std::move(fullPath);
482  return false;
483  });
484  }
485  return retPath;
486 }
487 
488 std::vector<std::string>
489 StandardPath::locateAll(Type type, const std::string &path) const {
490  std::vector<std::string> retPaths;
491  if (isAbsolutePath(path)) {
492  if (fs::isreg(path)) {
493  retPaths.push_back(path);
494  }
495  } else {
496  scanDirectories(type,
497  [&retPaths, &path](const std::string &dirPath, bool) {
498  auto fullPath = constructPath(dirPath, path);
499  if (fs::isreg(fullPath)) {
500  retPaths.push_back(fullPath);
501  }
502  return true;
503  });
504  }
505  return retPaths;
506 }
507 
508 StandardPathFile StandardPath::open(Type type, const std::string &path,
509  int flags) const {
510  int retFD = -1;
511  std::string fdPath;
512  if (isAbsolutePath(path)) {
513  int fd = ::open(path.c_str(), flags);
514  if (fd >= 0) {
515  retFD = fd;
516  fdPath = path;
517  }
518  } else {
519  scanDirectories(type, [flags, &retFD, &fdPath,
520  &path](const std::string &dirPath, bool) {
521  auto fullPath = constructPath(dirPath, path);
522  int fd = ::open(fullPath.c_str(), flags);
523  if (fd < 0) {
524  return true;
525  }
526  retFD = fd;
527  fdPath = std::move(fullPath);
528  return false;
529  });
530  }
531  return {retFD, fdPath};
532 }
533 
534 StandardPathFile StandardPath::openUser(Type type, const std::string &path,
535  int flags) const {
536  std::string fullPath;
537  if (isAbsolutePath(path)) {
538  fullPath = path;
539  } else {
540  auto dirPath = userDirectory(type);
541  if (dirPath.empty()) {
542  return {};
543  }
544  fullPath = constructPath(dirPath, path);
545  }
546 
547  // Try ensure directory exists if we want to write.
548  if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
549  if (!fs::makePath(fs::dirName(fullPath))) {
550  return {};
551  }
552  }
553 
554  int fd = ::open(fullPath.c_str(), flags, 0600);
555  if (fd >= 0) {
556  return {fd, fullPath};
557  }
558  return {};
559 }
560 
561 StandardPathFile StandardPath::openSystem(Type type, const std::string &path,
562  int flags) const {
563  int retFD = -1;
564  std::string fdPath;
565  if (isAbsolutePath(path)) {
566  int fd = ::open(path.c_str(), flags);
567  if (fd >= 0) {
568  retFD = fd;
569  fdPath = path;
570  }
571  } else {
572  scanDirectories(type, [flags, &retFD, &fdPath,
573  &path](const std::string &dirPath, bool user) {
574  if (user) {
575  return true;
576  }
577  auto fullPath = constructPath(dirPath, path);
578  int fd = ::open(fullPath.c_str(), flags);
579  if (fd < 0) {
580  return true;
581  }
582  retFD = fd;
583  fdPath = std::move(fullPath);
584  return false;
585  });
586  }
587  return {retFD, fdPath};
588 }
589 
590 std::vector<StandardPathFile> StandardPath::openAll(StandardPath::Type type,
591  const std::string &path,
592  int flags) const {
593  std::vector<StandardPathFile> result;
594  if (isAbsolutePath(path)) {
595  int fd = ::open(path.c_str(), flags);
596  if (fd >= 0) {
597  result.emplace_back(fd, path);
598  }
599  } else {
601  type, [flags, &result, &path](const std::string &dirPath, bool) {
602  auto fullPath = constructPath(dirPath, path);
603  int fd = ::open(fullPath.c_str(), flags);
604  if (fd < 0) {
605  return true;
606  }
607  result.emplace_back(fd, fullPath);
608  return true;
609  });
610  }
611  return result;
612 }
613 
615 StandardPath::openUserTemp(Type type, const std::string &pathOrig) const {
616  std::string path = pathOrig + "_XXXXXX";
617  std::string fullPath;
618  std::string fullPathOrig;
619  if (isAbsolutePath(pathOrig)) {
620  fullPath = std::move(path);
621  fullPathOrig = pathOrig;
622  } else {
623  auto dirPath = userDirectory(type);
624  if (dirPath.empty()) {
625  return {};
626  }
627  fullPath = constructPath(dirPath, path);
628  fullPathOrig = constructPath(dirPath, pathOrig);
629  }
630  if (fs::makePath(fs::dirName(fullPath))) {
631  std::vector<char> cPath(fullPath.data(),
632  fullPath.data() + fullPath.size() + 1);
633  int fd = mkstemp(cPath.data());
634  if (fd >= 0) {
635  return {fd, fullPathOrig, cPath.data()};
636  }
637  }
638  return {};
639 }
640 
641 bool StandardPath::safeSave(Type type, const std::string &pathOrig,
642  const std::function<bool(int)> &callback) const {
643  FCITX_D();
644  auto file = openUserTemp(type, pathOrig);
645  if (!file.isValid()) {
646  return false;
647  }
648  try {
649  if (callback(file.fd())) {
650 #ifdef _WIN32
651  auto wfile = utf8::UTF8ToUTF16(file.tempPath());
652  ::_wchmod(wfile.data(), 0666 & ~(d->umask()));
653 #else
654  // close it
655  fchmod(file.fd(), 0666 & ~(d->umask()));
656 #endif
657  return true;
658  }
659  } catch (const std::exception &) {
660  }
661  file.removeTemp();
662  return false;
663 }
664 
665 std::map<std::string, std::string> StandardPath::locateWithFilter(
666  Type type, const std::string &path,
667  std::function<bool(const std::string &path, const std::string &dir,
668  bool user)>
669  filter) const {
670  std::map<std::string, std::string> result;
671  scanFiles(type, path,
672  [&result, &filter](const std::string &path,
673  const std::string &dir, bool isUser) {
674  if (!result.contains(path) && filter(path, dir, isUser)) {
675  auto fullPath = constructPath(dir, path);
676  if (fs::isreg(fullPath)) {
677  result.emplace(path, std::move(fullPath));
678  }
679  }
680  return true;
681  });
682 
683  return result;
684 }
685 
686 StandardPathFileMap StandardPath::multiOpenFilter(
687  Type type, const std::string &path, int flags,
688  std::function<bool(const std::string &path, const std::string &dir,
689  bool user)>
690  filter) const {
691  StandardPathFileMap result;
692  scanFiles(type, path,
693  [&result, flags, &filter](const std::string &path,
694  const std::string &dir, bool isUser) {
695  if (!result.contains(path) && filter(path, dir, isUser)) {
696  auto fullPath = constructPath(dir, path);
697  int fd = ::open(fullPath.c_str(), flags);
698  if (fd >= 0) {
699  result.emplace(std::piecewise_construct,
700  std::forward_as_tuple(path),
701  std::forward_as_tuple(fd, fullPath));
702  }
703  }
704  return true;
705  });
706 
707  return result;
708 }
709 
710 StandardPathFilesMap StandardPath::multiOpenAllFilter(
711  Type type, const std::string &path, int flags,
712  std::function<bool(const std::string &path, const std::string &dir,
713  bool user)>
714  filter) const {
715  StandardPathFilesMap result;
716  scanFiles(type, path,
717  [&result, flags, &filter](const std::string &path,
718  const std::string &dir, bool isUser) {
719  if (filter(path, dir, isUser)) {
720  auto fullPath = constructPath(dir, path);
721  int fd = ::open(fullPath.c_str(), flags);
722  if (fd >= 0) {
723  result[path].emplace_back(fd, fullPath);
724  }
725  }
726  return true;
727  });
728 
729  return result;
730 }
731 
732 int64_t StandardPath::timestamp(Type type, const std::string &path) const {
733  if (isAbsolutePath(path)) {
734  return fs::modifiedTime(path);
735  }
736 
737  int64_t timestamp = 0;
739  type, [&timestamp, &path](const std::string &dirPath, bool) {
740  auto fullPath = constructPath(dirPath, path);
741  timestamp = std::max(timestamp, fs::modifiedTime(fullPath));
742  return true;
743  });
744  return timestamp;
745 }
746 
747 std::string StandardPath::findExecutable(const std::string &name) {
748  if (name.empty()) {
749  return "";
750  }
751 
752  if (name[0] == '/') {
753  return fs::isexe(name) ? name : "";
754  }
755 
756  std::string sEnv;
757  if (auto pEnv = getEnvironment("PATH")) {
758  sEnv = std::move(*pEnv);
759  } else {
760 #if defined(_PATH_DEFPATH)
761  sEnv = _PATH_DEFPATH;
762 #elif defined(_CS_PATH)
763  size_t n = confstr(_CS_PATH, nullptr, 0);
764  if (n) {
765  std::vector<char> data;
766  data.resize(n + 1);
767  confstr(_CS_PATH, data.data(), data.size());
768  data.push_back('\0');
769  sEnv = data.data();
770  }
771 #endif
772  }
773  auto paths = stringutils::split(sEnv, ":");
774  for (auto &path : paths) {
775  path = fs::cleanPath(path);
776  auto fullPath = constructPath(path, name);
777  if (!fullPath.empty() && fs::isexe(fullPath)) {
778  return fullPath;
779  }
780  }
781  return "";
782 }
783 
784 bool StandardPath::hasExecutable(const std::string &name) {
785  return !findExecutable(name).empty();
786 }
787 
788 void StandardPath::syncUmask() const { d_ptr->syncUmask(); }
789 
791  FCITX_D();
792  return d->skipBuiltIn();
793 }
794 
796  FCITX_D();
797  return d->skipUser();
798 }
799 
800 } // 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:152
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:180
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:109
Type
Enum for location type.
Definition: standardpath.h:183
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:126
int release() noexcept
Get the internal fd and release the ownership.
Definition: unixfd.cpp:81