• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2001-2004 Brandon Long
3  * All Rights Reserved.
4  *
5  * ClearSilver Templating System
6  *
7  * This code is made available under the terms of the ClearSilver License.
8  * http://www.clearsilver.net/license.hdf
9  *
10  */
11 
12 #include <Python.h>
13 #include "ClearSilver.h"
14 
15 #define NEO_CGI_MODULE
16 #include "p_neo_util.h"
17 
18 static PyObject *NeoError;
19 static PyObject *NeoParseError;
20 
p_neo_error(NEOERR * err)21 PyObject * p_neo_error (NEOERR *err)
22 {
23   STRING str;
24 
25   string_init (&str);
26   if (nerr_match(err, NERR_PARSE))
27   {
28     nerr_error_string (err, &str);
29     PyErr_SetString (NeoParseError, str.buf);
30   }
31   else
32   {
33     nerr_error_traceback (err, &str);
34     PyErr_SetString (NeoError, str.buf);
35   }
36   string_clear (&str);
37   return NULL;
38 }
39 
40 #define HDFObjectCheck(a) (!(strcmp((a)->ob_type->tp_name, HDFObjectType.tp_name)))
41 
42 typedef struct _HDFObject
43 {
44    PyObject_HEAD
45    HDF *data;
46    int dealloc;
47 } HDFObject;
48 
49 static PyObject *p_hdf_value_get_attr (HDFObject *self, char *name);
50 static void p_hdf_dealloc (HDFObject *ho);
51 
52 static PyTypeObject HDFObjectType = {
53   PyObject_HEAD_INIT(NULL)
54     0,			             /*ob_size*/
55   "HDFObjectType",	             /*tp_name*/
56   sizeof(HDFObject),	     /*tp_size*/
57   0,			             /*tp_itemsize*/
58   /* methods */
59   (destructor)p_hdf_dealloc,	     /*tp_dealloc*/
60   0,			             /*tp_print*/
61   (getattrfunc)p_hdf_value_get_attr,     /*tp_getattr*/
62   0,			             /*tp_setattr*/
63   0,			             /*tp_compare*/
64   (reprfunc)0,                       /*tp_repr*/
65   0,                                 /* tp_as_number */
66   0,                                 /* tp_as_sequence */
67   0,                                 /* tp_as_mapping */
68   0,                                 /* tp_as_hash */
69 };
70 
71 
p_hdf_dealloc(HDFObject * ho)72 static void p_hdf_dealloc (HDFObject *ho)
73 {
74   /* ne_warn("deallocating hdf: %X", ho); */
75   if (ho->data && ho->dealloc)
76   {
77     hdf_destroy (&(ho->data));
78   }
79   PyObject_DEL(ho);
80 }
81 
p_hdf_to_object(HDF * data,int dealloc)82 PyObject * p_hdf_to_object (HDF *data, int dealloc)
83 {
84   PyObject *rv;
85 
86   if (data == NULL)
87   {
88     rv = Py_None;
89     Py_INCREF (rv);
90   }
91   else
92   {
93     HDFObject *ho = PyObject_NEW (HDFObject, &HDFObjectType);
94     if (ho == NULL) return NULL;
95     ho->data = data;
96     ho->dealloc = dealloc;
97     rv = (PyObject *) ho;
98     /* ne_warn("allocating hdf: %X", ho); */
99   }
100   return rv;
101 }
102 
p_object_to_hdf(PyObject * ho)103 HDF * p_object_to_hdf (PyObject *ho)
104 {
105   if (HDFObjectCheck(ho))
106   {
107     return ((HDFObject *)ho)->data;
108   }
109   return NULL;
110 }
111 
p_hdf_init(PyObject * self,PyObject * args)112 static PyObject * p_hdf_init (PyObject *self, PyObject *args)
113 {
114   HDF *hdf = NULL;
115   NEOERR *err;
116 
117   err = hdf_init (&hdf);
118   if (err) return p_neo_error (err);
119   return p_hdf_to_object (hdf, 1);
120 }
121 
p_hdf_get_int_value(PyObject * self,PyObject * args)122 static PyObject * p_hdf_get_int_value (PyObject *self, PyObject *args)
123 {
124   HDFObject *ho = (HDFObject *)self;
125   PyObject *rv;
126   char *name;
127   int r, d = 0;
128 
129   if (!PyArg_ParseTuple(args, "si:getIntValue(name, default)", &name, &d))
130     return NULL;
131 
132   r = hdf_get_int_value (ho->data, name, d);
133   rv = Py_BuildValue ("i", r);
134   return rv;
135 }
136 
p_hdf_get_value(PyObject * self,PyObject * args)137 static PyObject * p_hdf_get_value (PyObject *self, PyObject *args)
138 {
139   HDFObject *ho = (HDFObject *)self;
140   PyObject *rv;
141   char *name;
142   char *r, *d = NULL;
143 
144   if (!PyArg_ParseTuple(args, "ss:getValue(name, default)", &name, &d))
145     return NULL;
146 
147   r = hdf_get_value (ho->data, name, d);
148   rv = Py_BuildValue ("s", r);
149   return rv;
150 }
151 
p_hdf_get_obj(PyObject * self,PyObject * args)152 static PyObject * p_hdf_get_obj (PyObject *self, PyObject *args)
153 {
154   HDFObject *ho = (HDFObject *)self;
155   PyObject *rv;
156   char *name;
157   HDF *r;
158 
159   if (!PyArg_ParseTuple(args, "s:getObj(name)", &name))
160     return NULL;
161 
162   r = hdf_get_obj (ho->data, name);
163   if (r == NULL)
164   {
165     rv = Py_None;
166     Py_INCREF(rv);
167     return rv;
168   }
169   rv = p_hdf_to_object (r, 0);
170   return rv;
171 }
172 
p_hdf_get_child(PyObject * self,PyObject * args)173 static PyObject * p_hdf_get_child (PyObject *self, PyObject *args)
174 {
175   HDFObject *ho = (HDFObject *)self;
176   PyObject *rv;
177   char *name;
178   HDF *r;
179 
180   if (!PyArg_ParseTuple(args, "s:getChild(name)", &name))
181     return NULL;
182 
183   r = hdf_get_child (ho->data, name);
184   if (r == NULL)
185   {
186     rv = Py_None;
187     Py_INCREF(rv);
188     return rv;
189   }
190   rv = p_hdf_to_object (r, 0);
191   return rv;
192 }
193 
p_hdf_get_attr(PyObject * self,PyObject * args)194 static PyObject * p_hdf_get_attr (PyObject *self, PyObject *args)
195 {
196   HDFObject *ho = (HDFObject *)self;
197   PyObject *rv, *item;
198   char *name;
199   HDF_ATTR *attr;
200 
201   if (!PyArg_ParseTuple(args, "s:getAttrs(name)", &name))
202     return NULL;
203 
204   rv = PyList_New(0);
205   if (rv == NULL) return NULL;
206   Py_INCREF(rv);
207   attr = hdf_get_attr (ho->data, name);
208   while (attr != NULL)
209   {
210     item = Py_BuildValue("(s,s)", attr->key, attr->value);
211     if (item == NULL)
212     {
213       Py_DECREF(rv);
214       return NULL;
215     }
216     if (PyList_Append(rv, item) == -1)
217     {
218       Py_DECREF(rv);
219       return NULL;
220     }
221     attr = attr->next;
222   }
223   return rv;
224 }
225 
p_hdf_obj_attr(PyObject * self,PyObject * args)226 static PyObject * p_hdf_obj_attr (PyObject *self, PyObject *args)
227 {
228   HDFObject *ho = (HDFObject *)self;
229   PyObject *rv, *item;
230   HDF_ATTR *attr;
231 
232   rv = PyList_New(0);
233   if (rv == NULL) return NULL;
234   Py_INCREF(rv);
235   attr = hdf_obj_attr (ho->data);
236   while (attr != NULL)
237   {
238     item = Py_BuildValue("(s,s)", attr->key, attr->value);
239     if (item == NULL)
240     {
241       Py_DECREF(rv);
242       return NULL;
243     }
244     if (PyList_Append(rv, item) == -1)
245     {
246       Py_DECREF(rv);
247       return NULL;
248     }
249     attr = attr->next;
250   }
251   return rv;
252 }
253 
p_hdf_obj_child(PyObject * self,PyObject * args)254 static PyObject * p_hdf_obj_child (PyObject *self, PyObject *args)
255 {
256   HDFObject *ho = (HDFObject *)self;
257   PyObject *rv;
258   HDF *r;
259 
260   r = hdf_obj_child (ho->data);
261   if (r == NULL)
262   {
263     rv = Py_None;
264     Py_INCREF(rv);
265     return rv;
266   }
267   rv = p_hdf_to_object (r, 0);
268   return rv;
269 }
270 
p_hdf_obj_next(PyObject * self,PyObject * args)271 static PyObject * p_hdf_obj_next (PyObject *self, PyObject *args)
272 {
273   HDFObject *ho = (HDFObject *)self;
274   PyObject *rv;
275   HDF *r;
276 
277   r = hdf_obj_next (ho->data);
278   if (r == NULL)
279   {
280     rv = Py_None;
281     Py_INCREF(rv);
282     return rv;
283   }
284   rv = p_hdf_to_object (r, 0);
285   return rv;
286 }
287 
p_hdf_obj_top(PyObject * self,PyObject * args)288 static PyObject * p_hdf_obj_top (PyObject *self, PyObject *args)
289 {
290   HDFObject *ho = (HDFObject *)self;
291   PyObject *rv;
292   HDF *r;
293 
294   r = hdf_obj_top (ho->data);
295   if (r == NULL)
296   {
297     rv = Py_None;
298     Py_INCREF(rv);
299     return rv;
300   }
301   rv = p_hdf_to_object (r, 0);
302   return rv;
303 }
304 
p_hdf_obj_name(PyObject * self,PyObject * args)305 static PyObject * p_hdf_obj_name (PyObject *self, PyObject *args)
306 {
307   HDFObject *ho = (HDFObject *)self;
308   PyObject *rv;
309   char *r;
310 
311   r = hdf_obj_name (ho->data);
312   if (r == NULL)
313   {
314     rv = Py_None;
315     Py_INCREF(rv);
316     return rv;
317   }
318   rv = Py_BuildValue ("s", r);
319   return rv;
320 }
321 
p_hdf_obj_value(PyObject * self,PyObject * args)322 static PyObject * p_hdf_obj_value (PyObject *self, PyObject *args)
323 {
324   HDFObject *ho = (HDFObject *)self;
325   PyObject *rv;
326   char *r;
327 
328   r = hdf_obj_value (ho->data);
329   if (r == NULL)
330   {
331     rv = Py_None;
332     Py_INCREF(rv);
333     return rv;
334   }
335   rv = Py_BuildValue ("s", r);
336   return rv;
337 }
338 
p_hdf_set_value(PyObject * self,PyObject * args)339 static PyObject * p_hdf_set_value (PyObject *self, PyObject *args)
340 {
341   HDFObject *ho = (HDFObject *)self;
342   PyObject *rv;
343   char *name, *value;
344   NEOERR *err;
345   int nlen = 0;
346   int vlen = 0;
347 
348   if (!PyArg_ParseTuple(args, "s#s#:setValue(name, value)", &name, &nlen, &value, &vlen))
349     return NULL;
350 
351   err = hdf_set_value (ho->data, name, value);
352   if (err) return p_neo_error(err);
353 
354   rv = Py_None;
355   Py_INCREF(rv);
356   return rv;
357 }
358 
p_hdf_set_attr(PyObject * self,PyObject * args)359 static PyObject * p_hdf_set_attr (PyObject *self, PyObject *args)
360 {
361   HDFObject *ho = (HDFObject *)self;
362   PyObject *rv;
363   char *name, *value, *key;
364   NEOERR *err;
365 
366   if (!PyArg_ParseTuple(args, "ssO:setAttr(name, key, value)", &name, &key, &rv))
367     return NULL;
368 
369   if (PyString_Check(rv))
370   {
371     value = PyString_AsString(rv);
372   }
373   else if (rv == Py_None)
374   {
375     value = NULL;
376   }
377   else
378   {
379     return PyErr_Format(PyExc_TypeError, "Invalid type for value, expected None or string");
380   }
381   err = hdf_set_attr (ho->data, name, key, value);
382   if (err) return p_neo_error(err);
383 
384   rv = Py_None;
385   Py_INCREF(rv);
386   return rv;
387 }
388 
p_hdf_read_file(PyObject * self,PyObject * args)389 static PyObject * p_hdf_read_file (PyObject *self, PyObject *args)
390 {
391   HDFObject *ho = (HDFObject *)self;
392   PyObject *rv;
393   char *path;
394   NEOERR *err;
395 
396   if (!PyArg_ParseTuple(args, "s:readFile(path)", &path))
397     return NULL;
398 
399   err = hdf_read_file (ho->data, path);
400   if (err) return p_neo_error(err);
401 
402   rv = Py_None;
403   Py_INCREF(rv);
404   return rv;
405 }
406 
p_hdf_write_file(PyObject * self,PyObject * args)407 static PyObject * p_hdf_write_file (PyObject *self, PyObject *args)
408 {
409   HDFObject *ho = (HDFObject *)self;
410   PyObject *rv;
411   char *path;
412   NEOERR *err;
413 
414   if (!PyArg_ParseTuple(args, "s:writeFile(path)", &path))
415     return NULL;
416 
417   err = hdf_write_file (ho->data, path);
418   if (err) return p_neo_error(err);
419 
420   rv = Py_None;
421   Py_INCREF(rv);
422   return rv;
423 }
424 
p_hdf_write_file_atomic(PyObject * self,PyObject * args)425 static PyObject * p_hdf_write_file_atomic (PyObject *self, PyObject *args)
426 {
427   HDFObject *ho = (HDFObject *)self;
428   PyObject *rv;
429   char *path;
430   NEOERR *err;
431 
432   if (!PyArg_ParseTuple(args, "s:writeFile(path)", &path))
433     return NULL;
434 
435   err = hdf_write_file_atomic (ho->data, path);
436   if (err) return p_neo_error(err);
437 
438   rv = Py_None;
439   Py_INCREF(rv);
440   return rv;
441 }
442 
p_hdf_remove_tree(PyObject * self,PyObject * args)443 static PyObject * p_hdf_remove_tree (PyObject *self, PyObject *args)
444 {
445   HDFObject *ho = (HDFObject *)self;
446   PyObject *rv;
447   char *name;
448   NEOERR *err;
449 
450   if (!PyArg_ParseTuple(args, "s:removeTree(name)", &name))
451     return NULL;
452 
453   err = hdf_remove_tree (ho->data, name);
454   if (err) return p_neo_error(err);
455 
456   rv = Py_None;
457   Py_INCREF(rv);
458   return rv;
459 }
460 
p_hdf_dump(PyObject * self,PyObject * args)461 static PyObject * p_hdf_dump (PyObject *self, PyObject *args)
462 {
463   HDFObject *ho = (HDFObject *)self;
464   PyObject *rv;
465   NEOERR *err;
466   STRING str;
467 
468   string_init (&str);
469 
470   err = hdf_dump_str (ho->data, NULL, 0, &str);
471   if (err) return p_neo_error(err);
472   rv = Py_BuildValue ("s", str.buf);
473   string_clear (&str);
474   return rv;
475 }
476 
p_hdf_write_string(PyObject * self,PyObject * args)477 static PyObject * p_hdf_write_string (PyObject *self, PyObject *args)
478 {
479   HDFObject *ho = (HDFObject *)self;
480   PyObject *rv;
481   NEOERR *err;
482   char *s = NULL;
483 
484   err = hdf_write_string (ho->data, &s);
485   if (err) return p_neo_error(err);
486   rv = Py_BuildValue ("s", s);
487   if (s) free(s);
488   return rv;
489 }
490 
p_hdf_read_string(PyObject * self,PyObject * args)491 static PyObject * p_hdf_read_string (PyObject *self, PyObject *args)
492 {
493   HDFObject *ho = (HDFObject *)self;
494   NEOERR *err;
495   char *s = NULL;
496   int ignore = 0;
497 
498   if (!PyArg_ParseTuple(args, "s|i:readString(string)", &s, &ignore))
499     return NULL;
500 
501   err = hdf_read_string_ignore (ho->data, s, ignore);
502   if (err) return p_neo_error(err);
503   Py_INCREF (Py_None);
504   return Py_None;
505 }
506 
p_hdf_copy(PyObject * self,PyObject * args)507 static PyObject * p_hdf_copy (PyObject *self, PyObject *args)
508 {
509   HDFObject *ho = (HDFObject *)self;
510   HDF *src = NULL;
511   PyObject *rv, *o = NULL;
512   char *name;
513   NEOERR *err;
514 
515   if (!PyArg_ParseTuple(args, "sO:copy(name, src_hdf)", &name, &o))
516     return NULL;
517 
518   src = p_object_to_hdf (o);
519   if (src == NULL)
520   {
521     PyErr_Format(PyExc_TypeError, "second argument must be an HDFObject");
522     return NULL;
523   }
524 
525   err = hdf_copy (ho->data, name, src);
526   if (err) return p_neo_error(err);
527 
528   rv = Py_None;
529   Py_INCREF(rv);
530   return rv;
531 }
532 
p_hdf_set_symlink(PyObject * self,PyObject * args)533 static PyObject * p_hdf_set_symlink (PyObject *self, PyObject *args)
534 {
535   HDFObject *ho = (HDFObject *)self;
536   PyObject *rv;
537   char *src;
538   char *dest;
539   NEOERR *err;
540 
541   if (!PyArg_ParseTuple(args, "ss:setSymLink(src, dest)", &src, &dest))
542     return NULL;
543 
544   err = hdf_set_symlink (ho->data, src, dest);
545   if (err) return p_neo_error(err);
546 
547   rv = Py_None;
548   Py_INCREF(rv);
549   return rv;
550 }
551 
p_hdf_search_path(PyObject * self,PyObject * args)552 static PyObject * p_hdf_search_path (PyObject *self, PyObject *args)
553 {
554   HDFObject *ho = (HDFObject *)self;
555   PyObject *rv;
556   char *path;
557   char full[_POSIX_PATH_MAX];
558   NEOERR *err;
559 
560   if (!PyArg_ParseTuple(args, "s:searchPath(path)", &path))
561     return NULL;
562 
563   err = hdf_search_path (ho->data, path, full);
564   if (err) return p_neo_error(err);
565 
566   rv = PyString_FromString(full);
567   return rv;
568 }
569 
570 static PyMethodDef HDFMethods[] =
571 {
572   {"getIntValue", p_hdf_get_int_value, METH_VARARGS, NULL},
573   {"getValue", p_hdf_get_value, METH_VARARGS, NULL},
574   {"getObj", p_hdf_get_obj, METH_VARARGS, NULL},
575   {"getChild", p_hdf_get_child, METH_VARARGS, NULL},
576   {"getAttrs", p_hdf_get_attr, METH_VARARGS, NULL},
577   {"child", p_hdf_obj_child, METH_VARARGS, NULL},
578   {"next", p_hdf_obj_next, METH_VARARGS, NULL},
579   {"name", p_hdf_obj_name, METH_VARARGS, NULL},
580   {"value", p_hdf_obj_value, METH_VARARGS, NULL},
581   {"top", p_hdf_obj_top, METH_VARARGS, NULL},
582   {"attrs", p_hdf_obj_attr, METH_VARARGS, NULL},
583   {"setValue", p_hdf_set_value, METH_VARARGS, NULL},
584   {"setAttr", p_hdf_set_attr, METH_VARARGS, NULL},
585   {"readFile", p_hdf_read_file, METH_VARARGS, NULL},
586   {"writeFile", p_hdf_write_file, METH_VARARGS, NULL},
587   {"writeFileAtomic", p_hdf_write_file_atomic, METH_VARARGS, NULL},
588   {"readString", p_hdf_read_string, METH_VARARGS, NULL},
589   {"writeString", p_hdf_write_string, METH_VARARGS, NULL},
590   {"removeTree", p_hdf_remove_tree, METH_VARARGS, NULL},
591   {"dump", p_hdf_dump, METH_VARARGS, NULL},
592   {"copy", p_hdf_copy, METH_VARARGS, NULL},
593   {"setSymLink", p_hdf_set_symlink, METH_VARARGS, NULL},
594   {"searchPath", p_hdf_search_path, METH_VARARGS, NULL},
595   {NULL, NULL}
596 };
597 
p_escape(PyObject * self,PyObject * args)598 static PyObject * p_escape (PyObject *self, PyObject *args)
599 {
600   PyObject *rv;
601   char *s;
602   char *escape;
603   char *esc_char;
604   int buflen;
605   char *ret = NULL;
606   NEOERR *err;
607 
608   if (!PyArg_ParseTuple(args, "s#ss:escape(str, char, escape)", &s, &buflen, &esc_char, &escape))
609     return NULL;
610 
611   err = neos_escape(s, buflen, esc_char[0], escape, &ret);
612   if (err) return p_neo_error(err);
613 
614   rv = Py_BuildValue("s", ret);
615   free(ret);
616   return rv;
617 }
618 
p_unescape(PyObject * self,PyObject * args)619 static PyObject * p_unescape (PyObject *self, PyObject *args)
620 {
621   PyObject *rv;
622   char *s;
623   char *copy;
624   char *esc_char;
625   int buflen;
626 
627   if (!PyArg_ParseTuple(args, "s#s:unescape(str, char)", &s, &buflen, &esc_char))
628     return NULL;
629 
630   copy = strdup(s);
631   if (copy == NULL) return PyErr_NoMemory();
632   neos_unescape(copy, buflen, esc_char[0]);
633 
634   rv = Py_BuildValue("s", copy);
635   free(copy);
636   return rv;
637 }
638 
639 /* This returns the expanded version in the standard python time tuple
640  * */
p_time_expand(PyObject * self,PyObject * args)641 static PyObject * p_time_expand (PyObject *self, PyObject *args)
642 {
643   PyObject *rv;
644   int tt;
645   struct tm ttm;
646   char *tz;
647 
648   if (!PyArg_ParseTuple(args, "is:time_expand(time_t, timezone string)", &tt, &tz))
649     return NULL;
650 
651   neo_time_expand(tt, tz, &ttm);
652 
653   rv = Py_BuildValue("(i,i,i,i,i,i,i,i,i)", ttm.tm_year + 1900, ttm.tm_mon + 1,
654       ttm.tm_mday, ttm.tm_hour, ttm.tm_min, ttm.tm_sec, ttm.tm_wday, 0, ttm.tm_isdst);
655   return rv;
656 }
657 
p_time_compact(PyObject * self,PyObject * args)658 static PyObject * p_time_compact (PyObject *self, PyObject *args)
659 {
660   PyObject *rv;
661   int tt;
662   struct tm ttm;
663   int junk;
664   char *tz;
665 
666   memset(&ttm, 0, sizeof(struct tm));
667 
668   if (!PyArg_ParseTuple(args, "(i,i,i,i,i,i,i,i,i)s:time_compact(time tuple, timezone string)", &ttm.tm_year, &ttm.tm_mon, &ttm.tm_mday, &ttm.tm_hour, &ttm.tm_min, &ttm.tm_sec, &ttm.tm_wday, &junk, &ttm.tm_isdst, &tz))
669     return NULL;
670 
671   /* fix up difference between ttm and python tup */
672   ttm.tm_year -= 1900;
673   ttm.tm_mon -= 1;
674 
675   tt = neo_time_compact (&ttm, tz);
676 
677   rv = Py_BuildValue("i", tt);
678   return rv;
679 }
680 
681 static PyMethodDef UtilMethods[] =
682 {
683   {"HDF", p_hdf_init, METH_VARARGS, NULL},
684   {"escape", p_escape, METH_VARARGS, NULL},
685   {"unescape", p_unescape, METH_VARARGS, NULL},
686   {"time_expand", p_time_expand, METH_VARARGS, NULL},
687   {"time_compact", p_time_compact, METH_VARARGS, NULL},
688   {NULL, NULL}
689 };
690 
p_hdf_value_get_attr(HDFObject * ho,char * name)691 PyObject *p_hdf_value_get_attr (HDFObject *ho, char *name)
692 {
693   return Py_FindMethod(HDFMethods, (PyObject *)ho, name);
694 }
695 
initneo_util(void)696 DL_EXPORT(void) initneo_util(void)
697 {
698   PyObject *m, *d;
699 
700   HDFObjectType.ob_type = &PyType_Type;
701 
702   m = Py_InitModule("neo_util", UtilMethods);
703   d = PyModule_GetDict(m);
704   NeoError = PyErr_NewException("neo_util.Error", NULL, NULL);
705   NeoParseError = PyErr_NewException("neo_util.ParseError", NULL, NULL);
706   PyDict_SetItemString(d, "Error", NeoError);
707   PyDict_SetItemString(d, "ParseError", NeoParseError);
708 }
709