Cutelee 6.1.0
stringfilters.cpp
1/*
2 This file is part of the Cutelee template system.
3
4 Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either version
9 2.1 of the Licence, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library. If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21#include "stringfilters.h"
22
23#include "util.h"
24
25#include <QtCore/QRegularExpression>
26#include <QtCore/QVariant>
27
29 const QVariant &argument,
30 bool autoescape) const
31{
32 Q_UNUSED(argument)
33 Q_UNUSED(autoescape)
34 auto safeString = getSafeString(input);
35 safeString.get()
36 .replace(QLatin1Char('\\'), QStringLiteral("\\\\"))
37 .get()
38 .replace(QLatin1Char('\"'), QStringLiteral("\\\""))
39 .get()
40 .replace(QLatin1Char('\''), QStringLiteral("\\\'"));
41 return safeString;
42}
43
45 const QVariant &argument,
46 bool autoescape) const
47{
48 Q_UNUSED(argument)
49 Q_UNUSED(autoescape)
50 auto safeString = getSafeString(input);
51 if (safeString.get().isEmpty())
52 return QString();
53
54 return QVariant(safeString.get().at(0).toUpper()
55 + static_cast<QString>(
56 safeString.get().right(safeString.get().size() - 1)));
57}
58
59EscapeJsFilter::EscapeJsFilter() {}
60
61static QList<std::pair<QString, QString>> getJsEscapes()
62{
64 jsEscapes << std::pair<QString, QString>(QChar::fromLatin1('\\'),
65 QStringLiteral("\\u005C"))
66 << std::pair<QString, QString>(QChar::fromLatin1('\''),
67 QStringLiteral("\\u0027"))
68 << std::pair<QString, QString>(QChar::fromLatin1('\"'),
69 QStringLiteral("\\u0022"))
70 << std::pair<QString, QString>(QChar::fromLatin1('>'),
71 QStringLiteral("\\u003E"))
72 << std::pair<QString, QString>(QChar::fromLatin1('<'),
73 QStringLiteral("\\u003C"))
74 << std::pair<QString, QString>(QChar::fromLatin1('&'),
75 QStringLiteral("\\u0026"))
76 << std::pair<QString, QString>(QChar::fromLatin1('='),
77 QStringLiteral("\\u003D"))
78 << std::pair<QString, QString>(QChar::fromLatin1('-'),
79 QStringLiteral("\\u002D"))
80 << std::pair<QString, QString>(QChar::fromLatin1(';'),
81 QStringLiteral("\\u003B"))
82 << std::pair<QString, QString>(QChar(0x2028), QStringLiteral("\\u2028"))
83 << std::pair<QString, QString>(QChar(0x2029),
84 QStringLiteral("\\u2029"));
85
86 for (auto i = 0; i < 32; ++i) {
87 jsEscapes << std::pair<QString, QString>(
88 QChar(i),
89 QStringLiteral("\\u00")
90 + QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper());
91 }
92 return jsEscapes;
93}
94
96 const QVariant &argument,
97 bool autoescape) const
98{
99 Q_UNUSED(argument)
100 Q_UNUSED(autoescape)
101 QString retString = getSafeString(input);
102
103 static const auto jsEscapes = getJsEscapes();
104
105 for (auto &escape : jsEscapes) {
106 retString = retString.replace(escape.first, escape.second);
107 }
108 return retString;
109}
110
112 const QVariant &argument,
113 bool autoescape) const
114{
115 Q_UNUSED(argument)
116 Q_UNUSED(autoescape)
117 auto safeString = getSafeString(input);
118
119 const QRegularExpression fixAmpersandsRegexp(
120 QStringLiteral("&(?!(\\w+|#\\d+);)"));
121
122 safeString.get().replace(fixAmpersandsRegexp, QStringLiteral("&amp;"));
123
124 return safeString;
125}
126
127QVariant CutFilter::doFilter(const QVariant &input, const QVariant &argument,
128 bool autoescape) const
129{
130 Q_UNUSED(autoescape)
131 auto retString = getSafeString(input);
132 auto argString = getSafeString(argument);
133
134 auto inputSafe = retString.isSafe();
135
136 retString.get().remove(argString);
137
138 if (inputSafe && argString.get() != QChar::fromLatin1(';'))
139 return SafeString(retString, true);
140 else
141 return retString;
142}
143
144QVariant SafeFilter::doFilter(const QVariant &input, const QVariant &argument,
145 bool autoescape) const
146{
147 Q_UNUSED(argument)
148 Q_UNUSED(autoescape)
149 return markSafe(getSafeString(input));
150}
151
153 const QVariant &argument,
154 bool autoescape) const
155{
156 Q_UNUSED(argument)
157 auto safeString = getSafeString(input);
158 auto lines = safeString.get().split(QLatin1Char('\n'));
159 auto width = QString::number(lines.size()).size();
160
161 const auto shouldEscape = (autoescape && !safeString.isSafe());
162 for (auto i = 0; i < lines.size(); ++i) {
163 lines[i]
164 = QStringLiteral("%1. %2")
165 .arg(i + 1, width)
166 .arg(shouldEscape ? QString(escape(lines.at(i))) : lines.at(i));
167 }
168
169 return SafeString(lines.join(QChar::fromLatin1('\n')), true);
170}
171
172QVariant LowerFilter::doFilter(const QVariant &input, const QVariant &argument,
173 bool autoescape) const
174{
175 Q_UNUSED(argument)
176 Q_UNUSED(autoescape)
177 return getSafeString(input).get().toLower();
178}
179
181 const QVariant &argument,
182 bool autoescape) const
183{
184 Q_UNUSED(autoescape)
185 SafeString a;
186 if (isSafeString(input))
187 a = getSafeString(input);
188 else if (input.userType() == qMetaTypeId<QVariantList>()) {
189 a = toString(input.value<QVariantList>());
190 }
191
192 return SafeString(getSafeString(argument).get().arg(a),
193 getSafeString(input).isSafe());
194}
195
196QVariant TitleFilter::doFilter(const QVariant &input, const QVariant &argument,
197 bool autoescape) const
198{
199 Q_UNUSED(argument)
200 Q_UNUSED(autoescape)
201
202 QString str = getSafeString(input);
203
204 auto it = str.begin();
205 const auto end = str.end();
206
207 auto toUpper = true;
208 for (; it != end; ++it) {
209 if (toUpper)
210 *it = it->toUpper();
211 else
212 *it = it->toLower();
213 toUpper = it->isSpace();
214 }
215
216 return str;
217}
218
220 const QVariant &argument,
221 bool autoescape) const
222{
223 Q_UNUSED(autoescape)
224 auto s = getSafeString(argument);
225
226 bool ok;
227 auto numWords = s.get().toInt(&ok);
228
229 if (!ok) {
230 return input.toString();
231 }
232
233 QString inputString = getSafeString(input);
234#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
235 auto words = inputString.split(QLatin1Char(' '), QString::SkipEmptyParts);
236#else
237 auto words = inputString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
238#endif
239
240 if (words.size() > numWords) {
241 words = words.mid(0, numWords);
242 if (!words.at(words.size() - 1).endsWith(QStringLiteral("..."))) {
243 words << QStringLiteral("...");
244 }
245 }
246 return words.join(QChar::fromLatin1(' '));
247}
248
249QVariant UpperFilter::doFilter(const QVariant &input, const QVariant &argument,
250 bool autoescape) const
251{
252 Q_UNUSED(argument)
253 Q_UNUSED(autoescape)
254 return getSafeString(input).get().toUpper();
255}
256
258 const QVariant &argument,
259 bool autoescape) const
260{
261 Q_UNUSED(argument)
262 Q_UNUSED(autoescape)
263 return QString::number(
264 getSafeString(input).get().split(QLatin1Char(' ')).size());
265}
266
267QVariant LJustFilter::doFilter(const QVariant &input, const QVariant &argument,
268 bool autoescape) const
269{
270 Q_UNUSED(autoescape)
271 return getSafeString(input).get().leftJustified(
272 getSafeString(argument).get().toInt());
273}
274
275QVariant RJustFilter::doFilter(const QVariant &input, const QVariant &argument,
276 bool autoescape) const
277{
278 Q_UNUSED(autoescape)
279 return getSafeString(input).get().rightJustified(
280 getSafeString(argument).get().toInt());
281}
282
283QVariant CenterFilter::doFilter(const QVariant &input, const QVariant &argument,
284 bool autoescape) const
285{
286 Q_UNUSED(autoescape)
287 QString value = getSafeString(input);
288 const auto valueWidth = value.size();
289 const auto width = getSafeString(argument).get().toInt();
290 const auto totalPadding = width - valueWidth;
291 const auto rightPadding = totalPadding >> 1;
292
293 return value.leftJustified(valueWidth + rightPadding).rightJustified(width);
294}
295
296QVariant EscapeFilter::doFilter(const QVariant &input, const QVariant &argument,
297 bool autoescape) const
298{
299 Q_UNUSED(argument)
300 Q_UNUSED(autoescape)
301 return markForEscaping(getSafeString(input));
302}
303
305 const QVariant &argument,
306 bool autoescape) const
307{
308 Q_UNUSED(argument)
309 Q_UNUSED(autoescape)
310 return markSafe(escape(getSafeString(input)));
311}
312
314 const QVariant &argument,
315 bool autoescape) const
316{
317 Q_UNUSED(autoescape)
318 const auto tags = getSafeString(argument).get().split(QLatin1Char(' '));
319 const auto tagRe
320 = QStringLiteral("(%1)").arg(tags.join(QChar::fromLatin1('|')));
321 const QRegularExpression startTag(
322 QStringLiteral("<%1(/?>|(\\s+[^>]*>))").arg(tagRe));
323 const QRegularExpression endTag(QStringLiteral("</%1>").arg(tagRe));
324
325 auto value = getSafeString(input);
326 const auto safeInput = value.isSafe();
327 value.get().remove(startTag);
328 value.get().remove(endTag);
329 if (safeInput)
330 return markSafe(value);
331 return value;
332}
333
335 const QVariant &argument,
336 bool autoescape) const
337{
338 Q_UNUSED(argument)
339 Q_UNUSED(autoescape)
340 static QRegularExpression tagRe(QStringLiteral("<[^>]*>"),
342
343 QString value = getSafeString(input);
344 value.remove(tagRe);
345 return value;
346}
347
349 const QVariant &argument,
350 bool autoescape) const
351{
352 Q_UNUSED(autoescape)
353 QString _input = getSafeString(input);
354 auto width = argument.value<int>();
355#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
356 auto partList = _input.split(QLatin1Char(' '), QString::SkipEmptyParts);
357#else
358 auto partList = _input.split(QLatin1Char(' '), Qt::SkipEmptyParts);
359#endif
360 if (partList.isEmpty())
361 return QVariant();
362 auto output = partList.takeFirst();
363 auto pos = output.size() - output.lastIndexOf(QLatin1Char('\n')) - 1;
364 Q_FOREACH (const QString &part, partList) {
365 QStringList lines;
366 if (part.contains(QLatin1Char('\n'))) {
367 lines = part.split(QLatin1Char('\n'));
368 } else {
369 lines.append(part);
370 }
371 pos += lines.first().size() + 1;
372 if (pos > width) {
373 output.append(QLatin1Char('\n'));
374 pos += lines.last().size();
375 } else {
376 output.append(QLatin1Char(' '));
377 if (lines.size() > 1)
378 pos += lines.last().size();
379 }
380 output.append(part);
381 }
382 return output;
383}
384
386 const QVariant &argument,
387 bool autoescape) const
388{
389 Q_UNUSED(autoescape)
390 double inputDouble;
391 switch (input.userType()) {
392 case QMetaType::Int:
393 case QMetaType::UInt:
397 inputDouble = input.toDouble();
398 break;
399 default:
400 inputDouble = getSafeString(input).get().toDouble();
401 }
402
403 int precision;
404 if (argument.isValid())
405 precision = getSafeString(argument).get().toInt();
406 else
407 precision = 1;
408
409 return QString::number(inputDouble, 'f', precision);
410}
411
413 const QVariant &argument,
414 bool autoescape) const
415{
416 Q_UNUSED(argument)
417 Q_UNUSED(autoescape)
418 QVariantList list;
419 if (input.userType() == qMetaTypeId<QVariantList>()) {
420 const auto inputList = input.value<QVariantList>();
421 for (const auto &item : inputList) {
422 list << markSafe(getSafeString(item));
423 }
424 }
425 return list;
426}
427
429 const QVariant &argument,
430 bool autoescape) const
431{
432 Q_UNUSED(argument)
433 auto inputString = getSafeString(input);
434 static const QRegularExpression re(QStringLiteral("\n{2,}"));
435 QStringList output;
436
437 Q_FOREACH (const QString &bit, inputString.get().split(re)) {
438 auto _bit = SafeString(bit, inputString.isSafe());
439 if (autoescape)
440 _bit = conditionalEscape(_bit);
441 _bit.get().replace(QLatin1Char('\n'), QStringLiteral("<br />"));
442 output.append(QStringLiteral("<p>%1</p>").arg(_bit));
443 }
444 return markSafe(output.join(QStringLiteral("\n\n")));
445}
446
448 const QVariant &argument,
449 bool autoescape) const
450{
451 Q_UNUSED(argument)
452 auto inputString = getSafeString(input);
453 if (autoescape && isSafeString(input)) {
454 inputString = conditionalEscape(inputString);
455 }
456 return markSafe(
457 inputString.get().replace(QLatin1Char('\n'), QStringLiteral("<br />")));
458}
459
460static QString nofailStringToAscii(const QString &input)
461{
462 QString output;
463 output.reserve(input.size());
464
465 auto it = input.constBegin();
466 const auto end = input.constEnd();
467 static const QChar asciiEndPoint(128);
468 for (; it != end; ++it)
469 if (*it < asciiEndPoint)
470 output.append(*it);
471
472 return output;
473}
474
476 const QVariant &argument,
477 bool autoescape) const
478{
479 Q_UNUSED(argument)
480 Q_UNUSED(autoescape)
481 QString inputString = getSafeString(input);
482 inputString = inputString.normalized(QString::NormalizationForm_KD);
483 inputString = nofailStringToAscii(inputString);
484 inputString = inputString.trimmed()
485 .toLower()
486 .remove(QRegularExpression(QStringLiteral("[^\\w\\s-]")))
487 .replace(QRegularExpression(QStringLiteral("[-\\s]+")), QStringLiteral("-"));
488 return SafeString(inputString, true);
489}
490
492 const QVariant &argument,
493 bool autoescape) const
494{
495 QVariant ret;
496
497 Q_UNUSED(autoescape)
498 const auto arg = getSafeString(argument);
499 bool numberConvert = true;
500
501 qreal size = 0.0f;
502 if (input.canConvert<qreal>()) {
503 size = input.toReal(&numberConvert);
504 if (!numberConvert) {
505 qWarning("%s", "Failed to convert input file size into floating point value.");
506 }
507 } else {
508 size = getSafeString(input).get().toDouble(&numberConvert);
509 if (!numberConvert) {
510 qWarning("%s", "Failed to convert input file size into floating point value.");
511 }
512 }
513
514 int unitSystem = 10;
515 int precision = 2;
516 qreal multiplier = 1.0f;
517
518 if (!arg.get().isEmpty()) {
519#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
520 const auto argList = arg.get().split(QLatin1Char(','), QString::SkipEmptyParts);
521#else
522 const auto argList = arg.get().split(QLatin1Char(','), Qt::SkipEmptyParts);
523#endif
524 const auto numArgs = argList.size();
525 if (numArgs > 0) {
526 unitSystem = argList.at(0).toInt(&numberConvert);
527 if (!numberConvert) {
528 qWarning("%s", "Failed to convert filse size format unit system into integer. Falling back to default 10.");
529 unitSystem = 10;
530 }
531 }
532
533 if (numArgs > 1) {
534 precision = argList.at(1).toInt(&numberConvert);
535 if (!numberConvert) {
536 qWarning("%s", "Failed to convert file size format decimal precision into integer. Falling back to default 2.");
537 precision = 2;
538 }
539 }
540
541 if (numArgs > 2) {
542 multiplier = argList.at(2).toDouble(&numberConvert);
543 if (!numberConvert) {
544 qWarning("%s", "Failed to convert file size format multiplier into double value. Falling back to default 1.0");
545 multiplier = 1.0f;
546 } else {
547 if (multiplier == 0.0f) {
548 qWarning("%s", "It makes no sense to multiply the file size by zero. Using default value 1.0.");
549 multiplier = 1.0f;
550 }
551 }
552 }
553 }
554
555 const double sizeMult = size * multiplier;
556
557 if (unitSystem == 10) {
558 if ((sizeMult > -1000) && (sizeMult < 1000)) {
559 precision = 0;
560 }
561 } else if (unitSystem == 2) {
562 if ((sizeMult > - 1024) && (sizeMult < 1024)) {
563 precision = 0;
564 }
565 }
566
567 const std::pair<qreal,QString> sizePair = calcFileSize(size, unitSystem, multiplier);
568
569 const QString retString = QString::number(sizePair.first, 'f', precision) + QLatin1Char(' ') + sizePair.second;
570
571 ret.setValue(retString);
572
573 return ret;
574}
575
576QVariant TruncateCharsFilter::doFilter(const QVariant &input, const QVariant &argument, bool autoescape) const
577{
578 Q_UNUSED(autoescape)
579 QString retString = getSafeString(input);
580 int count = getSafeString(argument).get().toInt();
581
582 if(retString.length() < count) return retString;
583 retString.truncate(count);
584 retString.append(QStringLiteral("..."));
585 return retString;
586}
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
SafeString escape(const QString &input) const
Definition filter.cpp:10
SafeString conditionalEscape(const SafeString &input) const
Definition filter.cpp:22
A QString wrapper class for containing whether a string is safe or needs to be escaped.
Definition safestring.h:92
const NestedString & get() const
Definition safestring.h:340
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
bool isSafe() const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
std::pair< qreal, QString > calcFileSize(qreal size, int unitSystem=10, qreal multiplier=1.0)
Definition util.cpp:215
bool isSafeString(const QVariant &input)
Definition util.cpp:117
Cutelee::SafeString getSafeString(const QVariant &input)
Definition util.cpp:108
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition util.cpp:90
Cutelee::SafeString markForEscaping(const Cutelee::SafeString &input)
Definition util.cpp:98
void append(QList< T > &&value)
T & first()
T & last()
qsizetype size() const const
NormalizationForm_KD
QString & append(QChar ch)
const QChar at(qsizetype position) const const
QString::iterator begin()
QString::const_iterator constBegin() const const
QString::const_iterator constEnd() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString::iterator end()
QString leftJustified(qsizetype width, QChar fill, bool truncate) const const
qsizetype length() const const
QString normalized(QString::NormalizationForm mode, int version) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
QString rightJustified(qsizetype width, QChar fill, bool truncate) const const
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QString toLower() const const
QString toUpper() const const
QString trimmed() const const
void truncate(qsizetype position)
QString join(QChar separator) const const
SkipEmptyParts
bool canConvert() const const
bool isValid() const const
void setValue(QVariant &&value)
double toDouble(bool *ok) const const
qreal toReal(bool *ok) const const
QString toString() const const
int userType() const const
T value() const &const
Utility functions used throughout Cutelee.