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 QRegularExpression separatorRegex(R"([/\\])");
70 #else
71  const QRegularExpression separatorRegex("/");
72 #endif
73  int separatorIdx = resolved.indexOf(separatorRegex);
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  QRegularExpression envVarRegex("\\$([a-zA-Z_][a-zA-Z0-9_]*)");
104  int offset = 0;
105  while (d->progdirEnabled || d->envVarsEnabled)
106  {
107  QRegularExpressionMatch match;
108  int varIdx = resolved.indexOf(envVarRegex, offset, &match);
109  if (varIdx < 0)
110  break;
111  QString name = match.captured(1);
112  QString varValue = "";
113 
114  // $PROGDIR is a special placeholder that always resolves to the directory
115  // where executable is stored, even if an env. var with the same name exists.
116  if (d->progdirEnabled && name == PROGDIR_TEMPLATE.mid(1))
117  {
118  varValue = QCoreApplication::applicationDirPath();
119  }
120  else if (d->envVarsEnabled && QProcessEnvironment::systemEnvironment().contains(name))
121  {
122  varValue = qgetenv(name.toUtf8());
123  }
124  else if (!d->envVarsEnabled)
125  {
126  varValue = QString("$%1").arg(name);
127  }
128  resolved = resolved.left(varIdx) + varValue + resolved.mid(varIdx + match.capturedLength());
129  offset = varIdx + varValue.length();
130  }
131  return resolved;
132 }
133 
134 QStringList TemplatedPathResolver::resolve(const QStringList &templatedPaths) const
135 {
136  QStringList transformed;
137  for (const QString &path : templatedPaths)
138  transformed << resolve(path);
139  return transformed;
140 }
141 
143 {
144  return d->progdirEnabled;
145 }
146 
147 void TemplatedPathResolver::setProgdirEnabled(bool enabled)
148 {
149  d->progdirEnabled = enabled;
150 }
151 
153 {
154  return d->envVarsEnabled;
155 }
156 
157 void TemplatedPathResolver::setEnvVarsEnabled(bool enabled)
158 {
159  d->envVarsEnabled = enabled;
160 }
161 
163 {
164  return d->userHomeEnabled;
165 }
166 
167 void TemplatedPathResolver::setUserHomeEnabled(bool enabled)
168 {
169  d->userHomeEnabled = enabled;
170 }
171 
172 // Functions
173 
175 {
176  TemplatedPathResolver resolver;
177  resolver.setEnvVarsEnabled(gConfig.doomseeker.bResolveTemplatedPathsPlaceholders);
178  resolver.setProgdirEnabled(gConfig.doomseeker.bResolveTemplatedPathsPlaceholders);
179  resolver.setUserHomeEnabled(true);
180  return resolver;
181 }