guess sh doesn't like those redirects
[m6w6/ext-pq] / src / php_pq_misc.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18 #include <ext/date/php_date.h>
19 #include <ext/standard/php_string.h>
20
21 #include <Zend/zend_interfaces.h>
22
23 #include <libpq/libpq-fs.h>
24
25 #include "php_pq.h"
26 #include "php_pqexc.h"
27 #include "php_pq_misc.h"
28 #undef PHP_PQ_TYPE
29 #include "php_pq_type.h"
30
31 char *php_pq_rtrim(char *e)
32 {
33 size_t l = strlen(e);
34
35 while (l-- > 0 && e[l] == '\n') {
36 e[l] = '\0';
37 }
38 return e;
39 }
40
41 const char *php_pq_strmode(long mode)
42 {
43 switch (mode & (INV_READ|INV_WRITE)) {
44 case INV_READ|INV_WRITE:
45 return "rw";
46 case INV_READ:
47 return "r";
48 case INV_WRITE:
49 return "w";
50 default:
51 return "-";
52 }
53 }
54
55 int php_pq_compare_index(const void *lptr, const void *rptr TSRMLS_DC)
56 {
57 const Bucket *l = *(const Bucket **) lptr;
58 const Bucket *r = *(const Bucket **) rptr;
59
60 if (l->h < r->h) {
61 return -1;
62 }
63 if (l->h > r->h) {
64 return 1;
65 }
66 return 0;
67 }
68
69 zend_class_entry *php_pqdt_class_entry;
70
71 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0)
72 ZEND_END_ARG_INFO();
73 static PHP_METHOD(pqdt, __toString)
74 {
75 zval *rv = NULL;
76
77 zend_call_method_with_1_params(&getThis(), php_pqdt_class_entry, NULL, "format", &rv,
78 zend_read_property(php_pqdt_class_entry, getThis(), ZEND_STRL("format"), 0 TSRMLS_CC));
79 if (rv) {
80 RETVAL_ZVAL(rv, 1, 1);
81 }
82 }
83
84 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_create_from_format, 0, 0, 2)
85 ZEND_ARG_INFO(0, format)
86 ZEND_ARG_INFO(0, datetime)
87 ZEND_ARG_INFO(0, timezone)
88 ZEND_END_ARG_INFO();
89 static PHP_METHOD(pqdt, createFromFormat)
90 {
91 zend_error_handling zeh;
92 char *fmt_str, *dt_str;
93 int fmt_len, dt_len;
94 zval *ztz = NULL;
95 ZEND_RESULT_CODE rv;
96
97 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
98 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &fmt_str, &fmt_len, &dt_str, &dt_len, &ztz, php_date_get_timezone_ce());
99 zend_restore_error_handling(&zeh TSRMLS_CC);
100
101 if (SUCCESS == rv) {
102 php_pqdt_from_string(return_value, fmt_str, dt_str, dt_len, "Y-m-d H:i:s.uO", ztz TSRMLS_CC);
103 }
104 }
105
106 static zend_function_entry php_pqdt_methods[] = {
107 PHP_ME(pqdt, createFromFormat, ai_pqdt_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
108 PHP_ME(pqdt, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC)
109 PHP_MALIAS(pqdt, jsonSerialize, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC)
110 {0}
111 };
112
113 zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, char *output_fmt, zval *ztimezone TSRMLS_DC)
114 {
115 php_date_obj *dobj;
116
117 if (!zv) {
118 MAKE_STD_ZVAL(zv);
119 }
120
121 php_date_instantiate(php_pqdt_class_entry, zv TSRMLS_CC);
122 dobj = zend_object_store_get_object(zv TSRMLS_CC);
123 if (!php_date_initialize(dobj, dt_str, dt_len, input_fmt, ztimezone, 1 TSRMLS_CC)) {
124 zval_dtor(zv);
125 ZVAL_NULL(zv);
126 } else if (output_fmt) {
127 zend_update_property_string(php_pqdt_class_entry, zv, ZEND_STRL("format"), output_fmt TSRMLS_CC);
128 }
129
130 return zv;
131 }
132
133 void php_pqdt_to_string(zval *zdt, const char *format, char **str_buf, size_t *str_len TSRMLS_DC)
134 {
135 zval rv;
136
137 INIT_PZVAL(&rv);
138 ZVAL_NULL(&rv);
139
140 if (Z_OBJ_HT_P(zdt)->cast_object
141 && SUCCESS == Z_OBJ_HT_P(zdt)->cast_object(zdt, &rv, IS_STRING TSRMLS_CC)
142 ) {
143 *str_len = Z_STRLEN(rv);
144 *str_buf = Z_STRVAL(rv);
145 } else if (instanceof_function(Z_OBJCE_P(zdt), php_date_get_date_ce() TSRMLS_CC)) {
146 zval *rv = NULL, *zfmt;
147
148 MAKE_STD_ZVAL(zfmt);
149 ZVAL_STRING(zfmt, format, 1);
150 zend_call_method_with_1_params(&zdt, Z_OBJCE_P(zdt), NULL, "format", &rv, zfmt);
151 zval_ptr_dtor(&zfmt);
152
153 if (rv) {
154 if (Z_TYPE_P(rv) == IS_STRING) {
155 *str_len = Z_STRLEN_P(rv);
156 *str_buf = estrndup(Z_STRVAL_P(rv), *str_len);
157 }
158 zval_ptr_dtor(&rv);
159 }
160 }
161 }
162
163 zend_class_entry *php_pqconv_class_entry;
164
165 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_types, 0, 0, 0)
166 ZEND_END_ARG_INFO();
167
168 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_from_string, 0, 0, 2)
169 ZEND_ARG_INFO(0, data)
170 ZEND_ARG_INFO(0, type)
171 ZEND_END_ARG_INFO();
172
173 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_to_string, 0, 0, 2)
174 ZEND_ARG_INFO(0, data)
175 ZEND_ARG_INFO(0, type)
176 ZEND_END_ARG_INFO();
177
178 zend_function_entry php_pqconv_methods[] = {
179 PHP_ABSTRACT_ME(pqconv, convertTypes, ai_pqconv_convert_types)
180 PHP_ABSTRACT_ME(pqconv, convertFromString, ai_pqconv_convert_from_string)
181 PHP_ABSTRACT_ME(pqconv, convertToString, ai_pqconv_convert_to_string)
182 {0}
183 };
184
185
186 PHP_MINIT_FUNCTION(pq_misc)
187 {
188 zend_class_entry **json, ce = {0};
189
190 INIT_NS_CLASS_ENTRY(ce, "pq", "Converter", php_pqconv_methods);
191 php_pqconv_class_entry = zend_register_internal_interface(&ce TSRMLS_CC);
192
193 memset(&ce, 0, sizeof(ce));
194 INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods);
195 php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce(), "DateTime" TSRMLS_CC);
196
197 zend_declare_property_stringl(php_pqdt_class_entry, ZEND_STRL("format"), ZEND_STRL("Y-m-d H:i:s.uO"), ZEND_ACC_PUBLIC TSRMLS_CC);
198
199 /* stop reading this file right here! */
200 if (SUCCESS == zend_hash_find(CG(class_table), ZEND_STRS("jsonserializable"), (void *) &json)) {
201 zend_class_implements(php_pqdt_class_entry TSRMLS_CC, 1, *json);
202 }
203
204 return SUCCESS;
205 }
206
207 typedef struct _HashTableList {
208 HashTable ht;
209 struct _HashTableList *parent;
210 } HashTableList;
211
212 typedef struct _ArrayParserState {
213 const char *ptr, *end;
214 HashTableList *list;
215 php_pqres_t *res;
216 #ifdef ZTS
217 void ***ts;
218 #endif
219 Oid typ;
220 unsigned quotes:1;
221 unsigned escaped:1;
222 } ArrayParserState;
223
224 static char caa(ArrayParserState *a, const char *any, unsigned advance)
225 {
226 const char *p = any;
227 TSRMLS_FETCH_FROM_CTX(a->ts);
228
229 do {
230 if (*p == *a->ptr) {
231 a->ptr += advance;
232 return *p;
233 }
234 } while (*++p);
235
236 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse array: expected one of '%s', got '%c'", any, *a->ptr); \
237 return 0;
238 }
239
240 static ZEND_RESULT_CODE add_element(ArrayParserState *a, const char *start)
241 {
242 zval *zelem;
243 size_t el_len = a->ptr - start;
244 char *el_str = estrndup(start, el_len);
245 TSRMLS_FETCH_FROM_CTX(a->ts);
246
247 if (a->quotes) {
248 int tmp_len = el_len;
249
250 php_stripslashes(el_str, &tmp_len TSRMLS_CC);
251 el_len = tmp_len;
252 } else if ((a->ptr - start == 4) && !strncmp(start, "NULL", 4)) {
253 efree(el_str);
254 el_str = NULL;
255 el_len = 0;
256 }
257
258 if (!el_str) {
259 MAKE_STD_ZVAL(zelem);
260 ZVAL_NULL(zelem);
261 } else {
262 zelem = php_pqres_typed_zval(a->res, el_str, el_len, a->typ TSRMLS_CC);
263
264 efree(el_str);
265 }
266
267 return zend_hash_next_index_insert(&a->list->ht, &zelem, sizeof(zval *), NULL);
268 }
269
270 static ZEND_RESULT_CODE parse_array(ArrayParserState *a);
271
272 static ZEND_RESULT_CODE parse_element(ArrayParserState *a, char delim)
273 {
274 const char *el;
275 TSRMLS_FETCH_FROM_CTX(a->ts);
276
277 switch (*a->ptr) {
278 case '{':
279 return parse_array(a);
280
281 case '"':
282 a->quotes = 1;
283 ++a->ptr;
284 break;
285 }
286
287 for (el = a->ptr; a->ptr < a->end; ++a->ptr) {
288 switch (*a->ptr) {
289 case '\\':
290 a->escaped = !a->escaped;
291 break;
292
293 case '"':
294 if (a->escaped) {
295 a->escaped = 0;
296 } else if (a->quotes) {
297 if (SUCCESS != add_element(a, el)) {
298 return FAILURE;
299 }
300 a->quotes = 0;
301 ++a->ptr;
302 return SUCCESS;
303 } else {
304 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, unexpected quote: '%.*s'", (int) (a->ptr - el), el);
305 return FAILURE;
306 }
307 break;
308
309 default:
310 if (delim != *a->ptr) {
311 a->escaped = 0;
312 break;
313 }
314 /* no break */
315 case '}':
316 if (!a->quotes) {
317 return add_element(a, el);
318 }
319 break;
320
321 }
322 }
323
324 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, reached end of input");
325 return FAILURE;
326 }
327
328 static ZEND_RESULT_CODE parse_elements(ArrayParserState *a)
329 {
330 char delims[] = {'}', PHP_PQ_DELIM_OF_ARRAY(a->typ), 0};
331 TSRMLS_FETCH_FROM_CTX(a->ts);
332
333 while (SUCCESS == parse_element(a, delims[1])) {
334 switch (caa(a, delims, 0)) {
335 case 0:
336 return FAILURE;
337
338 case '}':
339 return SUCCESS;
340
341 default:
342 if (!*++a->ptr) {
343 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse elements, reached end of input");
344 return FAILURE;
345 }
346 break;
347 }
348 }
349
350 return FAILURE;
351 }
352
353 static ZEND_RESULT_CODE parse_array(ArrayParserState *a)
354 {
355 HashTableList *list;
356
357 if (!caa(a, "{", 1)) {
358 return FAILURE;
359 }
360
361 list = ecalloc(1, sizeof(*list));
362 ZEND_INIT_SYMTABLE(&list->ht);
363
364 if (a->list) {
365 zval *zcur;
366
367 MAKE_STD_ZVAL(zcur);
368 Z_TYPE_P(zcur) = IS_ARRAY;
369 Z_ARRVAL_P(zcur) = &list->ht;
370
371 zend_hash_next_index_insert(&a->list->ht, &zcur, sizeof(zval *), NULL);
372
373 list->parent = a->list;
374 }
375 a->list = list;
376
377 if (SUCCESS != parse_elements(a)) {
378 return FAILURE;
379 }
380
381 if (!caa(a, "}", 1)) {
382 return FAILURE;
383 }
384
385 if (a->list->parent) {
386 a->list = a->list->parent;
387 }
388
389 return SUCCESS;
390 }
391
392 HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ TSRMLS_DC)
393 {
394 HashTable *ht = NULL;
395 ArrayParserState a = {0};
396 TSRMLS_SET_CTX(a.ts);
397
398 a.typ = typ;
399 a.ptr = val_str;
400 a.end = val_str + val_len;
401 a.res = res;
402
403 if (SUCCESS != parse_array(&a)) {
404 while (a.list) {
405 HashTableList *l = a.list->parent;
406
407 zend_hash_destroy(&a.list->ht);
408 efree(a.list);
409 a.list = l;
410 }
411 return ht;
412 }
413
414 if (*a.ptr) {
415 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Trailing input: '%s'", a.ptr);
416 }
417
418 do {
419 ht = &a.list->ht;
420 } while ((a.list = a.list->parent));
421
422 return ht;
423 }
424
425
426 /*
427 * Local variables:
428 * tab-width: 4
429 * c-basic-offset: 4
430 * End:
431 * vim600: noet sw=4 ts=4 fdm=marker
432 * vim<600: noet sw=4 ts=4
433 */