1 // Copyright (c) 1994 - 2010, Lawrence Livermore National Security, LLC.
2 // LLNL-CODE-425250.
3 // All rights reserved.
4 //
5 // This file is part of Silo. For details, see silo.llnl.gov.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //    * Redistributions of source code must retain the above copyright
12 //      notice, this list of conditions and the disclaimer below.
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the disclaimer (as noted
15 //      below) in the documentation and/or other materials provided with
16 //      the distribution.
17 //    * Neither the name of the LLNS/LLNL nor the names of its
18 //      contributors may be used to endorse or promote products derived
19 //      from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE  IS PROVIDED BY  THE COPYRIGHT HOLDERS  AND CONTRIBUTORS
22 // "AS  IS" AND  ANY EXPRESS  OR IMPLIED  WARRANTIES, INCLUDING,  BUT NOT
23 // LIMITED TO, THE IMPLIED  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A  PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN  NO  EVENT SHALL  LAWRENCE
25 // LIVERMORE  NATIONAL SECURITY, LLC,  THE U.S.  DEPARTMENT OF  ENERGY OR
26 // CONTRIBUTORS BE LIABLE FOR  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 // EXEMPLARY, OR  CONSEQUENTIAL DAMAGES  (INCLUDING, BUT NOT  LIMITED TO,
28 // PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS  OF USE,  DATA, OR
29 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 // LIABILITY, WHETHER  IN CONTRACT, STRICT LIABILITY,  OR TORT (INCLUDING
31 // NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT  OF THE USE  OF THIS
32 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 //
34 // This work was produced at Lawrence Livermore National Laboratory under
35 // Contract  No.   DE-AC52-07NA27344 with  the  DOE.  Neither the  United
36 // States Government  nor Lawrence  Livermore National Security,  LLC nor
37 // any of  their employees,  makes any warranty,  express or  implied, or
38 // assumes   any   liability   or   responsibility  for   the   accuracy,
39 // completeness, or usefulness of any information, apparatus, product, or
40 // process  disclosed, or  represents  that its  use  would not  infringe
41 // privately-owned   rights.  Any  reference   herein  to   any  specific
42 // commercial products,  process, or  services by trade  name, trademark,
43 // manufacturer or otherwise does not necessarily constitute or imply its
44 // endorsement,  recommendation,   or  favoring  by   the  United  States
45 // Government or Lawrence Livermore National Security, LLC. The views and
46 // opinions  of authors  expressed  herein do  not  necessarily state  or
47 // reflect those  of the United  States Government or  Lawrence Livermore
48 // National  Security, LLC,  and shall  not  be used  for advertising  or
49 // product endorsement purposes.
50 
51 #include "pydbfile.h"
52 #include "pydbtoc.h"
53 #include "pysilo.h"
54 
55 #include <string>
56 
57 using std::string;
58 
59 #if PY_MAJOR_VERSION >= 3
60 #define PyInt_FromLong(x) (PyLong_FromLong(x))
61 #define PyInt_Check(x) (PyLong_Check(x))
62 #define PyString_Check(x) PyUnicode_Check(x)
63 #define PyInt_AS_LONG(x) PyLong_AsLong(x)
64 #define PyString_FromStringAndSize(x,y) PyUnicode_FromStringAndSize(x,y)
65 #define PyString_FromString(x) PyUnicode_FromString(x)
66 #define PyString_AsString(x) PyUnicode_AsUTF8(x)
67 
68 #else
69 #define Py_RETURN_NOTIMPLEMENTED return NULL
70 #endif
71 
72 
73 // ****************************************************************************
74 //  Method:  DBfile_DBGetToc
75 //
76 //  Purpose:
77 //    Encapsulates DBGetToc
78 //
79 //  Python Arguments:
80 //    none
81 //
82 //  Programmer:  Jeremy Meredith
83 //  Creation:    July 12, 2005
84 //
85 // ****************************************************************************
DBfile_DBGetToc(PyObject * self,PyObject * args)86 static PyObject *DBfile_DBGetToc(PyObject *self, PyObject *args)
87 {
88     DBfileObject *obj = (DBfileObject*)self;
89 
90     if (!obj->db)
91     {
92       SiloErrorFunc(self, "This file has been closed.");
93       return NULL;
94     }
95 
96     DBtoc *toc = DBGetToc(obj->db);
97 
98     DBtocObject *retval = PyObject_NEW(DBtocObject, &DBtocType);
99     if (retval)
100     {
101         retval->toc = toc;
102     }
103     return (PyObject*)retval;
104 }
105 
106 // ****************************************************************************
107 //  Method:  DBfile_DBGetVar
108 //
109 //  Purpose:
110 //    Encapsulates DBGetVar
111 //
112 //  Python Arguments:
113 //    form 1: varname
114 //
115 //  Programmer:  Jeremy Meredith
116 //  Creation:    July 12, 2005
117 //
118 //  Modifications:
119 //
120 //    Mark C. Miller, Tue Aug  5 11:04:14 PDT 2008
121 //    I modifed case where we're returning a string valued variable to strip
122 //    off the trailing null character. The PyString_FromStringAndSize method
123 //    was being given a length argument that included the trailing null and
124 //    the result was a bit if an odd string in python.
125 //
126 //    Mark C. Miller, Wed Nov 14 15:35:44 PST 2012
127 //    Removed error return case for 'only flat variables.' This also fixes a
128 //    problem with string object members on HDF5 driver where the funky "'<s>"
129 //    construct is used for *both* string valued members and variable
130 //    members whose value is also a string but is the name of another dataset
131 //    in the file.
132 // ****************************************************************************
DBfile_DBGetVar(PyObject * self,PyObject * args)133 static PyObject *DBfile_DBGetVar(PyObject *self, PyObject *args)
134 {
135     DBfile *db = ((DBfileObject*)self)->db;
136 
137     if (!db)
138     {
139       SiloErrorFunc(self, "This file has been closed.");
140         return NULL;
141     }
142 
143     char *str;
144     if(!PyArg_ParseTuple(args, "s", &str))
145         return NULL;
146 
147     int vartype = DBInqVarType(db, str);
148     if (vartype != DB_VARIABLE)
149         return NULL;
150 
151     int len = DBGetVarLength(db,str);
152     int type = DBGetVarType(db,str);
153     void *var = DBGetVar(db,str);
154     if (len == 1 || type == DB_CHAR)
155     {
156         switch (type)
157         {
158           case DB_INT:
159             return PyInt_FromLong(*((int*)var));
160           case DB_SHORT:
161             return PyInt_FromLong(*((short*)var));
162           case DB_LONG:
163             return PyInt_FromLong(*((long*)var));
164           case DB_FLOAT:
165             return PyFloat_FromDouble(*((float*)var));
166           case DB_DOUBLE:
167             return PyFloat_FromDouble(*((double*)var));
168           case DB_CHAR:
169             if (len == 1)
170                 return PyInt_FromLong(*((char*)var));
171             else
172             {
173                 // strip trailing null if one exists
174                 char *p = (char *) var;
175                 if (p[len-1] == '\0') len--;
176                 return PyString_FromStringAndSize((char*)var, len);
177             }
178           default:
179             SiloErrorFunc(self, "Unknown variable type.");
180             return NULL;
181         }
182     }
183     else
184     {
185         PyObject *retval = PyTuple_New(len);
186         for (int i=0; i<len; i++)
187         {
188             PyObject *tmp;
189             switch (type)
190             {
191               case DB_INT:
192                 tmp = PyInt_FromLong(((int*)var)[i]);
193                 break;
194               case DB_SHORT:
195                 tmp = PyInt_FromLong(((short*)var)[i]);
196                 break;
197               case DB_LONG:
198                 tmp = PyInt_FromLong(((long*)var)[i]);
199                 break;
200               case DB_FLOAT:
201                 tmp = PyFloat_FromDouble(((float*)var)[i]);
202                 break;
203               case DB_DOUBLE:
204                 tmp = PyFloat_FromDouble(((double*)var)[i]);
205                 break;
206               case DB_CHAR:
207                 tmp = PyInt_FromLong(((char*)var)[i]);
208                 break;
209               default:
210                 SiloErrorFunc(self, "Unknown variable type.");
211                 return NULL;
212             }
213             PyTuple_SET_ITEM(retval, i, tmp);
214         }
215         return retval;
216     }
217 }
218 
219 // ****************************************************************************
220 //  Method:  DBfile_DBGetVarInfo
221 //
222 //  Purpose: Get metadata for a variable
223 //
224 //  Creation Mark C. Miller, Mon Nov 12 11:12:03 PST 2012
225 //  Plagerized liberally from Silex' SiloObjectView
226 //
227 //  Mark C. Miller, Tue Nov 13 17:29:29 PST 2012
228 //  Added optional 1/0 flag to descend into variable components and read their
229 //  data as a tuple member of the returned python dict.
230 // ****************************************************************************
DBfile_DBGetVarInfo(PyObject * self,PyObject * args)231 static PyObject *DBfile_DBGetVarInfo(PyObject *self, PyObject *args)
232 {
233     DBfile *db = ((DBfileObject*)self)->db;
234 
235     if (!db)
236     {
237       SiloErrorFunc(self, "This file has been closed.");
238         return NULL;
239     }
240 
241     char *str;
242     int get_data_flag = 0;
243     if (!PyArg_ParseTuple(args, "si", &str, &get_data_flag))
244     {
245         if (!PyArg_ParseTuple(args, "s", &str))
246             return NULL;
247         else
248             PyErr_Clear();
249     }
250 
251     //
252     // Note that because we read the object through Silo's generic object
253     // interface, the Silo library will not be able to correctly apply
254     // object-level de-compression algorithms. We could add logic to the
255     // implementation of the GetObject method to detect the kind of object
256     // being read and, if it is a compressed mesh/var object, do the work
257     // necessary to prepare for its decompression. Too much work for now.
258     //
259     DBobject *silo_obj = DBGetObject(db, str);
260     if (!silo_obj)
261     {
262         char msg[256];
263         snprintf(msg, sizeof(msg), "Unable to get object \"%s\"", str);
264         SiloErrorFunc(self, msg);
265         return NULL;
266     }
267 
268     PyObject *retval = PyDict_New();
269     PyDict_SetItemString(retval, "name", PyString_FromString(silo_obj->name));
270     PyDict_SetItemString(retval, "type", PyString_FromString(silo_obj->type));
271     for (int i=0; i<silo_obj->ncomponents; i++)
272     {
273         string compname = silo_obj->comp_names[i];
274         string pdbname  = silo_obj->pdb_names[i];
275         void *comp = DBGetComponent(db, str, compname.c_str());
276         if (!comp)
277         {
278             char msg[256];
279             snprintf(msg, sizeof(msg), "Unable to get component \"%s\" for object \%s\"", compname.c_str(), str);
280             SiloErrorFunc(self, msg);
281             continue;
282         }
283         int type = DBGetComponentType(db, str, compname.c_str());
284         string typestr = "";
285         int ival = -1;
286         switch (type)
287         {
288           case DB_INT:
289             typestr = "int";
290             ival = *((int*)comp);
291             PyDict_SetItemString(retval, compname.c_str(), PyInt_FromLong((long)ival));
292             break;
293           case DB_SHORT:
294             typestr = "short";
295             ival = *((short*)comp);
296             PyDict_SetItemString(retval, compname.c_str(), PyInt_FromLong((long)ival));
297             break;
298           case DB_LONG:
299             typestr = "long";
300             ival = (int) *((long*)comp);
301             PyDict_SetItemString(retval, compname.c_str(), PyInt_FromLong((long)ival));
302             break;
303           case DB_LONG_LONG:
304             typestr = "long long";
305             ival = (int) *((long long*)comp);
306             PyDict_SetItemString(retval, compname.c_str(), PyInt_FromLong((long)ival));
307             break;
308           case DB_FLOAT:
309             typestr = "float";
310             PyDict_SetItemString(retval, compname.c_str(), PyFloat_FromDouble(*((float*)comp)));
311             break;
312           case DB_DOUBLE:
313             typestr = "double";
314             PyDict_SetItemString(retval, compname.c_str(), PyFloat_FromDouble(*((double*)comp)));
315             break;
316           case DB_CHAR:
317             typestr = "char";
318             if (*((char*)comp)== 0)
319                 PyDict_SetItemString(retval, compname.c_str(), PyString_FromString(""));
320             else
321                 PyDict_SetItemString(retval, compname.c_str(), PyString_FromString((char*)comp));
322             break;
323           case DB_NOTYPE:
324             typestr = "notype";
325             break;
326           default:
327             typestr = "var";
328             string valStr = std::string(pdbname.c_str());
329             if (pdbname.find("'<s>") == 0)
330             {
331                 int len = pdbname.length();
332                 valStr = string((const char*)(pdbname.c_str()),4,len-5);
333             }
334             if (get_data_flag)
335             {
336 
337                 PyObject *argTuple = PyTuple_New(1);
338                 PyTuple_SetItem(argTuple, 0, PyString_FromString(valStr.c_str()));
339                 PyObject *dobj = DBfile_DBGetVar(self, argTuple);
340                 if (dobj)
341                     PyDict_SetItemString(retval, compname.c_str(), dobj);
342                 else
343                     PyDict_SetItemString(retval, compname.c_str(), PyString_FromString(valStr.c_str()));
344             }
345             else
346             {
347                 PyDict_SetItemString(retval, compname.c_str(), PyString_FromString(valStr.c_str()));
348             }
349             break;
350         }
351 
352         // No such call as "DBFreeComponent".  Maybe there should be one!
353         free(comp);
354         comp = NULL;
355 
356     }
357     DBFreeObject(silo_obj);
358 
359     return retval;
360 }
361 
362 // ****************************************************************************
363 //  Method:  DBfile_DBGetVar
364 //
365 //  Purpose:
366 //    Encapsulates DBGetVar
367 //
368 //  Python Arguments:
369 //    form 1: varname, integer
370 //    form 2: varname, real
371 //    form 3: varname, string
372 //    form 4: varname, tuple
373 //
374 //  Programmer:  Jeremy Meredith
375 //  Creation:    July 12, 2005
376 //
377 //  Modifications
378 //  Mark C. Miller, Thu Dec 20 00:05:41 PST 2012
379 //  Adjust parsing logic to avoid deprecation warning for parsing a float into
380 //  an integer variable.
381 // ****************************************************************************
DBfile_DBWrite(PyObject * self,PyObject * args)382 static PyObject *DBfile_DBWrite(PyObject *self, PyObject *args)
383 {
384     DBfile *db = ((DBfileObject*)self)->db;
385 
386     if (!db)
387     {
388       SiloErrorFunc(self, "This file has been closed.");
389         return NULL;
390     }
391 
392     int dims;
393     int err;
394     char *str;
395 
396     int ivar;
397     double dvar;
398     char *svar;
399     PyObject *tuple;
400     if (PyArg_ParseTuple(args, "sd", &str, &dvar))
401     {
402         dims = 1;
403         if (dvar == (int) dvar)
404         {
405             ivar = (int) dvar;
406             err = DBWrite(db, str, &ivar, &dims,1, DB_INT);
407         }
408         else
409         {
410             err = DBWrite(db, str, &dvar, &dims,1, DB_DOUBLE);
411         }
412     }
413     else if (PyArg_ParseTuple(args, "ss", &str, &svar))
414     {
415         dims = strlen(svar);
416         err = DBWrite(db, str, svar, &dims,1, DB_CHAR);
417     }
418     else if (PyArg_ParseTuple(args, "sO", &str, &tuple))
419     {
420         if(!PyTuple_Check(tuple))
421             return NULL;
422 
423         int len = PyTuple_Size(tuple);
424         if (len < 1)
425         {
426             PyErr_SetString(PyExc_TypeError, "Tuple must be of size > 0");
427             return NULL;
428         }
429 
430         PyObject *item = PyTuple_GET_ITEM(tuple, 0);
431         if (PyInt_Check(item))
432         {
433             int *values = new int[len];
434             for (int i=0; i<len; i++)
435             {
436                 item = PyTuple_GET_ITEM(tuple, i);
437                 if (PyInt_Check(item))
438                     values[i] = int(PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, i)));
439                 else if (PyFloat_Check(item))
440                     values[i] = int(PyFloat_AS_DOUBLE(PyTuple_GET_ITEM(tuple, i)));
441                 else
442                 {
443                     PyErr_SetString(PyExc_TypeError,
444                                     "Only int or float tuples are supported");
445                     return NULL;
446                 }
447             }
448 
449             dims = len;
450             err = DBWrite(db, str, values, &len,1, DB_INT);
451         }
452         else if (PyFloat_Check(item))
453         {
454             double *values = new double[len];
455             for (int i=0; i<len; i++)
456             {
457                 item = PyTuple_GET_ITEM(tuple, i);
458                 if (PyInt_Check(item))
459                     values[i] = double(PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, i)));
460                 else if (PyFloat_Check(item))
461                     values[i] = double(PyFloat_AS_DOUBLE(PyTuple_GET_ITEM(tuple, i)));
462                 else
463                 {
464                     PyErr_SetString(PyExc_TypeError,
465                                     "Only int or float tuples are supported");
466                     return NULL;
467                 }
468             }
469 
470             dims = len;
471             err = DBWrite(db, str, values, &len,1, DB_DOUBLE);
472         }
473         else
474         {
475             PyErr_SetString(PyExc_TypeError,
476                             "Only int or float tuples are supported");
477             return NULL;
478         }
479     }
480     else
481     {
482         PyErr_SetString(PyExc_TypeError, "Function takes 2 arguments");
483         return NULL;
484     }
485 
486     if (err != 0)
487     {
488         PyErr_SetString(PyExc_TypeError, "DBWrite failed");
489         return NULL;
490     }
491 
492     PyErr_Clear();
493     Py_INCREF(Py_None);
494     return Py_None;
495 }
496 
497 // ****************************************************************************
498 //  Method:  DBfile_DBWriteObject
499 //
500 //  Purpose: Generalized method for writing silo objects.
501 //
502 //  Python Arguments:
503 //    form 1: object name, python dictionary (with problem sized arrays as
504 //    members)
505 // ****************************************************************************
DBfile_DBWriteObject(PyObject * self,PyObject * args)506 static PyObject *DBfile_DBWriteObject(PyObject *self, PyObject *args)
507 {
508     DBfile *db = ((DBfileObject*)self)->db;
509 
510     if (!db)
511     {
512       SiloErrorFunc(self, "This file has been closed.");
513       return NULL;
514     }
515 
516     char *objname;
517     PyDictObject *dictobj;
518     if (!PyArg_ParseTuple(args, "sO!", &objname, &PyDict_Type, &dictobj)) return NULL;
519 
520     int ncomps = PyDict_Size((PyObject*)dictobj);
521     if (!ncomps) return NULL;
522     int objtype = DBGetObjtypeTag(PyString_AsString(PyDict_GetItemString((PyObject*)dictobj, "type")));
523     DBobject *siloobj = DBMakeObject(objname, objtype, ncomps);
524 printf("writing objname = \"%s\"\n", objname);
525     PyObject *key, *value;
526 #if PY_VERSION_GE(2,5,0)
527     Py_ssize_t pos = 0;
528 #else
529     int pos = 0;
530 #endif
531     while (PyDict_Next((PyObject*)dictobj, &pos, &key, &value))
532     {
533         if (PyInt_Check(value))
534             DBAddIntComponent(siloobj, PyString_AsString(key), PyInt_AS_LONG(value));
535         else if (PyFloat_Check(value))
536             DBAddDblComponent(siloobj, PyString_AsString(key), PyFloat_AS_DOUBLE(value));
537         else if (PyString_Check(value))
538             DBAddStrComponent(siloobj, PyString_AsString(key), PyString_AsString(value));
539         else if (PyTuple_Check(value))
540         {
541             long len = PyTuple_Size(value);
542             bool allint = true;
543             for (int i = 0; i < len && allint; i++)
544             {
545                 if (PyFloat_Check(PyTuple_GET_ITEM(value,i)))
546                     allint = false;
547             }
548             if (allint)
549             {
550                 int *vals = new int[len];
551                 for (int i = 0; i < len; i++)
552                 {
553                     if (PyFloat_Check(PyTuple_GET_ITEM(value,i)))
554                     {
555                         double dval = PyFloat_AS_DOUBLE(PyTuple_GET_ITEM(value,i));
556                         vals[i] = (int) dval;
557                     }
558                     else
559                     {
560                         vals[i] = PyInt_AS_LONG(PyTuple_GET_ITEM(value,i));
561                     }
562                 }
563                 DBWriteComponent(db, siloobj, PyString_AsString(key), objname, "integer", vals, 1, &len);
564                 delete [] vals;
565             }
566             else
567             {
568                 double *vals = new double[len];
569                 for (int i = 0; i < len; i++)
570                 {
571                     if (PyInt_Check(PyTuple_GET_ITEM(value,i)))
572                         vals[i] = (double) PyInt_AS_LONG(PyTuple_GET_ITEM(value,i));
573                     else
574                         vals[i] = PyFloat_AS_DOUBLE(PyTuple_GET_ITEM(value,i));
575                 }
576                 DBWriteComponent(db, siloobj, PyString_AsString(key), objname, "double", vals, 1, &len);
577                 delete [] vals;
578             }
579         }
580     }
581     DBWriteObject(db, siloobj, 1);
582 
583     PyErr_Clear();
584     Py_INCREF(Py_None);
585     return Py_None;
586 
587 }
588 
589 // ****************************************************************************
590 //  Method:  DBfile_DBMkDir
591 //
592 //  Purpose:
593 //    Encapsulates DBMkDir
594 //
595 //  Python Arguments:
596 //    form 1: dirname
597 //
598 //  Programmer:  Jeremy Meredith
599 //  Creation:    July 12, 2005
600 //
601 // ****************************************************************************
DBfile_DBMkDir(PyObject * self,PyObject * args)602 static PyObject *DBfile_DBMkDir(PyObject *self, PyObject *args)
603 {
604     DBfile *db = ((DBfileObject*)self)->db;
605 
606     if (!db)
607     {
608         SiloErrorFunc(self, "This file has been closed.");
609         return NULL;
610     }
611 
612     char *str;
613     if(!PyArg_ParseTuple(args, "s", &str))
614         return NULL;
615 
616     if (DBMkDir(db, str))
617     {
618         SiloErrorFunc(self, "Could not make the directory.");
619         return NULL;
620     }
621     else
622     {
623         Py_INCREF(Py_None);
624         return Py_None;
625     }
626 }
627 
628 // ****************************************************************************
629 //  Method:  DBfile_DBSetDir
630 //
631 //  Purpose:
632 //    Encapsulates DBSetDir
633 //
634 //  Python Arguments:
635 //    form 1: dirname
636 //
637 //  Programmer:  Jeremy Meredith
638 //  Creation:    July 12, 2005
639 //
640 // ****************************************************************************
DBfile_DBSetDir(PyObject * self,PyObject * args)641 static PyObject *DBfile_DBSetDir(PyObject *self, PyObject *args)
642 {
643     DBfile *db = ((DBfileObject*)self)->db;
644 
645     if (!db)
646     {
647         SiloErrorFunc(self, "This file has been closed.");
648         return NULL;
649     }
650 
651     char *str;
652     if(!PyArg_ParseTuple(args, "s", &str))
653         return NULL;
654 
655     if (DBSetDir(db, str))
656     {
657         SiloErrorFunc(self, "Could not change directories.");
658         return NULL;
659     }
660     else
661     {
662         Py_INCREF(Py_None);
663         return Py_None;
664     }
665 }
666 
667 // ****************************************************************************
668 //  Method:  DBfile_DBClose
669 //
670 //  Purpose:
671 //    Encapsulates DBClose
672 //
673 //  Python Arguments:
674 //    none
675 //
676 //  Programmer:  Jeremy Meredith
677 //  Creation:    July 12, 2005
678 //
679 // ****************************************************************************
DBfile_DBClose(PyObject * self,PyObject * args)680 static PyObject *DBfile_DBClose(PyObject *self, PyObject *args)
681 {
682     DBfile *db = ((DBfileObject*)self)->db;
683 
684     if (!db)
685     {
686       SiloErrorFunc(self, "This file has been closed.");
687         return NULL;
688     }
689 
690     if(!PyArg_ParseTuple(args, ""))
691         return NULL;
692 
693     if (DBClose(db))
694     {
695       SiloErrorFunc(self, "Could not close the file.");
696         return NULL;
697     }
698     else
699     {
700         ((DBfileObject*)self)->db = NULL;
701         Py_INCREF(Py_None);
702         return Py_None;
703     }
704 }
705 
706 // ****************************************************************************
707 //  DBfile method definitions
708 //
709 //  Programmer:  Jeremy Meredith
710 //  Creation:    July 12, 2005
711 //
712 // ****************************************************************************
713 static struct PyMethodDef DBfile_methods[] = {
714     {"GetToc", DBfile_DBGetToc, METH_VARARGS},
715     {"GetVar", DBfile_DBGetVar, METH_VARARGS},
716     {"GetVarInfo", DBfile_DBGetVarInfo, METH_VARARGS},
717     {"Write", DBfile_DBWrite, METH_VARARGS},
718     {"WriteObject", DBfile_DBWriteObject, METH_VARARGS},
719     {"MkDir", DBfile_DBMkDir, METH_VARARGS},
720     {"SetDir", DBfile_DBSetDir, METH_VARARGS},
721     {"Close", DBfile_DBClose, METH_VARARGS},
722     {NULL, NULL}
723 };
724 
725 // ****************************************************************************
726 //  Method:  DBfile_dealloc
727 //
728 //  Purpose:
729 //    Deallocate the object.
730 //
731 //  Arguments:
732 //    none
733 //
734 //  Programmer:  Jeremy Meredith
735 //  Creation:    July 12, 2005
736 //
737 // ****************************************************************************
DBfile_dealloc(PyObject * self)738 static void DBfile_dealloc(PyObject *self)
739 {
740     PyObject_Del(self);
741 }
742 
743 // ****************************************************************************
744 //  Method:  DBfile_as_string
745 //
746 //  Purpose:
747 //    Convert the DBfileObject to a string representation.
748 //
749 //  Arguments:
750 //    s          the target string, with space already allocated
751 //
752 //  Programmer:  Jeremy Meredith
753 //  Creation:    July 12, 2005
754 //
755 // ****************************************************************************
DBfile_as_string(PyObject * self,char * s)756 static void DBfile_as_string(PyObject *self, char *s)
757 {
758     DBfileObject *obj = (DBfileObject*)self;
759     if (obj->db)
760         sprintf(s, "<DBfile object, filename='%s'>", obj->db->pub.name);
761     else
762         sprintf(s, "<closed DBfile object>");
763 }
764 
765 // ****************************************************************************
766 //  Method:  DBfile_str
767 //
768 //  Purpose:
769 //    Convert the DBfileObject to a PyString
770 //
771 //  Arguments:
772 //    none
773 //
774 //  Programmer:  Jeremy Meredith
775 //  Creation:    July 12, 2005
776 //
777 // ****************************************************************************
DBfile_str(PyObject * self)778 static PyObject *DBfile_str(PyObject *self)
779 {
780     char str[1000];
781     DBfile_as_string(self, str);
782     return PyString_FromString(str);
783 }
784 
785 // ****************************************************************************
786 //  Method:  DBfile_print
787 //
788 //  Purpose:
789 //    Print the DBfileObject into a file as text
790 //
791 //  Arguments:
792 //    fp         the file pointer
793 //    flags      (unused)
794 //
795 //  Programmer:  Jeremy Meredith
796 //  Creation:    July 12, 2005
797 //
798 // ****************************************************************************
DBfile_print(PyObject * self,FILE * fp,int flags)799 static int DBfile_print(PyObject *self, FILE *fp, int flags)
800 {
801     char str[1000];
802     DBfile_as_string(self, str);
803     fprintf(fp, str);
804     return 0;
805 }
806 
807 // ****************************************************************************
808 //  Method: DBfile_getattr
809 //
810 //  Purpose:
811 //    Return an attribute by name.  There is only one attribute of a
812 //    DBfile, which is its filename.
813 //
814 //  Arguments:
815 //    name       the name of the attribute to return
816 //
817 //  Programmer:  Jeremy Meredith
818 //  Creation:    July 12, 2005
819 //
820 // ****************************************************************************
821 
822 #if PY_MAJOR_VERSION < 3
DBfile_getattr(PyObject * self,char * name)823 static PyObject *DBfile_getattr(PyObject *self, char *name)
824 {
825     DBfileObject *obj = (DBfileObject*)self;
826 
827     if (!obj->db)
828     {
829       SiloErrorFunc(self, "This file has been closed.");
830         return NULL;
831     }
832 
833     if (!strcmp(name, "filename"))
834     {
835         if (obj->db)
836         {
837             return PyString_FromString(obj->db->pub.name);
838         }
839         else
840         {
841             return PyString_FromString("<closed file>");
842         }
843     }
844 
845     return Py_FindMethod(DBfile_methods, self, name);
846 }
847 #endif
848 
849 // ****************************************************************************
850 //  Method:  DBfile_compare
851 //
852 //  Purpose:
853 //    Compare two DBfileObjects.
854 //
855 //  Arguments:
856 //    u, v       the objects to compare
857 //
858 //  Programmer:  Jeremy Meredith
859 //  Creation:    July 12, 2005
860 //
861 // ****************************************************************************
DBfile_compare(PyObject * v,PyObject * w)862 static int DBfile_compare(PyObject *v, PyObject *w)
863 {
864     DBfile *a = ((DBfileObject *)v)->db;
865     DBfile *b = ((DBfileObject *)w)->db;
866     return (a<b) ? -1 : ((a==b) ? 0 : +1);
867 }
868 
869 // TODO Check this
DBfile_richcompare(PyObject * v,PyObject * w,int op)870 static PyObject* DBfile_richcompare(PyObject *v, PyObject *w, int op)
871 {
872   switch(op){
873   case Py_EQ:{
874     if(v == w) Py_RETURN_TRUE;
875     Py_RETURN_FALSE;
876   }
877   default: Py_RETURN_NOTIMPLEMENTED;
878   }
879 }
880 
881 // ****************************************************************************
882 //  DBfile Python Type Object
883 //
884 //  Programmer:  Jeremy Meredith
885 //  Creation:    July 12, 2005
886 //
887 // ****************************************************************************
888 PyTypeObject DBfileType =
889 {
890   //
891   // Type header
892   //
893   PyObject_HEAD_INIT(&PyType_Type)
894 #if PY_MAJOR_VERSION < 3
895   ob_size        : 0,
896 #endif
897   tp_name        : "DBfil",
898   tp_basicsize   : sizeof(DBfileObject),
899   tp_itemsize    : 0,
900   //
901   // Standard methods
902   //
903   tp_dealloc     : (destructor)DBfile_dealloc,
904 #if (PY_MAJOR_VERSION <= 3)  && (PY_MINOR_VERSION <= 7)
905   tp_print       : (printfunc)DBfile_print,
906 #else
907   tp_vectorcall_offset : (printfunc)DBfile_print,
908 #endif
909 #if PY_MAJOR_VERSION >= 3
910   tp_getattr     : 0,
911 #else
912   tp_getattr     : (getattrfunc)DBfile_getattr,
913 #endif
914   tp_setattr     : 0, // object is read-only
915 #if PY_MAJOR_VERSION >= 3
916   tp_as_async    : (PyAsyncMethods*) NULL,
917 #else
918   tp_compare     : (cmpfunc)DBfile_compare,
919 #endif
920   tp_repr        : (reprfunc)0,
921   //
922   // Type categories
923   //
924   tp_as_number   : 0,
925   tp_as_sequence : 0,
926   tp_as_mapping  : 0,
927     //
928     // More methods
929     //
930   tp_hash        : 0,
931   tp_call        : 0,
932   tp_str         : (reprfunc)DBfile_str,
933   tp_getattro    : 0,
934   tp_setattro    : 0,
935   tp_as_buffer   : 0,
936 #if PY_MAJOR_VERSION >= 3
937   tp_flags       : Py_TPFLAGS_DEFAULT,
938 #else
939   tp_flags       : Py_TPFLAGS_CHECKTYPES,
940 #endif
941   tp_doc         : "This class wraps a Silo DBfile object.",
942   tp_traverse    : 0,
943   tp_clear       : 0,
944   tp_richcompare : (richcmpfunc)DBfile_richcompare,
945   tp_weaklistoffset : 0,
946 };
947 
948 // ****************************************************************************
949 //  Method:  DBfile_NEW
950 //
951 //  Purpose:
952 //    Allocate and initialize a DBfileObject.
953 //
954 //  Arguments:
955 //    init       the initial value
956 //
957 //  Programmer:  Jeremy Meredith
958 //  Creation:    July 12, 2005
959 //
960 // ****************************************************************************
DBfile_NEW(DBfile * init)961 PyObject *DBfile_NEW(DBfile *init)
962 {
963     DBfileObject *obj = PyObject_NEW(DBfileObject, &DBfileType);
964     if (obj)
965     {
966         obj->db = init;
967     }
968     return (PyObject*)obj;
969 }
970 
971 // ****************************************************************************
972 //  Method:  DBfile_NEW
973 //
974 //  Purpose:
975 //    Allocate and initialize a DBfileObject with default values.
976 //
977 //  Python Arguments:
978 //    none
979 //
980 //  Programmer:  Jeremy Meredith
981 //  Creation:    July 12, 2005
982 //
983 // ****************************************************************************
DBfile_new(PyObject * self,PyObject * args)984 PyObject *DBfile_new(PyObject *self, PyObject *args)
985 {
986     return DBfile_NEW(NULL);
987 }
988 
989