templatedpathresolver.cpp
1 //------------------------------------------------------------------------------
2 // templatedpathresolver.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2022 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "templatedpathresolver.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 
27 #include <QCoreApplication>
28 #include <QDir>
29 #include <QProcessEnvironment>
30 
31 #include <utility>
32 
33 #ifdef Q_OS_UNIX
34 #include <sys/types.h>
35 #include <pwd.h>
36 #endif
37 
38 const QString TemplatedPathResolver::PROGDIR_TEMPLATE = "$PROGDIR";
39 
40 DClass<TemplatedPathResolver>
41 {
42 public:
43  bool progdirEnabled;
44  bool envVarsEnabled;
45  bool userHomeEnabled;
46 };
47 
48 DPointered(TemplatedPathResolver)
49 
51 {
52  d->progdirEnabled = false;
53  d->envVarsEnabled = false;
54  d->userHomeEnabled = true;
55 }
56 
57 TemplatedPathResolver::~TemplatedPathResolver()
58 {
59 }
60 
61 QString TemplatedPathResolver::resolve(const QString &templatedPath) const
62 {
63  QString resolved = templatedPath;
64 
65  // Match ~ to home dir.
66  if (d->userHomeEnabled && resolved.startsWith("~"))
67  {
68 #ifdef Q_OS_WIN
69  const QRegExp separatorRegex(R"([/\\])");
70 #else
71  const QRegExp separatorRegex("/");
72 #endif
73  int separatorIdx = separatorRegex.indexIn(resolved);
74 
75  QString username = resolved.mid(1, separatorIdx - 1);
76  QString home;
77  if (username.isEmpty())
78  {
79  home = QDir::homePath();
80  }
81  else
82  {
83  home = QString("~%1").arg(username);
84 #ifdef DOOMSEEKER_TEMPLATED_PATH_RESOLVER_TILDEUSER
85  const struct passwd *pwent = getpwnam(username.toLatin1().constData());
86  if (pwent != nullptr)
87  {
88  home = pwent->pw_dir;
89  }
90 #endif
91  }
92  if (separatorIdx > 0)
93  {
94  resolved = home + resolved.mid(separatorIdx);
95  }
96  else
97  {
98  resolved = home;
99  }
100  }
101 
102  // Match arbitrary env. vars.
103  QRegExp envVarRegex("\\$([a-zA-Z_][a-zA-Z0-9_]*)");
104  int offset = 0;
105  while (d->progdirEnabled || d->envVarsEnabled)
106  {
107  int varIdx = envVarRegex.indexIn(resolved, offset);
108  if (varIdx < 0)
109  break;
110  QString name = envVarRegex.cap(1);
111  QString varValue = "";
112 
113  // $PROGDIR is a special placeholder that always resolves to the directory
114  // where executable is stored, even if an env. var with the same name exists.
115  if (d->progdirEnabled && name == PROGDIR_TEMPLATE.mid(1))
116  {
117  varValue = QCoreApplication::applicationDirPath();
118  }
119  else if (d->envVarsEnabled && QProcessEnvironment::systemEnvironment().contains(name))
120  {
121  varValue = qgetenv(name.toUtf8());
122  }
123  else if (!d->envVarsEnabled)
124  {
125  varValue = QString("$%1").arg(name);
126  }
127  resolved = resolved.left(varIdx) + varValue + resolved.mid(varIdx + envVarRegex.matchedLength());
128  offset = varIdx + varValue.length();
129  }
130  return resolved;
131 }
132 
133 QStringList TemplatedPathResolver::resolve(const QStringList &templatedPaths) const
134 {
135  QStringList transformed;
136  for (const QString &path : templatedPaths)
137  transformed << resolve(path);
138  return transformed;
139 }
140 
142 {
143  return d->progdirEnabled;
144 }
145 
146 void TemplatedPathResolver::setProgdirEnabled(bool enabled)
147 {
148  d->progdirEnabled = enabled;
149 }
150 
152 {
153  return d->envVarsEnabled;
154 }
155 
156 void TemplatedPathResolver::setEnvVarsEnabled(bool enabled)
157 {
158  d->envVarsEnabled = enabled;
159 }
160 
162 {
163  return d->userHomeEnabled;
164 }
165 
166 void TemplatedPathResolver::setUserHomeEnabled(bool enabled)
167 {
168  d->userHomeEnabled = enabled;
169 }
170 
171 // Functions
172 
174 {
175  TemplatedPathResolver resolver;
176  resolver.setEnvVarsEnabled(gConfig.doomseeker.bResolveTemplatedPathsPlaceholders);
177  resolver.setProgdirEnabled(gConfig.doomseeker.bResolveTemplatedPathsPlaceholders);
178  resolver.setUserHomeEnabled(true);
179  return resolver;
180 }