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