4eec3b8795fb965f69fe3de915c6c5503f4a2fca
[m6w6/ext-pq] / src / php_pqstm.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 <Zend/zend_smart_str.h>
19
20 #include "php_pq.h"
21 #include "php_pq_misc.h"
22 #include "php_pq_object.h"
23 #include "php_pqexc.h"
24 #include "php_pqconn.h"
25 #include "php_pqres.h"
26 #include "php_pqstm.h"
27
28 zend_class_entry *php_pqstm_class_entry;
29 static zend_object_handlers php_pqstm_object_handlers;
30 static HashTable php_pqstm_object_prophandlers;
31
32 static void php_pqstm_deallocate(php_pqstm_object_t *obj, zend_bool async, zend_bool silent)
33 {
34 if (obj->intern->allocated) {
35 char *quoted_name = PQescapeIdentifier(obj->intern->conn->intern->conn, obj->intern->name, strlen(obj->intern->name));
36
37 if (quoted_name) {
38 smart_str cmd = {0};
39
40 smart_str_appends(&cmd, "DEALLOCATE ");
41 smart_str_appends(&cmd, quoted_name);
42 smart_str_0(&cmd);
43
44 if (async) {
45 if (PQsendQuery(obj->intern->conn->intern->conn, smart_str_v(&cmd))) {
46 obj->intern->conn->intern->poller = PQconsumeInput;
47 php_pqconn_notify_listeners(obj->intern->conn);
48 } else if (!silent) {
49 throw_exce(EX_IO, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
50 }
51 } else {
52 PGresult *res;
53
54 if ((res = php_pq_exec(obj->intern->conn->intern->conn, smart_str_v(&cmd)))) {
55 php_pqres_clear(res);
56 } else if (!silent) {
57 throw_exce(EX_RUNTIME, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
58 }
59 }
60
61 PQfreemem(quoted_name);
62 smart_str_free(&cmd);
63 }
64
65 obj->intern->allocated = 0;
66 }
67 }
68
69 static void php_pqstm_object_free(zend_object *o)
70 {
71 php_pqstm_object_t *obj = PHP_PQ_OBJ(NULL, o);
72 #if DBG_GC
73 fprintf(stderr, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj->zo.handle, obj, obj->intern->conn->zo.handle, obj->intern->conn);
74 #endif
75 if (obj->intern) {
76 if (obj->intern->conn->intern) {
77 php_pq_callback_dtor(&obj->intern->conn->intern->onevent);
78 php_pqstm_deallocate(obj, 0, 1);
79 php_pq_object_delref(obj->intern->conn);
80 }
81 efree(obj->intern->name);
82 efree(obj->intern->query);
83 zend_hash_destroy(&obj->intern->bound);
84 if (obj->intern->params) {
85 php_pq_params_free(&obj->intern->params);
86 }
87 efree(obj->intern);
88 obj->intern = NULL;
89 }
90 php_pq_object_dtor(o);
91 }
92
93 php_pqstm_object_t *php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern)
94 {
95 return php_pq_object_create(ce, intern, sizeof(php_pqstm_object_t),
96 &php_pqstm_object_handlers, &php_pqstm_object_prophandlers);
97 }
98
99 static zend_object *php_pqstm_create_object(zend_class_entry *class_type)
100 {
101 return &php_pqstm_create_object_ex(class_type, NULL)->zo;
102 }
103
104 static void php_pqstm_object_read_name(zval *object, void *o, zval *return_value)
105 {
106 php_pqstm_object_t *obj = o;
107
108 RETVAL_STRING(obj->intern->name);
109 }
110
111 static void php_pqstm_object_read_connection(zval *object, void *o, zval *return_value)
112 {
113 php_pqstm_object_t *obj = o;
114
115 php_pq_object_to_zval(obj->intern->conn, return_value);
116 }
117
118 static void php_pqstm_object_gc_connection(zval *object, void *o, zval *return_value)
119 {
120 php_pqstm_object_t *obj = o;
121 zval zconn;
122
123 php_pq_object_to_zval_no_addref(obj->intern->conn, &zconn);
124 add_next_index_zval(return_value, &zconn);
125 }
126
127 static void php_pqstm_object_read_query(zval *object, void *o, zval *return_value)
128 {
129 php_pqstm_object_t *obj = o;
130
131 RETVAL_STRING(obj->intern->query);
132 }
133
134 static void php_pqstm_object_read_types(zval *object, void *o, zval *return_value)
135 {
136 int i;
137 php_pqstm_object_t *obj = o;
138
139 array_init_size(return_value, obj->intern->params->type.count);
140 for (i = 0; i < obj->intern->params->type.count; i++) {
141 add_next_index_long(return_value, (long)obj->intern->params->type.oids[i]);
142 }
143 }
144
145 php_pqstm_t *php_pqstm_init(php_pqconn_object_t *conn, const char *name, const char *query, php_pq_params_t *params)
146 {
147 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
148
149 php_pq_object_addref(conn);
150 stm->conn = conn;
151 stm->name = estrdup(name);
152 stm->params = params;
153 stm->query = estrdup(query);
154 stm->allocated = 1;
155
156 ZEND_INIT_SYMTABLE(&stm->bound);
157
158 return stm;
159 }
160
161 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct, 0, 0, 3)
162 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
163 ZEND_ARG_INFO(0, name)
164 ZEND_ARG_INFO(0, query)
165 ZEND_ARG_ARRAY_INFO(0, types, 1)
166 ZEND_ARG_INFO(0, async)
167 ZEND_END_ARG_INFO();
168 static PHP_METHOD(pqstm, __construct) {
169 zend_error_handling zeh;
170 zval *zconn, *ztypes = NULL;
171 char *name_str, *query_str;
172 size_t name_len, *query_len;
173 zend_bool async = 0;
174 ZEND_RESULT_CODE rv;
175
176 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
177 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async);
178 zend_restore_error_handling(&zeh);
179
180 if (SUCCESS == rv) {
181 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
182 php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL);
183
184 if (obj->intern) {
185 throw_exce(EX_BAD_METHODCALL, "pq\\Statement already initialized");
186 } else if (!conn_obj->intern) {
187 throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized");
188 } else {
189 php_pq_params_t *params = php_pq_params_init(&conn_obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL);
190
191 if (async) {
192 rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, params);
193 } else {
194 rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, params);
195 }
196
197 if (SUCCESS == rv) {
198 obj->intern = php_pqstm_init(conn_obj, name_str, query_str, params);
199 }
200 }
201 }
202 }
203 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind, 0, 0, 2)
204 ZEND_ARG_INFO(0, param_no)
205 ZEND_ARG_INFO(1, param_ref)
206 ZEND_END_ARG_INFO();
207 static PHP_METHOD(pqstm, bind) {
208 zend_long param_no;
209 zval *param_ref;
210 zend_error_handling zeh;
211 ZEND_RESULT_CODE rv;
212
213 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
214 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &param_no, &param_ref);
215 zend_restore_error_handling(&zeh);
216
217 if (SUCCESS == rv) {
218 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
219
220 if (!obj->intern) {
221 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
222 } else if (!obj->intern->allocated) {
223 throw_exce(EX_UNINITIALIZED, "pq\\Statement has been deallocated");
224 } else {
225 Z_ADDREF_P(param_ref);
226 zend_hash_index_update(&obj->intern->bound, param_no, param_ref);
227 zend_hash_sort(&obj->intern->bound, php_pq_compare_index, 0);
228 }
229 }
230 }
231
232 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec, 0, 0, 0)
233 ZEND_ARG_ARRAY_INFO(0, params, 1)
234 ZEND_END_ARG_INFO();
235 static PHP_METHOD(pqstm, exec) {
236 zend_error_handling zeh;
237 zval *zparams = NULL;
238 ZEND_RESULT_CODE rv;
239
240 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
241 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|a/!", &zparams);
242 zend_restore_error_handling(&zeh);
243
244 if (SUCCESS == rv) {
245 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
246
247 if (!obj->intern) {
248 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
249 } else if (!obj->intern->allocated) {
250 throw_exce(EX_UNINITIALIZED, "pq\\Statement has been deallocated");
251 } else {
252 PGresult *res;
253
254 php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
255 res = php_pq_exec_prepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0);
256 php_pq_params_set_params(obj->intern->params, NULL);
257
258 if (!res) {
259 throw_exce(EX_RUNTIME, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
260 } else if (SUCCESS == php_pqres_success(res)) {
261 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), return_value);
262 php_pqconn_notify_listeners(obj->intern->conn);
263 }
264 }
265 }
266 }
267
268 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async, 0, 0, 0)
269 ZEND_ARG_ARRAY_INFO(0, params, 1)
270 ZEND_ARG_INFO(0, callable)
271 ZEND_END_ARG_INFO();
272 static PHP_METHOD(pqstm, execAsync) {
273 zend_error_handling zeh;
274 zval *zparams = NULL;
275 php_pq_callback_t resolver = {{0}};
276 ZEND_RESULT_CODE rv;
277
278 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
279 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|a/!f", &zparams, &resolver.fci, &resolver.fcc);
280 zend_restore_error_handling(&zeh);
281
282 if (SUCCESS == rv) {
283 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
284
285 if (!obj->intern) {
286 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
287 } else if (!obj->intern->allocated) {
288 throw_exce(EX_UNINITIALIZED, "pq\\Statement has been deallocated");
289 } else {
290 int rc;
291
292 php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
293 rc = PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0);
294 php_pq_params_set_params(obj->intern->params, NULL);
295
296 if (!rc) {
297 throw_exce(EX_IO, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
298 #if HAVE_PQSETSINGLEROWMODE
299 } else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) {
300 throw_exce(EX_RUNTIME, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
301 #endif
302 } else {
303 php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver);
304 obj->intern->conn->intern->poller = PQconsumeInput;
305 }
306
307 php_pqconn_notify_listeners(obj->intern->conn);
308 }
309 }
310 }
311
312 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0)
313 ZEND_END_ARG_INFO();
314 static PHP_METHOD(pqstm, desc) {
315 zend_error_handling zeh;
316 ZEND_RESULT_CODE rv;
317
318 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
319 rv = zend_parse_parameters_none();
320 zend_restore_error_handling(&zeh);
321
322 if (SUCCESS == rv) {
323 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
324
325 if (!obj->intern) {
326 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
327 } else if (!obj->intern->allocated) {
328 throw_exce(EX_UNINITIALIZED, "pq\\Statement has been deallocated");
329 } else {
330 PGresult *res = PQdescribePrepared(obj->intern->conn->intern->conn, obj->intern->name);
331
332 if (!res) {
333 throw_exce(EX_RUNTIME, "Failed to describe statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
334 } else {
335 if (SUCCESS == php_pqres_success(res)) {
336 int p, params;
337
338 array_init(return_value);
339 for (p = 0, params = PQnparams(res); p < params; ++p) {
340 add_next_index_long(return_value, PQparamtype(res, p));
341 }
342 }
343 php_pqres_clear(res);
344 php_pqconn_notify_listeners(obj->intern->conn);
345 }
346 }
347 }
348 }
349
350 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc_async, 0, 0, 1)
351 ZEND_ARG_INFO(0, callable)
352 ZEND_END_ARG_INFO();
353 static PHP_METHOD(pqstm, descAsync) {
354 zend_error_handling zeh;
355 php_pq_callback_t resolver = {{0}};
356 ZEND_RESULT_CODE rv;
357
358 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
359 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "f", &resolver.fci, &resolver.fcc);
360 zend_restore_error_handling(&zeh);
361
362 if (SUCCESS == rv) {
363 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
364
365 if (!obj->intern) {
366 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
367 } else if (!obj->intern->allocated) {
368 throw_exce(EX_UNINITIALIZED, "pq\\Statement has been deallocated");
369 } else if (!PQsendDescribePrepared(obj->intern->conn->intern->conn, obj->intern->name)) {
370 throw_exce(EX_IO, "Failed to describe statement: %s", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
371 } else {
372 php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver);
373 obj->intern->conn->intern->poller = PQconsumeInput;
374 php_pqconn_notify_listeners(obj->intern->conn);
375 }
376 }
377 }
378
379 static zend_always_inline void php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAMETERS, zend_bool async)
380 {
381 zend_error_handling zeh;
382 ZEND_RESULT_CODE rv;
383
384 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
385 rv = zend_parse_parameters_none();
386 zend_restore_error_handling(&zeh);
387
388 if (rv == SUCCESS) {
389 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
390
391 if (!obj->intern) {
392 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
393 } else {
394 php_pqstm_deallocate(obj, async, 0);
395 }
396 }
397 }
398
399 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate, 0, 0, 0)
400 ZEND_END_ARG_INFO();
401 static PHP_METHOD(pqstm, deallocate)
402 {
403 php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
404 }
405
406 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate_async, 0, 0, 0)
407 ZEND_END_ARG_INFO();
408 static PHP_METHOD(pqstm, deallocateAsync)
409 {
410 php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
411 }
412
413 static zend_always_inline void php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAMETERS, zend_bool async)
414 {
415 zend_error_handling zeh;
416 ZEND_RESULT_CODE rv;
417
418 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
419 rv = zend_parse_parameters_none();
420 zend_restore_error_handling(&zeh);
421
422 if (rv == SUCCESS) {
423 php_pqstm_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
424
425 if (!obj->intern) {
426 throw_exce(EX_UNINITIALIZED, "pq\\Statement not initialized");
427 } else if (!obj->intern->allocated) {
428 if (async) {
429 rv = php_pqconn_prepare_async(NULL, obj->intern->conn, obj->intern->name, obj->intern->query, obj->intern->params);
430 } else {
431 rv = php_pqconn_prepare(NULL, obj->intern->conn, obj->intern->name, obj->intern->query, obj->intern->params);
432 }
433
434 if (SUCCESS == rv) {
435 obj->intern->allocated = 1;
436 }
437 }
438 }
439 }
440
441 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare, 0, 0, 0)
442 ZEND_END_ARG_INFO();
443 static PHP_METHOD(pqstm, prepare)
444 {
445 php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
446 }
447
448 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare_async, 0, 0, 0)
449 ZEND_END_ARG_INFO();
450 static PHP_METHOD(pqstm, prepareAsync)
451 {
452 php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
453 }
454
455 static zend_function_entry php_pqstm_methods[] = {
456 PHP_ME(pqstm, __construct, ai_pqstm_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
457 PHP_ME(pqstm, bind, ai_pqstm_bind, ZEND_ACC_PUBLIC)
458 PHP_ME(pqstm, deallocate, ai_pqstm_deallocate, ZEND_ACC_PUBLIC)
459 PHP_ME(pqstm, deallocateAsync, ai_pqstm_deallocate_async, ZEND_ACC_PUBLIC)
460 PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC)
461 PHP_ME(pqstm, descAsync, ai_pqstm_desc_async, ZEND_ACC_PUBLIC)
462 PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC)
463 PHP_ME(pqstm, execAsync, ai_pqstm_exec_async, ZEND_ACC_PUBLIC)
464 PHP_ME(pqstm, prepare, ai_pqstm_prepare, ZEND_ACC_PUBLIC)
465 PHP_ME(pqstm, prepareAsync, ai_pqstm_prepare_async, ZEND_ACC_PUBLIC)
466 {0}
467 };
468
469 PHP_MSHUTDOWN_FUNCTION(pqstm)
470 {
471 zend_hash_destroy(&php_pqstm_object_prophandlers);
472 return SUCCESS;
473 }
474
475 PHP_MINIT_FUNCTION(pqstm)
476 {
477 zend_class_entry ce = {0};
478 php_pq_object_prophandler_t ph = {0};
479
480 INIT_NS_CLASS_ENTRY(ce, "pq", "Statement", php_pqstm_methods);
481 php_pqstm_class_entry = zend_register_internal_class_ex(&ce, NULL);
482 php_pqstm_class_entry->create_object = php_pqstm_create_object;
483
484 memcpy(&php_pqstm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
485 php_pqstm_object_handlers.offset = XtOffsetOf(php_pqstm_object_t, zo);
486 php_pqstm_object_handlers.free_obj = php_pqstm_object_free;
487 php_pqstm_object_handlers.read_property = php_pq_object_read_prop;
488 php_pqstm_object_handlers.write_property = php_pq_object_write_prop;
489 php_pqstm_object_handlers.clone_obj = NULL;
490 php_pqstm_object_handlers.get_property_ptr_ptr = NULL;
491 php_pqstm_object_handlers.get_gc = php_pq_object_get_gc;
492 php_pqstm_object_handlers.get_properties = php_pq_object_properties;
493 php_pqstm_object_handlers.get_debug_info = php_pq_object_debug_info;
494
495 zend_hash_init(&php_pqstm_object_prophandlers, 4, NULL, php_pq_object_prophandler_dtor, 1);
496
497 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC);
498 ph.read = php_pqstm_object_read_name;
499 zend_hash_str_add_mem(&php_pqstm_object_prophandlers, "name", sizeof("name")-1, (void *) &ph, sizeof(ph));
500
501 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC);
502 ph.read = php_pqstm_object_read_connection;
503 ph.gc = php_pqstm_object_gc_connection;
504 zend_hash_str_add_mem(&php_pqstm_object_prophandlers, "connection", sizeof("connection")-1, (void *) &ph, sizeof(ph));
505 ph.gc = NULL;
506
507 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC);
508 ph.read = php_pqstm_object_read_query;
509 zend_hash_str_add_mem(&php_pqstm_object_prophandlers, "query", sizeof("query")-1, (void *) &ph, sizeof(ph));
510
511 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("types"), ZEND_ACC_PUBLIC);
512 ph.read = php_pqstm_object_read_types;
513 zend_hash_str_add_mem(&php_pqstm_object_prophandlers, "types", sizeof("types")-1, (void *) &ph, sizeof(ph));
514
515 return SUCCESS;
516 }
517
518 /*
519 * Local variables:
520 * tab-width: 4
521 * c-basic-offset: 4
522 * End:
523 * vim600: noet sw=4 ts=4 fdm=marker
524 * vim<600: noet sw=4 ts=4
525 */