Electroneum
query_string.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include <string>
6 #include <vector>
7 #include <iostream>
8 
9 // ----------------------------------------------------------------------------
10 // qs_parse (modified)
11 // https://github.com/bartgrantham/qs_parse
12 // ----------------------------------------------------------------------------
13 /* Similar to strncmp, but handles URL-encoding for either string */
14 int qs_strncmp(const char * s, const char * qs, size_t n);
15 
16 
17 /* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
18  * Also decodes the value portion of the k/v pair *in-place*. In a future
19  * enhancement it will also have a compile-time option of sorting qs_kv
20  * alphabetically by key. */
21 int qs_parse(char * qs, char * qs_kv[], int qs_kv_size);
22 
23 
24 /* Used by qs_parse to decode the value portion of a k/v pair */
25 int qs_decode(char * qs);
26 
27 
28 /* Looks up the value according to the key on a pre-processed query string
29  * A future enhancement will be a compile-time option to look up the key
30  * in a pre-sorted qs_kv array via a binary search. */
31 //char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
32  char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth);
33 
34 
35 /* Non-destructive lookup of value, based on key. User provides the
36  * destinaton string and length. */
37 char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len);
38 
39 // TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
40 #undef _qsSORTING
41 
42 // isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
43 #define CROW_QS_ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0)
44 #define CROW_QS_HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0)
45 #define CROW_QS_ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1)
46 
47 inline int qs_strncmp(const char * s, const char * qs, size_t n)
48 {
49  int i=0;
50  unsigned char u1, u2, unyb, lnyb;
51 
52  while(n-- > 0)
53  {
54  u1 = (unsigned char) *s++;
55  u2 = (unsigned char) *qs++;
56 
57  if ( ! CROW_QS_ISQSCHR(u1) ) { u1 = '\0'; }
58  if ( ! CROW_QS_ISQSCHR(u2) ) { u2 = '\0'; }
59 
60  if ( u1 == '+' ) { u1 = ' '; }
61  if ( u1 == '%' ) // easier/safer than scanf
62  {
63  unyb = (unsigned char) *s++;
64  lnyb = (unsigned char) *s++;
65  if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) )
66  u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb);
67  else
68  u1 = '\0';
69  }
70 
71  if ( u2 == '+' ) { u2 = ' '; }
72  if ( u2 == '%' ) // easier/safer than scanf
73  {
74  unyb = (unsigned char) *qs++;
75  lnyb = (unsigned char) *qs++;
76  if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) )
77  u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb);
78  else
79  u2 = '\0';
80  }
81 
82  if ( u1 != u2 )
83  return u1 - u2;
84  if ( u1 == '\0' )
85  return 0;
86  i++;
87  }
88  if ( CROW_QS_ISQSCHR(*qs) )
89  return -1;
90  else
91  return 0;
92 }
93 
94 
95 inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
96 {
97  int i, j;
98  char * substr_ptr;
99 
100  for(i=0; i<qs_kv_size; i++) qs_kv[i] = NULL;
101 
102  // find the beginning of the k/v substrings or the fragment
103  substr_ptr = qs + strcspn(qs, "?#");
104  if (substr_ptr[0] != '\0')
105  substr_ptr++;
106  else
107  return 0; // no query or fragment
108 
109  i=0;
110  while(i<qs_kv_size)
111  {
112  qs_kv[i] = substr_ptr;
113  j = strcspn(substr_ptr, "&");
114  if ( substr_ptr[j] == '\0' ) { break; }
115  substr_ptr += j + 1;
116  i++;
117  }
118  i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs
119 
120  // we only decode the values in place, the keys could have '='s in them
121  // which will hose our ability to distinguish keys from values later
122  for(j=0; j<i; j++)
123  {
124  substr_ptr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
125  if ( substr_ptr[0] == '&' || substr_ptr[0] == '\0') // blank value: skip decoding
126  substr_ptr[0] = '\0';
127  else
128  qs_decode(++substr_ptr);
129  }
130 
131 #ifdef _qsSORTING
132 // TODO: qsort qs_kv, using qs_strncmp() for the comparison
133 #endif
134 
135  return i;
136 }
137 
138 
139 inline int qs_decode(char * qs)
140 {
141  int i=0, j=0;
142 
143  while( CROW_QS_ISQSCHR(qs[j]) )
144  {
145  if ( qs[j] == '+' ) { qs[i] = ' '; }
146  else if ( qs[j] == '%' ) // easier/safer than scanf
147  {
148  if ( ! CROW_QS_ISHEX(qs[j+1]) || ! CROW_QS_ISHEX(qs[j+2]) )
149  {
150  qs[i] = '\0';
151  return i;
152  }
153  qs[i] = (CROW_QS_HEX2DEC(qs[j+1]) * 16) + CROW_QS_HEX2DEC(qs[j+2]);
154  j+=2;
155  }
156  else
157  {
158  qs[i] = qs[j];
159  }
160  i++; j++;
161  }
162  qs[i] = '\0';
163 
164  return i;
165 }
166 
167 
168 inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth = 0)
169 {
170  int i;
171  size_t key_len, skip;
172 
173  key_len = strlen(key);
174 
175 #ifdef _qsSORTING
176 // TODO: binary search for key in the sorted qs_kv
177 #else // _qsSORTING
178  for(i=0; i<qs_kv_size; i++)
179  {
180  // we rely on the unambiguous '=' to find the value in our k/v pair
181  if ( qs_strncmp(key, qs_kv[i], key_len) == 0 )
182  {
183  skip = strcspn(qs_kv[i], "=");
184  if ( qs_kv[i][skip] == '=' )
185  skip++;
186  // return (zero-char value) ? ptr to trailing '\0' : ptr to value
187  if(nth == 0)
188  return qs_kv[i] + skip;
189  else
190  --nth;
191  }
192  }
193 #endif // _qsSORTING
194 
195  return NULL;
196 }
197 
198 
199 inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len)
200 {
201  size_t i, key_len;
202  const char * tmp;
203 
204  // find the beginning of the k/v substrings
205  if ( (tmp = strchr(qs, '?')) != NULL )
206  qs = tmp + 1;
207 
208  key_len = strlen(key);
209  while(qs[0] != '#' && qs[0] != '\0')
210  {
211  if ( qs_strncmp(key, qs, key_len) == 0 )
212  break;
213  qs += strcspn(qs, "&") + 1;
214  }
215 
216  if ( qs[0] == '\0' ) return NULL;
217 
218  qs += strcspn(qs, "=&#");
219  if ( qs[0] == '=' )
220  {
221  qs++;
222  i = strcspn(qs, "&=#");
223  strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1));
224  qs_decode(val);
225  }
226  else
227  {
228  if ( val_len > 0 )
229  val[0] = '\0';
230  }
231 
232  return val;
233 }
234 // ----------------------------------------------------------------------------
235 
236 
237 namespace crow
238 {
240  {
241  public:
242  static const int MAX_KEY_VALUE_PAIRS_COUNT = 256;
243 
245  {
246 
247  }
248 
250  : url_(qs.url_)
251  {
252  for(auto p:qs.key_value_pairs_)
253  {
254  key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
255  }
256  }
257 
259  {
260  url_ = qs.url_;
261  key_value_pairs_.clear();
262  for(auto p:qs.key_value_pairs_)
263  {
264  key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
265  }
266  return *this;
267  }
268 
270  {
271  key_value_pairs_ = std::move(qs.key_value_pairs_);
272  char* old_data = (char*)qs.url_.c_str();
273  url_ = std::move(qs.url_);
274  for(auto& p:key_value_pairs_)
275  {
276  p += (char*)url_.c_str() - old_data;
277  }
278  return *this;
279  }
280 
281 
282  query_string(std::string url)
283  : url_(std::move(url))
284  {
285  if (url_.empty())
286  return;
287 
289 
291  key_value_pairs_.resize(count);
292  }
293 
294  void clear()
295  {
296  key_value_pairs_.clear();
297  url_.clear();
298  }
299 
300  friend std::ostream& operator<<(std::ostream& os, const query_string& qs)
301  {
302  os << "[ ";
303  for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) {
304  if (i)
305  os << ", ";
306  os << qs.key_value_pairs_[i];
307  }
308  os << " ]";
309  return os;
310 
311  }
312 
313  char* get (const std::string& name) const
314  {
315  char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size());
316  return ret;
317  }
318 
319  std::vector<char*> get_list (const std::string& name) const
320  {
321  std::vector<char*> ret;
322  std::string plus = name + "[]";
323  char* element = nullptr;
324 
325  int count = 0;
326  while(1)
327  {
328  element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++);
329  if (!element)
330  break;
331  ret.push_back(element);
332  }
333  return ret;
334  }
335 
336 
337  private:
338  std::string url_;
339  std::vector<char*> key_value_pairs_;
340  };
341 
342 } // end namespace
static const int MAX_KEY_VALUE_PAIRS_COUNT
Definition: query_string.h:242
int qs_strncmp(const char *s, const char *qs, size_t n)
Definition: query_string.h:47
void clear()
Definition: query_string.h:294
#define CROW_QS_ISHEX(x)
Definition: query_string.h:43
Definition: block_queue.cpp:41
#define CROW_QS_HEX2DEC(x)
Definition: query_string.h:44
std::vector< char * > key_value_pairs_
Definition: query_string.h:339
#define CROW_QS_ISQSCHR(x)
Definition: query_string.h:45
friend std::ostream & operator<<(std::ostream &os, const query_string &qs)
Definition: query_string.h:300
#define u1(p)
Definition: aesb.c:111
std::vector< char * > get_list(const std::string &name) const
Definition: query_string.h:319
char * qs_k2v(const char *key, char *const *qs_kv, int qs_kv_size, int nth)
Definition: query_string.h:168
int qs_parse(char *qs, char *qs_kv[], int qs_kv_size)
Definition: query_string.h:95
query_string & operator=(const query_string &qs)
Definition: query_string.h:258
std::string url_
Definition: query_string.h:338
#define u2(p)
Definition: aesb.c:112
query_string()
Definition: query_string.h:244
Definition: ci_map.h:7
int qs_decode(char *qs)
Definition: query_string.h:139
char * qs_scanvalue(const char *key, const char *qs, char *val, size_t val_len)
Definition: query_string.h:199
const char * name
Definition: simplewallet.cpp:180
query_string(const query_string &qs)
Definition: query_string.h:249
query_string(std::string url)
Definition: query_string.h:282
#define s(x, c)
Definition: aesb.c:46
Definition: query_string.h:239