1 /***************************************************************************
2 * Copyright (C) 2008-2021 by Ilya Kotov *
3 * forkotov02@ya.ru *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program 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 *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include <QDir>
22 #include <QCoreApplication>
23 #include <QSettings>
24 #include <QLocale>
25 #include <QFile>
26 #include <QByteArray>
27
28 #define STR_HELPER(x) #x
29 #define STR(x) STR_HELPER(x)
30 #define DEV_SUFFIX "dev"
31
32 #include "qmmp.h"
33
34 QString Qmmp::m_configDir;
35 QString Qmmp::m_langID;
36 #ifdef Q_OS_WIN
37 QString Qmmp::m_appDir;
38 #endif
39
configFile()40 QString Qmmp::configFile()
41 {
42 return configDir() + "/qmmprc";
43 }
44
configDir()45 QString Qmmp::configDir()
46 {
47 #ifdef Q_OS_WIN
48 if(m_configDir.isEmpty())
49 {
50 if(isPortable())
51 return m_appDir + "/.qmmp";
52 else
53 return QDir::homePath() +"/.qmmp";
54 }
55 else
56 return m_configDir;
57 #else
58 return m_configDir.isEmpty() ? QDir::homePath() +"/.qmmp" : m_configDir;
59 #endif
60 }
61
setConfigDir(const QString & path)62 void Qmmp::setConfigDir(const QString &path)
63 {
64 m_configDir = path;
65 }
66
strVersion()67 QString Qmmp::strVersion()
68 {
69 QString ver = QString("%1.%2.%3")
70 .arg(QMMP_VERSION_MAJOR)
71 .arg(QMMP_VERSION_MINOR)
72 .arg(QMMP_VERSION_PATCH);
73 #if !QMMP_VERSION_STABLE
74 #ifdef SVN_REVISION
75 ver += "-svn-" SVN_REVISION;
76 #else
77 ver += "-" DEV_SUFFIX;
78 #endif
79 #endif
80 return ver;
81 }
82
pluginPath()83 QString Qmmp::pluginPath()
84 {
85 QByteArray path = qgetenv("QMMP_PLUGINS");
86 if(!path.isEmpty())
87 return path;
88 QString fallbackPath = qApp->applicationDirPath() + "/../lib/qmmp-" STR(QMMP_VERSION_MAJOR) "." STR(QMMP_VERSION_MINOR);
89 #ifdef QMMP_PLUGIN_DIR
90 QDir dir(QMMP_PLUGIN_DIR);
91 #elif defined(Q_OS_WIN) && !defined(Q_OS_CYGWIN)
92 QDir dir(qApp->applicationDirPath() + "/plugins");
93 #else
94 QDir dir(fallbackPath);
95 #endif
96 if(!dir.exists())
97 dir = QDir(fallbackPath);
98 return dir.canonicalPath();
99 }
100
findPlugins(const QString & prefix)101 QStringList Qmmp::findPlugins(const QString &prefix)
102 {
103 QDir pluginDir(pluginPath() + "/" + prefix);
104 QStringList paths;
105 for(const QFileInfo &info : pluginDir.entryInfoList(QStringList() << "*.dll" << "*.so", QDir::Files))
106 paths << info.canonicalFilePath();
107 return paths;
108 }
109
systemLanguageID()110 QString Qmmp::systemLanguageID()
111 {
112 if(m_langID.isEmpty())
113 {
114 m_langID = uiLanguageID();
115 //qDebug("Qmmp: setting ui language to '%s'", qPrintable(m_langID));
116 }
117
118 if(m_langID != "auto")
119 return m_langID;
120
121 #ifdef Q_OS_UNIX
122 QByteArray v = qgetenv ("LC_ALL");
123 if (v.isEmpty())
124 v = qgetenv ("LC_MESSAGES");
125 if (v.isEmpty())
126 v = qgetenv ("LANG");
127 if (!v.isEmpty())
128 return QLocale (v).name();
129 #endif
130 return QLocale::system().name();
131 }
132
uiLanguageID()133 QString Qmmp::uiLanguageID()
134 {
135 QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
136 QString langID = settings.value("General/locale", "auto").toString();
137 langID = langID.isEmpty() ? "auto" : langID;
138 return langID;
139 }
140
setUiLanguageID(const QString & code)141 void Qmmp::setUiLanguageID(const QString &code)
142 {
143 QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
144 settings.setValue("General/locale", code);
145 m_langID.clear();
146 }
147
dataPath()148 QString Qmmp::dataPath()
149 {
150 #if defined(Q_OS_WIN) && !defined(Q_OS_CYGWIN)
151 return qApp->applicationDirPath();
152 #else
153 return QDir(qApp->applicationDirPath() + "/../share/qmmp" APP_NAME_SUFFIX).absolutePath();
154 #endif
155 }
156
157 #ifdef Q_OS_WIN
isPortable()158 bool Qmmp::isPortable()
159 {
160 if(m_appDir.isEmpty())
161 m_appDir = QCoreApplication::applicationDirPath();
162 return QFile::exists(m_appDir + "/qmmp_portable.txt");
163 }
164 #endif
165
166 #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
wildcardToRegularExpression(const QString & pattern)167 QString Qmmp::wildcardToRegularExpression(const QString &pattern)
168 {
169 const int wclen = pattern.length();
170 QString rx;
171 rx.reserve(wclen + wclen / 16);
172 int i = 0;
173 const QChar *wc = pattern.data();
174
175 #ifdef Q_OS_WIN
176 const QLatin1Char nativePathSeparator('\\');
177 const QLatin1String starEscape("[^/\\\\]*");
178 const QLatin1String questionMarkEscape("[^/\\\\]");
179 #else
180 const QLatin1Char nativePathSeparator('/');
181 const QLatin1String starEscape("[^/]*");
182 const QLatin1String questionMarkEscape("[^/]");
183 #endif
184
185 while (i < wclen) {
186 const QChar c = wc[i++];
187 switch (c.unicode())
188 {
189 case '*':
190 rx += starEscape;
191 break;
192 case '?':
193 rx += questionMarkEscape;
194 break;
195 case '\\':
196 #ifdef Q_OS_WIN
197 case '/':
198 rx += QLatin1String("[/\\\\]");
199 break;
200 #endif
201 case '$':
202 case '(':
203 case ')':
204 case '+':
205 case '.':
206 case '^':
207 case '{':
208 case '|':
209 case '}':
210 rx += QLatin1Char('\\');
211 rx += c;
212 break;
213 case '[':
214 rx += c;
215 // Support for the [!abc] or [!a-c] syntax
216 if (i < wclen) {
217 if (wc[i] == QLatin1Char('!'))
218 {
219 rx += QLatin1Char('^');
220 ++i;
221 }
222
223 if (i < wclen && wc[i] == QLatin1Char(']'))
224 rx += wc[i++];
225
226 while (i < wclen && wc[i] != QLatin1Char(']'))
227 {
228 // The '/' appearing in a character class invalidates the
229 // regular expression parsing. It also concerns '\\' on
230 // Windows OS types.
231 if (wc[i] == QLatin1Char('/') || wc[i] == nativePathSeparator)
232 return rx;
233 if (wc[i] == QLatin1Char('\\'))
234 rx += QLatin1Char('\\');
235 rx += wc[i++];
236 }
237 }
238 break;
239 default:
240 rx += c;
241 break;
242 }
243 }
244
245 return anchoredPattern(rx);
246 }
247
anchoredPattern(const QString & expression)248 QString Qmmp::anchoredPattern(const QString &expression)
249 {
250 return QString()
251 + QLatin1String("\\A(?:")
252 + expression
253 + QLatin1String(")\\z");
254 }
255 #endif
256