1 /*============================================================================
2   WCSLIB 7.7 - an implementation of the FITS WCS standard.
3   Copyright (C) 1995-2021, Mark Calabretta
4 
5   This file is part of WCSLIB.
6 
7   WCSLIB is free software: you can redistribute it and/or modify it under the
8   terms of the GNU Lesser General Public License as published by the Free
9   Software Foundation, either version 3 of the License, or (at your option)
10   any later version.
11 
12   WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
15   more details.
16 
17   You should have received a copy of the GNU Lesser General Public License
18   along with WCSLIB.  If not, see http://www.gnu.org/licenses.
19 
20   Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
21   http://www.atnf.csiro.au/people/Mark.Calabretta
22   $Id: fitshdr.h,v 7.7 2021/07/12 06:36:49 mcalabre Exp $
23 *=============================================================================
24 *
25 * WCSLIB 7.7 - C routines that implement the FITS World Coordinate System
26 * (WCS) standard.  Refer to the README file provided with WCSLIB for an
27 * overview of the library.
28 *
29 *
30 * Summary of the fitshdr routines
31 * -------------------------------
32 * The Flexible Image Transport System (FITS), is a data format widely used in
33 * astronomy for data interchange and archive.  It is described in
34 *
35 =   "Definition of the Flexible Image Transport System (FITS), version 3.0",
36 =   Pence, W.D., Chiappetti, L., Page, C.G., Shaw, R.A., & Stobie, E. 2010,
37 =   A&A, 524, A42 - http://dx.doi.org/10.1051/0004-6361/201015362
38 *
39 * See also http://fits.gsfc.nasa.gov
40 *
41 * fitshdr() is a generic FITS header parser provided to handle keyrecords that
42 * are ignored by the WCS header parsers, wcspih() and wcsbth().  Typically the
43 * latter may be set to remove WCS keyrecords from a header leaving fitshdr()
44 * to handle the remainder.
45 *
46 *
47 * fitshdr() - FITS header parser routine
48 * --------------------------------------
49 * fitshdr() parses a character array containing a FITS header, extracting
50 * all keywords and their values into an array of fitskey structs.
51 *
52 * Given:
53 *   header    const char []
54 *                       Character array containing the (entire) FITS header,
55 *                       for example, as might be obtained conveniently via the
56 *                       CFITSIO routine fits_hdr2str().
57 *
58 *                       Each header "keyrecord" (formerly "card image")
59 *                       consists of exactly 80 7-bit ASCII printing characters
60 *                       in the range 0x20 to 0x7e (which excludes NUL, BS,
61 *                       TAB, LF, FF and CR) especially noting that the
62 *                       keyrecords are NOT null-terminated.
63 *
64 *   nkeyrec   int       Number of keyrecords in header[].
65 *
66 *   nkeyids   int       Number of entries in keyids[].
67 *
68 * Given and returned:
69 *   keyids    struct fitskeyid []
70 *                       While all keywords are extracted from the header,
71 *                       keyids[] provides a convienient way of indexing them.
72 *                       The fitskeyid struct contains three members;
73 *                       fitskeyid::name must be set by the user while
74 *                       fitskeyid::count and fitskeyid::idx are returned by
75 *                       fitshdr().  All matched keywords will have their
76 *                       fitskey::keyno member negated.
77 *
78 * Returned:
79 *   nreject   int*      Number of header keyrecords rejected for syntax
80 *                       errors.
81 *
82 *   keys      struct fitskey**
83 *                       Pointer to an array of nkeyrec fitskey structs
84 *                       containing all keywords and keyvalues extracted from
85 *                       the header.
86 *
87 *                       Memory for the array is allocated by fitshdr() and
88 *                       this must be freed by the user.  See wcsdealloc().
89 *
90 * Function return value:
91 *             int       Status return value:
92 *                         0: Success.
93 *                         1: Null fitskey pointer passed.
94 *                         2: Memory allocation failed.
95 *                         3: Fatal error returned by Flex parser.
96 *                         4: Unrecognised data type.
97 *
98 * Notes:
99 *   1: Keyword parsing is done in accordance with the syntax defined by
100 *      NOST 100-2.0, noting the following points in particular:
101 *
102 *      a: Sect. 5.1.2.1 specifies that keywords be left-justified in columns
103 *         1-8, blank-filled with no embedded spaces, composed only of the
104 *         ASCII characters ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789-_
105 *
106 *         fitshdr() accepts any characters in columns 1-8 but flags keywords
107 *         that do not conform to standard syntax.
108 *
109 *      b: Sect. 5.1.2.2 defines the "value indicator" as the characters "= "
110 *         occurring in columns 9 and 10.  If these are absent then the
111 *         keyword has no value and columns 9-80 may contain any ASCII text
112 *         (but see note 2 for CONTINUE keyrecords).  This is copied to the
113 *         comment member of the fitskey struct.
114 *
115 *      c: Sect. 5.1.2.3 states that a keyword may have a null (undefined)
116 *         value if the value/comment field, columns 11-80, consists entirely
117 *         of spaces, possibly followed by a comment.
118 *
119 *      d: Sect. 5.1.1 states that trailing blanks in a string keyvalue are
120 *         not significant and the parser always removes them.  A string
121 *         containing nothing but blanks will be replaced with a single
122 *         blank.
123 *
124 *         Sect. 5.2.1 also states that a quote character (') in a string
125 *         value is to be represented by two successive quote characters and
126 *         the parser removes the repeated quote.
127 *
128 *      e: The parser recognizes free-format character (NOST 100-2.0,
129 *         Sect. 5.2.1), integer (Sect. 5.2.3), and floating-point values
130 *         (Sect. 5.2.4) for all keywords.
131 *
132 *      f: Sect. 5.2.3 offers no comment on the size of an integer keyvalue
133 *         except indirectly in limiting it to 70 digits.  The parser will
134 *         translate an integer keyvalue to a 32-bit signed integer if it
135 *         lies in the range -2147483648 to +2147483647, otherwise it
136 *         interprets it as a 64-bit signed integer if possible, or else a
137 *         "very long" integer (see fitskey::type).
138 *
139 *      g: END not followed by 77 blanks is not considered to be a legitimate
140 *         end keyrecord.
141 *
142 *   2: The parser supports a generalization of the OGIP Long String Keyvalue
143 *      Convention (v1.0) whereby strings may be continued onto successive
144 *      header keyrecords.  A keyrecord contains a segment of a continued
145 *      string if and only if
146 *
147 *      a: it contains the pseudo-keyword CONTINUE,
148 *
149 *      b: columns 9 and 10 are both blank,
150 *
151 *      c: columns 11 to 80 contain what would be considered a valid string
152 *         keyvalue, including optional keycomment, if column 9 had contained
153 *         '=',
154 *
155 *      d: the previous keyrecord contained either a valid string keyvalue or
156 *         a valid CONTINUE keyrecord.
157 *
158 *      If any of these conditions is violated, the keyrecord is considered in
159 *      isolation.
160 *
161 *      Syntax errors in keycomments in a continued string are treated more
162 *      permissively than usual; the '/' delimiter may be omitted provided that
163 *      parsing of the string keyvalue is not compromised.  However, the
164 *      FITSHDR_COMMENT status bit will be set for the keyrecord (see
165 *      fitskey::status).
166 *
167 *      As for normal strings, trailing blanks in a continued string are not
168 *      significant.
169 *
170 *      In the OGIP convention "the '&' character is used as the last non-blank
171 *      character of the string to indicate that the string is (probably)
172 *      continued on the following keyword".  This additional syntax is not
173 *      required by fitshdr(), but if '&' does occur as the last non-blank
174 *      character of a continued string keyvalue then it will be removed, along
175 *      with any trailing blanks.  However, blanks that occur before the '&'
176 *      will be preserved.
177 *
178 *
179 * fitskeyid struct - Keyword indexing
180 * -----------------------------------
181 * fitshdr() uses the fitskeyid struct to return indexing information for
182 * specified keywords.  The struct contains three members, the first of which,
183 * fitskeyid::name, must be set by the user with the remainder returned by
184 * fitshdr().
185 *
186 *   char name[12]:
187 *     (Given) Name of the required keyword.  This is to be set by the user;
188 *     the '.' character may be used for wildcarding.  Trailing blanks will be
189 *     replaced with nulls.
190 *
191 *   int count:
192 *     (Returned) The number of matches found for the keyword.
193 *
194 *   int idx[2]:
195 *     (Returned) Indices into keys[], the array of fitskey structs returned by
196 *     fitshdr().  Note that these are 0-relative array indices, not keyrecord
197 *     numbers.
198 *
199 *     If the keyword is found in the header the first index will be set to the
200 *     array index of its first occurrence, otherwise it will be set to -1.
201 *
202 *     If multiples of the keyword are found, the second index will be set to
203 *     the array index of its last occurrence, otherwise it will be set to -1.
204 *
205 *
206 * fitskey struct - Keyword/value information
207 * ------------------------------------------
208 * fitshdr() returns an array of fitskey structs, each of which contains the
209 * result of parsing one FITS header keyrecord.  All members of the fitskey
210 * struct are returned by fitshdr(), none are given by the user.
211 *
212 *   int keyno
213 *     (Returned) Keyrecord number (1-relative) in the array passed as input to
214 *     fitshdr().  This will be negated if the keyword matched any specified in
215 *     the keyids[] index.
216 *
217 *   int keyid
218 *     (Returned) Index into the first entry in keyids[] with which the
219 *     keyrecord matches, else -1.
220 *
221 *   int status
222 *     (Returned) Status flag bit-vector for the header keyrecord employing the
223 *     following bit masks defined as preprocessor macros:
224 *
225 *       - FITSHDR_KEYWORD:    Illegal keyword syntax.
226 *       - FITSHDR_KEYVALUE:   Illegal keyvalue syntax.
227 *       - FITSHDR_COMMENT:    Illegal keycomment syntax.
228 *       - FITSHDR_KEYREC:     Illegal keyrecord, e.g. an END keyrecord with
229 *                             trailing text.
230 *       - FITSHDR_TRAILER:    Keyrecord following a valid END keyrecord.
231 *
232 *     The header keyrecord is syntactically correct if no bits are set.
233 *
234 *   char keyword[12]
235 *     (Returned) Keyword name, null-filled for keywords of less than eight
236 *     characters (trailing blanks replaced by nulls).
237 *
238 *     Use
239 *
240 =       sprintf(dst, "%.8s", keyword)
241 *
242 *     to copy it to a character array with null-termination, or
243 *
244 =       sprintf(dst, "%8.8s", keyword)
245 *
246 *     to blank-fill to eight characters followed by null-termination.
247 *
248 *   int type
249 *     (Returned) Keyvalue data type:
250 *       - 0: No keyvalue (both the value and type are undefined).
251 *       - 1: Logical, represented as int.
252 *       - 2: 32-bit signed integer.
253 *       - 3: 64-bit signed integer (see below).
254 *       - 4: Very long integer (see below).
255 *       - 5: Floating point (stored as double).
256 *       - 6: Integer complex (stored as double[2]).
257 *       - 7: Floating point complex (stored as double[2]).
258 *       - 8: String.
259 *       - 8+10*n: Continued string (described below and in fitshdr() note 2).
260 *
261 *     A negative type indicates that a syntax error was encountered when
262 *     attempting to parse a keyvalue of the particular type.
263 *
264 *     Comments on particular data types:
265 *       - 64-bit signed integers lie in the range
266 *
267 =           (-9223372036854775808 <= int64 <  -2147483648) ||
268 =                    (+2147483647 <  int64 <= +9223372036854775807)
269 *
270 *         A native 64-bit data type may be defined via preprocessor macro
271 *         WCSLIB_INT64 defined in wcsconfig.h, e.g. as 'long long int'; this
272 *         will be typedef'd to 'int64' here.  If WCSLIB_INT64 is not set, then
273 *         int64 is typedef'd to int[3] instead and fitskey::keyvalue is to be
274 *         computed as
275 *
276 =           ((keyvalue.k[2]) * 1000000000 +
277 =             keyvalue.k[1]) * 1000000000 +
278 =             keyvalue.k[0]
279 *
280 *         and may reported via
281 *
282 =            if (keyvalue.k[2]) {
283 =              printf("%d%09d%09d", keyvalue.k[2], abs(keyvalue.k[1]),
284 =                                   abs(keyvalue.k[0]));
285 =            } else {
286 =              printf("%d%09d", keyvalue.k[1], abs(keyvalue.k[0]));
287 =            }
288 *
289 *         where keyvalue.k[0] and keyvalue.k[1] range from -999999999 to
290 *         +999999999.
291 *
292 *       - Very long integers, up to 70 decimal digits in length, are encoded
293 *         in keyvalue.l as an array of int[8], each of which stores 9 decimal
294 *         digits.  fitskey::keyvalue is to be computed as
295 *
296 =           (((((((keyvalue.l[7]) * 1000000000 +
297 =                  keyvalue.l[6]) * 1000000000 +
298 =                  keyvalue.l[5]) * 1000000000 +
299 =                  keyvalue.l[4]) * 1000000000 +
300 =                  keyvalue.l[3]) * 1000000000 +
301 =                  keyvalue.l[2]) * 1000000000 +
302 =                  keyvalue.l[1]) * 1000000000 +
303 =                  keyvalue.l[0]
304 *
305 *       - Continued strings are not reconstructed, they remain split over
306 *         successive fitskey structs in the keys[] array returned by
307 *         fitshdr().  fitskey::keyvalue data type, 8 + 10n, indicates the
308 *         segment number, n, in the continuation.
309 *
310 *   int padding
311 *     (An unused variable inserted for alignment purposes only.)
312 *
313 *   union keyvalue
314 *     (Returned) A union comprised of
315 *
316 *       - fitskey::i,
317 *       - fitskey::k,
318 *       - fitskey::l,
319 *       - fitskey::f,
320 *       - fitskey::c,
321 *       - fitskey::s,
322 *
323 *     used by the fitskey struct to contain the value associated with a
324 *     keyword.
325 *
326 *   int i
327 *     (Returned) Logical (fitskey::type == 1) and 32-bit signed integer
328 *     (fitskey::type == 2) data types in the fitskey::keyvalue union.
329 *
330 *   int64 k
331 *     (Returned) 64-bit signed integer (fitskey::type == 3) data type in the
332 *     fitskey::keyvalue union.
333 *
334 *   int l[8]
335 *     (Returned) Very long integer (fitskey::type == 4) data type in the
336 *     fitskey::keyvalue union.
337 *
338 *   double f
339 *     (Returned) Floating point (fitskey::type == 5) data type in the
340 *     fitskey::keyvalue union.
341 *
342 *   double c[2]
343 *     (Returned) Integer and floating point complex (fitskey::type == 6 || 7)
344 *     data types in the fitskey::keyvalue union.
345 *
346 *   char s[72]
347 *     (Returned) Null-terminated string (fitskey::type == 8) data type in the
348 *     fitskey::keyvalue union.
349 *
350 *   int ulen
351 *     (Returned) Where a keycomment contains a units string in the standard
352 *     form, e.g. [m/s], the ulen member indicates its length, inclusive of
353 *     square brackets.  Otherwise ulen is zero.
354 *
355 *   char comment[84]
356 *     (Returned) Keycomment, i.e. comment associated with the keyword or, for
357 *     keyrecords rejected because of syntax errors, the compete keyrecord
358 *     itself with null-termination.
359 *
360 *     Comments are null-terminated with trailing spaces removed.  Leading
361 *     spaces are also removed from keycomments (i.e. those immediately
362 *     following the '/' character), but not from COMMENT or HISTORY keyrecords
363 *     or keyrecords without a value indicator ("= " in columns 9-80).
364 *
365 *
366 * Global variable: const char *fitshdr_errmsg[] - Status return messages
367 * ----------------------------------------------------------------------
368 * Error messages to match the status value returned from each function.
369 *
370 *===========================================================================*/
371 
372 #ifndef WCSLIB_FITSHDR
373 #define WCSLIB_FITSHDR
374 
375 #include "wcsconfig.h"
376 
377 #ifdef __cplusplus
378 extern "C" {
379 #endif
380 
381 #define FITSHDR_KEYWORD  0x01
382 #define FITSHDR_KEYVALUE 0x02
383 #define FITSHDR_COMMENT  0x04
384 #define FITSHDR_KEYREC   0x08
385 #define FITSHDR_CARD     0x08	// Alias for backwards compatibility.
386 #define FITSHDR_TRAILER  0x10
387 
388 
389 extern const char *fitshdr_errmsg[];
390 
391 enum fitshdr_errmsg_enum {
392   FITSHDRERR_SUCCESS      = 0,	// Success.
393   FITSHDRERR_NULL_POINTER = 1,	// Null fitskey pointer passed.
394   FITSHDRERR_MEMORY       = 2,	// Memory allocation failed.
395   FITSHDRERR_FLEX_PARSER  = 3,	// Fatal error returned by Flex parser.
396   FITSHDRERR_DATA_TYPE    = 4 	// Unrecognised data type.
397 };
398 
399 #ifdef WCSLIB_INT64
400   typedef WCSLIB_INT64 int64;
401 #else
402   typedef int int64[3];
403 #endif
404 
405 
406 // Struct used for indexing the keywords.
407 struct fitskeyid {
408   char name[12];		// Keyword name, null-terminated.
409   int  count;			// Number of occurrences of keyword.
410   int  idx[2];			// Indices into fitskey array.
411 };
412 
413 // Size of the fitskeyid struct in int units, used by the Fortran wrappers.
414 #define KEYIDLEN (sizeof(struct fitskeyid)/sizeof(int))
415 
416 
417 // Struct used for storing FITS keywords.
418 struct fitskey {
419   int  keyno;			// Header keyrecord sequence number (1-rel).
420   int  keyid;			// Index into fitskeyid[].
421   int  status;			// Header keyrecord status bit flags.
422   char keyword[12];		// Keyword name, null-filled.
423   int  type;			// Keyvalue type (see above).
424   int  padding;			// (Dummy inserted for alignment purposes.)
425   union {
426     int    i;			// 32-bit integer and logical values.
427     int64  k;			// 64-bit integer values.
428     int    l[8];		// Very long signed integer values.
429     double f;			// Floating point values.
430     double c[2];		// Complex values.
431     char   s[72];		// String values, null-terminated.
432   } keyvalue;			// Keyvalue.
433   int  ulen;			// Length of units string.
434   char comment[84];		// Comment (or keyrecord), null-terminated.
435 };
436 
437 // Size of the fitskey struct in int units, used by the Fortran wrappers.
438 #define KEYLEN (sizeof(struct fitskey)/sizeof(int))
439 
440 
441 int fitshdr(const char header[], int nkeyrec, int nkeyids,
442             struct fitskeyid keyids[], int *nreject, struct fitskey **keys);
443 
444 
445 #ifdef __cplusplus
446 }
447 #endif
448 
449 #endif // WCSLIB_FITSHDR
450