Cutelyst  3.1.0
utils.cpp
1 /*
2  * Copyright (C) 2015-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "utils.h"
19 
20 #include <QTextStream>
21 #include <QVector>
22 
23 using namespace Cutelyst;
24 
25 QByteArray buildTableDivision(const QVector<int> &columnsSize)
26 {
27  QByteArray buffer;
28 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
29  QTextStream out(&buffer, QTextStream::WriteOnly);
30 #else
31  QTextStream out(&buffer, QIODevice::WriteOnly);
32 #endif
33  for (int i = 0; i < columnsSize.size(); ++i) {
34  if (i) {
35  out << '+';
36  } else {
37  out << '.';
38  }
39  out << QByteArray().fill('-', columnsSize[i] + 2).data();
40  }
41  out << '.';
42 
43  return buffer;
44 }
45 
46 QByteArray Utils::buildTable(const QVector<QStringList> &table, const QStringList &headers, const QString &title)
47 {
48  QByteArray buffer;
49  QVector<int> columnsSize;
50 
51  if (!headers.isEmpty()) {
52  for (const QString &header : headers) {
53  columnsSize.push_back(header.size());
54  }
55  } else {
56  for (const QStringList &rows : table) {
57  if (columnsSize.empty()) {
58  for (const QString &row : rows) {
59  columnsSize.push_back(row.size());
60  }
61  } else if (rows.size() != columnsSize.size()) {
62  qFatal("Incomplete table");
63  }
64  }
65  }
66 
67  for (const QStringList &row : table) {
68  if (row.size() > columnsSize.size()) {
69  qFatal("Incomplete table");
70  break;
71  }
72 
73  for (int i = 0; i < row.size(); ++i) {
74  columnsSize[i] = qMax(columnsSize[i], row[i].size());
75  }
76  }
77 
78  // printing
79 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
80  QTextStream out(&buffer, QTextStream::WriteOnly);
81 #else
82  QTextStream out(&buffer, QIODevice::WriteOnly);
83 #endif
84 
85  out.setFieldAlignment(QTextStream::AlignLeft);
86  QByteArray div = buildTableDivision(columnsSize);
87 
88  if (!title.isEmpty()) {
89  out << title << '\n';
90  }
91 
92  // Top line
93  out << div << '\n';
94 
95  if (!headers.isEmpty()) {
96  // header titles
97  for (int i = 0; i < headers.size(); ++i) {
98  out << "| ";
99 
100  out.setFieldWidth(columnsSize[i]);
101  out << headers[i];
102 
103  out.setFieldWidth(0);
104  out << ' ';
105  }
106  out << '|' << '\n';
107 
108  // header bottom line
109  out << div << '\n';
110  }
111 
112  for (const QStringList &row : table) {
113  // content table
114  for (int i = 0; i < row.size(); ++i) {
115  out << "| ";
116 
117  out.setFieldWidth(columnsSize[i]);
118  out << row[i];
119 
120  out.setFieldWidth(0);
121  out << ' ';
122  }
123  out << '|' << '\n';
124  }
125 
126  // table bottom line
127  out << div;
128 
129  return buffer;
130 }
131 
132 QString Utils::decodePercentEncoding(QString *s)
133 {
134  if (s->isEmpty()) {
135  return *s;
136  }
137 
138  QByteArray ba = s->toLatin1();
139 
140  char *data = ba.data();
141  const char *inputPtr = data;
142 
143  const int len = ba.count();
144  bool skipUtf8 = true;
145  int outlen = 0;
146  for (int i = 0 ; i < len; ++i, ++outlen) {
147  const char c = inputPtr[i];
148  if (c == '%' && i + 2 < len) {
149  int a = inputPtr[++i];
150  int b = inputPtr[++i];
151 
152  if (a >= '0' && a <= '9') a -= '0';
153  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
154  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
155 
156  if (b >= '0' && b <= '9') b -= '0';
157  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
158  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
159 
160  *data++ = (char)((a << 4) | b);
161  skipUtf8 = false;
162  } else if (c == '+') {
163  *data++ = ' ';
164  } else {
165  *data++ = c;
166  }
167  }
168 
169  if (skipUtf8) {
170  return *s;
171  }
172 
173  return QString::fromUtf8(ba.data(), outlen);
174 }
175 
176 ParamsMultiMap Utils::decodePercentEncoding(char *data, int len)
177 {
178  ParamsMultiMap ret;
179  if (len <= 0) {
180  return ret;
181  }
182 
183  QString key;
184 
185  const char *inputPtr = data;
186 
187  bool hasKey = false;
188  bool skipUtf8 = true;
189  char *from = data;
190  int outlen = 0;
191 
192  auto processKeyPair = [&] {
193  if (hasKey) {
194  if ((data - from) == 0) {
195  if (!key.isEmpty()) {
196  ret.insertMulti(key, {});
197  }
198  } else {
199  ret.insertMulti(key, skipUtf8 ? QString::fromLatin1(from, data - from) : QString::fromUtf8(from, data - from));
200  }
201  } else if ((data - from) > 0) {
202  ret.insertMulti(skipUtf8 ? QString::fromLatin1(from, data - from) : QString::fromUtf8(from, data - from), {});
203  }
204  };
205 
206  for (int i = 0; i < len; ++i, ++outlen) {
207  const char c = inputPtr[i];
208  if (c == '%' && i + 2 < len) {
209  int a = inputPtr[++i];
210  int b = inputPtr[++i];
211 
212  if (a >= '0' && a <= '9') a -= '0';
213  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
214  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
215 
216  if (b >= '0' && b <= '9') b -= '0';
217  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
218  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
219 
220  *data++ = (char)((a << 4) | b);
221  skipUtf8 = false;
222  } else if (c == '+') {
223  *data++ = ' ';
224  } else if (c == '=') {
225  key = skipUtf8 ? QString::fromLatin1(from, data - from) : QString::fromUtf8(from, data - from);
226  from = data;
227  hasKey = true;
228  skipUtf8 = true; // reset
229  } else if (c == '&') {
230  processKeyPair();
231  key.clear();
232  hasKey = false;
233  from = data;
234  skipUtf8 = true; // reset
235  } else {
236  *data++ = c;
237  }
238  }
239 
240  processKeyPair();
241 
242  return ret;
243 }
244 
245 QString Utils::decodePercentEncoding(QByteArray *ba)
246 {
247  if (ba->isEmpty()) {
248  return {};
249  }
250 
251  char *data = ba->data();
252  const char *inputPtr = data;
253 
254  int len = ba->count();
255  bool skipUtf8 = true;
256  int outlen = 0;
257  for (int i = 0; i < len; ++i, ++outlen) {
258  const char c = inputPtr[i];
259  if (c == '%' && i + 2 < len) {
260  int a = inputPtr[++i];
261  int b = inputPtr[++i];
262 
263  if (a >= '0' && a <= '9') a -= '0';
264  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
265  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
266 
267  if (b >= '0' && b <= '9') b -= '0';
268  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
269  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
270 
271  *data++ = (char)((a << 4) | b);
272  skipUtf8 = false;
273  } else if (c == '+') {
274  *data++ = ' ';
275  } else {
276  *data++ = c;
277  }
278  }
279 
280  if (skipUtf8) {
281  return QString::fromLatin1(ba->data(), outlen);
282  } else {
283  return QString::fromUtf8(ba->data(), outlen);
284  }
285 }
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
int count(char ch) const const
char * data()
QByteArray & fill(char ch, int size)
bool isEmpty() const const
bool isEmpty() const const
QMap::iterator insertMulti(const Key &key, const T &value)
void clear()
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)
bool isEmpty() const const
QByteArray toLatin1() const const
bool empty() const const
void push_back(const T &value)
int size() const const