File indexing completed on 2024-05-26 04:57:58
0001 /** 0002 * \file formatreplacer.cpp 0003 * Replaces format codes in a string. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 06 Jul 2008 0008 * 0009 * Copyright (C) 2008-2024 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "formatreplacer.h" 0028 #include <QUrl> 0029 #include "saferename.h" 0030 0031 /** 0032 * Constructor. 0033 * 0034 * @param str string with format codes 0035 */ 0036 FormatReplacer::FormatReplacer(const QString& str) : m_str(str) {} 0037 0038 /** 0039 * Destructor. 0040 */ 0041 FormatReplacer::~FormatReplacer() 0042 { 0043 } 0044 0045 /** 0046 * Replace escaped characters. 0047 * Replaces the escaped characters ("\n", "\t", "\r", "\\", "\a", "\b", 0048 * "\f", "\v") with the corresponding characters. 0049 */ 0050 void FormatReplacer::replaceEscapedChars() 0051 { 0052 if (!m_str.isEmpty()) { 0053 constexpr int numEscCodes = 8; 0054 constexpr QChar escCode[numEscCodes] = { 0055 QLatin1Char('n'), QLatin1Char('t'), QLatin1Char('r'), QLatin1Char('\\'), 0056 QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('f'), QLatin1Char('v')}; 0057 constexpr char escChar[numEscCodes] = { 0058 '\n', '\t', '\r', '\\', '\a', '\b', '\f', '\v'}; 0059 0060 for (int pos = 0; pos < m_str.length();) { 0061 pos = m_str.indexOf(QLatin1Char('\\'), pos); 0062 if (pos == -1) break; 0063 ++pos; 0064 for (int k = 0;; ++k) { 0065 if (k >= numEscCodes) { 0066 // invalid code at pos 0067 ++pos; 0068 break; 0069 } 0070 if (m_str[pos] == escCode[k]) { 0071 // code found, replace it 0072 m_str.replace(pos - 1, 2, QLatin1Char(escChar[k])); 0073 break; 0074 } 0075 } 0076 } 0077 } 0078 } 0079 0080 /** 0081 * Replace percent codes. 0082 * 0083 * @param flags flags: FSF_SupportUrlEncode to support modifier u 0084 * (with code c "%uc") to URL encode, 0085 * FSF_ReplaceSeparators to replace directory separators 0086 * ('/', '\\', ':') in tags, 0087 * FSF_SupportHtmlEscape to support modifier h 0088 * (with code c "%hc") to replace HTML metacharacters 0089 * ('<', '>', '&', '"', ''', non-ascii) in tags. 0090 */ 0091 void FormatReplacer::replacePercentCodes(unsigned flags) 0092 { 0093 if (!m_str.isEmpty()) { 0094 for (int pos = 0; pos < m_str.length();) { 0095 pos = m_str.indexOf(QLatin1Char('%'), pos); 0096 if (pos == -1) break; 0097 0098 int codePos = pos + 1; 0099 int codeLen = 0; 0100 QString prefix, postfix; 0101 bool urlEncode = false; 0102 bool htmlEscape = false; 0103 QString repl; 0104 if ((flags & FSF_SupportUrlEncode) && m_str[codePos] == QLatin1Char('u')) { 0105 ++codePos; 0106 urlEncode = true; 0107 } 0108 if ((flags & FSF_SupportHtmlEscape) && m_str[codePos] == QLatin1Char('h')) { 0109 ++codePos; 0110 htmlEscape = true; 0111 } 0112 if (m_str[codePos] == QLatin1Char('{')) { 0113 if (int closingBracePos = m_str.indexOf(QLatin1Char('}'), codePos + 1); 0114 closingBracePos > codePos + 1) { 0115 QString longCode = 0116 m_str.mid(codePos + 1, closingBracePos - codePos - 1).toLower(); 0117 if (longCode.startsWith(QLatin1Char('"'))) { 0118 if (int prefixEnd = longCode.indexOf(QLatin1Char('"'), 1); 0119 prefixEnd != -1 && prefixEnd < longCode.length() - 2) { 0120 prefix = longCode.mid(1, prefixEnd - 1); 0121 longCode.remove(0, prefixEnd + 1); 0122 } 0123 } 0124 if (longCode.endsWith(QLatin1Char('"'))) { 0125 if (int postfixStart = longCode.lastIndexOf(QLatin1Char('"'), -2); 0126 postfixStart > 1) { 0127 postfix = longCode.mid(postfixStart + 1, 0128 longCode.length() - postfixStart - 2); 0129 longCode.truncate(postfixStart); 0130 } 0131 } 0132 repl = getReplacement(longCode); 0133 codeLen = closingBracePos - pos + 1; 0134 } 0135 } else { 0136 repl = getReplacement(QString(m_str[codePos])); 0137 codeLen = codePos - pos + 1; 0138 } 0139 0140 if (codeLen > 0) { 0141 if (flags & FSF_ReplaceSeparators) { 0142 #ifdef Q_OS_WIN32 0143 static constexpr char illegalChars[] = "<>:\"|?*\\/"; 0144 #else 0145 // ':' and '\' are included in the set of illegal characters to 0146 // keep the old behavior when no string replacement is enabled. 0147 static constexpr char illegalChars[] = ":\\/"; 0148 #endif 0149 Utils::replaceIllegalFileNameCharacters(repl, QLatin1String("-"), 0150 illegalChars); 0151 } 0152 if (urlEncode) { 0153 repl = QString::fromLatin1(QUrl::toPercentEncoding(repl)); 0154 } 0155 if (htmlEscape) { 0156 repl = escapeHtml(repl); 0157 } 0158 if (!repl.isEmpty()) { 0159 if (!prefix.isEmpty()) { 0160 repl = prefix + repl; 0161 } 0162 if (!postfix.isEmpty()) { 0163 repl += postfix; 0164 } 0165 } 0166 if (!repl.isNull() || codeLen > 2) { 0167 m_str.replace(pos, codeLen, repl); 0168 pos += repl.length(); 0169 } else { 0170 ++pos; 0171 } 0172 } else { 0173 ++pos; 0174 } 0175 } 0176 } 0177 } 0178 0179 /** 0180 * Converts the plain text string @a plain to a HTML string with 0181 * HTML metacharacters replaced by HTML entities. 0182 * @param plain plain text 0183 * @return html text with HTML entities. 0184 */ 0185 QString FormatReplacer::escapeHtml(const QString& plain) 0186 { 0187 QString rich; 0188 rich.reserve(static_cast<int>(plain.length() * 1.1)); 0189 for (int i = 0; i < plain.length(); ++i) { 0190 if (ushort ch = plain.at(i).unicode(); ch == '<') 0191 rich += QLatin1String("<"); 0192 else if (ch == '>') 0193 rich += QLatin1String(">"); 0194 else if (ch == '&') 0195 rich += QLatin1String("&"); 0196 else if (ch == '"') 0197 rich += QLatin1String("""); 0198 else if (ch == '\'') 0199 rich += QLatin1String("'"); 0200 else if (ch >= 128) 0201 rich += QString(QLatin1String("&#%1;")).arg(ch); 0202 else 0203 rich += plain.at(i); 0204 } 0205 return rich; 0206 }