1 /*  This file, getkey.c, contains routines that read keywords from         */
2 /*  a FITS header.                                                         */
3 
4 /*  The FITSIO software was written by William Pence at the High Energy    */
5 /*  Astrophysic Science Archive Research Center (HEASARC) at the NASA      */
6 /*  Goddard Space Flight Center.                                           */
7 
8 #include <string.h>
9 #include <limits.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 /* stddef.h is apparently needed to define size_t */
13 #include <stddef.h>
14 #include "fitsio2.h"
15 
16 /*--------------------------------------------------------------------------*/
ffghsp(fitsfile * fptr,int * nexist,int * nmore,int * status)17 int ffghsp(fitsfile *fptr,  /* I - FITS file pointer                     */
18            int *nexist,     /* O - number of existing keywords in header */
19            int *nmore,      /* O - how many more keywords will fit       */
20            int *status)     /* IO - error status                         */
21 /*
22   returns the number of existing keywords (not counting the END keyword)
23   and the number of more keyword that will fit in the current header
24   without having to insert more FITS blocks.
25 */
26 {
27     if (*status > 0)
28         return(*status);
29 
30     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
31         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
32 
33     if (nexist)
34         *nexist = (int) (( ((fptr->Fptr)->headend) -
35                 ((fptr->Fptr)->headstart[(fptr->Fptr)->curhdu]) ) / 80);
36 
37     if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
38     {
39       if (nmore)
40         *nmore = -1;   /* data not written yet, so room for any keywords */
41     }
42     else
43     {
44       /* calculate space available between the data and the END card */
45       if (nmore)
46         *nmore = (int) (((fptr->Fptr)->datastart - (fptr->Fptr)->headend) / 80 - 1);
47     }
48 
49     return(*status);
50 }
51 /*--------------------------------------------------------------------------*/
ffghps(fitsfile * fptr,int * nexist,int * position,int * status)52 int ffghps(fitsfile *fptr, /* I - FITS file pointer                     */
53           int *nexist,     /* O - number of existing keywords in header */
54           int *position,   /* O - position of next keyword to be read   */
55           int *status)     /* IO - error status                         */
56 /*
57   return the number of existing keywords and the position of the next
58   keyword that will be read.
59 */
60 {
61     if (*status > 0)
62         return(*status);
63 
64     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
65         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
66 
67     if (nexist)
68       *nexist = (int) (( ((fptr->Fptr)->headend) - ((fptr->Fptr)->headstart[(fptr->Fptr)->curhdu]) ) / 80);
69 
70     if (position)
71       *position = (int) (( ((fptr->Fptr)->nextkey) - ((fptr->Fptr)->headstart[(fptr->Fptr)->curhdu]) ) / 80 + 1);
72 
73     return(*status);
74 }
75 /*--------------------------------------------------------------------------*/
ffnchk(fitsfile * fptr,int * status)76 int ffnchk(fitsfile *fptr,  /* I - FITS file pointer                     */
77            int *status)     /* IO - error status                         */
78 /*
79   function returns the position of the first null character (ASCII 0), if
80   any, in the current header.  Null characters are illegal, but the other
81   CFITSIO routines that read the header will not detect this error, because
82   the null gets interpreted as a normal end of string character.
83 */
84 {
85     long ii, nblock;
86     LONGLONG bytepos;
87     int length, nullpos;
88     char block[2881];
89 
90     if (*status > 0)
91         return(*status);
92 
93     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
94         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
95 
96     if ((fptr->Fptr)->datastart == DATA_UNDEFINED)
97     {
98         return(0);  /* Don't check a file that is just being created.  */
99                     /* It cannot contain nulls since CFITSIO wrote it. */
100     }
101     else
102     {
103         /* calculate number of blocks in the header */
104         nblock = (long) (( (fptr->Fptr)->datastart -
105                    (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu] ) / 2880);
106     }
107 
108     bytepos = (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu];
109     ffmbyt(fptr, bytepos, REPORT_EOF, status);  /* move to read pos. */
110 
111     block[2880] = '\0';
112     for (ii = 0; ii < nblock; ii++)
113     {
114         if (ffgbyt(fptr, 2880, block, status) > 0)
115             return(0);   /* read error of some sort */
116 
117         length = strlen(block);
118         if (length != 2880)
119         {
120             nullpos = (ii * 2880) + length + 1;
121             return(nullpos);
122         }
123     }
124 
125     return(0);
126 }
127 /*--------------------------------------------------------------------------*/
ffmaky(fitsfile * fptr,int nrec,int * status)128 int ffmaky(fitsfile *fptr,    /* I - FITS file pointer                    */
129           int nrec,           /* I - one-based keyword number to move to  */
130           int *status)        /* IO - error status                        */
131 {
132 /*
133   move pointer to the specified absolute keyword position.  E.g. this keyword
134   will then be read by the next call to ffgnky.
135 */
136     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
137         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
138 
139     (fptr->Fptr)->nextkey = (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu] + ( (nrec - 1) * 80);
140 
141     return(*status);
142 }
143 /*--------------------------------------------------------------------------*/
ffmrky(fitsfile * fptr,int nmove,int * status)144 int ffmrky(fitsfile *fptr,    /* I - FITS file pointer                   */
145           int nmove,          /* I - relative number of keywords to move */
146           int *status)        /* IO - error status                       */
147 {
148 /*
149   move pointer to the specified keyword position relative to the current
150   position.  E.g. this keyword  will then be read by the next call to ffgnky.
151 */
152 
153     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
154         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
155 
156     (fptr->Fptr)->nextkey += (nmove * 80);
157 
158     return(*status);
159 }
160 /*--------------------------------------------------------------------------*/
ffgnky(fitsfile * fptr,char * card,int * status)161 int ffgnky(fitsfile *fptr,  /* I - FITS file pointer     */
162            char *card,      /* O - card string           */
163            int *status)     /* IO - error status         */
164 /*
165   read the next keyword from the header - used internally by cfitsio
166 */
167 {
168     int jj, nrec;
169     LONGLONG bytepos, endhead;
170     char message[FLEN_ERRMSG];
171 
172     if (*status > 0)
173         return(*status);
174 
175     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
176         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
177 
178     card[0] = '\0';  /* make sure card is terminated, even affer read error */
179 
180 /*
181   Check that nextkey points to a legal keyword position.  Note that headend
182   is the current end of the header, i.e., the position where a new keyword
183   would be appended, however, if there are more than 1 FITS block worth of
184   blank keywords at the end of the header (36 keywords per 2880 byte block)
185   then the actual physical END card must be located at a starting position
186   which is just 2880 bytes prior to the start of the data unit.
187 */
188 
189     bytepos = (fptr->Fptr)->nextkey;
190     endhead = maxvalue( ((fptr->Fptr)->headend), ((fptr->Fptr)->datastart - 2880) );
191 
192     /* nextkey must be < endhead and > than  headstart */
193     if (bytepos > endhead ||
194         bytepos < (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu] )
195     {
196         nrec= (int) ((bytepos - (fptr->Fptr)->headstart[(fptr->Fptr)->curhdu]) / 80 + 1);
197         snprintf(message, FLEN_ERRMSG,"Cannot get keyword number %d.  It does not exist.",
198                 nrec);
199         ffpmsg(message);
200         return(*status = KEY_OUT_BOUNDS);
201     }
202 
203     ffmbyt(fptr, bytepos, REPORT_EOF, status);  /* move to read pos. */
204 
205     card[80] = '\0';  /* make sure card is terminate, even if ffgbyt fails */
206 
207     if (ffgbyt(fptr, 80, card, status) <= 0)
208     {
209         (fptr->Fptr)->nextkey += 80;   /* increment pointer to next keyword */
210 
211         /* strip off trailing blanks with terminated string */
212         jj = 79;
213         while (jj >= 0 && card[jj] == ' ')
214                jj--;
215 
216         card[jj + 1] = '\0';
217     }
218     return(*status);
219 }
220 /*--------------------------------------------------------------------------*/
ffgnxk(fitsfile * fptr,char ** inclist,int ninc,char ** exclist,int nexc,char * card,int * status)221 int ffgnxk( fitsfile *fptr,     /* I - FITS file pointer              */
222             char **inclist,     /* I - list of included keyword names */
223             int ninc,           /* I - number of names in inclist     */
224             char **exclist,     /* I - list of excluded keyword names */
225             int nexc,           /* I - number of names in exclist     */
226             char *card,         /* O - first matching keyword         */
227             int  *status)       /* IO - error status                  */
228 /*
229     Return the next keyword that matches one of the names in inclist
230     but does not match any of the names in exclist.  The search
231     goes from the current position to the end of the header, only.
232     Wild card characters may be used in the name lists ('*', '?' and '#').
233 */
234 {
235     int casesn, match, exact, namelen;
236     long ii, jj;
237     char keybuf[FLEN_CARD], keyname[FLEN_KEYWORD];
238 
239     card[0] = '\0';
240     if (*status > 0)
241         return(*status);
242 
243     casesn = FALSE;
244 
245     /* get next card, and return with an error if hit end of header */
246     while( ffgcrd(fptr, "*", keybuf, status) <= 0)
247     {
248         ffgknm(keybuf, keyname, &namelen, status); /* get the keyword name */
249 
250         /* does keyword match any names in the include list? */
251         for (ii = 0; ii < ninc; ii++)
252         {
253             ffcmps(inclist[ii], keyname, casesn, &match, &exact);
254             if (match)
255             {
256                 /* does keyword match any names in the exclusion list? */
257                 jj = -1;
258                 while ( ++jj < nexc )
259                 {
260                     ffcmps(exclist[jj], keyname, casesn, &match, &exact);
261                     if (match)
262                         break;
263                 }
264 
265                 if (jj >= nexc)
266                 {
267                     /* not in exclusion list, so return this keyword */
268                     strcat(card, keybuf);
269                     return(*status);
270                 }
271             }
272         }
273     }
274     return(*status);
275 }
276 /*--------------------------------------------------------------------------*/
ffgky(fitsfile * fptr,int datatype,const char * keyname,void * value,char * comm,int * status)277 int ffgky( fitsfile *fptr,     /* I - FITS file pointer        */
278            int  datatype,      /* I - datatype of the value    */
279            const char *keyname,      /* I - name of keyword to read  */
280            void *value,        /* O - keyword value            */
281            char *comm,         /* O - keyword comment          */
282            int  *status)       /* IO - error status            */
283 /*
284   Read (get) the keyword value and comment from the FITS header.
285   Reads a keyword value with the datatype specified by the 2nd argument.
286 */
287 {
288     LONGLONG longval;
289     double doubleval;
290 
291     if (*status > 0)           /* inherit input status value if > 0 */
292         return(*status);
293 
294     if (datatype == TSTRING)
295     {
296         ffgkys(fptr, keyname, (char *) value, comm, status);
297     }
298     else if (datatype == TBYTE)
299     {
300         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
301         {
302             if (longval > UCHAR_MAX || longval < 0)
303                 *status = NUM_OVERFLOW;
304             else
305                 *(unsigned char *) value = (unsigned char) longval;
306         }
307     }
308     else if (datatype == TSBYTE)
309     {
310         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
311         {
312             if (longval > 127 || longval < -128)
313                 *status = NUM_OVERFLOW;
314             else
315                 *(signed char *) value = (signed char) longval;
316         }
317     }
318     else if (datatype == TUSHORT)
319     {
320         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
321         {
322             if (longval > (long) USHRT_MAX || longval < 0)
323                 *status = NUM_OVERFLOW;
324             else
325                 *(unsigned short *) value = (unsigned short) longval;
326         }
327     }
328     else if (datatype == TSHORT)
329     {
330         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
331         {
332             if (longval > SHRT_MAX || longval < SHRT_MIN)
333                 *status = NUM_OVERFLOW;
334             else
335                 *(short *) value = (short) longval;
336         }
337     }
338     else if (datatype == TUINT)
339     {
340         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
341         {
342             if (longval > (long) UINT_MAX || longval < 0)
343                 *status = NUM_OVERFLOW;
344             else
345                 *(unsigned int *) value = longval;
346         }
347     }
348     else if (datatype == TINT)
349     {
350         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
351         {
352             if (longval > INT_MAX || longval < INT_MIN)
353                 *status = NUM_OVERFLOW;
354             else
355                 *(int *) value = longval;
356         }
357     }
358     else if (datatype == TLOGICAL)
359     {
360         ffgkyl(fptr, keyname, (int *) value, comm, status);
361     }
362     else if (datatype == TULONG)
363     {
364         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
365         {
366             if (longval > ULONG_MAX || longval < 0)
367                 *status = NUM_OVERFLOW;
368             else
369                  *(unsigned long *) value = longval;
370         }
371     }
372     else if (datatype == TLONG)
373     {
374         if (ffgkyjj(fptr, keyname, &longval, comm, status) <= 0)
375         {
376             if (longval > LONG_MAX || longval < LONG_MIN)
377                 *status = NUM_OVERFLOW;
378             else
379                 *(int *) value = longval;
380         }
381         ffgkyj(fptr, keyname, (long *) value, comm, status);
382     }
383     else if (datatype == TULONGLONG)
384     {
385         ffgkyujj(fptr, keyname, (ULONGLONG *) value, comm, status);
386     }
387     else if (datatype == TLONGLONG)
388     {
389         ffgkyjj(fptr, keyname, (LONGLONG *) value, comm, status);
390     }
391     else if (datatype == TFLOAT)
392     {
393         ffgkye(fptr, keyname, (float *) value, comm, status);
394     }
395     else if (datatype == TDOUBLE)
396     {
397         ffgkyd(fptr, keyname, (double *) value, comm, status);
398     }
399     else if (datatype == TCOMPLEX)
400     {
401         ffgkyc(fptr, keyname, (float *) value, comm, status);
402     }
403     else if (datatype == TDBLCOMPLEX)
404     {
405         ffgkym(fptr, keyname, (double *) value, comm, status);
406     }
407     else
408         *status = BAD_DATATYPE;
409 
410     return(*status);
411 }
412 /*--------------------------------------------------------------------------*/
ffgkey(fitsfile * fptr,const char * keyname,char * keyval,char * comm,int * status)413 int ffgkey( fitsfile *fptr,     /* I - FITS file pointer        */
414             const char *keyname,      /* I - name of keyword to read  */
415             char *keyval,       /* O - keyword value            */
416             char *comm,         /* O - keyword comment          */
417             int  *status)       /* IO - error status            */
418 /*
419   Read (get) the named keyword, returning the keyword value and comment.
420   The value is just the literal string of characters in the value field
421   of the keyword.  In the case of a string valued keyword, the returned
422   value includes the leading and closing quote characters.  The value may be
423   up to 70 characters long, and the comment may be up to 72 characters long.
424   If the keyword has no value (no equal sign in column 9) then a null value
425   is returned.
426 */
427 {
428     char card[FLEN_CARD];
429 
430     keyval[0] = '\0';
431     if (comm)
432        comm[0] = '\0';
433 
434     if (*status > 0)
435         return(*status);
436 
437     if (ffgcrd(fptr, keyname, card, status) > 0)    /* get the 80-byte card */
438         return(*status);
439 
440     ffpsvc(card, keyval, comm, status);      /* parse the value and comment */
441 
442     return(*status);
443 }
444 /*--------------------------------------------------------------------------*/
ffgrec(fitsfile * fptr,int nrec,char * card,int * status)445 int ffgrec( fitsfile *fptr,     /* I - FITS file pointer          */
446             int nrec,           /* I - number of keyword to read  */
447             char *card,         /* O - keyword card               */
448             int  *status)       /* IO - error status              */
449 /*
450   Read (get) the nrec-th keyword, returning the entire keyword card up to
451   80 characters long.  The first keyword in the header has nrec = 1, not 0.
452   The returned card value is null terminated with any trailing blank
453   characters removed.  If nrec = 0, then this routine simply moves the
454   current header pointer to the top of the header.
455 */
456 {
457     if (*status > 0)
458         return(*status);
459 
460     if (nrec == 0)
461     {
462         ffmaky(fptr, 1, status);  /* simply move to beginning of header */
463         if (card)
464             card[0] = '\0';           /* and return null card */
465     }
466     else if (nrec > 0)
467     {
468         ffmaky(fptr, nrec, status);
469         ffgnky(fptr, card, status);
470     }
471 
472     return(*status);
473 }
474 /*--------------------------------------------------------------------------*/
ffgcrd(fitsfile * fptr,const char * name,char * card,int * status)475 int ffgcrd( fitsfile *fptr,     /* I - FITS file pointer        */
476             const char *name,         /* I - name of keyword to read  */
477             char *card,         /* O - keyword card             */
478             int  *status)       /* IO - error status            */
479 /*
480   Read (get) the named keyword, returning the entire keyword card up to
481   80 characters long.
482   The returned card value is null terminated with any trailing blank
483   characters removed.
484 
485   If the input name contains wild cards ('?' matches any single char
486   and '*' matches any sequence of chars, # matches any string of decimal
487   digits) then the search ends once the end of header is reached and does
488   not automatically resume from the top of the header.
489 */
490 {
491     int nkeys, nextkey, ntodo, namelen, namelen_limit, namelenminus1, cardlen;
492     int ii = 0, jj, kk, wild, match, exact, hier = 0;
493     char keyname[FLEN_KEYWORD], cardname[FLEN_KEYWORD];
494     char *ptr1, *ptr2, *gotstar;
495 
496     if (*status > 0)
497         return(*status);
498 
499     *keyname = '\0';
500 
501     while (name[ii] == ' ')  /* skip leading blanks in name */
502         ii++;
503 
504     strncat(keyname, &name[ii], FLEN_KEYWORD - 1);
505 
506     namelen = strlen(keyname);
507 
508     while (namelen > 0 && keyname[namelen - 1] == ' ')
509          namelen--;            /* ignore trailing blanks in name */
510 
511     keyname[namelen] = '\0';  /* terminate the name */
512 
513     for (ii=0; ii < namelen; ii++)
514         keyname[ii] = toupper(keyname[ii]);    /*  make upper case  */
515 
516     if (FSTRNCMP("HIERARCH", keyname, 8) == 0)
517     {
518         if (namelen == 8)
519         {
520             /* special case: just looking for any HIERARCH keyword */
521             hier = 1;
522         }
523         else
524         {
525             /* ignore the leading HIERARCH and look for the 'real' name */
526             /* starting with first non-blank character following HIERARCH */
527             ptr1 = keyname;
528             ptr2 = &keyname[8];
529 
530             while(*ptr2 == ' ')
531                 ptr2++;
532 
533             namelen = 0;
534             while(*ptr2)
535             {
536                 *ptr1 = *ptr2;
537                  ptr1++;
538                  ptr2++;
539                  namelen++;
540             }
541             *ptr1 = '\0';
542         }
543     }
544 
545     /* does input name contain wild card chars?  ('?',  '*', or '#') */
546     /* wild cards are currently not supported with HIERARCH keywords */
547 
548     namelen_limit = namelen;
549     gotstar = 0;
550     if (namelen < 9 &&
551        (strchr(keyname,'?') || (gotstar = strchr(keyname,'*')) ||
552         strchr(keyname,'#')) )
553     {
554         wild = 1;
555 
556         /* if we found a '*' wild card in the name, there might be */
557         /* more than one.  Support up to 2 '*' in the template. */
558         /* Thus we need to compare keywords whose names have at least */
559         /* namelen - 2 characters.                                   */
560         if (gotstar)
561            namelen_limit -= 2;
562     }
563     else
564         wild = 0;
565 
566     ffghps(fptr, &nkeys, &nextkey, status); /* get no. keywords and position */
567 
568     namelenminus1 = maxvalue(namelen - 1, 1);
569     ntodo = nkeys - nextkey + 1;  /* first, read from next keyword to end */
570     for (jj=0; jj < 2; jj++)
571     {
572       for (kk = 0; kk < ntodo; kk++)
573       {
574         ffgnky(fptr, card, status);     /* get next keyword */
575 
576         if (hier)
577         {
578            if (FSTRNCMP("HIERARCH", card, 8) == 0)
579                 return(*status);  /* found a HIERARCH keyword */
580         }
581         else
582         {
583           ffgknm(card, cardname, &cardlen, status); /* get the keyword name */
584 
585           if (cardlen >= namelen_limit)  /* can't match if card < name */
586           {
587             /* if there are no wild cards, lengths must be the same */
588             if (!( !wild && cardlen != namelen) )
589             {
590               for (ii=0; ii < cardlen; ii++)
591               {
592                 /* make sure keyword is in uppercase */
593                 if (cardname[ii] > 96)
594                 {
595                   /* This assumes the ASCII character set in which */
596                   /* upper case characters start at ASCII(97)  */
597                   /* Timing tests showed that this is 20% faster */
598                   /* than calling the isupper function.          */
599 
600                   cardname[ii] = toupper(cardname[ii]);  /* make upper case */
601                 }
602               }
603 
604               if (wild)
605               {
606                 ffcmps(keyname, cardname, 1, &match, &exact);
607                 if (match)
608                     return(*status); /* found a matching keyword */
609               }
610               else if (keyname[namelenminus1] == cardname[namelenminus1])
611               {
612                 /* test the last character of the keyword name first, on */
613                 /* the theory that it is less likely to match then the first */
614                 /* character since many keywords begin with 'T', for example */
615 
616                 if (FSTRNCMP(keyname, cardname, namelenminus1) == 0)
617                 {
618                   return(*status);   /* found the matching keyword */
619                 }
620               }
621 	      else if (namelen == 0 && cardlen == 0)
622 	      {
623 	         /* matched a blank keyword */
624 		 return(*status);
625 	      }
626             }
627           }
628         }
629       }
630 
631       if (wild || jj == 1)
632             break;  /* stop at end of header if template contains wildcards */
633 
634       ffmaky(fptr, 1, status);  /* reset pointer to beginning of header */
635       ntodo = nextkey - 1;      /* number of keyword to read */
636     }
637 
638     return(*status = KEY_NO_EXIST);  /* couldn't find the keyword */
639 }
640 /*--------------------------------------------------------------------------*/
ffgstr(fitsfile * fptr,const char * string,char * card,int * status)641 int ffgstr( fitsfile *fptr,     /* I - FITS file pointer        */
642             const char *string, /* I - string to match  */
643             char *card,         /* O - keyword card             */
644             int  *status)       /* IO - error status            */
645 /*
646   Read (get) the next keyword record that contains the input character string,
647   returning the entire keyword card up to 80 characters long.
648   The returned card value is null terminated with any trailing blank
649   characters removed.
650 */
651 {
652     int nkeys, nextkey, ntodo, stringlen;
653     int jj, kk;
654 
655     if (*status > 0)
656         return(*status);
657 
658     stringlen = strlen(string);
659     if (stringlen > 80) {
660         return(*status = KEY_NO_EXIST);  /* matching string is too long to exist */
661     }
662 
663     ffghps(fptr, &nkeys, &nextkey, status); /* get no. keywords and position */
664     ntodo = nkeys - nextkey + 1;  /* first, read from next keyword to end */
665 
666     for (jj=0; jj < 2; jj++)
667     {
668       for (kk = 0; kk < ntodo; kk++)
669       {
670         ffgnky(fptr, card, status);     /* get next keyword */
671         if (strstr(card, string) != 0) {
672             return(*status);   /* found the matching string */
673         }
674       }
675 
676       ffmaky(fptr, 1, status);  /* reset pointer to beginning of header */
677       ntodo = nextkey - 1;      /* number of keyword to read */
678     }
679 
680     return(*status = KEY_NO_EXIST);  /* couldn't find the keyword */
681 }
682 /*--------------------------------------------------------------------------*/
ffgknm(char * card,char * name,int * length,int * status)683 int ffgknm( char *card,         /* I - keyword card                   */
684             char *name,         /* O - name of the keyword            */
685             int *length,        /* O - length of the keyword name     */
686             int  *status)       /* IO - error status                  */
687 
688 /*
689   Return the name of the keyword, and the name length.  This supports the
690   ESO HIERARCH convention where keyword names may be > 8 characters long.
691 */
692 {
693     char *ptr1, *ptr2;
694     int ii, namelength;
695 
696     namelength = FLEN_KEYWORD - 1;
697     *name = '\0';
698     *length = 0;
699 
700     /* support for ESO HIERARCH keywords; find the '=' */
701     if (FSTRNCMP(card, "HIERARCH ", 9) == 0)
702     {
703         ptr2 = strchr(card, '=');
704 
705         if (!ptr2)   /* no value indicator ??? */
706         {
707             /* this probably indicates an error, so just return FITS name */
708             strcat(name, "HIERARCH");
709             *length = 8;
710             return(*status);
711         }
712 
713         /* find the start and end of the HIERARCH name */
714         ptr1 = &card[9];
715         while (*ptr1 == ' ')   /* skip spaces */
716             ptr1++;
717 
718         strncat(name, ptr1, ptr2 - ptr1);
719         ii = ptr2 - ptr1;
720 
721         while (ii > 0 && name[ii - 1] == ' ')  /* remove trailing spaces */
722             ii--;
723 
724         name[ii] = '\0';
725         *length = ii;
726     }
727     else
728     {
729         for (ii = 0; ii < namelength; ii++)
730         {
731            /* look for string terminator, or a blank */
732            if (*(card+ii) != ' ' && *(card+ii) != '=' && *(card+ii) !='\0')
733            {
734                *(name+ii) = *(card+ii);
735            }
736            else
737            {
738                name[ii] = '\0';
739                *length = ii;
740                return(*status);
741            }
742         }
743 
744         /* if we got here, keyword is namelength characters long */
745         name[namelength] = '\0';
746         *length = namelength;
747     }
748 
749     return(*status);
750 }
751 /*--------------------------------------------------------------------------*/
ffgunt(fitsfile * fptr,const char * keyname,char * unit,int * status)752 int ffgunt( fitsfile *fptr,     /* I - FITS file pointer         */
753             const char *keyname,      /* I - name of keyword to read   */
754             char *unit,         /* O - keyword units             */
755             int  *status)       /* IO - error status             */
756 /*
757     Read (get) the units string from the comment field of the existing
758     keyword. This routine uses a local FITS convention (not defined in the
759     official FITS standard) in which the units are enclosed in
760     square brackets following the '/' comment field delimiter, e.g.:
761 
762     KEYWORD =                   12 / [kpc] comment string goes here
763 */
764 {
765     char valstring[FLEN_VALUE];
766     char comm[FLEN_COMMENT];
767     char *loc;
768 
769     if (*status > 0)
770         return(*status);
771 
772     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
773 
774     if (comm[0] == '[')
775     {
776         loc = strchr(comm, ']');   /*  find the closing bracket */
777         if (loc)
778             *loc = '\0';           /*  terminate the string */
779 
780         strcpy(unit, &comm[1]);    /*  copy the string */
781      }
782      else
783         unit[0] = '\0';
784 
785     return(*status);
786 }
787 /*--------------------------------------------------------------------------*/
ffgkys(fitsfile * fptr,const char * keyname,char * value,char * comm,int * status)788 int ffgkys( fitsfile *fptr,     /* I - FITS file pointer         */
789             const char *keyname,      /* I - name of keyword to read   */
790             char *value,        /* O - keyword value             */
791             char *comm,         /* O - keyword comment           */
792             int  *status)       /* IO - error status             */
793 /*
794   Get KeYword with a String value:
795   Read (get) a simple string valued keyword.  The returned value may be up to
796   68 chars long ( + 1 null terminator char).  The routine does not support the
797   HEASARC convention for continuing long string values over multiple keywords.
798   The ffgkls routine may be used to read long continued strings. The returned
799   comment string may be up to 69 characters long (including null terminator).
800 */
801 {
802     char valstring[FLEN_VALUE];
803 
804     if (*status > 0)
805         return(*status);
806 
807     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
808     value[0] = '\0';
809     ffc2s(valstring, value, status);   /* remove quotes from string */
810 
811     return(*status);
812 }
813 /*--------------------------------------------------------------------------*/
ffgksl(fitsfile * fptr,const char * keyname,int * length,int * status)814 int ffgksl( fitsfile *fptr,     /* I - FITS file pointer             */
815            const char *keyname, /* I - name of keyword to read       */
816            int *length,         /* O - length of the string value    */
817            int  *status)        /* IO - error status                 */
818 /*
819   Get the length of the keyword value string.
820   This routine explicitly supports the CONTINUE convention for long string values.
821 */
822 {
823     char valstring[FLEN_VALUE], value[FLEN_VALUE];
824     int position, contin, len;
825 
826     if (*status > 0)
827         return(*status);
828 
829     ffgkey(fptr, keyname, valstring, NULL, status);  /* read the keyword */
830 
831     if (*status > 0)
832         return(*status);
833 
834     ffghps(fptr, NULL,  &position, status); /* save the current header position */
835 
836     if (!valstring[0])  { /* null value string? */
837         *length = 0;
838     } else {
839       ffc2s(valstring, value, status);  /* in case string contains "/" char  */
840       *length = strlen(value);
841 
842       /* If last character is a & then value may be continued on next keyword */
843       contin = 1;
844       while (contin)
845       {
846         len = strlen(value);
847 
848         if (len && *(value+len-1) == '&')  /*  is last char an anpersand?  */
849         {
850             ffgcnt(fptr, value, NULL, status);
851             if (*value)    /* a null valstring indicates no continuation */
852             {
853                *length += strlen(value) - 1;
854             }
855             else
856 	    {
857                 contin = 0;
858             }
859         }
860         else
861 	{
862             contin = 0;
863 	}
864       }
865     }
866 
867     ffmaky(fptr, position - 1, status); /* reset header pointer to the keyword */
868                                         /* since in many cases the program will read */
869 					/* the string value after getting the length */
870 
871     return(*status);
872 }
873 /*--------------------------------------------------------------------------*/
ffgkls(fitsfile * fptr,const char * keyname,char ** value,char * comm,int * status)874 int ffgkls( fitsfile *fptr,     /* I - FITS file pointer             */
875            const char *keyname, /* I - name of keyword to read       */
876            char **value,        /* O - pointer to keyword value      */
877            char *comm,          /* O - keyword comment (may be NULL) */
878            int  *status)        /* IO - error status                 */
879 /*
880   This is the original routine for reading long string keywords that use
881   the CONTINUE keyword convention.  In 2016 a new routine called
882   ffgsky / fits_read_string_key was added, which may provide a more
883   convenient user interface  for most applications.
884 
885   Get Keyword with possible Long String value:
886   Read (get) the named keyword, returning the value and comment.
887   The returned value string may be arbitrarily long (by using the HEASARC
888   convention for continuing long string values over multiple keywords) so
889   this routine allocates the required memory for the returned string value.
890   It is up to the calling routine to free the memory once it is finished
891   with the value string.  The returned comment string may be up to 69
892   characters long.
893 */
894 {
895     char valstring[FLEN_VALUE], nextcomm[FLEN_COMMENT];
896     int contin, commspace = 0;
897     size_t len;
898 
899     if (*status > 0)
900         return(*status);
901 
902     *value = NULL;  /* initialize a null pointer in case of error */
903 
904     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
905 
906     if (*status > 0)
907         return(*status);
908 
909     if (comm)
910     {
911         /* remaining space in comment string */
912         commspace = FLEN_COMMENT - strlen(comm) - 2;
913     }
914 
915     if (!valstring[0])   /* null value string? */
916     {
917       *value = (char *) malloc(1);  /* allocate and return a null string */
918       **value = '\0';
919     }
920     else
921     {
922       /* allocate space,  plus 1 for null */
923       *value = (char *) malloc(strlen(valstring) + 1);
924 
925       ffc2s(valstring, *value, status);   /* convert string to value */
926       len = strlen(*value);
927 
928       /* If last character is a & then value may be continued on next keyword */
929       contin = 1;
930       while (contin)
931       {
932         if (len && *(*value+len-1) == '&')  /*  is last char an ampersand?  */
933         {
934             ffgcnt(fptr, valstring, nextcomm, status);
935             if (*valstring)    /* a null valstring indicates no continuation */
936             {
937                *(*value+len-1) = '\0';         /* erase the trailing & char */
938                len += strlen(valstring) - 1;
939                *value = (char *) realloc(*value, len + 1); /* increase size */
940                strcat(*value, valstring);     /* append the continued chars */
941             }
942             else
943 	    {
944                 contin = 0;
945                 /* Without this, for case of a last CONTINUE statement ending
946                    with a '&', nextcomm would retain the same string from
947                    from the previous loop iteration and the comment
948                    would get concantenated twice. */
949                 nextcomm[0] = 0;
950             }
951 
952             /* concantenate comment strings (if any) */
953 	    if ((commspace > 0) && (*nextcomm != 0))
954 	    {
955                 strcat(comm, " ");
956 		strncat(comm, nextcomm, commspace);
957                 commspace = FLEN_COMMENT - strlen(comm) - 2;
958             }
959         }
960         else
961 	{
962             contin = 0;
963 	}
964       }
965     }
966     return(*status);
967 }
968 /*--------------------------------------------------------------------------*/
ffgsky(fitsfile * fptr,const char * keyname,int firstchar,int maxchar,char * value,int * valuelen,char * comm,int * status)969 int ffgsky( fitsfile *fptr,     /* I - FITS file pointer             */
970            const char *keyname, /* I - name of keyword to read       */
971            int firstchar,       /* I - first character of string to return */
972            int maxchar,         /* I - maximum length of string to return */
973 	                        /*    (string will be null terminated)  */
974            char *value,         /* O - pointer to keyword value      */
975            int *valuelen,       /* O - total length of the keyword value string */
976                                 /*     The returned 'value' string may only */
977 				/*     contain a piece of the total string, depending */
978 				/*     on the value of firstchar and maxchar */
979            char *comm,          /* O - keyword comment (may be NULL) */
980            int  *status)        /* IO - error status                 */
981 /*
982   Read and return the value of the specified string-valued keyword.
983 
984   This new routine was added in 2016 to provide a more convenient user
985   interface than the older ffgkls routine.
986 
987   Read a string keyword, returning up to 'naxchars' characters of the value
988   starting with the 'firstchar' character.
989   The input 'value' string must be allocated at least 1 char bigger to
990   allow for the terminating null character.
991 
992   This routine may be used to read continued string keywords that use
993   the CONTINUE keyword convention, as well as normal string keywords
994   that are contained within a single header record.
995 
996   This routine differs from the ffkls routine in that it does not
997   internally allocate memory for the returned value string, and consequently
998   the calling routine does not need to call fffree to free the memory.
999 */
1000 {
1001     char valstring[FLEN_VALUE], nextcomm[FLEN_COMMENT];
1002     char *tempstring;
1003     int contin, commspace = 0;
1004     size_t len;
1005 
1006     if (*status > 0)
1007         return(*status);
1008 
1009     tempstring = NULL;  /* initialize in case of error */
1010     *value = '\0';
1011     if (valuelen) *valuelen = 0;
1012 
1013     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1014 
1015     if (*status > 0)
1016         return(*status);
1017 
1018     if (comm)
1019     {
1020         /* remaining space in comment string */
1021         commspace = FLEN_COMMENT - strlen(comm) - 2;
1022     }
1023 
1024     if (!valstring[0])   /* null value string? */
1025     {
1026       tempstring = (char *) malloc(1);  /* allocate and return a null string */
1027       *tempstring = '\0';
1028     }
1029     else
1030     {
1031       /* allocate space,  plus 1 for null */
1032       tempstring = (char *) malloc(strlen(valstring) + 1);
1033 
1034       ffc2s(valstring, tempstring, status);   /* convert string to value */
1035       len = strlen(tempstring);
1036 
1037       /* If last character is a & then value may be continued on next keyword */
1038       contin = 1;
1039       while (contin && *status <= 0)
1040       {
1041         if (len && *(tempstring+len-1) == '&')  /*  is last char an anpersand?  */
1042         {
1043             ffgcnt(fptr, valstring, nextcomm, status);
1044             if (*valstring)    /* a null valstring indicates no continuation */
1045             {
1046                *(tempstring+len-1) = '\0';         /* erase the trailing & char */
1047                len += strlen(valstring) - 1;
1048                tempstring = (char *) realloc(tempstring, len + 1); /* increase size */
1049                strcat(tempstring, valstring);     /* append the continued chars */
1050             }
1051             else
1052 	    {
1053                 contin = 0;
1054                 /* Without this, for case of a last CONTINUE statement ending
1055                    with a '&', nextcomm would retain the same string from
1056                    from the previous loop iteration and the comment
1057                    would get concantenated twice. */
1058                 nextcomm[0] = 0;
1059             }
1060 
1061             /* concantenate comment strings (if any) */
1062 	    if ((commspace > 0) && (*nextcomm != 0))
1063 	    {
1064                 strcat(comm, " ");
1065 		strncat(comm, nextcomm, commspace);
1066                 commspace = FLEN_COMMENT - strlen(comm) - 2;
1067             }
1068         }
1069         else
1070 	{
1071             contin = 0;
1072 	}
1073       }
1074     }
1075 
1076     if (tempstring)
1077     {
1078         len = strlen(tempstring);
1079 	if (firstchar <= len)
1080             strncat(value, tempstring + (firstchar - 1), maxchar);
1081         free(tempstring);
1082 	if (valuelen) *valuelen = len;  /* total length of the keyword value */
1083     }
1084 
1085     return(*status);
1086 }
1087 /*--------------------------------------------------------------------------*/
fffree(void * value,int * status)1088 int fffree( void *value,       /* I - pointer to keyword value  */
1089             int  *status)      /* IO - error status             */
1090 /*
1091   Free the memory that was previously allocated by CFITSIO,
1092   such as by ffgkls or fits_hdr2str
1093 */
1094 {
1095     if (*status > 0)
1096         return(*status);
1097 
1098     if (value)
1099         free(value);
1100 
1101     return(*status);
1102 }
1103  /*--------------------------------------------------------------------------*/
ffgcnt(fitsfile * fptr,char * value,char * comm,int * status)1104 int ffgcnt( fitsfile *fptr,     /* I - FITS file pointer         */
1105             char *value,        /* O - continued string value    */
1106             char *comm,         /* O - continued comment string  */
1107             int  *status)       /* IO - error status             */
1108 /*
1109   Attempt to read the next keyword, returning the string value
1110   if it is a continuation of the previous string keyword value.
1111   This uses the HEASARC convention for continuing long string values
1112   over multiple keywords.  Each continued string is terminated with a
1113   backslash character, and the continuation follows on the next keyword
1114   which must have the name CONTINUE without an equal sign in column 9
1115   of the card.  If the next card is not a continuation, then the returned
1116   value string will be null.
1117 */
1118 {
1119     int tstatus;
1120     char card[FLEN_CARD], strval[FLEN_VALUE];
1121 
1122     if (*status > 0)
1123         return(*status);
1124 
1125     tstatus = 0;
1126     value[0] = '\0';
1127 
1128     if (ffgnky(fptr, card, &tstatus) > 0)  /*  read next keyword  */
1129         return(*status);                   /*  hit end of header  */
1130 
1131     if (strncmp(card, "CONTINUE  ", 10) == 0)  /* a continuation card? */
1132     {
1133         strncpy(card, "D2345678=  ", 10); /* overwrite a dummy keyword name */
1134         ffpsvc(card, strval, comm, &tstatus);  /*  get the string value & comment */
1135         ffc2s(strval, value, &tstatus);    /* remove the surrounding quotes */
1136 
1137         if (tstatus)       /*  return null if error status was returned  */
1138            value[0] = '\0';
1139     }
1140     else
1141         ffmrky(fptr, -1, status);  /* reset the keyword pointer */
1142 
1143     return(*status);
1144 }
1145 /*--------------------------------------------------------------------------*/
ffgkyl(fitsfile * fptr,const char * keyname,int * value,char * comm,int * status)1146 int ffgkyl( fitsfile *fptr,     /* I - FITS file pointer         */
1147             const char *keyname,      /* I - name of keyword to read   */
1148             int  *value,        /* O - keyword value             */
1149             char *comm,         /* O - keyword comment           */
1150             int  *status)       /* IO - error status             */
1151 /*
1152   Read (get) the named keyword, returning the value and comment.
1153   The returned value = 1 if the keyword is true, else = 0 if false.
1154   The comment may be up to 69 characters long.
1155 */
1156 {
1157     char valstring[FLEN_VALUE];
1158 
1159     if (*status > 0)
1160         return(*status);
1161 
1162     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1163     ffc2l(valstring, value, status);   /* convert string to value */
1164 
1165     return(*status);
1166 }
1167 /*--------------------------------------------------------------------------*/
ffgkyj(fitsfile * fptr,const char * keyname,long * value,char * comm,int * status)1168 int ffgkyj( fitsfile *fptr,     /* I - FITS file pointer         */
1169             const char *keyname,      /* I - name of keyword to read   */
1170             long *value,        /* O - keyword value             */
1171             char *comm,         /* O - keyword comment           */
1172             int  *status)       /* IO - error status             */
1173 /*
1174   Read (get) the named keyword, returning the value and comment.
1175   The value will be implicitly converted to a (long) integer if it not
1176   already of this datatype.  The comment may be up to 69 characters long.
1177 */
1178 {
1179     char valstring[FLEN_VALUE];
1180 
1181     if (*status > 0)
1182         return(*status);
1183 
1184     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1185     ffc2i(valstring, value, status);   /* convert string to value */
1186 
1187     return(*status);
1188 }
1189 /*--------------------------------------------------------------------------*/
ffgkyjj(fitsfile * fptr,const char * keyname,LONGLONG * value,char * comm,int * status)1190 int ffgkyjj( fitsfile *fptr,     /* I - FITS file pointer         */
1191             const char *keyname,      /* I - name of keyword to read   */
1192             LONGLONG *value,    /* O - keyword value             */
1193             char *comm,         /* O - keyword comment           */
1194             int  *status)       /* IO - error status             */
1195 /*
1196   Read (get) the named keyword, returning the value and comment.
1197   The value will be implicitly converted to a (LONGLONG) integer if it not
1198   already of this datatype.  The comment may be up to 69 characters long.
1199 */
1200 {
1201     char valstring[FLEN_VALUE];
1202 
1203     if (*status > 0)
1204         return(*status);
1205 
1206     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1207     ffc2j(valstring, value, status);   /* convert string to value */
1208 
1209     return(*status);
1210 }
1211 /*--------------------------------------------------------------------------*/
ffgkyujj(fitsfile * fptr,const char * keyname,ULONGLONG * value,char * comm,int * status)1212 int ffgkyujj( fitsfile *fptr,     /* I - FITS file pointer         */
1213             const char *keyname,      /* I - name of keyword to read   */
1214             ULONGLONG *value,    /* O - keyword value             */
1215             char *comm,         /* O - keyword comment           */
1216             int  *status)       /* IO - error status             */
1217 /*
1218   Read (get) the named keyword, returning the value and comment.
1219   The value will be implicitly converted to a (ULONGLONG) integer if it not
1220   already of this datatype.  The comment may be up to 69 characters long.
1221 */
1222 {
1223     char valstring[FLEN_VALUE];
1224 
1225     if (*status > 0)
1226         return(*status);
1227 
1228     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1229     ffc2uj(valstring, value, status);   /* convert string to value */
1230 
1231     return(*status);
1232 }
1233 /*--------------------------------------------------------------------------*/
ffgkye(fitsfile * fptr,const char * keyname,float * value,char * comm,int * status)1234 int ffgkye( fitsfile *fptr,     /* I - FITS file pointer         */
1235             const char  *keyname,     /* I - name of keyword to read   */
1236             float *value,       /* O - keyword value             */
1237             char  *comm,        /* O - keyword comment           */
1238             int   *status)      /* IO - error status             */
1239 /*
1240   Read (get) the named keyword, returning the value and comment.
1241   The value will be implicitly converted to a float if it not
1242   already of this datatype.  The comment may be up to 69 characters long.
1243 */
1244 {
1245     char valstring[FLEN_VALUE];
1246 
1247     if (*status > 0)
1248         return(*status);
1249 
1250     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1251     ffc2r(valstring, value, status);   /* convert string to value */
1252 
1253     return(*status);
1254 }
1255 /*--------------------------------------------------------------------------*/
ffgkyd(fitsfile * fptr,const char * keyname,double * value,char * comm,int * status)1256 int ffgkyd( fitsfile *fptr,      /* I - FITS file pointer         */
1257             const char   *keyname,     /* I - name of keyword to read   */
1258             double *value,       /* O - keyword value             */
1259             char   *comm,        /* O - keyword comment           */
1260             int    *status)      /* IO - error status             */
1261 /*
1262   Read (get) the named keyword, returning the value and comment.
1263   The value will be implicitly converted to a double if it not
1264   already of this datatype.  The comment may be up to 69 characters long.
1265 */
1266 {
1267     char valstring[FLEN_VALUE];
1268 
1269     if (*status > 0)
1270         return(*status);
1271 
1272     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1273     ffc2d(valstring, value, status);   /* convert string to value */
1274 
1275     return(*status);
1276 }
1277 /*--------------------------------------------------------------------------*/
ffgkyc(fitsfile * fptr,const char * keyname,float * value,char * comm,int * status)1278 int ffgkyc( fitsfile *fptr,     /* I - FITS file pointer         */
1279             const char  *keyname,     /* I - name of keyword to read   */
1280             float *value,       /* O - keyword value (real,imag) */
1281             char  *comm,        /* O - keyword comment           */
1282             int   *status)      /* IO - error status             */
1283 /*
1284   Read (get) the named keyword, returning the value and comment.
1285   The keyword must have a complex value. No implicit data conversion
1286   will be performed.
1287 */
1288 {
1289     char valstring[FLEN_VALUE], message[FLEN_ERRMSG];
1290     int len;
1291 
1292     if (*status > 0)
1293         return(*status);
1294 
1295     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1296 
1297     if (valstring[0] != '(' )   /* test that this is a complex keyword */
1298     {
1299       snprintf(message, FLEN_ERRMSG, "keyword %s does not have a complex value (ffgkyc):",
1300               keyname);
1301       ffpmsg(message);
1302       ffpmsg(valstring);
1303       return(*status = BAD_C2F);
1304     }
1305 
1306     valstring[0] = ' ';            /* delete the opening parenthesis */
1307     len = strcspn(valstring, ")" );
1308     valstring[len] = '\0';         /* delete the closing parenthesis */
1309 
1310     len = strcspn(valstring, ",");
1311     valstring[len] = '\0';
1312 
1313     ffc2r(valstring, &value[0], status);       /* convert the real part */
1314     ffc2r(&valstring[len + 1], &value[1], status); /* convert imag. part */
1315     return(*status);
1316 }
1317 /*--------------------------------------------------------------------------*/
ffgkym(fitsfile * fptr,const char * keyname,double * value,char * comm,int * status)1318 int ffgkym( fitsfile *fptr,     /* I - FITS file pointer         */
1319             const char  *keyname,     /* I - name of keyword to read   */
1320             double *value,      /* O - keyword value (real,imag) */
1321             char  *comm,        /* O - keyword comment           */
1322             int   *status)      /* IO - error status             */
1323 /*
1324   Read (get) the named keyword, returning the value and comment.
1325   The keyword must have a complex value. No implicit data conversion
1326   will be performed.
1327 */
1328 {
1329     char valstring[FLEN_VALUE], message[FLEN_ERRMSG];
1330     int len;
1331 
1332     if (*status > 0)
1333         return(*status);
1334 
1335     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1336 
1337     if (valstring[0] != '(' )   /* test that this is a complex keyword */
1338     {
1339       snprintf(message, FLEN_ERRMSG, "keyword %s does not have a complex value (ffgkym):",
1340               keyname);
1341       ffpmsg(message);
1342       ffpmsg(valstring);
1343       return(*status = BAD_C2D);
1344     }
1345 
1346     valstring[0] = ' ';            /* delete the opening parenthesis */
1347     len = strcspn(valstring, ")" );
1348     valstring[len] = '\0';         /* delete the closing parenthesis */
1349 
1350     len = strcspn(valstring, ",");
1351     valstring[len] = '\0';
1352 
1353     ffc2d(valstring, &value[0], status);        /* convert the real part */
1354     ffc2d(&valstring[len + 1], &value[1], status);  /* convert the imag. part */
1355 
1356     return(*status);
1357 }
1358 /*--------------------------------------------------------------------------*/
ffgkyt(fitsfile * fptr,const char * keyname,long * ivalue,double * fraction,char * comm,int * status)1359 int ffgkyt( fitsfile *fptr,      /* I - FITS file pointer                 */
1360             const char   *keyname,     /* I - name of keyword to read           */
1361             long   *ivalue,      /* O - integer part of keyword value     */
1362             double *fraction,    /* O - fractional part of keyword value  */
1363             char   *comm,        /* O - keyword comment                   */
1364             int    *status)      /* IO - error status                     */
1365 /*
1366   Read (get) the named keyword, returning the value and comment.
1367   The integer and fractional parts of the value are returned in separate
1368   variables, to allow more numerical precision to be passed.  This
1369   effectively passes a 'triple' precision value, with a 4-byte integer
1370   and an 8-byte fraction.  The comment may be up to 69 characters long.
1371 */
1372 {
1373     char valstring[FLEN_VALUE];
1374     char *loc;
1375 
1376     if (*status > 0)
1377         return(*status);
1378 
1379     ffgkey(fptr, keyname, valstring, comm, status);  /* read the keyword */
1380 
1381     /*  read the entire value string as a double, to get the integer part */
1382     ffc2d(valstring, fraction, status);
1383 
1384     *ivalue = (long) *fraction;
1385 
1386     *fraction = *fraction - *ivalue;
1387 
1388     /* see if we need to read the fractional part again with more precision */
1389     /* look for decimal point, without an exponential E or D character */
1390 
1391     loc = strchr(valstring, '.');
1392     if (loc)
1393     {
1394         if (!strchr(valstring, 'E') && !strchr(valstring, 'D'))
1395             ffc2d(loc, fraction, status);
1396     }
1397 
1398     return(*status);
1399 }
1400 /*--------------------------------------------------------------------------*/
ffgkyn(fitsfile * fptr,int nkey,char * keyname,char * value,char * comm,int * status)1401 int ffgkyn( fitsfile *fptr,      /* I - FITS file pointer             */
1402             int    nkey,         /* I - number of the keyword to read */
1403             char   *keyname,     /* O - name of the keyword           */
1404             char   *value,       /* O - keyword value                 */
1405             char   *comm,        /* O - keyword comment               */
1406             int    *status)      /* IO - error status                 */
1407 /*
1408   Read (get) the nkey-th keyword returning the keyword name, value and comment.
1409   The value is just the literal string of characters in the value field
1410   of the keyword.  In the case of a string valued keyword, the returned
1411   value includes the leading and closing quote characters.  The value may be
1412   up to 70 characters long, and the comment may be up to 72 characters long.
1413   If the keyword has no value (no equal sign in column 9) then a null value
1414   is returned.  If comm = NULL, then do not return the comment string.
1415 */
1416 {
1417     char card[FLEN_CARD], sbuff[FLEN_CARD];
1418     int namelen;
1419 
1420     keyname[0] = '\0';
1421     value[0] = '\0';
1422     if (comm)
1423         comm[0] = '\0';
1424 
1425     if (*status > 0)
1426         return(*status);
1427 
1428     if (ffgrec(fptr, nkey, card, status) > 0 )  /* get the 80-byte card */
1429         return(*status);
1430 
1431     ffgknm(card, keyname, &namelen, status); /* get the keyword name */
1432 
1433     if (ffpsvc(card, value, comm, status) > 0)   /* parse value and comment */
1434         return(*status);
1435 
1436     if (fftrec(keyname, status) > 0)  /* test keyword name; catches no END */
1437     {
1438      snprintf(sbuff, FLEN_CARD, "Name of keyword no. %d contains illegal character(s): %s",
1439               nkey, keyname);
1440      ffpmsg(sbuff);
1441 
1442      if (nkey % 36 == 0)  /* test if at beginning of 36-card FITS record */
1443             ffpmsg("  (This may indicate a missing END keyword).");
1444     }
1445     return(*status);
1446 }
1447 /*--------------------------------------------------------------------------*/
ffgkns(fitsfile * fptr,const char * keyname,int nstart,int nmax,char * value[],int * nfound,int * status)1448 int ffgkns( fitsfile *fptr,     /* I - FITS file pointer                    */
1449             const char *keyname,      /* I - root name of keywords to read        */
1450             int  nstart,        /* I - starting index number                */
1451             int  nmax,          /* I - maximum number of keywords to return */
1452             char *value[],      /* O - array of pointers to keyword values  */
1453             int  *nfound,       /* O - number of values that were returned  */
1454             int  *status)       /* IO - error status                        */
1455 /*
1456   Read (get) an indexed array of keywords with index numbers between
1457   NSTART and (NSTART + NMAX -1) inclusive.
1458   This routine does NOT support the HEASARC long string convention.
1459 */
1460 {
1461     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1462     long ival;
1463     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1464     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1465 
1466     if (*status > 0)
1467         return(*status);
1468 
1469     *nfound = 0;
1470     nend = nstart + nmax - 1;
1471 
1472     keyroot[0] = '\0';
1473     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1474 
1475     lenroot = strlen(keyroot);
1476 
1477     if (lenroot == 0)     /*  root must be at least 1 char long  */
1478         return(*status);
1479 
1480     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1481         keyroot[ii] = toupper(keyroot[ii]);
1482 
1483     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1484 
1485     undefinedval = FALSE;
1486     for (ii=3; ii <= nkeys; ii++)
1487     {
1488        if (ffgrec(fptr, ii, card, status) > 0)     /*  get next keyword  */
1489            return(*status);
1490 
1491        if (strncmp(keyroot, card, lenroot) == 0)  /* see if keyword matches */
1492        {
1493           keyindex[0] = '\0';
1494           equalssign = strchr(card, '=');
1495 	  if (equalssign == 0) continue;  /* keyword has no value */
1496 
1497           if (equalssign - card - lenroot > 7)
1498           {
1499              return (*status=BAD_KEYCHAR);
1500           }
1501           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1502           tstatus = 0;
1503           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)     /*  test suffix  */
1504           {
1505              if (ival <= nend && ival >= nstart)
1506              {
1507                 ffpsvc(card, svalue, comm, status);  /*  parse the value */
1508                 ffc2s(svalue, value[ival-nstart], status); /* convert */
1509                 if (ival - nstart + 1 > *nfound)
1510                       *nfound = ival - nstart + 1;  /*  max found */
1511 
1512                 if (*status == VALUE_UNDEFINED)
1513                 {
1514                    undefinedval = TRUE;
1515                    *status = 0;  /* reset status to read remaining values */
1516                 }
1517              }
1518           }
1519        }
1520     }
1521     if (undefinedval && (*status <= 0) )
1522         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1523 
1524     return(*status);
1525 }
1526 /*--------------------------------------------------------------------------*/
ffgknl(fitsfile * fptr,const char * keyname,int nstart,int nmax,int * value,int * nfound,int * status)1527 int ffgknl( fitsfile *fptr,     /* I - FITS file pointer                    */
1528             const char *keyname,      /* I - root name of keywords to read        */
1529             int  nstart,        /* I - starting index number                */
1530             int  nmax,          /* I - maximum number of keywords to return */
1531             int  *value,        /* O - array of keyword values              */
1532             int  *nfound,       /* O - number of values that were returned  */
1533             int  *status)       /* IO - error status                        */
1534 /*
1535   Read (get) an indexed array of keywords with index numbers between
1536   NSTART and (NSTART + NMAX -1) inclusive.
1537   The returned value = 1 if the keyword is true, else = 0 if false.
1538 */
1539 {
1540     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1541     long ival;
1542     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1543     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1544 
1545     if (*status > 0)
1546         return(*status);
1547 
1548     *nfound = 0;
1549     nend = nstart + nmax - 1;
1550 
1551     keyroot[0] = '\0';
1552     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1553 
1554     lenroot = strlen(keyroot);
1555 
1556     if (lenroot == 0)     /*  root must be at least 1 char long  */
1557         return(*status);
1558 
1559     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1560         keyroot[ii] = toupper(keyroot[ii]);
1561 
1562     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1563 
1564     ffmaky(fptr, 3, status);  /* move to 3rd keyword (skip 1st 2 keywords) */
1565 
1566     undefinedval = FALSE;
1567     for (ii=3; ii <= nkeys; ii++)
1568     {
1569        if (ffgnky(fptr, card, status) > 0)     /*  get next keyword  */
1570            return(*status);
1571 
1572        if (strncmp(keyroot, card, lenroot) == 0)  /* see if keyword matches */
1573        {
1574           keyindex[0] = '\0';
1575           equalssign = strchr(card, '=');
1576 	  if (equalssign == 0) continue;  /* keyword has no value */
1577 
1578           if (equalssign - card - lenroot > 7)
1579           {
1580              return (*status=BAD_KEYCHAR);
1581           }
1582           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1583 
1584           tstatus = 0;
1585           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)    /*  test suffix  */
1586           {
1587              if (ival <= nend && ival >= nstart)
1588              {
1589                 ffpsvc(card, svalue, comm, status);   /*  parse the value */
1590                 ffc2l(svalue, &value[ival-nstart], status); /* convert*/
1591                 if (ival - nstart + 1 > *nfound)
1592                       *nfound = ival - nstart + 1;  /*  max found */
1593 
1594                 if (*status == VALUE_UNDEFINED)
1595                 {
1596                     undefinedval = TRUE;
1597                    *status = 0;  /* reset status to read remaining values */
1598                 }
1599              }
1600           }
1601        }
1602     }
1603     if (undefinedval && (*status <= 0) )
1604         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1605 
1606     return(*status);
1607 }
1608 /*--------------------------------------------------------------------------*/
ffgknj(fitsfile * fptr,const char * keyname,int nstart,int nmax,long * value,int * nfound,int * status)1609 int ffgknj( fitsfile *fptr,     /* I - FITS file pointer                    */
1610             const char *keyname,      /* I - root name of keywords to read        */
1611             int  nstart,        /* I - starting index number                */
1612             int  nmax,          /* I - maximum number of keywords to return */
1613             long *value,        /* O - array of keyword values              */
1614             int  *nfound,       /* O - number of values that were returned  */
1615             int  *status)       /* IO - error status                        */
1616 /*
1617   Read (get) an indexed array of keywords with index numbers between
1618   NSTART and (NSTART + NMAX -1) inclusive.
1619 */
1620 {
1621     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1622     long ival;
1623     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1624     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1625 
1626     if (*status > 0)
1627         return(*status);
1628 
1629     *nfound = 0;
1630     nend = nstart + nmax - 1;
1631 
1632     keyroot[0] = '\0';
1633     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1634 
1635     lenroot = strlen(keyroot);
1636 
1637     if (lenroot == 0)     /*  root must be at least 1 char long  */
1638         return(*status);
1639 
1640     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1641         keyroot[ii] = toupper(keyroot[ii]);
1642 
1643     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1644 
1645     ffmaky(fptr, 3, status);  /* move to 3rd keyword (skip 1st 2 keywords) */
1646 
1647     undefinedval = FALSE;
1648     for (ii=3; ii <= nkeys; ii++)
1649     {
1650        if (ffgnky(fptr, card, status) > 0)     /*  get next keyword  */
1651            return(*status);
1652 
1653        if (strncmp(keyroot, card, lenroot) == 0)  /* see if keyword matches */
1654        {
1655           keyindex[0] = '\0';
1656           equalssign = strchr(card, '=');
1657 	  if (equalssign == 0) continue;  /* keyword has no value */
1658 
1659           if (equalssign - card - lenroot > 7)
1660           {
1661              return (*status=BAD_KEYCHAR);
1662           }
1663           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1664 
1665           tstatus = 0;
1666           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)     /*  test suffix  */
1667           {
1668              if (ival <= nend && ival >= nstart)
1669              {
1670                 ffpsvc(card, svalue, comm, status);   /*  parse the value */
1671                 ffc2i(svalue, &value[ival-nstart], status);  /* convert */
1672                 if (ival - nstart + 1 > *nfound)
1673                       *nfound = ival - nstart + 1;  /*  max found */
1674 
1675                 if (*status == VALUE_UNDEFINED)
1676                 {
1677                     undefinedval = TRUE;
1678                    *status = 0;  /* reset status to read remaining values */
1679                 }
1680              }
1681           }
1682        }
1683     }
1684     if (undefinedval && (*status <= 0) )
1685         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1686 
1687     return(*status);
1688 }
1689 /*--------------------------------------------------------------------------*/
ffgknjj(fitsfile * fptr,const char * keyname,int nstart,int nmax,LONGLONG * value,int * nfound,int * status)1690 int ffgknjj( fitsfile *fptr,    /* I - FITS file pointer                    */
1691             const char *keyname,      /* I - root name of keywords to read        */
1692             int  nstart,        /* I - starting index number                */
1693             int  nmax,          /* I - maximum number of keywords to return */
1694             LONGLONG *value,    /* O - array of keyword values              */
1695             int  *nfound,       /* O - number of values that were returned  */
1696             int  *status)       /* IO - error status                        */
1697 /*
1698   Read (get) an indexed array of keywords with index numbers between
1699   NSTART and (NSTART + NMAX -1) inclusive.
1700 */
1701 {
1702     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1703     long ival;
1704     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1705     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1706 
1707     if (*status > 0)
1708         return(*status);
1709 
1710     *nfound = 0;
1711     nend = nstart + nmax - 1;
1712 
1713     keyroot[0] = '\0';
1714     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1715 
1716     lenroot = strlen(keyroot);
1717 
1718     if (lenroot == 0)     /*  root must be at least 1 char long  */
1719         return(*status);
1720 
1721     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1722         keyroot[ii] = toupper(keyroot[ii]);
1723 
1724     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1725 
1726     ffmaky(fptr, 3, status);  /* move to 3rd keyword (skip 1st 2 keywords) */
1727 
1728     undefinedval = FALSE;
1729     for (ii=3; ii <= nkeys; ii++)
1730     {
1731        if (ffgnky(fptr, card, status) > 0)     /*  get next keyword  */
1732            return(*status);
1733 
1734        if (strncmp(keyroot, card, lenroot) == 0)  /* see if keyword matches */
1735        {
1736           keyindex[0] = '\0';
1737           equalssign = strchr(card, '=');
1738 	  if (equalssign == 0) continue;  /* keyword has no value */
1739 
1740           if (equalssign - card - lenroot > 7)
1741           {
1742              return (*status=BAD_KEYCHAR);
1743           }
1744           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1745 
1746           tstatus = 0;
1747           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)     /*  test suffix  */
1748           {
1749              if (ival <= nend && ival >= nstart)
1750              {
1751                 ffpsvc(card, svalue, comm, status);   /*  parse the value */
1752                 ffc2j(svalue, &value[ival-nstart], status);  /* convert */
1753                 if (ival - nstart + 1 > *nfound)
1754                       *nfound = ival - nstart + 1;  /*  max found */
1755 
1756                 if (*status == VALUE_UNDEFINED)
1757                 {
1758                     undefinedval = TRUE;
1759                    *status = 0;  /* reset status to read remaining values */
1760                 }
1761              }
1762           }
1763        }
1764     }
1765     if (undefinedval && (*status <= 0) )
1766         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1767 
1768     return(*status);
1769 }
1770 /*--------------------------------------------------------------------------*/
ffgkne(fitsfile * fptr,const char * keyname,int nstart,int nmax,float * value,int * nfound,int * status)1771 int ffgkne( fitsfile *fptr,     /* I - FITS file pointer                    */
1772             const char *keyname,      /* I - root name of keywords to read        */
1773             int  nstart,        /* I - starting index number                */
1774             int  nmax,          /* I - maximum number of keywords to return */
1775             float *value,       /* O - array of keyword values              */
1776             int  *nfound,       /* O - number of values that were returned  */
1777             int  *status)       /* IO - error status                        */
1778 /*
1779   Read (get) an indexed array of keywords with index numbers between
1780   NSTART and (NSTART + NMAX -1) inclusive.
1781 */
1782 {
1783     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1784     long ival;
1785     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1786     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1787 
1788     if (*status > 0)
1789         return(*status);
1790 
1791     *nfound = 0;
1792     nend = nstart + nmax - 1;
1793 
1794     keyroot[0] = '\0';
1795     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1796 
1797     lenroot = strlen(keyroot);
1798 
1799     if (lenroot == 0)     /*  root must be at least 1 char long  */
1800         return(*status);
1801 
1802     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1803         keyroot[ii] = toupper(keyroot[ii]);
1804 
1805     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1806 
1807     ffmaky(fptr, 3, status);  /* move to 3rd keyword (skip 1st 2 keywords) */
1808 
1809     undefinedval = FALSE;
1810     for (ii=3; ii <= nkeys; ii++)
1811     {
1812        if (ffgnky(fptr, card, status) > 0)     /*  get next keyword  */
1813            return(*status);
1814 
1815        if (strncmp(keyroot, card, lenroot) == 0)  /* see if keyword matches */
1816        {
1817           keyindex[0] = '\0';
1818           equalssign = strchr(card, '=');
1819 	  if (equalssign == 0) continue;  /* keyword has no value */
1820 
1821           if (equalssign - card - lenroot > 7)
1822           {
1823              return (*status=BAD_KEYCHAR);
1824           }
1825           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1826 
1827           tstatus = 0;
1828           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)     /*  test suffix  */
1829           {
1830              if (ival <= nend && ival >= nstart)
1831              {
1832                 ffpsvc(card, svalue, comm, status);   /*  parse the value */
1833                 ffc2r(svalue, &value[ival-nstart], status); /* convert */
1834                 if (ival - nstart + 1 > *nfound)
1835                       *nfound = ival - nstart + 1;  /*  max found */
1836 
1837                 if (*status == VALUE_UNDEFINED)
1838                 {
1839                     undefinedval = TRUE;
1840                    *status = 0;  /* reset status to read remaining values */
1841                 }
1842              }
1843           }
1844        }
1845     }
1846     if (undefinedval && (*status <= 0) )
1847         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1848 
1849     return(*status);
1850 }
1851 /*--------------------------------------------------------------------------*/
ffgknd(fitsfile * fptr,const char * keyname,int nstart,int nmax,double * value,int * nfound,int * status)1852 int ffgknd( fitsfile *fptr,     /* I - FITS file pointer                    */
1853             const char *keyname,      /* I - root name of keywords to read        */
1854             int  nstart,        /* I - starting index number                */
1855             int  nmax,          /* I - maximum number of keywords to return */
1856             double *value,      /* O - array of keyword values              */
1857             int  *nfound,       /* O - number of values that were returned  */
1858             int  *status)       /* IO - error status                        */
1859 /*
1860   Read (get) an indexed array of keywords with index numbers between
1861   NSTART and (NSTART + NMAX -1) inclusive.
1862 */
1863 {
1864     int nend, lenroot, ii, nkeys, mkeys, tstatus, undefinedval;
1865     long ival;
1866     char keyroot[FLEN_KEYWORD], keyindex[8], card[FLEN_CARD];
1867     char svalue[FLEN_VALUE], comm[FLEN_COMMENT], *equalssign;
1868 
1869     if (*status > 0)
1870         return(*status);
1871 
1872     *nfound = 0;
1873     nend = nstart + nmax - 1;
1874 
1875     keyroot[0] = '\0';
1876     strncat(keyroot, keyname, FLEN_KEYWORD - 1);
1877 
1878     lenroot = strlen(keyroot);
1879 
1880     if (lenroot == 0)     /*  root must be at least 1 char long  */
1881         return(*status);
1882 
1883     for (ii=0; ii < lenroot; ii++)           /*  make sure upper case  */
1884         keyroot[ii] = toupper(keyroot[ii]);
1885 
1886     ffghps(fptr, &nkeys, &mkeys, status);  /*  get the number of keywords  */
1887 
1888     ffmaky(fptr, 3, status);  /* move to 3rd keyword (skip 1st 2 keywords) */
1889 
1890     undefinedval = FALSE;
1891     for (ii=3; ii <= nkeys; ii++)
1892     {
1893        if (ffgnky(fptr, card, status) > 0)     /*  get next keyword  */
1894            return(*status);
1895        if (strncmp(keyroot, card, lenroot) == 0)   /* see if keyword matches */
1896        {
1897           keyindex[0] = '\0';
1898           equalssign = strchr(card, '=');
1899 	  if (equalssign == 0) continue;  /* keyword has no value */
1900 
1901           if (equalssign - card - lenroot > 7)
1902           {
1903              return (*status=BAD_KEYCHAR);
1904           }
1905           strncat(keyindex, &card[lenroot], equalssign - card  - lenroot);  /*  copy suffix  */
1906           tstatus = 0;
1907           if (ffc2ii(keyindex, &ival, &tstatus) <= 0)      /*  test suffix */
1908           {
1909              if (ival <= nend && ival >= nstart) /* is index within range? */
1910              {
1911                 ffpsvc(card, svalue, comm, status);   /*  parse the value */
1912                 ffc2d(svalue, &value[ival-nstart], status); /* convert */
1913                 if (ival - nstart + 1 > *nfound)
1914                       *nfound = ival - nstart + 1;  /*  max found */
1915 
1916                 if (*status == VALUE_UNDEFINED)
1917                 {
1918                     undefinedval = TRUE;
1919                    *status = 0;  /* reset status to read remaining values */
1920                 }
1921              }
1922           }
1923        }
1924     }
1925     if (undefinedval && (*status <= 0) )
1926         *status = VALUE_UNDEFINED;  /* report at least 1 value undefined */
1927 
1928     return(*status);
1929 }
1930 /*--------------------------------------------------------------------------*/
ffgtdm(fitsfile * fptr,int colnum,int maxdim,int * naxis,long naxes[],int * status)1931 int ffgtdm(fitsfile *fptr,  /* I - FITS file pointer                        */
1932            int colnum,      /* I - number of the column to read             */
1933            int maxdim,      /* I - maximum no. of dimensions to read;       */
1934            int *naxis,      /* O - number of axes in the data array         */
1935            long naxes[],    /* O - length of each data axis                 */
1936            int *status)     /* IO - error status                            */
1937 /*
1938   read and parse the TDIMnnn keyword to get the dimensionality of a column
1939 */
1940 {
1941     int tstatus = 0;
1942     char keyname[FLEN_KEYWORD], tdimstr[FLEN_VALUE];
1943 
1944     if (*status > 0)
1945         return(*status);
1946 
1947     ffkeyn("TDIM", colnum, keyname, status);      /* construct keyword name */
1948 
1949     ffgkys(fptr, keyname, tdimstr, NULL, &tstatus); /* try reading keyword */
1950 
1951     ffdtdm(fptr, tdimstr, colnum, maxdim,naxis, naxes, status); /* decode it */
1952 
1953     return(*status);
1954 }
1955 /*--------------------------------------------------------------------------*/
ffgtdmll(fitsfile * fptr,int colnum,int maxdim,int * naxis,LONGLONG naxes[],int * status)1956 int ffgtdmll(fitsfile *fptr,  /* I - FITS file pointer                      */
1957            int colnum,      /* I - number of the column to read             */
1958            int maxdim,      /* I - maximum no. of dimensions to read;       */
1959            int *naxis,      /* O - number of axes in the data array         */
1960            LONGLONG naxes[], /* O - length of each data axis                 */
1961            int *status)     /* IO - error status                            */
1962 /*
1963   read and parse the TDIMnnn keyword to get the dimensionality of a column
1964 */
1965 {
1966     int tstatus = 0;
1967     char keyname[FLEN_KEYWORD], tdimstr[FLEN_VALUE];
1968 
1969     if (*status > 0)
1970         return(*status);
1971 
1972     ffkeyn("TDIM", colnum, keyname, status);      /* construct keyword name */
1973 
1974     ffgkys(fptr, keyname, tdimstr, NULL, &tstatus); /* try reading keyword */
1975 
1976     ffdtdmll(fptr, tdimstr, colnum, maxdim,naxis, naxes, status); /* decode it */
1977 
1978     return(*status);
1979 }
1980 /*--------------------------------------------------------------------------*/
ffdtdm(fitsfile * fptr,char * tdimstr,int colnum,int maxdim,int * naxis,long naxes[],int * status)1981 int ffdtdm(fitsfile *fptr,  /* I - FITS file pointer                        */
1982            char *tdimstr,   /* I - TDIMn keyword value string. e.g. (10,10) */
1983            int colnum,      /* I - number of the column             */
1984            int maxdim,      /* I - maximum no. of dimensions to read;       */
1985            int *naxis,      /* O - number of axes in the data array         */
1986            long naxes[],    /* O - length of each data axis                 */
1987            int *status)     /* IO - error status                            */
1988 /*
1989   decode the TDIMnnn keyword to get the dimensionality of a column.
1990   Check that the value is legal and consistent with the TFORM value.
1991   If colnum = 0, then the validity checking is disabled.
1992 */
1993 {
1994     long dimsize, totalpix = 1;
1995     char *loc, *lastloc, message[FLEN_ERRMSG];
1996     tcolumn *colptr = 0;
1997 
1998     if (*status > 0)
1999         return(*status);
2000 
2001     if (colnum != 0) {
2002         if (fptr->HDUposition != (fptr->Fptr)->curhdu)
2003             ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
2004 
2005         if (colnum < 1 || colnum > (fptr->Fptr)->tfield)
2006             return(*status = BAD_COL_NUM);
2007 
2008         colptr = (fptr->Fptr)->tableptr;   /* set pointer to the first column */
2009         colptr += (colnum - 1);    /* increment to the correct column */
2010 
2011         if (!tdimstr[0])   /* TDIMn keyword doesn't exist? */
2012         {
2013             *naxis = 1;                   /* default = 1 dimensional */
2014             if (maxdim > 0)
2015                 naxes[0] = (long) colptr->trepeat; /* default length = repeat */
2016 
2017             return(*status);
2018         }
2019     }
2020 
2021     *naxis = 0;
2022 
2023     loc = strchr(tdimstr, '(' );  /* find the opening quote */
2024     if (!loc)
2025     {
2026             snprintf(message, FLEN_ERRMSG, "Illegal dimensions format: %s", tdimstr);
2027             return(*status = BAD_TDIM);
2028     }
2029 
2030     while (loc)
2031     {
2032             loc++;
2033             dimsize = strtol(loc, &loc, 10);  /* read size of next dimension */
2034             if (*naxis < maxdim)
2035                 naxes[*naxis] = dimsize;
2036 
2037             if (dimsize < 0)
2038             {
2039                 ffpmsg("one or more dimension are less than 0 (ffdtdm)");
2040                 ffpmsg(tdimstr);
2041                 return(*status = BAD_TDIM);
2042             }
2043 
2044             totalpix *= dimsize;
2045             (*naxis)++;
2046             lastloc = loc;
2047             loc = strchr(loc, ',');  /* look for comma before next dimension */
2048     }
2049 
2050     loc = strchr(lastloc, ')' );  /* check for the closing quote */
2051     if (!loc)
2052     {
2053             snprintf(message, FLEN_ERRMSG, "Illegal dimensions format: %s", tdimstr);
2054             return(*status = BAD_TDIM);
2055     }
2056 
2057     if (colnum != 0) {
2058         if ((colptr->tdatatype > 0) && ((long) colptr->trepeat != totalpix))
2059         {
2060           snprintf(message, FLEN_ERRMSG,
2061           "column vector length, %ld, does not equal TDIMn array size, %ld",
2062           (long) colptr->trepeat, totalpix);
2063           ffpmsg(message);
2064           ffpmsg(tdimstr);
2065           return(*status = BAD_TDIM);
2066         }
2067     }
2068     return(*status);
2069 }
2070 /*--------------------------------------------------------------------------*/
ffdtdmll(fitsfile * fptr,char * tdimstr,int colnum,int maxdim,int * naxis,LONGLONG naxes[],int * status)2071 int ffdtdmll(fitsfile *fptr,  /* I - FITS file pointer                        */
2072            char *tdimstr,   /* I - TDIMn keyword value string. e.g. (10,10) */
2073            int colnum,      /* I - number of the column             */
2074            int maxdim,      /* I - maximum no. of dimensions to read;       */
2075            int *naxis,      /* O - number of axes in the data array         */
2076            LONGLONG naxes[],    /* O - length of each data axis                 */
2077            int *status)     /* IO - error status                            */
2078 /*
2079   decode the TDIMnnn keyword to get the dimensionality of a column.
2080   Check that the value is legal and consistent with the TFORM value.
2081 */
2082 {
2083     LONGLONG dimsize;
2084     LONGLONG totalpix = 1;
2085     char *loc, *lastloc, message[FLEN_ERRMSG];
2086     tcolumn *colptr;
2087     double doublesize;
2088 
2089     if (*status > 0)
2090         return(*status);
2091 
2092     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
2093         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
2094 
2095     if (colnum < 1 || colnum > (fptr->Fptr)->tfield)
2096         return(*status = BAD_COL_NUM);
2097 
2098     colptr = (fptr->Fptr)->tableptr;   /* set pointer to the first column */
2099     colptr += (colnum - 1);    /* increment to the correct column */
2100 
2101     if (!tdimstr[0])   /* TDIMn keyword doesn't exist? */
2102     {
2103         *naxis = 1;                   /* default = 1 dimensional */
2104         if (maxdim > 0)
2105             naxes[0] = colptr->trepeat; /* default length = repeat */
2106     }
2107     else
2108     {
2109         *naxis = 0;
2110 
2111         loc = strchr(tdimstr, '(' );  /* find the opening quote */
2112         if (!loc)
2113         {
2114             snprintf(message, FLEN_ERRMSG, "Illegal TDIM keyword value: %s", tdimstr);
2115             return(*status = BAD_TDIM);
2116         }
2117 
2118         while (loc)
2119         {
2120             loc++;
2121 
2122     /* Read value as a double because the string to 64-bit int function is  */
2123     /* platform dependent (strtoll, strtol, _atoI64).  This still gives     */
2124     /* about 48 bits of precision, which is plenty for this purpose.        */
2125 
2126             doublesize = strtod(loc, &loc);
2127             dimsize = (LONGLONG) (doublesize + 0.1);
2128 
2129             if (*naxis < maxdim)
2130                 naxes[*naxis] = dimsize;
2131 
2132             if (dimsize < 0)
2133             {
2134                 ffpmsg("one or more TDIM values are less than 0 (ffdtdm)");
2135                 ffpmsg(tdimstr);
2136                 return(*status = BAD_TDIM);
2137             }
2138 
2139             totalpix *= dimsize;
2140             (*naxis)++;
2141             lastloc = loc;
2142             loc = strchr(loc, ',');  /* look for comma before next dimension */
2143         }
2144 
2145         loc = strchr(lastloc, ')' );  /* check for the closing quote */
2146         if (!loc)
2147         {
2148             snprintf(message, FLEN_ERRMSG, "Illegal TDIM keyword value: %s", tdimstr);
2149             return(*status = BAD_TDIM);
2150         }
2151 
2152         if ((colptr->tdatatype > 0) && (colptr->trepeat != totalpix))
2153         {
2154           snprintf(message, FLEN_ERRMSG,
2155           "column vector length, %.0f, does not equal TDIMn array size, %.0f",
2156           (double) (colptr->trepeat), (double) totalpix);
2157           ffpmsg(message);
2158           ffpmsg(tdimstr);
2159           return(*status = BAD_TDIM);
2160         }
2161     }
2162     return(*status);
2163 }
2164 /*--------------------------------------------------------------------------*/
ffghpr(fitsfile * fptr,int maxdim,int * simple,int * bitpix,int * naxis,long naxes[],long * pcount,long * gcount,int * extend,int * status)2165 int ffghpr(fitsfile *fptr,  /* I - FITS file pointer                        */
2166            int maxdim,      /* I - maximum no. of dimensions to read;       */
2167            int *simple,     /* O - does file conform to FITS standard? 1/0  */
2168            int *bitpix,     /* O - number of bits per data value pixel      */
2169            int *naxis,      /* O - number of axes in the data array         */
2170            long naxes[],    /* O - length of each data axis                 */
2171            long *pcount,    /* O - number of group parameters (usually 0)   */
2172            long *gcount,    /* O - number of random groups (usually 1 or 0) */
2173            int *extend,     /* O - may FITS file haave extensions?          */
2174            int *status)     /* IO - error status                            */
2175 /*
2176   Get keywords from the Header of the PRimary array:
2177   Check that the keywords conform to the FITS standard and return the
2178   parameters which determine the size and structure of the primary array
2179   or IMAGE extension.
2180 */
2181 {
2182     int idummy, ii;
2183     LONGLONG lldummy;
2184     double ddummy;
2185     LONGLONG tnaxes[99];
2186 
2187     ffgphd(fptr, maxdim, simple, bitpix, naxis, tnaxes, pcount, gcount, extend,
2188           &ddummy, &ddummy, &lldummy, &idummy, status);
2189 
2190     if (naxis && naxes) {
2191          for (ii = 0; (ii < *naxis) && (ii < maxdim); ii++)
2192 	     naxes[ii] = (long) tnaxes[ii];
2193     } else if (naxes) {
2194          for (ii = 0; ii < maxdim; ii++)
2195 	     naxes[ii] = (long) tnaxes[ii];
2196     }
2197 
2198     return(*status);
2199 }
2200 /*--------------------------------------------------------------------------*/
ffghprll(fitsfile * fptr,int maxdim,int * simple,int * bitpix,int * naxis,LONGLONG naxes[],long * pcount,long * gcount,int * extend,int * status)2201 int ffghprll(fitsfile *fptr,  /* I - FITS file pointer                        */
2202            int maxdim,      /* I - maximum no. of dimensions to read;       */
2203            int *simple,     /* O - does file conform to FITS standard? 1/0  */
2204            int *bitpix,     /* O - number of bits per data value pixel      */
2205            int *naxis,      /* O - number of axes in the data array         */
2206            LONGLONG naxes[],    /* O - length of each data axis                 */
2207            long *pcount,    /* O - number of group parameters (usually 0)   */
2208            long *gcount,    /* O - number of random groups (usually 1 or 0) */
2209            int *extend,     /* O - may FITS file haave extensions?          */
2210            int *status)     /* IO - error status                            */
2211 /*
2212   Get keywords from the Header of the PRimary array:
2213   Check that the keywords conform to the FITS standard and return the
2214   parameters which determine the size and structure of the primary array
2215   or IMAGE extension.
2216 */
2217 {
2218     int idummy;
2219     LONGLONG lldummy;
2220     double ddummy;
2221 
2222     ffgphd(fptr, maxdim, simple, bitpix, naxis, naxes, pcount, gcount, extend,
2223           &ddummy, &ddummy, &lldummy, &idummy, status);
2224 
2225     return(*status);
2226 }
2227 /*--------------------------------------------------------------------------*/
ffghtb(fitsfile * fptr,int maxfield,long * naxis1,long * naxis2,int * tfields,char ** ttype,long * tbcol,char ** tform,char ** tunit,char * extnm,int * status)2228 int ffghtb(fitsfile *fptr,  /* I - FITS file pointer                        */
2229            int maxfield,    /* I - maximum no. of columns to read;          */
2230            long *naxis1,    /* O - length of table row in bytes             */
2231            long *naxis2,    /* O - number of rows in the table              */
2232            int *tfields,    /* O - number of columns in the table           */
2233            char **ttype,    /* O - name of each column                      */
2234            long *tbcol,     /* O - byte offset in row to each column        */
2235            char **tform,    /* O - value of TFORMn keyword for each column  */
2236            char **tunit,    /* O - value of TUNITn keyword for each column  */
2237            char *extnm,   /* O - value of EXTNAME keyword, if any         */
2238            int *status)     /* IO - error status                            */
2239 /*
2240   Get keywords from the Header of the ASCII TaBle:
2241   Check that the keywords conform to the FITS standard and return the
2242   parameters which describe the table.
2243 */
2244 {
2245     int ii, maxf, nfound, tstatus;
2246     long fields;
2247     char name[FLEN_KEYWORD], value[FLEN_VALUE], comm[FLEN_COMMENT];
2248     char xtension[FLEN_VALUE], message[FLEN_ERRMSG];
2249     LONGLONG llnaxis1, llnaxis2, pcount;
2250 
2251     if (*status > 0)
2252         return(*status);
2253 
2254     /* read the first keyword of the extension */
2255     ffgkyn(fptr, 1, name, value, comm, status);
2256 
2257     if (!strcmp(name, "XTENSION"))
2258     {
2259             if (ffc2s(value, xtension, status) > 0)  /* get the value string */
2260             {
2261                 ffpmsg("Bad value string for XTENSION keyword:");
2262                 ffpmsg(value);
2263                 return(*status);
2264             }
2265 
2266             /* allow the quoted string value to begin in any column and */
2267             /* allow any number of trailing blanks before the closing quote */
2268             if ( (value[0] != '\'')   ||  /* first char must be a quote */
2269                  ( strcmp(xtension, "TABLE") ) )
2270             {
2271                 snprintf(message, FLEN_ERRMSG,
2272                 "This is not a TABLE extension: %s", value);
2273                 ffpmsg(message);
2274                 return(*status = NOT_ATABLE);
2275             }
2276     }
2277 
2278     else  /* error: 1st keyword of extension != XTENSION */
2279     {
2280         snprintf(message, FLEN_ERRMSG,
2281         "First keyword of the extension is not XTENSION: %s", name);
2282         ffpmsg(message);
2283         return(*status = NO_XTENSION);
2284     }
2285 
2286     if (ffgttb(fptr, &llnaxis1, &llnaxis2, &pcount, &fields, status) > 0)
2287         return(*status);
2288 
2289     if (naxis1)
2290        *naxis1 = (long) llnaxis1;
2291 
2292     if (naxis2)
2293        *naxis2 = (long) llnaxis2;
2294 
2295     if (pcount != 0)
2296     {
2297        snprintf(message, FLEN_ERRMSG, "PCOUNT = %.0f is illegal in ASCII table; must = 0",
2298                (double) pcount);
2299        ffpmsg(message);
2300        return(*status = BAD_PCOUNT);
2301     }
2302 
2303     if (tfields)
2304        *tfields = fields;
2305 
2306     if (maxfield < 0)
2307         maxf = fields;
2308     else
2309         maxf = minvalue(maxfield, fields);
2310 
2311     if (maxf > 0)
2312     {
2313         for (ii = 0; ii < maxf; ii++)
2314         {   /* initialize optional keyword values */
2315             if (ttype)
2316                 *ttype[ii] = '\0';
2317 
2318             if (tunit)
2319                 *tunit[ii] = '\0';
2320         }
2321 
2322 
2323         if (ttype)
2324             ffgkns(fptr, "TTYPE", 1, maxf, ttype, &nfound, status);
2325 
2326         if (tunit)
2327             ffgkns(fptr, "TUNIT", 1, maxf, tunit, &nfound, status);
2328 
2329         if (*status > 0)
2330             return(*status);
2331 
2332         if (tbcol)
2333         {
2334             ffgknj(fptr, "TBCOL", 1, maxf, tbcol, &nfound, status);
2335 
2336             if (*status > 0 || nfound != maxf)
2337             {
2338                 ffpmsg(
2339         "Required TBCOL keyword(s) not found in ASCII table header (ffghtb).");
2340                 return(*status = NO_TBCOL);
2341             }
2342         }
2343 
2344         if (tform)
2345         {
2346             ffgkns(fptr, "TFORM", 1, maxf, tform, &nfound, status);
2347 
2348             if (*status > 0 || nfound != maxf)
2349             {
2350                 ffpmsg(
2351         "Required TFORM keyword(s) not found in ASCII table header (ffghtb).");
2352                 return(*status = NO_TFORM);
2353             }
2354         }
2355     }
2356 
2357     if (extnm)
2358     {
2359         extnm[0] = '\0';
2360 
2361         tstatus = *status;
2362         ffgkys(fptr, "EXTNAME", extnm, comm, status);
2363 
2364         if (*status == KEY_NO_EXIST)
2365             *status = tstatus;  /* keyword not required, so ignore error */
2366     }
2367 
2368     return(*status);
2369 }
2370 /*--------------------------------------------------------------------------*/
ffghtbll(fitsfile * fptr,int maxfield,LONGLONG * naxis1,LONGLONG * naxis2,int * tfields,char ** ttype,LONGLONG * tbcol,char ** tform,char ** tunit,char * extnm,int * status)2371 int ffghtbll(fitsfile *fptr, /* I - FITS file pointer                        */
2372            int maxfield,    /* I - maximum no. of columns to read;          */
2373            LONGLONG *naxis1, /* O - length of table row in bytes             */
2374            LONGLONG *naxis2, /* O - number of rows in the table              */
2375            int *tfields,    /* O - number of columns in the table           */
2376            char **ttype,    /* O - name of each column                      */
2377            LONGLONG *tbcol, /* O - byte offset in row to each column        */
2378            char **tform,    /* O - value of TFORMn keyword for each column  */
2379            char **tunit,    /* O - value of TUNITn keyword for each column  */
2380            char *extnm,     /* O - value of EXTNAME keyword, if any         */
2381            int *status)     /* IO - error status                            */
2382 /*
2383   Get keywords from the Header of the ASCII TaBle:
2384   Check that the keywords conform to the FITS standard and return the
2385   parameters which describe the table.
2386 */
2387 {
2388     int ii, maxf, nfound, tstatus;
2389     long fields;
2390     char name[FLEN_KEYWORD], value[FLEN_VALUE], comm[FLEN_COMMENT];
2391     char xtension[FLEN_VALUE], message[FLEN_ERRMSG];
2392     LONGLONG llnaxis1, llnaxis2, pcount;
2393 
2394     if (*status > 0)
2395         return(*status);
2396 
2397     /* read the first keyword of the extension */
2398     ffgkyn(fptr, 1, name, value, comm, status);
2399 
2400     if (!strcmp(name, "XTENSION"))
2401     {
2402             if (ffc2s(value, xtension, status) > 0)  /* get the value string */
2403             {
2404                 ffpmsg("Bad value string for XTENSION keyword:");
2405                 ffpmsg(value);
2406                 return(*status);
2407             }
2408 
2409             /* allow the quoted string value to begin in any column and */
2410             /* allow any number of trailing blanks before the closing quote */
2411             if ( (value[0] != '\'')   ||  /* first char must be a quote */
2412                  ( strcmp(xtension, "TABLE") ) )
2413             {
2414                 snprintf(message, FLEN_ERRMSG,
2415                 "This is not a TABLE extension: %s", value);
2416                 ffpmsg(message);
2417                 return(*status = NOT_ATABLE);
2418             }
2419     }
2420 
2421     else  /* error: 1st keyword of extension != XTENSION */
2422     {
2423         snprintf(message, FLEN_ERRMSG,
2424         "First keyword of the extension is not XTENSION: %s", name);
2425         ffpmsg(message);
2426         return(*status = NO_XTENSION);
2427     }
2428 
2429     if (ffgttb(fptr, &llnaxis1, &llnaxis2, &pcount, &fields, status) > 0)
2430         return(*status);
2431 
2432     if (naxis1)
2433        *naxis1 = llnaxis1;
2434 
2435     if (naxis2)
2436        *naxis2 = llnaxis2;
2437 
2438     if (pcount != 0)
2439     {
2440        snprintf(message, FLEN_ERRMSG, "PCOUNT = %.0f is illegal in ASCII table; must = 0",
2441              (double) pcount);
2442        ffpmsg(message);
2443        return(*status = BAD_PCOUNT);
2444     }
2445 
2446     if (tfields)
2447        *tfields = fields;
2448 
2449     if (maxfield < 0)
2450         maxf = fields;
2451     else
2452         maxf = minvalue(maxfield, fields);
2453 
2454     if (maxf > 0)
2455     {
2456         for (ii = 0; ii < maxf; ii++)
2457         {   /* initialize optional keyword values */
2458             if (ttype)
2459                 *ttype[ii] = '\0';
2460 
2461             if (tunit)
2462                 *tunit[ii] = '\0';
2463         }
2464 
2465 
2466         if (ttype)
2467             ffgkns(fptr, "TTYPE", 1, maxf, ttype, &nfound, status);
2468 
2469         if (tunit)
2470             ffgkns(fptr, "TUNIT", 1, maxf, tunit, &nfound, status);
2471 
2472         if (*status > 0)
2473             return(*status);
2474 
2475         if (tbcol)
2476         {
2477             ffgknjj(fptr, "TBCOL", 1, maxf, tbcol, &nfound, status);
2478 
2479             if (*status > 0 || nfound != maxf)
2480             {
2481                 ffpmsg(
2482         "Required TBCOL keyword(s) not found in ASCII table header (ffghtbll).");
2483                 return(*status = NO_TBCOL);
2484             }
2485         }
2486 
2487         if (tform)
2488         {
2489             ffgkns(fptr, "TFORM", 1, maxf, tform, &nfound, status);
2490 
2491             if (*status > 0 || nfound != maxf)
2492             {
2493                 ffpmsg(
2494         "Required TFORM keyword(s) not found in ASCII table header (ffghtbll).");
2495                 return(*status = NO_TFORM);
2496             }
2497         }
2498     }
2499 
2500     if (extnm)
2501     {
2502         extnm[0] = '\0';
2503 
2504         tstatus = *status;
2505         ffgkys(fptr, "EXTNAME", extnm, comm, status);
2506 
2507         if (*status == KEY_NO_EXIST)
2508             *status = tstatus;  /* keyword not required, so ignore error */
2509     }
2510 
2511     return(*status);
2512 }
2513 /*--------------------------------------------------------------------------*/
ffghbn(fitsfile * fptr,int maxfield,long * naxis2,int * tfields,char ** ttype,char ** tform,char ** tunit,char * extnm,long * pcount,int * status)2514 int ffghbn(fitsfile *fptr,  /* I - FITS file pointer                        */
2515            int maxfield,    /* I - maximum no. of columns to read;          */
2516            long *naxis2,    /* O - number of rows in the table              */
2517            int *tfields,    /* O - number of columns in the table           */
2518            char **ttype,    /* O - name of each column                      */
2519            char **tform,    /* O - TFORMn value for each column             */
2520            char **tunit,    /* O - TUNITn value for each column             */
2521            char *extnm,     /* O - value of EXTNAME keyword, if any         */
2522            long *pcount,    /* O - value of PCOUNT keyword                  */
2523            int *status)     /* IO - error status                            */
2524 /*
2525   Get keywords from the Header of the BiNary table:
2526   Check that the keywords conform to the FITS standard and return the
2527   parameters which describe the table.
2528 */
2529 {
2530     int ii, maxf, nfound, tstatus;
2531     long  fields;
2532     char name[FLEN_KEYWORD], value[FLEN_VALUE], comm[FLEN_COMMENT];
2533     char xtension[FLEN_VALUE], message[FLEN_ERRMSG];
2534     LONGLONG naxis1ll, naxis2ll, pcountll;
2535 
2536     if (*status > 0)
2537         return(*status);
2538 
2539     /* read the first keyword of the extension */
2540     ffgkyn(fptr, 1, name, value, comm, status);
2541 
2542     if (!strcmp(name, "XTENSION"))
2543     {
2544             if (ffc2s(value, xtension, status) > 0)  /* get the value string */
2545             {
2546                 ffpmsg("Bad value string for XTENSION keyword:");
2547                 ffpmsg(value);
2548                 return(*status);
2549             }
2550 
2551             /* allow the quoted string value to begin in any column and */
2552             /* allow any number of trailing blanks before the closing quote */
2553             if ( (value[0] != '\'')   ||  /* first char must be a quote */
2554                  ( strcmp(xtension, "BINTABLE") &&
2555                    strcmp(xtension, "A3DTABLE") &&
2556                    strcmp(xtension, "3DTABLE")
2557                  ) )
2558             {
2559                 snprintf(message, FLEN_ERRMSG,
2560                 "This is not a BINTABLE extension: %s", value);
2561                 ffpmsg(message);
2562                 return(*status = NOT_BTABLE);
2563             }
2564     }
2565 
2566     else  /* error: 1st keyword of extension != XTENSION */
2567     {
2568         snprintf(message, FLEN_ERRMSG,
2569         "First keyword of the extension is not XTENSION: %s", name);
2570         ffpmsg(message);
2571         return(*status = NO_XTENSION);
2572     }
2573 
2574     if (ffgttb(fptr, &naxis1ll, &naxis2ll, &pcountll, &fields, status) > 0)
2575         return(*status);
2576 
2577     if (naxis2)
2578        *naxis2 = (long) naxis2ll;
2579 
2580     if (pcount)
2581        *pcount = (long) pcountll;
2582 
2583     if (tfields)
2584         *tfields = fields;
2585 
2586     if (maxfield < 0)
2587         maxf = fields;
2588     else
2589         maxf = minvalue(maxfield, fields);
2590 
2591     if (maxf > 0)
2592     {
2593         for (ii = 0; ii < maxf; ii++)
2594         {   /* initialize optional keyword values */
2595             if (ttype)
2596                 *ttype[ii] = '\0';
2597 
2598             if (tunit)
2599                 *tunit[ii] = '\0';
2600         }
2601 
2602         if (ttype)
2603             ffgkns(fptr, "TTYPE", 1, maxf, ttype, &nfound, status);
2604 
2605         if (tunit)
2606             ffgkns(fptr, "TUNIT", 1, maxf, tunit, &nfound, status);
2607 
2608         if (*status > 0)
2609             return(*status);
2610 
2611         if (tform)
2612         {
2613             ffgkns(fptr, "TFORM", 1, maxf, tform, &nfound, status);
2614 
2615             if (*status > 0 || nfound != maxf)
2616             {
2617                 ffpmsg(
2618         "Required TFORM keyword(s) not found in binary table header (ffghbn).");
2619                 return(*status = NO_TFORM);
2620             }
2621         }
2622     }
2623 
2624     if (extnm)
2625     {
2626         extnm[0] = '\0';
2627 
2628         tstatus = *status;
2629         ffgkys(fptr, "EXTNAME", extnm, comm, status);
2630 
2631         if (*status == KEY_NO_EXIST)
2632           *status = tstatus;  /* keyword not required, so ignore error */
2633     }
2634     return(*status);
2635 }
2636 /*--------------------------------------------------------------------------*/
ffghbnll(fitsfile * fptr,int maxfield,LONGLONG * naxis2,int * tfields,char ** ttype,char ** tform,char ** tunit,char * extnm,LONGLONG * pcount,int * status)2637 int ffghbnll(fitsfile *fptr,  /* I - FITS file pointer                        */
2638            int maxfield,    /* I - maximum no. of columns to read;          */
2639            LONGLONG *naxis2,    /* O - number of rows in the table              */
2640            int *tfields,    /* O - number of columns in the table           */
2641            char **ttype,    /* O - name of each column                      */
2642            char **tform,    /* O - TFORMn value for each column             */
2643            char **tunit,    /* O - TUNITn value for each column             */
2644            char *extnm,     /* O - value of EXTNAME keyword, if any         */
2645            LONGLONG *pcount,    /* O - value of PCOUNT keyword                  */
2646            int *status)     /* IO - error status                            */
2647 /*
2648   Get keywords from the Header of the BiNary table:
2649   Check that the keywords conform to the FITS standard and return the
2650   parameters which describe the table.
2651 */
2652 {
2653     int ii, maxf, nfound, tstatus;
2654     long  fields;
2655     char name[FLEN_KEYWORD], value[FLEN_VALUE], comm[FLEN_COMMENT];
2656     char xtension[FLEN_VALUE], message[FLEN_ERRMSG];
2657     LONGLONG naxis1ll, naxis2ll, pcountll;
2658 
2659     if (*status > 0)
2660         return(*status);
2661 
2662     /* read the first keyword of the extension */
2663     ffgkyn(fptr, 1, name, value, comm, status);
2664 
2665     if (!strcmp(name, "XTENSION"))
2666     {
2667             if (ffc2s(value, xtension, status) > 0)  /* get the value string */
2668             {
2669                 ffpmsg("Bad value string for XTENSION keyword:");
2670                 ffpmsg(value);
2671                 return(*status);
2672             }
2673 
2674             /* allow the quoted string value to begin in any column and */
2675             /* allow any number of trailing blanks before the closing quote */
2676             if ( (value[0] != '\'')   ||  /* first char must be a quote */
2677                  ( strcmp(xtension, "BINTABLE") &&
2678                    strcmp(xtension, "A3DTABLE") &&
2679                    strcmp(xtension, "3DTABLE")
2680                  ) )
2681             {
2682                 snprintf(message, FLEN_ERRMSG,
2683                 "This is not a BINTABLE extension: %s", value);
2684                 ffpmsg(message);
2685                 return(*status = NOT_BTABLE);
2686             }
2687     }
2688 
2689     else  /* error: 1st keyword of extension != XTENSION */
2690     {
2691         snprintf(message, FLEN_ERRMSG,
2692         "First keyword of the extension is not XTENSION: %s", name);
2693         ffpmsg(message);
2694         return(*status = NO_XTENSION);
2695     }
2696 
2697     if (ffgttb(fptr, &naxis1ll, &naxis2ll, &pcountll, &fields, status) > 0)
2698         return(*status);
2699 
2700     if (naxis2)
2701        *naxis2 = naxis2ll;
2702 
2703     if (pcount)
2704        *pcount = pcountll;
2705 
2706     if (tfields)
2707         *tfields = fields;
2708 
2709     if (maxfield < 0)
2710         maxf = fields;
2711     else
2712         maxf = minvalue(maxfield, fields);
2713 
2714     if (maxf > 0)
2715     {
2716         for (ii = 0; ii < maxf; ii++)
2717         {   /* initialize optional keyword values */
2718             if (ttype)
2719                 *ttype[ii] = '\0';
2720 
2721             if (tunit)
2722                 *tunit[ii] = '\0';
2723         }
2724 
2725         if (ttype)
2726             ffgkns(fptr, "TTYPE", 1, maxf, ttype, &nfound, status);
2727 
2728         if (tunit)
2729             ffgkns(fptr, "TUNIT", 1, maxf, tunit, &nfound, status);
2730 
2731         if (*status > 0)
2732             return(*status);
2733 
2734         if (tform)
2735         {
2736             ffgkns(fptr, "TFORM", 1, maxf, tform, &nfound, status);
2737 
2738             if (*status > 0 || nfound != maxf)
2739             {
2740                 ffpmsg(
2741         "Required TFORM keyword(s) not found in binary table header (ffghbn).");
2742                 return(*status = NO_TFORM);
2743             }
2744         }
2745     }
2746 
2747     if (extnm)
2748     {
2749         extnm[0] = '\0';
2750 
2751         tstatus = *status;
2752         ffgkys(fptr, "EXTNAME", extnm, comm, status);
2753 
2754         if (*status == KEY_NO_EXIST)
2755           *status = tstatus;  /* keyword not required, so ignore error */
2756     }
2757     return(*status);
2758 }
2759 /*--------------------------------------------------------------------------*/
ffgphd(fitsfile * fptr,int maxdim,int * simple,int * bitpix,int * naxis,LONGLONG naxes[],long * pcount,long * gcount,int * extend,double * bscale,double * bzero,LONGLONG * blank,int * nspace,int * status)2760 int ffgphd(fitsfile *fptr,  /* I - FITS file pointer                        */
2761            int maxdim,      /* I - maximum no. of dimensions to read;       */
2762            int *simple,     /* O - does file conform to FITS standard? 1/0  */
2763            int *bitpix,     /* O - number of bits per data value pixel      */
2764            int *naxis,      /* O - number of axes in the data array         */
2765            LONGLONG naxes[],    /* O - length of each data axis                 */
2766            long *pcount,    /* O - number of group parameters (usually 0)   */
2767            long *gcount,    /* O - number of random groups (usually 1 or 0) */
2768            int *extend,     /* O - may FITS file haave extensions?          */
2769            double *bscale,  /* O - array pixel linear scaling factor        */
2770            double *bzero,   /* O - array pixel linear scaling zero point    */
2771            LONGLONG *blank, /* O - value used to represent undefined pixels */
2772            int *nspace,     /* O - number of blank keywords prior to END    */
2773            int *status)     /* IO - error status                            */
2774 {
2775 /*
2776   Get the Primary HeaDer parameters.  Check that the keywords conform to
2777   the FITS standard and return the parameters which determine the size and
2778   structure of the primary array or IMAGE extension.
2779 */
2780     int unknown, found_end, tstatus, ii, nextkey, namelen;
2781     long longbitpix, longnaxis;
2782     LONGLONG axislen;
2783     char message[FLEN_ERRMSG], keyword[FLEN_KEYWORD];
2784     char card[FLEN_CARD];
2785     char name[FLEN_KEYWORD], value[FLEN_VALUE], comm[FLEN_COMMENT];
2786     char xtension[FLEN_VALUE];
2787 
2788     if (*status > 0)
2789         return(*status);
2790 
2791     if (fptr->HDUposition != (fptr->Fptr)->curhdu)
2792         ffmahd(fptr, (fptr->HDUposition) + 1, NULL, status);
2793 
2794     if (simple)
2795        *simple = 1;
2796 
2797     unknown = 0;
2798 
2799     /*--------------------------------------------------------------------*/
2800     /*  Get 1st keyword of HDU and test whether it is SIMPLE or XTENSION  */
2801     /*--------------------------------------------------------------------*/
2802     ffgkyn(fptr, 1, name, value, comm, status);
2803 
2804     if ((fptr->Fptr)->curhdu == 0) /* Is this the beginning of the FITS file? */
2805     {
2806         if (!strcmp(name, "SIMPLE"))
2807         {
2808             if (value[0] == 'F')
2809             {
2810                 if (simple)
2811                     *simple=0;          /* not a simple FITS file */
2812             }
2813             else if (value[0] != 'T')
2814                 return(*status = BAD_SIMPLE);
2815         }
2816 
2817         else
2818         {
2819             snprintf(message, FLEN_ERRMSG,
2820                    "First keyword of the file is not SIMPLE: %s", name);
2821             ffpmsg(message);
2822             return(*status = NO_SIMPLE);
2823         }
2824     }
2825 
2826     else    /* not beginning of the file, so presumably an IMAGE extension */
2827     {       /* or it could be a compressed image in a binary table */
2828 
2829         if (!strcmp(name, "XTENSION"))
2830         {
2831             if (ffc2s(value, xtension, status) > 0)  /* get the value string */
2832             {
2833                 ffpmsg("Bad value string for XTENSION keyword:");
2834                 ffpmsg(value);
2835                 return(*status);
2836             }
2837 
2838             /* allow the quoted string value to begin in any column and */
2839             /* allow any number of trailing blanks before the closing quote */
2840             if ( (value[0] != '\'')   ||  /* first char must be a quote */
2841                   ( strcmp(xtension, "IMAGE")  &&
2842                     strcmp(xtension, "IUEIMAGE") ) )
2843             {
2844                 unknown = 1;  /* unknown type of extension; press on anyway */
2845                 snprintf(message, FLEN_ERRMSG,
2846                    "This is not an IMAGE extension: %s", value);
2847                 ffpmsg(message);
2848             }
2849         }
2850 
2851         else  /* error: 1st keyword of extension != XTENSION */
2852         {
2853             snprintf(message, FLEN_ERRMSG,
2854             "First keyword of the extension is not XTENSION: %s", name);
2855             ffpmsg(message);
2856             return(*status = NO_XTENSION);
2857         }
2858     }
2859 
2860     if (unknown && (fptr->Fptr)->compressimg)
2861     {
2862         /* this is a compressed image, so read ZBITPIX, ZNAXIS keywords */
2863         unknown = 0;  /* reset flag */
2864         ffxmsg(3, message); /* clear previous spurious error message */
2865 
2866         if (bitpix)
2867         {
2868             ffgidt(fptr, bitpix, status); /* get bitpix value */
2869 
2870             if (*status > 0)
2871             {
2872                 ffpmsg("Error reading BITPIX value of compressed image");
2873                 return(*status);
2874             }
2875         }
2876 
2877         if (naxis)
2878         {
2879             ffgidm(fptr, naxis, status); /* get NAXIS value */
2880 
2881             if (*status > 0)
2882             {
2883                 ffpmsg("Error reading NAXIS value of compressed image");
2884                 return(*status);
2885             }
2886         }
2887 
2888         if (naxes)
2889         {
2890             ffgiszll(fptr, maxdim, naxes, status);  /* get NAXISn value */
2891 
2892             if (*status > 0)
2893             {
2894                 ffpmsg("Error reading NAXISn values of compressed image");
2895                 return(*status);
2896             }
2897         }
2898 
2899         nextkey = 9; /* skip required table keywords in the following search */
2900     }
2901     else
2902     {
2903 
2904         /*----------------------------------------------------------------*/
2905         /*  Get 2nd keyword;  test whether it is BITPIX with legal value  */
2906         /*----------------------------------------------------------------*/
2907         ffgkyn(fptr, 2, name, value, comm, status);  /* BITPIX = 2nd keyword */
2908 
2909         if (strcmp(name, "BITPIX"))
2910         {
2911             snprintf(message, FLEN_ERRMSG,
2912             "Second keyword of the extension is not BITPIX: %s", name);
2913             ffpmsg(message);
2914             return(*status = NO_BITPIX);
2915         }
2916 
2917         if (ffc2ii(value,  &longbitpix, status) > 0)
2918         {
2919             snprintf(message, FLEN_ERRMSG,
2920             "Value of BITPIX keyword is not an integer: %s", value);
2921             ffpmsg(message);
2922             return(*status = BAD_BITPIX);
2923         }
2924         else if (longbitpix != BYTE_IMG && longbitpix != SHORT_IMG &&
2925              longbitpix != LONG_IMG && longbitpix != LONGLONG_IMG &&
2926              longbitpix != FLOAT_IMG && longbitpix != DOUBLE_IMG)
2927         {
2928             snprintf(message, FLEN_ERRMSG,
2929             "Illegal value for BITPIX keyword: %s", value);
2930             ffpmsg(message);
2931             return(*status = BAD_BITPIX);
2932         }
2933         if (bitpix)
2934             *bitpix = longbitpix;  /* do explicit type conversion */
2935 
2936         /*---------------------------------------------------------------*/
2937         /*  Get 3rd keyword;  test whether it is NAXIS with legal value  */
2938         /*---------------------------------------------------------------*/
2939         ffgtkn(fptr, 3, "NAXIS",  &longnaxis, status);
2940 
2941         if (*status == BAD_ORDER)
2942             return(*status = NO_NAXIS);
2943         else if (*status == NOT_POS_INT || longnaxis > 999)
2944         {
2945             snprintf(message,FLEN_ERRMSG,"NAXIS = %ld is illegal", longnaxis);
2946             ffpmsg(message);
2947             return(*status = BAD_NAXIS);
2948         }
2949         else
2950             if (naxis)
2951                  *naxis = longnaxis;  /* do explicit type conversion */
2952 
2953         /*---------------------------------------------------------*/
2954         /*  Get the next NAXISn keywords and test for legal values */
2955         /*---------------------------------------------------------*/
2956         for (ii=0, nextkey=4; ii < longnaxis; ii++, nextkey++)
2957         {
2958             ffkeyn("NAXIS", ii+1, keyword, status);
2959             ffgtknjj(fptr, 4+ii, keyword, &axislen, status);
2960 
2961             if (*status == BAD_ORDER)
2962                 return(*status = NO_NAXES);
2963             else if (*status == NOT_POS_INT)
2964                 return(*status = BAD_NAXES);
2965             else if (ii < maxdim)
2966                 if (naxes)
2967                     naxes[ii] = axislen;
2968         }
2969     }
2970 
2971     /*---------------------------------------------------------*/
2972     /*  now look for other keywords of interest:               */
2973     /*  BSCALE, BZERO, BLANK, PCOUNT, GCOUNT, EXTEND, and END  */
2974     /*---------------------------------------------------------*/
2975 
2976     /*  initialize default values in case keyword is not present */
2977     if (bscale)
2978         *bscale = 1.0;
2979     if (bzero)
2980         *bzero  = 0.0;
2981     if (pcount)
2982         *pcount = 0;
2983     if (gcount)
2984         *gcount = 1;
2985     if (extend)
2986         *extend = 0;
2987     if (blank)
2988       *blank = NULL_UNDEFINED; /* no default null value for BITPIX=8,16,32 */
2989 
2990     *nspace = 0;
2991     found_end = 0;
2992     tstatus = *status;
2993 
2994     for (; !found_end; nextkey++)
2995     {
2996       /* get next keyword */
2997       /* don't use ffgkyn here because it trys to parse the card to read */
2998       /* the value string, thus failing to read the file just because of */
2999       /* minor syntax errors in optional keywords.                       */
3000 
3001       if (ffgrec(fptr, nextkey, card, status) > 0 )  /* get the 80-byte card */
3002       {
3003         if (*status == KEY_OUT_BOUNDS)
3004         {
3005           found_end = 1;  /* simply hit the end of the header */
3006           *status = tstatus;  /* reset error status */
3007         }
3008         else
3009         {
3010           ffpmsg("Failed to find the END keyword in header (ffgphd).");
3011         }
3012       }
3013       else /* got the next keyword without error */
3014       {
3015         ffgknm(card, name, &namelen, status); /* get the keyword name */
3016 
3017         if (fftrec(name, status) > 0)  /* test keyword name; catches no END */
3018         {
3019           snprintf(message, FLEN_ERRMSG,
3020               "Name of keyword no. %d contains illegal character(s): %s",
3021               nextkey, name);
3022           ffpmsg(message);
3023 
3024           if (nextkey % 36 == 0) /* test if at beginning of 36-card record */
3025             ffpmsg("  (This may indicate a missing END keyword).");
3026         }
3027 
3028         if (!strcmp(name, "BSCALE") && bscale)
3029         {
3030             *nspace = 0;  /* reset count of blank keywords */
3031             ffpsvc(card, value, comm, status); /* parse value and comment */
3032 
3033             if (ffc2dd(value, bscale, status) > 0) /* convert to double */
3034             {
3035                 /* reset error status and continue, but still issue warning */
3036                 *status = tstatus;
3037                 *bscale = 1.0;
3038 
3039                 snprintf(message, FLEN_ERRMSG,
3040                 "Error reading BSCALE keyword value as a double: %s", value);
3041                 ffpmsg(message);
3042             }
3043         }
3044 
3045         else if (!strcmp(name, "BZERO") && bzero)
3046         {
3047             *nspace = 0;  /* reset count of blank keywords */
3048             ffpsvc(card, value, comm, status); /* parse value and comment */
3049 
3050             if (ffc2dd(value, bzero, status) > 0) /* convert to double */
3051             {
3052                 /* reset error status and continue, but still issue warning */
3053                 *status = tstatus;
3054                 *bzero = 0.0;
3055 
3056                 snprintf(message, FLEN_ERRMSG,
3057                 "Error reading BZERO keyword value as a double: %s", value);
3058                 ffpmsg(message);
3059             }
3060         }
3061 
3062         else if (!strcmp(name, "BLANK") && blank)
3063         {
3064             *nspace = 0;  /* reset count of blank keywords */
3065             ffpsvc(card, value, comm, status); /* parse value and comment */
3066 
3067             if (ffc2jj(value, blank, status) > 0) /* convert to LONGLONG */
3068             {
3069                 /* reset error status and continue, but still issue warning */
3070                 *status = tstatus;
3071                 *blank = NULL_UNDEFINED;
3072 
3073                 snprintf(message, FLEN_ERRMSG,
3074                 "Error reading BLANK keyword value as an integer: %s", value);
3075                 ffpmsg(message);
3076             }
3077         }
3078 
3079         else if (!strcmp(name, "PCOUNT") && pcount)
3080         {
3081             *nspace = 0;  /* reset count of blank keywords */
3082             ffpsvc(card, value, comm, status); /* parse value and comment */
3083 
3084             if (ffc2ii(value, pcount, status) > 0) /* convert to long */
3085             {
3086                 snprintf(message, FLEN_ERRMSG,
3087                 "Error reading PCOUNT keyword value as an integer: %s", value);
3088                 ffpmsg(message);
3089             }
3090         }
3091 
3092         else if (!strcmp(name, "GCOUNT") && gcount)
3093         {
3094             *nspace = 0;  /* reset count of blank keywords */
3095             ffpsvc(card, value, comm, status); /* parse value and comment */
3096 
3097             if (ffc2ii(value, gcount, status) > 0) /* convert to long */
3098             {
3099                 snprintf(message, FLEN_ERRMSG,
3100                 "Error reading GCOUNT keyword value as an integer: %s", value);
3101                 ffpmsg(message);
3102             }
3103         }
3104 
3105         else if (!strcmp(name, "EXTEND") && extend)
3106         {
3107             *nspace = 0;  /* reset count of blank keywords */
3108             ffpsvc(card, value, comm, status); /* parse value and comment */
3109 
3110             if (ffc2ll(value, extend, status) > 0) /* convert to logical */
3111             {
3112                 /* reset error status and continue, but still issue warning */
3113                 *status = tstatus;
3114                 *extend = 0;
3115 
3116                 snprintf(message, FLEN_ERRMSG,
3117                 "Error reading EXTEND keyword value as a logical: %s", value);
3118                 ffpmsg(message);
3119             }
3120         }
3121 
3122         else if (!strcmp(name, "END"))
3123             found_end = 1;
3124 
3125         else if (!card[0] )
3126             *nspace = *nspace + 1;  /* this is a blank card in the header */
3127 
3128         else
3129             *nspace = 0;  /* reset count of blank keywords immediately
3130                             before the END keyword to zero   */
3131       }
3132 
3133       if (*status > 0)  /* exit on error after writing error message */
3134       {
3135         if ((fptr->Fptr)->curhdu == 0)
3136             ffpmsg(
3137             "Failed to read the required primary array header keywords.");
3138         else
3139             ffpmsg(
3140             "Failed to read the required image extension header keywords.");
3141 
3142         return(*status);
3143       }
3144     }
3145 
3146     if (unknown)
3147        *status = NOT_IMAGE;
3148 
3149     return(*status);
3150 }
3151 /*--------------------------------------------------------------------------*/
ffgttb(fitsfile * fptr,LONGLONG * rowlen,LONGLONG * nrows,LONGLONG * pcount,long * tfields,int * status)3152 int ffgttb(fitsfile *fptr,      /* I - FITS file pointer*/
3153            LONGLONG *rowlen,        /* O - length of a table row, in bytes */
3154            LONGLONG *nrows,         /* O - number of rows in the table */
3155            LONGLONG *pcount,    /* O - value of PCOUNT keyword */
3156            long *tfields,       /* O - number of fields in the table */
3157            int *status)         /* IO - error status    */
3158 {
3159 /*
3160   Get and Test TaBle;
3161   Test that this is a legal ASCII or binary table and get some keyword values.
3162   We assume that the calling routine has already tested the 1st keyword
3163   of the extension to ensure that this is really a table extension.
3164 */
3165     if (*status > 0)
3166         return(*status);
3167 
3168     if (fftkyn(fptr, 2, "BITPIX", "8", status) == BAD_ORDER) /* 2nd keyword */
3169         return(*status = NO_BITPIX);  /* keyword not BITPIX */
3170     else if (*status == NOT_POS_INT)
3171         return(*status = BAD_BITPIX); /* value != 8 */
3172 
3173     if (fftkyn(fptr, 3, "NAXIS", "2", status) == BAD_ORDER) /* 3rd keyword */
3174         return(*status = NO_NAXIS);  /* keyword not NAXIS */
3175     else if (*status == NOT_POS_INT)
3176         return(*status = BAD_NAXIS); /* value != 2 */
3177 
3178     if (ffgtknjj(fptr, 4, "NAXIS1", rowlen, status) == BAD_ORDER) /* 4th key */
3179         return(*status = NO_NAXES);  /* keyword not NAXIS1 */
3180     else if (*status == NOT_POS_INT)
3181         return(*status == BAD_NAXES); /* bad NAXIS1 value */
3182 
3183     if (ffgtknjj(fptr, 5, "NAXIS2", nrows, status) == BAD_ORDER) /* 5th key */
3184         return(*status = NO_NAXES);  /* keyword not NAXIS2 */
3185     else if (*status == NOT_POS_INT)
3186         return(*status == BAD_NAXES); /* bad NAXIS2 value */
3187 
3188     if (ffgtknjj(fptr, 6, "PCOUNT", pcount, status) == BAD_ORDER) /* 6th key */
3189         return(*status = NO_PCOUNT);  /* keyword not PCOUNT */
3190     else if (*status == NOT_POS_INT)
3191         return(*status = BAD_PCOUNT); /* bad PCOUNT value */
3192 
3193     if (fftkyn(fptr, 7, "GCOUNT", "1", status) == BAD_ORDER) /* 7th keyword */
3194         return(*status = NO_GCOUNT);  /* keyword not GCOUNT */
3195     else if (*status == NOT_POS_INT)
3196         return(*status = BAD_GCOUNT); /* value != 1 */
3197 
3198     if (ffgtkn(fptr, 8, "TFIELDS", tfields, status) == BAD_ORDER) /* 8th key*/
3199         return(*status = NO_TFIELDS);  /* keyword not TFIELDS */
3200     else if (*status == NOT_POS_INT || *tfields > 999)
3201         return(*status == BAD_TFIELDS); /* bad TFIELDS value */
3202 
3203 
3204     if (*status > 0)
3205        ffpmsg(
3206        "Error reading required keywords in the table header (FTGTTB).");
3207 
3208     return(*status);
3209 }
3210 /*--------------------------------------------------------------------------*/
ffgtkn(fitsfile * fptr,int numkey,char * name,long * value,int * status)3211 int ffgtkn(fitsfile *fptr,  /* I - FITS file pointer              */
3212            int numkey,      /* I - number of the keyword to read  */
3213            char *name,      /* I - expected name of the keyword   */
3214            long *value,     /* O - integer value of the keyword   */
3215            int *status)     /* IO - error status                  */
3216 {
3217 /*
3218   test that keyword number NUMKEY has the expected name and get the
3219   integer value of the keyword.  Return an error if the keyword
3220   name does not match the input name, or if the value of the
3221   keyword is not a positive integer.
3222 */
3223     char keyname[FLEN_KEYWORD], valuestring[FLEN_VALUE];
3224     char comm[FLEN_COMMENT], message[FLEN_ERRMSG];
3225 
3226     if (*status > 0)
3227         return(*status);
3228 
3229     keyname[0] = '\0';
3230     valuestring[0] = '\0';
3231 
3232     if (ffgkyn(fptr, numkey, keyname, valuestring, comm, status) <= 0)
3233     {
3234         if (strcmp(keyname, name) )
3235             *status = BAD_ORDER;  /* incorrect keyword name */
3236 
3237         else
3238         {
3239             ffc2ii(valuestring, value, status);  /* convert to integer */
3240 
3241             if (*status > 0 || *value < 0 )
3242                *status = NOT_POS_INT;
3243         }
3244 
3245         if (*status > 0)
3246         {
3247             snprintf(message, FLEN_ERRMSG,
3248               "ffgtkn found unexpected keyword or value for keyword no. %d.",
3249               numkey);
3250             ffpmsg(message);
3251 
3252             snprintf(message, FLEN_ERRMSG,
3253               " Expected positive integer keyword %s, but instead", name);
3254             ffpmsg(message);
3255 
3256             snprintf(message, FLEN_ERRMSG,
3257               " found keyword %s with value %s", keyname, valuestring);
3258             ffpmsg(message);
3259         }
3260     }
3261 
3262     return(*status);
3263 }
3264 /*--------------------------------------------------------------------------*/
ffgtknjj(fitsfile * fptr,int numkey,char * name,LONGLONG * value,int * status)3265 int ffgtknjj(fitsfile *fptr,  /* I - FITS file pointer              */
3266            int numkey,      /* I - number of the keyword to read  */
3267            char *name,      /* I - expected name of the keyword   */
3268            LONGLONG *value, /* O - integer value of the keyword   */
3269            int *status)     /* IO - error status                  */
3270 {
3271 /*
3272   test that keyword number NUMKEY has the expected name and get the
3273   integer value of the keyword.  Return an error if the keyword
3274   name does not match the input name, or if the value of the
3275   keyword is not a positive integer.
3276 */
3277     char keyname[FLEN_KEYWORD], valuestring[FLEN_VALUE];
3278     char comm[FLEN_COMMENT], message[FLEN_ERRMSG];
3279 
3280     if (*status > 0)
3281         return(*status);
3282 
3283     keyname[0] = '\0';
3284     valuestring[0] = '\0';
3285 
3286     if (ffgkyn(fptr, numkey, keyname, valuestring, comm, status) <= 0)
3287     {
3288         if (strcmp(keyname, name) )
3289             *status = BAD_ORDER;  /* incorrect keyword name */
3290 
3291         else
3292         {
3293             ffc2jj(valuestring, value, status);  /* convert to integer */
3294 
3295             if (*status > 0 || *value < 0 )
3296                *status = NOT_POS_INT;
3297         }
3298 
3299         if (*status > 0)
3300         {
3301             snprintf(message, FLEN_ERRMSG,
3302               "ffgtknjj found unexpected keyword or value for keyword no. %d.",
3303               numkey);
3304             ffpmsg(message);
3305 
3306             snprintf(message, FLEN_ERRMSG,
3307               " Expected positive integer keyword %s, but instead", name);
3308             ffpmsg(message);
3309 
3310             snprintf(message, FLEN_ERRMSG,
3311               " found keyword %s with value %s", keyname, valuestring);
3312             ffpmsg(message);
3313         }
3314     }
3315 
3316     return(*status);
3317 }
3318 /*--------------------------------------------------------------------------*/
fftkyn(fitsfile * fptr,int numkey,char * name,char * value,int * status)3319 int fftkyn(fitsfile *fptr,  /* I - FITS file pointer              */
3320            int numkey,      /* I - number of the keyword to read  */
3321            char *name,      /* I - expected name of the keyword   */
3322            char *value,     /* I - expected value of the keyword  */
3323            int *status)     /* IO - error status                  */
3324 {
3325 /*
3326   test that keyword number NUMKEY has the expected name and the
3327   expected value string.
3328 */
3329     char keyname[FLEN_KEYWORD], valuestring[FLEN_VALUE];
3330     char comm[FLEN_COMMENT], message[FLEN_ERRMSG];
3331 
3332     if (*status > 0)
3333         return(*status);
3334 
3335     keyname[0] = '\0';
3336     valuestring[0] = '\0';
3337 
3338     if (ffgkyn(fptr, numkey, keyname, valuestring, comm, status) <= 0)
3339     {
3340         if (strcmp(keyname, name) )
3341             *status = BAD_ORDER;  /* incorrect keyword name */
3342 
3343         if (strcmp(value, valuestring) )
3344             *status = NOT_POS_INT;  /* incorrect keyword value */
3345     }
3346 
3347     if (*status > 0)
3348     {
3349         snprintf(message, FLEN_ERRMSG,
3350           "fftkyn found unexpected keyword or value for keyword no. %d.",
3351           numkey);
3352         ffpmsg(message);
3353 
3354         snprintf(message, FLEN_ERRMSG,
3355           " Expected keyword %s with value %s, but", name, value);
3356         ffpmsg(message);
3357 
3358         snprintf(message, FLEN_ERRMSG,
3359           " found keyword %s with value %s", keyname, valuestring);
3360         ffpmsg(message);
3361     }
3362 
3363     return(*status);
3364 }
3365 /*--------------------------------------------------------------------------*/
ffh2st(fitsfile * fptr,char ** header,int * status)3366 int ffh2st(fitsfile *fptr,   /* I - FITS file pointer           */
3367            char **header,    /* O - returned header string      */
3368            int  *status)     /* IO - error status               */
3369 
3370 /*
3371   read header keywords into a long string of chars.  This routine allocates
3372   memory for the string, so the calling routine must eventually free the
3373   memory when it is not needed any more.
3374 */
3375 {
3376     int nkeys;
3377     long nrec;
3378     LONGLONG headstart;
3379 
3380     if (*status > 0)
3381         return(*status);
3382 
3383     /* get number of keywords in the header (doesn't include END) */
3384     if (ffghsp(fptr, &nkeys, NULL, status) > 0)
3385         return(*status);
3386 
3387     nrec = (nkeys / 36 + 1);
3388 
3389     /* allocate memory for all the keywords (multiple of 2880 bytes) */
3390     *header = (char *) calloc ( nrec * 2880 + 1, 1);
3391     if (!(*header))
3392     {
3393          *status = MEMORY_ALLOCATION;
3394          ffpmsg("failed to allocate memory to hold all the header keywords");
3395          return(*status);
3396     }
3397 
3398     ffghadll(fptr, &headstart, NULL, NULL, status); /* get header address */
3399     ffmbyt(fptr, headstart, REPORT_EOF, status);   /* move to header */
3400     ffgbyt(fptr, nrec * 2880, *header, status);     /* copy header */
3401     *(*header + (nrec * 2880)) = '\0';
3402 
3403     return(*status);
3404 }
3405 /*--------------------------------------------------------------------------*/
ffhdr2str(fitsfile * fptr,int exclude_comm,char ** exclist,int nexc,char ** header,int * nkeys,int * status)3406 int ffhdr2str( fitsfile *fptr,  /* I - FITS file pointer                    */
3407             int exclude_comm,   /* I - if TRUE, exclude commentary keywords */
3408             char **exclist,     /* I - list of excluded keyword names       */
3409             int nexc,           /* I - number of names in exclist           */
3410             char **header,      /* O - returned header string               */
3411             int *nkeys,         /* O - returned number of 80-char keywords  */
3412             int  *status)       /* IO - error status                        */
3413 /*
3414   read header keywords into a long string of chars.  This routine allocates
3415   memory for the string, so the calling routine must eventually free the
3416   memory when it is not needed any more.  If exclude_comm is TRUE, then all
3417   the COMMENT, HISTORY, and <blank> keywords will be excluded from the output
3418   string of keywords.  Any other list of keywords to be excluded may be
3419   specified with the exclist parameter.
3420 */
3421 {
3422     int casesn, match, exact, totkeys;
3423     long ii, jj;
3424     char keybuf[162], keyname[FLEN_KEYWORD], *headptr;
3425 
3426     *nkeys = 0;
3427 
3428     if (*status > 0)
3429         return(*status);
3430 
3431     /* get number of keywords in the header (doesn't include END) */
3432     if (ffghsp(fptr, &totkeys, NULL, status) > 0)
3433         return(*status);
3434 
3435     /* allocate memory for all the keywords */
3436     /* (will reallocate it later to minimize the memory size) */
3437 
3438     *header = (char *) calloc ( (totkeys + 1) * 80 + 1, 1);
3439     if (!(*header))
3440     {
3441          *status = MEMORY_ALLOCATION;
3442          ffpmsg("failed to allocate memory to hold all the header keywords");
3443          return(*status);
3444     }
3445 
3446     headptr = *header;
3447     casesn = FALSE;
3448 
3449     /* read every keyword */
3450     for (ii = 1; ii <= totkeys; ii++)
3451     {
3452         ffgrec(fptr, ii, keybuf, status);
3453         /* pad record with blanks so that it is at least 80 chars long */
3454         strcat(keybuf,
3455     "                                                                                ");
3456 
3457         keyname[0] = '\0';
3458         strncat(keyname, keybuf, 8); /* copy the keyword name */
3459 
3460         if (exclude_comm)
3461         {
3462             if (!FSTRCMP("COMMENT ", keyname) ||
3463                 !FSTRCMP("HISTORY ", keyname) ||
3464                 !FSTRCMP("        ", keyname) )
3465               continue;  /* skip this commentary keyword */
3466         }
3467 
3468         /* does keyword match any names in the exclusion list? */
3469         for (jj = 0; jj < nexc; jj++ )
3470         {
3471             ffcmps(exclist[jj], keyname, casesn, &match, &exact);
3472                  if (match)
3473                      break;
3474         }
3475 
3476         if (jj == nexc)
3477         {
3478             /* not in exclusion list, add this keyword to the string */
3479             strcpy(headptr, keybuf);
3480             headptr += 80;
3481             (*nkeys)++;
3482         }
3483     }
3484 
3485     /* add the END keyword */
3486     strcpy(headptr,
3487     "END                                                                             ");
3488     headptr += 80;
3489     (*nkeys)++;
3490 
3491     *headptr = '\0';   /* terminate the header string */
3492     /* minimize the allocated memory */
3493     *header = (char *) realloc(*header, (*nkeys *80) + 1);
3494 
3495     return(*status);
3496 }
3497 /*--------------------------------------------------------------------------*/
ffcnvthdr2str(fitsfile * fptr,int exclude_comm,char ** exclist,int nexc,char ** header,int * nkeys,int * status)3498 int ffcnvthdr2str( fitsfile *fptr,  /* I - FITS file pointer                    */
3499             int exclude_comm,   /* I - if TRUE, exclude commentary keywords */
3500             char **exclist,     /* I - list of excluded keyword names       */
3501             int nexc,           /* I - number of names in exclist           */
3502             char **header,      /* O - returned header string               */
3503             int *nkeys,         /* O - returned number of 80-char keywords  */
3504             int  *status)       /* IO - error status                        */
3505 /*
3506   Same as ffhdr2str, except that if the input HDU is a tile compressed image
3507   (stored in a binary table) then it will first convert that header back
3508   to that of a normal uncompressed FITS image before concatenating the header
3509   keyword records.
3510 */
3511 {
3512     fitsfile *tempfptr;
3513 
3514     if (*status > 0)
3515         return(*status);
3516 
3517     if (fits_is_compressed_image(fptr, status) )
3518     {
3519         /* this is a tile compressed image, so need to make an uncompressed */
3520 	/* copy of the image header in memory before concatenating the keywords */
3521         if (fits_create_file(&tempfptr, "mem://", status) > 0) {
3522 	    return(*status);
3523 	}
3524 
3525 	if (fits_img_decompress_header(fptr, tempfptr, status) > 0) {
3526 	    fits_delete_file(tempfptr, status);
3527 	    return(*status);
3528 	}
3529 
3530 	ffhdr2str(tempfptr, exclude_comm, exclist, nexc, header, nkeys, status);
3531 	fits_close_file(tempfptr, status);
3532 
3533     } else {
3534         ffhdr2str(fptr, exclude_comm, exclist, nexc, header, nkeys, status);
3535     }
3536 
3537     return(*status);
3538 }
3539