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