1 /*
2  Author: Michael Droettboom
3          mdroe@stsci.edu
4 */
5 
6 #define NO_IMPORT_ARRAY
7 
8 /* util.h must be imported first */
9 #include "astropy_wcs/pyutil.h"
10 
11 #include "astropy_wcs/docstrings.h"
12 
13 #include "wcsfix.h"
14 #include "wcshdr.h"
15 #include "wcsprintf.h"
16 #include "wcsunits.h"
17 
18 /*@null@*/ static INLINE PyObject*
_PyArrayProxy_New(PyObject * self,int nd,const npy_intp * dims,int typenum,const void * data,const int flags)19 _PyArrayProxy_New(
20     /*@shared@*/ PyObject* self,
21     int nd,
22     const npy_intp* dims,
23     int typenum,
24     const void* data,
25     const int flags) {
26 
27   PyArray_Descr* type_descr = NULL;
28   PyObject*      result     = NULL;
29 
30   type_descr = (PyArray_Descr*)PyArray_DescrFromType(typenum);
31   if (type_descr == NULL) {
32     return NULL;
33   }
34 
35   result = (PyObject*)PyArray_NewFromDescr(
36       &PyArray_Type,
37       type_descr,
38       nd, (npy_intp*)dims,
39       NULL,
40       (void*)data,
41       NPY_ARRAY_C_CONTIGUOUS | flags,
42       NULL);
43 
44   if (result == NULL) {
45     return NULL;
46   }
47   Py_INCREF(self);
48   PyArray_SetBaseObject((PyArrayObject *)result, self);
49   return result;
50 }
51 
52 /*@null@*/ PyObject*
PyArrayProxy_New(PyObject * self,int nd,const npy_intp * dims,int typenum,const void * data)53 PyArrayProxy_New(
54     /*@shared@*/ PyObject* self,
55     int nd,
56     const npy_intp* dims,
57     int typenum,
58     const void* data) {
59 
60   return _PyArrayProxy_New(self, nd, dims, typenum, data, NPY_ARRAY_WRITEABLE);
61 }
62 
63 /*@null@*/ PyObject*
PyArrayReadOnlyProxy_New(PyObject * self,int nd,const npy_intp * dims,int typenum,const void * data)64 PyArrayReadOnlyProxy_New(
65     /*@shared@*/ PyObject* self,
66     int nd,
67     const npy_intp* dims,
68     int typenum,
69     const void* data) {
70 
71   return _PyArrayProxy_New(self, nd, dims, typenum, data, 0);
72 }
73 
74 void
preoffset_array(PyArrayObject * array,int value)75 preoffset_array(
76     PyArrayObject* array,
77     int value) {
78 
79   npy_intp  size;
80   double   *data;
81 
82   if (value == 1) {
83     return;
84   }
85 
86   size = PyArray_Size((PyObject*)array);
87   data = (double*)PyArray_DATA(array);
88   offset_c_array(data, size, (double)(1 - value));
89 }
90 
91 void
unoffset_array(PyArrayObject * array,int value)92 unoffset_array(
93     PyArrayObject* array,
94     int value) {
95 
96   npy_intp  size;
97   double   *data;
98 
99   if (value == 1) {
100     return;
101   }
102 
103   size = PyArray_Size((PyObject*)array);
104   data = (double*)PyArray_DATA(array);
105   offset_c_array(data, size, (double)-(1 - value));
106 }
107 
108 void
copy_array_to_c_double(PyArrayObject * array,double * dest)109 copy_array_to_c_double(
110     PyArrayObject* array,
111     double* dest) {
112 
113   npy_intp size = 1;
114   double*  data = NULL;
115 
116   size = PyArray_Size((PyObject*)array);
117   data = (double*)PyArray_DATA(array);
118 
119   memcpy(dest, data, size * sizeof(double));
120 }
121 
122 void
copy_array_to_c_int(PyArrayObject * array,int * dest)123 copy_array_to_c_int(
124     PyArrayObject* array,
125     int* dest) {
126 
127   npy_intp size = 1;
128   int*     data = NULL;
129 
130   size = PyArray_Size((PyObject*)array);
131   data = (int*)PyArray_DATA(array);
132 
133   memcpy(dest, data, size * sizeof(int));
134 }
135 
136 int
is_null(void * p)137 is_null(
138     /*@null@*/ void *p) {
139 
140   if (p == NULL) {
141     PyErr_SetString(PyExc_AssertionError, "Underlying object is NULL.");
142     return 1;
143   }
144   return 0;
145 }
146 
147 /* wcslib represents undefined values using its own special constant,
148    UNDEFINED.  To be consistent with the Pythonic way of doing things,
149    it's nicer to represent undefined values using NaN.  Unfortunately,
150    in order to get nice mutable arrays in Python, Python must be able
151    to edit the wcsprm values directly.  The solution is to store NaNs
152    in the struct "canonically", but convert those NaNs to/from
153    UNDEFINED around every call into a wcslib function.  It's not as
154    computationally expensive as it sounds, as all these arrays are
155    quite small.
156 */
157 
158 static INLINE void
wcsprm_fix_values(struct wcsprm * x,value_fixer_t value_fixer)159 wcsprm_fix_values(
160     struct wcsprm* x,
161     value_fixer_t value_fixer) {
162 
163   unsigned int naxis = (unsigned int)x->naxis;
164 
165   value_fixer(x->cd, naxis * naxis);
166   value_fixer(x->cdelt, naxis);
167   value_fixer(x->crder, naxis);
168   value_fixer(x->crota, naxis);
169   value_fixer(x->crpix, naxis);
170   value_fixer(x->crval, naxis);
171   value_fixer(x->csyer, naxis);
172   value_fixer(&x->equinox, 1);
173   value_fixer(&x->latpole, 1);
174   value_fixer(&x->lonpole, 1);
175   value_fixer(&x->mjdavg, 1);
176   value_fixer(&x->mjdobs, 1);
177   value_fixer(x->obsgeo, 6);
178   value_fixer(&x->cel.phi0, 1);
179   value_fixer(&x->restfrq, 1);
180   value_fixer(&x->restwav, 1);
181   value_fixer(&x->cel.theta0, 1);
182   value_fixer(&x->velangl, 1);
183   value_fixer(&x->velosys, 1);
184   value_fixer(&x->zsource, 1);
185   value_fixer(x->czphs, naxis);
186   value_fixer(x->cperi, naxis);
187   value_fixer(x->mjdref, 2);
188   value_fixer(&x->mjdbeg, 1);
189   value_fixer(&x->mjdend, 1);
190   value_fixer(&x->jepoch, 1);
191   value_fixer(&x->bepoch, 1);
192   value_fixer(&x->tstart, 1);
193   value_fixer(&x->tstop, 1);
194   value_fixer(&x->xposure, 1);
195   value_fixer(&x->timsyer, 1);
196   value_fixer(&x->timrder, 1);
197   value_fixer(&x->timedel, 1);
198   value_fixer(&x->timepixr, 1);
199   value_fixer(&x->timeoffs, 1);
200   value_fixer(&x->telapse, 1);
201 }
202 
203 void
wcsprm_c2python(struct wcsprm * x)204 wcsprm_c2python(
205     /*@null@*/ struct wcsprm* x) {
206 
207   if (x != NULL) {
208     wcsprm_fix_values(x, &undefined2nan);
209   }
210 }
211 
212 void
wcsprm_python2c(struct wcsprm * x)213 wcsprm_python2c(
214     /*@null@*/ struct wcsprm* x) {
215 
216   if (x != NULL) {
217     wcsprm_fix_values(x, &nan2undefined);
218   }
219 }
220 
221 /***************************************************************************
222  * Exceptions                                                              *
223  ***************************************************************************/
224 
225 PyObject* WcsExc_Wcs;
226 PyObject* WcsExc_SingularMatrix;
227 PyObject* WcsExc_InconsistentAxisTypes;
228 PyObject* WcsExc_InvalidTransform;
229 PyObject* WcsExc_InvalidCoordinate;
230 PyObject* WcsExc_NoSolution;
231 PyObject* WcsExc_InvalidSubimageSpecification;
232 PyObject* WcsExc_NonseparableSubimageCoordinateSystem;
233 PyObject* WcsExc_NoWcsKeywordsFound;
234 PyObject* WcsExc_InvalidTabularParameters;
235 
236 /* This is an array mapping the wcs status codes to Python exception
237  * types.  The exception string is stored as part of wcslib itself in
238  * wcs_errmsg.
239  */
240 PyObject** wcs_errexc[14];
241 
242 static PyObject*
_new_exception_with_doc(char * name,char * doc,PyObject * base)243 _new_exception_with_doc(char *name, char *doc, PyObject *base)
244 {
245   return PyErr_NewExceptionWithDoc(name, doc, base, NULL);
246 }
247 
248 #define DEFINE_EXCEPTION(exc) \
249   WcsExc_##exc = _new_exception_with_doc(                             \
250       "astropy.wcs._wcs." #exc "Error",                                 \
251       doc_##exc,                                                        \
252       WcsExc_Wcs);                                                      \
253   if (WcsExc_##exc == NULL) \
254     return 1; \
255   PyModule_AddObject(m, #exc "Error", WcsExc_##exc); \
256 
257 int
_define_exceptions(PyObject * m)258 _define_exceptions(
259     PyObject* m) {
260 
261   WcsExc_Wcs = _new_exception_with_doc(
262       "astropy.wcs._wcs.WcsError",
263       doc_WcsError,
264       PyExc_ValueError);
265   if (WcsExc_Wcs == NULL) {
266     return 1;
267   }
268   PyModule_AddObject(m, "WcsError", WcsExc_Wcs);
269 
270   DEFINE_EXCEPTION(SingularMatrix);
271   DEFINE_EXCEPTION(InconsistentAxisTypes);
272   DEFINE_EXCEPTION(InvalidTransform);
273   DEFINE_EXCEPTION(InvalidCoordinate);
274   DEFINE_EXCEPTION(NoSolution);
275   DEFINE_EXCEPTION(InvalidSubimageSpecification);
276   DEFINE_EXCEPTION(NonseparableSubimageCoordinateSystem);
277   DEFINE_EXCEPTION(NoWcsKeywordsFound);
278   DEFINE_EXCEPTION(InvalidTabularParameters);
279   return 0;
280 }
281 
282 const char*
wcslib_get_error_message(int status)283 wcslib_get_error_message(int status) {
284   return wcs_errmsg[status];
285 }
286 
287 void
wcserr_to_python_exc(const struct wcserr * err)288 wcserr_to_python_exc(const struct wcserr *err) {
289   PyObject *exc;
290   if (err == NULL) {
291     PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib");
292   } else {
293     if (err->status > 0 && err->status <= WCS_ERRMSG_MAX) {
294       exc = *wcs_errexc[err->status];
295     } else {
296       exc = PyExc_RuntimeError;
297     }
298     /* This is technically not thread-safe -- make sure we have the GIL */
299     wcsprintf_set(NULL);
300     wcserr_prt(err, "");
301     PyErr_SetString(exc, wcsprintf_buf());
302   }
303 }
304 
305 void
wcs_to_python_exc(const struct wcsprm * wcs)306 wcs_to_python_exc(const struct wcsprm *wcs) {
307   PyObject* exc;
308   const struct wcserr *err = wcs->err;
309   if (err == NULL) {
310     PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib");
311   } else {
312     if (err->status > 0 && err->status < WCS_ERRMSG_MAX) {
313       exc = *wcs_errexc[err->status];
314     } else {
315       exc = PyExc_RuntimeError;
316     }
317     /* This is technically not thread-safe -- make sure we have the GIL */
318     wcsprintf_set(NULL);
319     wcsperr(wcs, "");
320     PyErr_SetString(exc, wcsprintf_buf());
321   }
322 }
323 
324 void
wcserr_fix_to_python_exc(const struct wcserr * err)325 wcserr_fix_to_python_exc(const struct wcserr *err) {
326   PyObject *exc;
327   if (err == NULL) {
328     PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib");
329   } else {
330     if (err->status > 0 && err->status <= FIXERR_NO_REF_PIX_VAL) {
331       exc = PyExc_ValueError;
332     } else {
333       exc = PyExc_RuntimeError;
334     }
335     /* This is technically not thread-safe -- make sure we have the GIL */
336     wcsprintf_set(NULL);
337     wcserr_prt(err, "");
338     PyErr_SetString(exc, wcsprintf_buf());
339   }
340 }
341 
342 void
wcshdr_err_to_python_exc(int status,const struct wcsprm * wcs)343 wcshdr_err_to_python_exc(int status, const struct wcsprm *wcs) {
344   /* Add error to wcslib error buffer */
345   wcsperr(wcs, NULL);
346   if (status > 0 && status != WCSHDRERR_PARSER) {
347     PyErr_Format(
348       PyExc_MemoryError,
349       "Memory allocation error:\n%s",
350       wcsprintf_buf()
351     );
352   } else {
353     PyErr_Format(
354       PyExc_ValueError,
355       "Internal error in wcslib header parser:\n %s",
356       wcsprintf_buf()
357     );
358   }
359 }
360 
361 
362 /***************************************************************************
363   Property helpers
364  ***************************************************************************/
365 
366 #define SHAPE_STR_LEN 2048
367 
368 /* Helper function to display the desired shape of an array as a
369    string, eg. 2x2 */
370 static void
shape_to_string(int ndims,const npy_intp * dims,char * str)371 shape_to_string(
372     int ndims,
373     const npy_intp* dims,
374     char* str /* [SHAPE_STR_LEN] */) {
375 
376   int i;
377   char value[32]; /* More than large enough to hold string rep of a
378                      64-bit integer (way overkill) */
379 
380   if (ndims > 3) {
381     strncpy(str, "ERROR", 6);
382     return;
383   }
384 
385   str[0] = 0;
386   for (i = 0; i < ndims; ++i) {
387       snprintf(value, 32, "%d", (int)dims[i]);
388     strncat(str, value, 32);
389     if (i != ndims - 1) {
390       strncat(str, "x", 2);
391     }
392   }
393 }
394 
395 /* get_string is inlined */
396 
397 int
set_string(const char * propname,PyObject * value,char * dest,Py_ssize_t maxlen)398 set_string(
399     const char* propname,
400     PyObject* value,
401     char* dest,
402     Py_ssize_t maxlen) {
403 
404   char*      buffer;
405   Py_ssize_t len;
406   PyObject*  ascii_obj = NULL;
407   int        result = -1;
408 
409   if (check_delete(propname, value)) {
410     return -1;
411   }
412 
413   if (PyUnicode_Check(value)) {
414     ascii_obj = PyUnicode_AsASCIIString(value);
415     if (ascii_obj == NULL) {
416       goto end;
417     }
418     if (PyBytes_AsStringAndSize(ascii_obj, &buffer, &len) == -1) {
419       goto end;
420     }
421   } else if (PyBytes_Check(value)) {
422     if (PyBytes_AsStringAndSize(value, &buffer, &len) == -1) {
423       goto end;
424     }
425   } else {
426     PyErr_SetString(PyExc_TypeError, "value must be bytes or unicode");
427     goto end;
428   }
429 
430   if (len > maxlen) {
431     PyErr_Format(
432         PyExc_ValueError,
433         "'%s' must be less than %u characters",
434         propname,
435         (unsigned int)maxlen);
436     goto end;
437   }
438 
439   strncpy(dest, buffer, (size_t)maxlen);
440 
441   result = 0;
442 
443  end:
444   Py_XDECREF(ascii_obj);
445 
446   return result;
447 }
448 
449 /* get_bool is inlined */
450 
451 int
set_bool(const char * propname,PyObject * value,int * dest)452 set_bool(
453     const char* propname,
454     PyObject* value,
455     int* dest) {
456 
457   if (check_delete(propname, value)) {
458     return -1;
459   }
460 
461   *dest = PyObject_IsTrue(value);
462 
463   return 0;
464 }
465 
466 /* get_int is inlined */
467 
468 int
set_int(const char * propname,PyObject * value,int * dest)469 set_int(
470     const char* propname,
471     PyObject* value,
472     int* dest) {
473   long value_int;
474 
475   if (check_delete(propname, value)) {
476     return -1;
477   }
478 
479   value_int = PyLong_AsLong(value);
480   if (value_int == -1 && PyErr_Occurred()) {
481     return -1;
482   }
483 
484   if ((unsigned long)value_int > 0x7fffffff) {
485     PyErr_SetString(PyExc_OverflowError, "integer value too large");
486     return -1;
487   }
488 
489   *dest = (int)value_int;
490 
491   return 0;
492 }
493 
494 /* get_double is inlined */
495 
496 int
set_double(const char * propname,PyObject * value,double * dest)497 set_double(
498     const char* propname,
499     PyObject* value,
500     double* dest) {
501 
502   if (check_delete(propname, value)) {
503     return -1;
504   }
505 
506   *dest = PyFloat_AsDouble(value);
507 
508   if (PyErr_Occurred()) {
509     return -1;
510   } else {
511     return 0;
512   }
513 }
514 
515 /* get_double_array is inlined */
516 
517 int
set_double_array(const char * propname,PyObject * value,int ndims,const npy_intp * dims,double * dest)518 set_double_array(
519     const char* propname,
520     PyObject* value,
521     int ndims,
522     const npy_intp* dims,
523     double* dest) {
524 
525   PyArrayObject* value_array = NULL;
526   npy_int        i           = 0;
527   char           shape_str[SHAPE_STR_LEN];
528 
529   if (check_delete(propname, value)) {
530     return -1;
531   }
532 
533   value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_DOUBLE,
534                                                           ndims, ndims);
535   if (value_array == NULL) {
536     return -1;
537   }
538 
539   if (dims != NULL) {
540     for (i = 0; i < ndims; ++i) {
541       if (PyArray_DIM(value_array, i) != dims[i]) {
542         shape_to_string(ndims, dims, shape_str);
543         PyErr_Format(
544             PyExc_ValueError,
545             "'%s' array is the wrong shape, must be %s",
546             propname, shape_str);
547         Py_DECREF(value_array);
548         return -1;
549       }
550     }
551   }
552 
553   copy_array_to_c_double(value_array, dest);
554 
555   Py_DECREF(value_array);
556 
557   return 0;
558 }
559 
560 int
set_int_array(const char * propname,PyObject * value,int ndims,const npy_intp * dims,int * dest)561 set_int_array(
562     const char* propname,
563     PyObject* value,
564     int ndims,
565     const npy_intp* dims,
566     int* dest) {
567   PyArrayObject* value_array = NULL;
568   npy_int        i           = 0;
569   char           shape_str[SHAPE_STR_LEN];
570 
571   if (check_delete(propname, value)) {
572     return -1;
573   }
574 
575   value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_INT,
576                                                           ndims, ndims);
577   if (value_array == NULL) {
578     return -1;
579   }
580 
581   if (dims != NULL) {
582     for (i = 0; i < ndims; ++i) {
583       if (PyArray_DIM(value_array, i) != dims[i]) {
584         shape_to_string(ndims, dims, shape_str);
585         PyErr_Format(
586             PyExc_ValueError,
587             "'%s' array is the wrong shape, must be %s",
588             propname, shape_str);
589         Py_DECREF(value_array);
590         return -1;
591       }
592     }
593   }
594 
595   copy_array_to_c_int(value_array, dest);
596 
597   Py_DECREF(value_array);
598 
599   return 0;
600 }
601 
602 /* get_str_list is inlined */
603 
604 int
set_str_list(const char * propname,PyObject * value,Py_ssize_t len,Py_ssize_t maxlen,char (* dest)[72])605 set_str_list(
606     const char* propname,
607     PyObject* value,
608     Py_ssize_t len,
609     Py_ssize_t maxlen,
610     char (*dest)[72]) {
611 
612   PyObject*  str      = NULL;
613   Py_ssize_t input_len;
614   Py_ssize_t i        = 0;
615 
616   if (check_delete(propname, value)) {
617     return -1;
618   }
619 
620   if (maxlen == 0) {
621     maxlen = 68;
622   }
623 
624   if (!PySequence_Check(value)) {
625     PyErr_Format(
626         PyExc_TypeError,
627         "'%s' must be a sequence of strings",
628         propname);
629     return -1;
630   }
631 
632   if (PySequence_Size(value) != len) {
633     PyErr_Format(
634         PyExc_ValueError,
635         "len(%s) must be %u",
636         propname,
637         (unsigned int)len);
638     return -1;
639   }
640 
641   /* We go through the list twice, once to verify that the list is
642      in the correct format, and then again to do the data copy.  This
643      way, we won't partially copy the contents and then throw an
644      exception. */
645   for (i = 0; i < len; ++i) {
646     str = PySequence_GetItem(value, i);
647     if (str == NULL) {
648       return -1;
649     }
650 
651     if (!(PyBytes_CheckExact(str) || PyUnicode_CheckExact(str))) {
652       PyErr_Format(
653           PyExc_TypeError,
654           "'%s' must be a sequence of bytes or strings",
655           propname);
656       Py_DECREF(str);
657       return -1;
658     }
659 
660     input_len = PySequence_Size(str);
661     if (input_len > maxlen) {
662       PyErr_Format(
663           PyExc_ValueError,
664           "Each entry in '%s' must be less than %u characters",
665           propname, (unsigned int)maxlen);
666       Py_DECREF(str);
667       return -1;
668     } else if (input_len == -1) {
669       Py_DECREF(str);
670       return -1;
671     }
672 
673     Py_DECREF(str);
674   }
675 
676   for (i = 0; i < len; ++i) {
677     str = PySequence_GetItem(value, i);
678     if (str == NULL) {
679       /* Theoretically, something has gone really wrong here, since
680          we've already verified the list. */
681       PyErr_Clear();
682       PyErr_Format(
683           PyExc_RuntimeError,
684           "Input values have changed underneath us.  Something is seriously wrong.");
685       return -1;
686     }
687 
688     if (set_string(propname, str, dest[i], maxlen)) {
689       PyErr_Clear();
690       PyErr_Format(
691           PyExc_RuntimeError,
692           "Input values have changed underneath us.  Something is seriously wrong.");
693       Py_DECREF(str);
694       return -1;
695     }
696 
697     Py_DECREF(str);
698   }
699 
700   return 0;
701 }
702 
703 
704 /*@null@*/ PyObject*
get_pscards(const char * propname,struct pscard * ps,int nps)705 get_pscards(
706     /*@unused@*/ const char* propname,
707     struct pscard* ps,
708     int nps) {
709 
710   PyObject*  result    = NULL;
711   PyObject*  subresult = NULL;
712   Py_ssize_t i         = 0;
713 
714   if (nps < 0) {
715     nps = 0;
716   }
717 
718   result = PyList_New((Py_ssize_t)nps);
719   if (result == NULL) {
720     return NULL;
721   }
722 
723   if (nps && ps == NULL) {
724     PyErr_SetString(PyExc_MemoryError, "NULL pointer");
725     return NULL;
726   }
727 
728   for (i = 0; i < (Py_ssize_t)nps; ++i) {
729     subresult = Py_BuildValue("iis", ps[i].i, ps[i].m, ps[i].value);
730     if (subresult == NULL) {
731       Py_DECREF(result);
732       return NULL;
733     }
734 
735     if (PyList_SetItem(result, i, subresult)) {
736       Py_DECREF(subresult);
737       Py_DECREF(result);
738       return NULL;
739     }
740   }
741 
742   return result;
743 }
744 
745 int
set_pscards(const char * propname,PyObject * value,struct pscard ** ps,int * nps,int * npsmax)746 set_pscards(
747     /*@unused@*/ const char* propname,
748     PyObject* value,
749     struct pscard** ps,
750     int *nps,
751     int *npsmax) {
752 
753   PyObject*   subvalue  = NULL;
754   Py_ssize_t  i         = 0;
755   Py_ssize_t  size      = 0;
756   int         ival      = 0;
757   int         mval      = 0;
758   const char* strvalue  = 0;
759   void*       newmem    = NULL;
760 
761   if (!PySequence_Check(value))
762     return -1;
763   size = PySequence_Size(value);
764   if (size > 0x7fffffff) {
765     /* Must be a 32-bit size */
766     return -1;
767   }
768 
769   if (size > (Py_ssize_t)*npsmax) {
770     newmem = malloc(sizeof(struct pscard) * size);
771     if (newmem == NULL) {
772       PyErr_SetString(PyExc_MemoryError, "Could not allocate memory.");
773       return -1;
774     }
775     free(*ps);
776     *ps = newmem;
777     *npsmax = (int)size;
778   }
779 
780   /* Verify the entire list for correct types first, so we don't have
781      to undo anything copied into the canonical array. */
782   for (i = 0; i < size; ++i) {
783     subvalue = PySequence_GetItem(value, i);
784     if (subvalue == NULL) {
785       return -1;
786     }
787     if (!PyArg_ParseTuple(subvalue, "iis", &ival, &mval, &strvalue)) {
788       Py_DECREF(subvalue);
789       return -1;
790     }
791     Py_DECREF(subvalue);
792   }
793 
794   for (i = 0; i < size; ++i) {
795     subvalue = PySequence_GetItem(value, i);
796     if (subvalue == NULL) {
797       return -1;
798     }
799     if (!PyArg_ParseTuple(subvalue, "iis", &ival, &mval, &strvalue)) {
800       Py_DECREF(subvalue);
801       return -1;
802     }
803     Py_DECREF(subvalue);
804 
805     (*ps)[i].i = ival;
806     (*ps)[i].m = mval;
807     strncpy((*ps)[i].value, strvalue, 72);
808     (*ps)[i].value[71] = '\0';
809     (*nps) = (int)(i + 1);
810   }
811 
812   return 0;
813 }
814 
815 /*@null@*/ PyObject*
get_pvcards(const char * propname,struct pvcard * pv,int npv)816 get_pvcards(
817     /*@unused@*/ const char* propname,
818     struct pvcard* pv,
819     int npv) {
820 
821   PyObject*  result    = NULL;
822   PyObject*  subresult = NULL;
823   Py_ssize_t i         = 0;
824 
825   if (npv < 0) {
826     npv = 0;
827   }
828 
829   result = PyList_New((Py_ssize_t)npv);
830   if (result == NULL) {
831     return NULL;
832   }
833 
834   if (npv && pv == NULL) {
835     PyErr_SetString(PyExc_MemoryError, "NULL pointer");
836     return NULL;
837   }
838 
839   for (i = 0; i < (Py_ssize_t)npv; ++i) {
840     subresult = Py_BuildValue("iid", pv[i].i, pv[i].m, pv[i].value);
841     if (subresult == NULL) {
842       Py_DECREF(result);
843       return NULL;
844     }
845 
846     if (PyList_SetItem(result, i, subresult)) {
847       Py_DECREF(subresult);
848       Py_DECREF(result);
849       return NULL;
850     }
851   }
852 
853   return result;
854 }
855 
856 int
set_pvcards(const char * propname,PyObject * value,struct pvcard ** pv,int * npv,int * npvmax)857 set_pvcards(
858     /*@propname@*/ const char* propname,
859     PyObject* value,
860     struct pvcard** pv,
861     int *npv,
862     int *npvmax) {
863 
864   PyObject* fastseq = NULL;
865   struct pvcard* newmem = NULL;
866   Py_ssize_t size;
867   int ret = -1;
868   int i;
869 
870   fastseq = PySequence_Fast(value, "Expected sequence type");
871   if (!fastseq)
872     goto done;
873 
874   size = PySequence_Fast_GET_SIZE(value);
875   newmem = malloc(sizeof(struct pvcard) * size);
876 
877   /* Raise exception if size is nonzero but newmem
878    * could not be allocated. */
879   if (size && !newmem) {
880     PyErr_SetString(PyExc_MemoryError, "Could not allocate memory.");
881     return -1;
882   }
883 
884   for (i = 0; i < size; ++i)
885   {
886     if (!PyArg_ParseTuple(PySequence_Fast_GET_ITEM(value, i), "iid",
887         &newmem[i].i, &newmem[i].m, &newmem[i].value))
888     {
889       goto done;
890     }
891   }
892 
893   if (size <= (Py_ssize_t)*npvmax) {
894     memcpy(*pv, newmem, sizeof(struct pvcard) * size);
895   } else { /* (size > (Py_ssize_t)*npvmax) */
896     free(*pv);
897     *npv = (int)size;
898     *pv = newmem;
899     newmem = NULL;
900   }
901   *npv = (int)size;
902 
903   ret = 0;
904 done:
905   Py_XDECREF(fastseq);
906   free(newmem);
907   return ret;
908 }
909 
910 PyObject*
get_deepcopy(PyObject * obj,PyObject * memo)911 get_deepcopy(
912     PyObject* obj,
913     PyObject* memo) {
914 
915   if (PyObject_HasAttrString(obj, "__deepcopy__")) {
916     return PyObject_CallMethod(obj, "__deepcopy__", "O", memo);
917   } else {
918     return PyObject_CallMethod(obj, "__copy__", "");
919   }
920 }
921 
922 /***************************************************************************
923  * Miscellaneous helper functions                                          *
924  ***************************************************************************/
925 
926 int
parse_unsafe_unit_conversion_spec(const char * arg,int * ctrl)927 parse_unsafe_unit_conversion_spec(
928     const char* arg, int* ctrl) {
929 
930   const char* p = NULL;
931 
932   *ctrl = 0;
933 
934   for (p = arg; *p != '\0'; ++p) {
935     switch (*p) {
936     case 's':
937     case 'S':
938       *ctrl |= 1;
939       break;
940     case 'h':
941     case 'H':
942       *ctrl |= 2;
943       break;
944     case 'd':
945     case 'D':
946       *ctrl |= 4;
947       break;
948     default:
949       PyErr_SetString(
950           PyExc_ValueError,
951           "translate_units may only contain the characters 's', 'h' or 'd'");
952       return 1;
953     }
954   }
955 
956   return 0;
957 }
958