1 /* T E M P L A T E P A R S E R
2 =============================
3
4 by Jerzy.Borkowski@obs.unige.ch
5
6 Integral Science Data Center
7 ch. d'Ecogia 16
8 1290 Versoix
9 Switzerland
10
11 14-Oct-98: initial release
12 16-Oct-98: code cleanup, #include <string.h> included, now gcc -Wall prints no
13 warnings during compilation. Bugfix: now one can specify additional
14 columns in group HDU. Autoindexing also works in this situation
15 (colunms are number from 7 however).
16 17-Oct-98: bugfix: complex keywords were incorrectly written (was TCOMPLEX should
17 be TDBLCOMPLEX).
18 20-Oct-98: bugfix: parser was writing EXTNAME twice, when first HDU in template is
19 defined with XTENSION IMAGE then parser creates now dummy PHDU,
20 SIMPLE T is now allowed only at most once and in first HDU only.
21 WARNING: one should not define EXTNAME keyword for GROUP HDUs, as
22 they have them already defined by parser (EXTNAME = GROUPING).
23 Parser accepts EXTNAME oin GROUP HDU definition, but in this
24 case multiple EXTNAME keywords will present in HDU header.
25 23-Oct-98: bugfix: unnecessary space was written to FITS file for blank
26 keywords.
27 24-Oct-98: syntax change: empty lines and lines with only whitespaces are
28 written to FITS files as blank keywords (if inside group/hdu
29 definition). Previously lines had to have at least 8 spaces.
30 Please note, that due to pecularities of CFITSIO if the
31 last keyword(s) defined for given HDU are blank keywords
32 consisting of only 80 spaces, then (some of) those keywords
33 may be silently deleted by CFITSIO.
34 13-Nov-98: bugfix: parser was writing GRPNAME twice. Parser still creates
35 GRPNAME keywords for GROUP HDU's which do not specify them.
36 However, values (of form DEFAULT_GROUP_XXX) are assigned
37 not necessarily in order HDUs appear in template file, but
38 rather in order parser completes their creation in FITS
39 file. Also, when including files, if fopen fails, parser
40 tries to open file with a name = directory_of_top_level
41 file + name of file to be included, as long as name
42 of file to be included does not specify absolute pathname.
43 16-Nov-98: bugfix to bugfix from 13-Nov-98
44 19-Nov-98: EXTVER keyword is now automatically assigned value by parser.
45 17-Dev-98: 2 new things added: 1st: CFITSIO_INCLUDE_FILES environment
46 variable can contain a colon separated list of directories
47 to look for when looking for template include files (and master
48 template also). 2nd: it is now possible to append template
49 to nonempty FITS. file. fitsfile *ff no longer needs to point
50 to an empty FITS file with 0 HDUs in it. All data written by
51 parser will simple be appended at the end of file.
52 22-Jan-99: changes to parser: when in append mode parser initially scans all
53 existing HDUs to built a list of already used EXTNAME/EXTVERs
54 22-Jan-99: Bruce O'Neel, bugfix : TLONG should always reference long type
55 variable on OSF/Alpha and on 64-bit archs in general
56 20-Jun-2002 Wm Pence, added support for the HIERARCH keyword convention in
57 which keyword names can effectively be longer than 8 characters.
58 Example:
59 HIERARCH LongKeywordName = 'value' / comment
60 30-Jan-2003 Wm Pence, bugfix: ngp_read_xtension was testing for "ASCIITABLE"
61 instead of "TABLE" as the XTENSION value of an ASCII table,
62 and it did not allow for optional trailing spaces in the
63 "IMAGE" or "TABLE" string.
64 16-Dec-2003 James Peachey: ngp_keyword_all_write was modified to apply
65 comments from the template file to the output file in
66 the case of reserved keywords (e.g. tform#, ttype# etcetera).
67 */
68
69
70 #include <stdio.h>
71 #include <stdlib.h>
72
73 #ifdef sparc
74 #include <malloc.h>
75 #include <memory.h>
76 #endif
77
78 #include <string.h>
79 #include "fitsio2.h"
80 #include "grparser.h"
81
82 NGP_RAW_LINE ngp_curline = { NULL, NULL, NULL, NGP_TTYPE_UNKNOWN, NULL, NGP_FORMAT_OK, 0 };
83 NGP_RAW_LINE ngp_prevline = { NULL, NULL, NULL, NGP_TTYPE_UNKNOWN, NULL, NGP_FORMAT_OK, 0 };
84
85 int ngp_inclevel = 0; /* number of included files, 1 - means mean file */
86 int ngp_grplevel = 0; /* group nesting level, 0 - means no grouping */
87
88 FILE *ngp_fp[NGP_MAX_INCLUDE]; /* stack of included file handles */
89 int ngp_keyidx = NGP_TOKEN_UNKNOWN; /* index of token in current line */
90 NGP_TOKEN ngp_linkey; /* keyword after line analyze */
91
92 char ngp_master_dir[NGP_MAX_FNAME]; /* directory of top level include file */
93
94 NGP_TKDEF ngp_tkdef[] = /* tokens recognized by parser */
95 { { "\\INCLUDE", NGP_TOKEN_INCLUDE },
96 { "\\GROUP", NGP_TOKEN_GROUP },
97 { "\\END", NGP_TOKEN_END },
98 { "XTENSION", NGP_TOKEN_XTENSION },
99 { "SIMPLE", NGP_TOKEN_SIMPLE },
100 { NULL, NGP_TOKEN_UNKNOWN }
101 };
102
103 int master_grp_idx = 1; /* current unnamed group in object */
104
105 int ngp_extver_tab_size = 0;
106 NGP_EXTVER_TAB *ngp_extver_tab = NULL;
107
108
ngp_get_extver(char * extname,int * version)109 int ngp_get_extver(char *extname, int *version)
110 { NGP_EXTVER_TAB *p;
111 char *p2;
112 int i;
113
114 if ((NULL == extname) || (NULL == version)) return(NGP_BAD_ARG);
115 if ((NULL == ngp_extver_tab) && (ngp_extver_tab_size > 0)) return(NGP_BAD_ARG);
116 if ((NULL != ngp_extver_tab) && (ngp_extver_tab_size <= 0)) return(NGP_BAD_ARG);
117
118 for (i=0; i<ngp_extver_tab_size; i++)
119 { if (0 == strcmp(extname, ngp_extver_tab[i].extname))
120 { *version = (++ngp_extver_tab[i].version);
121 return(NGP_OK);
122 }
123 }
124
125 if (NULL == ngp_extver_tab)
126 { p = (NGP_EXTVER_TAB *)ngp_alloc(sizeof(NGP_EXTVER_TAB)); }
127 else
128 { p = (NGP_EXTVER_TAB *)ngp_realloc(ngp_extver_tab, (ngp_extver_tab_size + 1) * sizeof(NGP_EXTVER_TAB)); }
129
130 if (NULL == p) return(NGP_NO_MEMORY);
131
132 p2 = ngp_alloc(strlen(extname) + 1);
133 if (NULL == p2)
134 { ngp_free(p);
135 return(NGP_NO_MEMORY);
136 }
137
138 strcpy(p2, extname);
139 ngp_extver_tab = p;
140 ngp_extver_tab[ngp_extver_tab_size].extname = p2;
141 *version = ngp_extver_tab[ngp_extver_tab_size].version = 1;
142
143 ngp_extver_tab_size++;
144
145 return(NGP_OK);
146 }
147
ngp_set_extver(char * extname,int version)148 int ngp_set_extver(char *extname, int version)
149 { NGP_EXTVER_TAB *p;
150 char *p2;
151 int i;
152
153 if (NULL == extname) return(NGP_BAD_ARG);
154 if ((NULL == ngp_extver_tab) && (ngp_extver_tab_size > 0)) return(NGP_BAD_ARG);
155 if ((NULL != ngp_extver_tab) && (ngp_extver_tab_size <= 0)) return(NGP_BAD_ARG);
156
157 for (i=0; i<ngp_extver_tab_size; i++)
158 { if (0 == strcmp(extname, ngp_extver_tab[i].extname))
159 { if (version > ngp_extver_tab[i].version) ngp_extver_tab[i].version = version;
160 return(NGP_OK);
161 }
162 }
163
164 if (NULL == ngp_extver_tab)
165 { p = (NGP_EXTVER_TAB *)ngp_alloc(sizeof(NGP_EXTVER_TAB)); }
166 else
167 { p = (NGP_EXTVER_TAB *)ngp_realloc(ngp_extver_tab, (ngp_extver_tab_size + 1) * sizeof(NGP_EXTVER_TAB)); }
168
169 if (NULL == p) return(NGP_NO_MEMORY);
170
171 p2 = ngp_alloc(strlen(extname) + 1);
172 if (NULL == p2)
173 { ngp_free(p);
174 return(NGP_NO_MEMORY);
175 }
176
177 strcpy(p2, extname);
178 ngp_extver_tab = p;
179 ngp_extver_tab[ngp_extver_tab_size].extname = p2;
180 ngp_extver_tab[ngp_extver_tab_size].version = version;
181
182 ngp_extver_tab_size++;
183
184 return(NGP_OK);
185 }
186
187
ngp_delete_extver_tab(void)188 int ngp_delete_extver_tab(void)
189 { int i;
190
191 if ((NULL == ngp_extver_tab) && (ngp_extver_tab_size > 0)) return(NGP_BAD_ARG);
192 if ((NULL != ngp_extver_tab) && (ngp_extver_tab_size <= 0)) return(NGP_BAD_ARG);
193 if ((NULL == ngp_extver_tab) && (0 == ngp_extver_tab_size)) return(NGP_OK);
194
195 for (i=0; i<ngp_extver_tab_size; i++)
196 { if (NULL != ngp_extver_tab[i].extname)
197 { ngp_free(ngp_extver_tab[i].extname);
198 ngp_extver_tab[i].extname = NULL;
199 }
200 ngp_extver_tab[i].version = 0;
201 }
202 ngp_free(ngp_extver_tab);
203 ngp_extver_tab = NULL;
204 ngp_extver_tab_size = 0;
205 return(NGP_OK);
206 }
207
208 /* read one line from file */
209
ngp_line_from_file(FILE * fp,char ** p)210 int ngp_line_from_file(FILE *fp, char **p)
211 { int c, r, llen, allocsize, alen;
212 char *p2;
213
214 if (NULL == fp) return(NGP_NUL_PTR); /* check for stupid args */
215 if (NULL == p) return(NGP_NUL_PTR); /* more foolproof checks */
216
217 r = NGP_OK; /* initialize stuff, reset err code */
218 llen = 0; /* 0 characters read so far */
219 *p = (char *)ngp_alloc(1); /* preallocate 1 byte */
220 allocsize = 1; /* signal that we have allocated 1 byte */
221 if (NULL == *p) return(NGP_NO_MEMORY); /* if this failed, system is in dire straits */
222
223 for (;;)
224 { c = getc(fp); /* get next character */
225 if ('\r' == c) continue; /* carriage return character ? Just ignore it */
226 if (EOF == c) /* EOF signalled ? */
227 {
228 if (ferror(fp)) r = NGP_READ_ERR; /* was it real error or simply EOF ? */
229 if (0 == llen) return(NGP_EOF); /* signal EOF only if 0 characters read so far */
230 break;
231 }
232 if ('\n' == c) break; /* end of line character ? */
233
234 llen++; /* we have new character, make room for it */
235 alen = ((llen + NGP_ALLOCCHUNK) / NGP_ALLOCCHUNK) * NGP_ALLOCCHUNK;
236 if (alen > allocsize)
237 { p2 = (char *)ngp_realloc(*p, alen); /* realloc buffer, if there is need */
238 if (NULL == p2)
239 { r = NGP_NO_MEMORY;
240 break;
241 }
242 *p = p2;
243 allocsize = alen;
244 }
245 (*p)[llen - 1] = c; /* copy character to buffer */
246 }
247
248 llen++; /* place for terminating \0 */
249 if (llen != allocsize)
250 { p2 = (char *)ngp_realloc(*p, llen);
251 if (NULL == p2) r = NGP_NO_MEMORY;
252 else
253 { *p = p2;
254 (*p)[llen - 1] = 0; /* copy \0 to buffer */
255 }
256 }
257 else
258 { (*p)[llen - 1] = 0; /* necessary when line read was empty */
259 }
260
261 if ((NGP_EOF != r) && (NGP_OK != r)) /* in case of errors free resources */
262 { ngp_free(*p);
263 *p = NULL;
264 }
265
266 return(r); /* return status code */
267 }
268
269 /* free current line structure */
270
ngp_free_line(void)271 int ngp_free_line(void)
272 {
273 if (NULL != ngp_curline.line)
274 { ngp_free(ngp_curline.line);
275 ngp_curline.line = NULL;
276 ngp_curline.name = NULL;
277 ngp_curline.value = NULL;
278 ngp_curline.comment = NULL;
279 ngp_curline.type = NGP_TTYPE_UNKNOWN;
280 ngp_curline.format = NGP_FORMAT_OK;
281 ngp_curline.flags = 0;
282 }
283 return(NGP_OK);
284 }
285
286 /* free cached line structure */
287
ngp_free_prevline(void)288 int ngp_free_prevline(void)
289 {
290 if (NULL != ngp_prevline.line)
291 { ngp_free(ngp_prevline.line);
292 ngp_prevline.line = NULL;
293 ngp_prevline.name = NULL;
294 ngp_prevline.value = NULL;
295 ngp_prevline.comment = NULL;
296 ngp_prevline.type = NGP_TTYPE_UNKNOWN;
297 ngp_prevline.format = NGP_FORMAT_OK;
298 ngp_prevline.flags = 0;
299 }
300 return(NGP_OK);
301 }
302
303 /* read one line */
304
ngp_read_line_buffered(FILE * fp)305 int ngp_read_line_buffered(FILE *fp)
306 {
307 ngp_free_line(); /* first free current line (if any) */
308
309 if (NULL != ngp_prevline.line) /* if cached, return cached line */
310 { ngp_curline = ngp_prevline;
311 ngp_prevline.line = NULL;
312 ngp_prevline.name = NULL;
313 ngp_prevline.value = NULL;
314 ngp_prevline.comment = NULL;
315 ngp_prevline.type = NGP_TTYPE_UNKNOWN;
316 ngp_prevline.format = NGP_FORMAT_OK;
317 ngp_prevline.flags = 0;
318 ngp_curline.flags = NGP_LINE_REREAD;
319 return(NGP_OK);
320 }
321
322 ngp_curline.flags = 0; /* if not cached really read line from file */
323 return(ngp_line_from_file(fp, &(ngp_curline.line)));
324 }
325
326 /* unread line */
327
ngp_unread_line(void)328 int ngp_unread_line(void)
329 {
330 if (NULL == ngp_curline.line) /* nothing to unread */
331 return(NGP_EMPTY_CURLINE);
332
333 if (NULL != ngp_prevline.line) /* we cannot unread line twice */
334 return(NGP_UNREAD_QUEUE_FULL);
335
336 ngp_prevline = ngp_curline;
337 ngp_curline.line = NULL;
338 return(NGP_OK);
339 }
340
341 /* a first guess line decomposition */
342
ngp_extract_tokens(NGP_RAW_LINE * cl)343 int ngp_extract_tokens(NGP_RAW_LINE *cl)
344 { char *p, *s;
345 int cl_flags, i;
346
347 p = cl->line; /* start from beginning of line */
348 if (NULL == p) return(NGP_NUL_PTR);
349
350 cl->name = cl->value = cl->comment = NULL;
351 cl->type = NGP_TTYPE_UNKNOWN;
352 cl->format = NGP_FORMAT_OK;
353
354 cl_flags = 0;
355
356 for (i=0;; i++) /* if 8 spaces at beginning then line is comment */
357 { if ((0 == *p) || ('\n' == *p))
358 { /* if line has only blanks -> write blank keyword */
359 cl->line[0] = 0; /* create empty name (0 length string) */
360 cl->comment = cl->name = cl->line;
361 cl->type = NGP_TTYPE_RAW; /* signal write unformatted to FITS file */
362 return(NGP_OK);
363 }
364 if ((' ' != *p) && ('\t' != *p)) break;
365 if (i >= 7)
366 {
367 cl->comment = p + 1;
368 for (s = cl->comment;; s++) /* filter out any EOS characters in comment */
369 { if ('\n' == *s) *s = 0;
370 if (0 == *s) break;
371 }
372 cl->line[0] = 0; /* create empty name (0 length string) */
373 cl->name = cl->line;
374 cl->type = NGP_TTYPE_RAW;
375 return(NGP_OK);
376 }
377 p++;
378 }
379
380 cl->name = p;
381
382 for (;;) /* we need to find 1st whitespace */
383 { if ((0 == *p) || ('\n' == *p))
384 { *p = 0;
385 break;
386 }
387
388 /*
389 from Richard Mathar, 2002-05-03, add 10 lines:
390 if upper/lowercase HIERARCH followed also by an equal sign...
391 */
392 if( fits_strncasecmp("HIERARCH",p,strlen("HIERARCH")) == 0 )
393 {
394 char * const eqsi=strchr(p,'=') ;
395 if( eqsi )
396 {
397 cl_flags |= NGP_FOUND_EQUAL_SIGN ;
398 p=eqsi ;
399 break ;
400 }
401 }
402
403 if ((' ' == *p) || ('\t' == *p)) break;
404 if ('=' == *p)
405 { cl_flags |= NGP_FOUND_EQUAL_SIGN;
406 break;
407 }
408
409 p++;
410 }
411
412 if (*p) *(p++) = 0; /* found end of keyname so terminate string with zero */
413
414 if ((!fits_strcasecmp("HISTORY", cl->name))
415 || (!fits_strcasecmp("COMMENT", cl->name))
416 || (!fits_strcasecmp("CONTINUE", cl->name)))
417 { cl->comment = p;
418 for (s = cl->comment;; s++) /* filter out any EOS characters in comment */
419 { if ('\n' == *s) *s = 0;
420 if (0 == *s) break;
421 }
422 cl->type = NGP_TTYPE_RAW;
423 return(NGP_OK);
424 }
425
426 if (!fits_strcasecmp("\\INCLUDE", cl->name))
427 {
428 for (;; p++) if ((' ' != *p) && ('\t' != *p)) break; /* skip whitespace */
429
430 cl->value = p;
431 for (s = cl->value;; s++) /* filter out any EOS characters */
432 { if ('\n' == *s) *s = 0;
433 if (0 == *s) break;
434 }
435 cl->type = NGP_TTYPE_UNKNOWN;
436 return(NGP_OK);
437 }
438
439 for (;; p++)
440 { if ((0 == *p) || ('\n' == *p)) return(NGP_OK); /* test if at end of string */
441 if ((' ' == *p) || ('\t' == *p)) continue; /* skip whitespace */
442 if (cl_flags & NGP_FOUND_EQUAL_SIGN) break;
443 if ('=' != *p) break; /* ignore initial equal sign */
444 cl_flags |= NGP_FOUND_EQUAL_SIGN;
445 }
446
447 if ('/' == *p) /* no value specified, comment only */
448 { p++;
449 if ((' ' == *p) || ('\t' == *p)) p++;
450 cl->comment = p;
451 for (s = cl->comment;; s++) /* filter out any EOS characters in comment */
452 { if ('\n' == *s) *s = 0;
453 if (0 == *s) break;
454 }
455 return(NGP_OK);
456 }
457
458 if ('\'' == *p) /* we have found string within quotes */
459 { cl->value = s = ++p; /* set pointer to beginning of that string */
460 cl->type = NGP_TTYPE_STRING; /* signal that it is of string type */
461
462 for (;;) /* analyze it */
463 { if ((0 == *p) || ('\n' == *p)) /* end of line -> end of string */
464 { *s = 0; return(NGP_OK); }
465
466 if ('\'' == *p) /* we have found doublequote */
467 { if ((0 == p[1]) || ('\n' == p[1]))/* doublequote is the last character in line */
468 { *s = 0; return(NGP_OK); }
469 if (('\t' == p[1]) || (' ' == p[1])) /* duoblequote was string terminator */
470 { *s = 0; p++; break; }
471 if ('\'' == p[1]) p++; /* doublequote is inside string, convert "" -> " */
472 }
473
474 *(s++) = *(p++); /* compact string in place, necess. by "" -> " conversion */
475 }
476 }
477 else /* regular token */
478 {
479 cl->value = p; /* set pointer to token */
480 cl->type = NGP_TTYPE_UNKNOWN; /* we dont know type at the moment */
481 for (;; p++) /* we need to find 1st whitespace */
482 { if ((0 == *p) || ('\n' == *p))
483 { *p = 0; return(NGP_OK); }
484 if ((' ' == *p) || ('\t' == *p)) break;
485 }
486 if (*p) *(p++) = 0; /* found so terminate string with zero */
487 }
488
489 for (;; p++)
490 { if ((0 == *p) || ('\n' == *p)) return(NGP_OK); /* test if at end of string */
491 if ((' ' != *p) && ('\t' != *p)) break; /* skip whitespace */
492 }
493
494 if ('/' == *p) /* no value specified, comment only */
495 { p++;
496 if ((' ' == *p) || ('\t' == *p)) p++;
497 cl->comment = p;
498 for (s = cl->comment;; s++) /* filter out any EOS characters in comment */
499 { if ('\n' == *s) *s = 0;
500 if (0 == *s) break;
501 }
502 return(NGP_OK);
503 }
504
505 cl->format = NGP_FORMAT_ERROR;
506 return(NGP_OK); /* too many tokens ... */
507 }
508
509 /* try to open include file. If open fails and fname
510 does not specify absolute pathname, try to open fname
511 in any directory specified in CFITSIO_INCLUDE_FILES
512 environment variable. Finally try to open fname
513 relative to ngp_master_dir, which is directory of top
514 level include file
515 */
516
ngp_include_file(char * fname)517 int ngp_include_file(char *fname) /* try to open include file */
518 { char *p, *p2, *cp, *envar, envfiles[NGP_MAX_ENVFILES];
519 char *saveptr;
520
521 if (NULL == fname) return(NGP_NUL_PTR);
522
523 if (ngp_inclevel >= NGP_MAX_INCLUDE) /* too many include files */
524 return(NGP_INC_NESTING);
525
526 if (NULL == (ngp_fp[ngp_inclevel] = fopen(fname, "r")))
527 { /* if simple open failed .. */
528 envar = getenv("CFITSIO_INCLUDE_FILES"); /* scan env. variable, and retry to open */
529
530 if (NULL != envar) /* is env. variable defined ? */
531 { strncpy(envfiles, envar, NGP_MAX_ENVFILES - 1);
532 envfiles[NGP_MAX_ENVFILES - 1] = 0; /* copy search path to local variable, env. is fragile */
533
534 for (p2 = ffstrtok(envfiles, ":",&saveptr); NULL != p2; p2 = ffstrtok(NULL, ":",&saveptr))
535 {
536 cp = (char *)ngp_alloc(strlen(fname) + strlen(p2) + 2);
537 if (NULL == cp) return(NGP_NO_MEMORY);
538
539 strcpy(cp, p2);
540 #ifdef MSDOS
541 strcat(cp, "\\"); /* abs. pathname for MSDOS */
542
543 #else
544 strcat(cp, "/"); /* and for unix */
545 #endif
546 strcat(cp, fname);
547
548 ngp_fp[ngp_inclevel] = fopen(cp, "r");
549 ngp_free(cp);
550
551 if (NULL != ngp_fp[ngp_inclevel]) break;
552 }
553 }
554
555 if (NULL == ngp_fp[ngp_inclevel]) /* finally try to open relative to top level */
556 {
557 #ifdef MSDOS
558 if ('\\' == fname[0]) return(NGP_ERR_FOPEN); /* abs. pathname for MSDOS, does not support C:\\PATH */
559 #else
560 if ('/' == fname[0]) return(NGP_ERR_FOPEN); /* and for unix */
561 #endif
562 if (0 == ngp_master_dir[0]) return(NGP_ERR_FOPEN);
563
564 p = ngp_alloc(strlen(fname) + strlen(ngp_master_dir) + 1);
565 if (NULL == p) return(NGP_NO_MEMORY);
566
567 strcpy(p, ngp_master_dir); /* construct composite pathname */
568 strcat(p, fname); /* comp = master + fname */
569
570 ngp_fp[ngp_inclevel] = fopen(p, "r");/* try to open composite */
571 ngp_free(p); /* we don't need buffer anymore */
572
573 if (NULL == ngp_fp[ngp_inclevel])
574 return(NGP_ERR_FOPEN); /* fail if error */
575 }
576 }
577
578 ngp_inclevel++;
579 return(NGP_OK);
580 }
581
582
583 /* read line in the intelligent way. All \INCLUDE directives are handled,
584 empty and comment line skipped. If this function returns NGP_OK, than
585 decomposed line (name, type, value in proper type and comment) are
586 stored in ngp_linkey structure. ignore_blank_lines parameter is zero
587 when parser is inside GROUP or HDU definition. Nonzero otherwise.
588 */
589
ngp_read_line(int ignore_blank_lines)590 int ngp_read_line(int ignore_blank_lines)
591 { int r, nc, savec;
592 unsigned k;
593
594 if (ngp_inclevel <= 0) /* do some sanity checking first */
595 { ngp_keyidx = NGP_TOKEN_EOF; /* no parents, so report error */
596 return(NGP_OK);
597 }
598 if (ngp_inclevel > NGP_MAX_INCLUDE) return(NGP_INC_NESTING);
599 if (NULL == ngp_fp[ngp_inclevel - 1]) return(NGP_NUL_PTR);
600
601 for (;;)
602 { switch (r = ngp_read_line_buffered(ngp_fp[ngp_inclevel - 1]))
603 { case NGP_EOF:
604 ngp_inclevel--; /* end of file, revert to parent */
605 if (ngp_fp[ngp_inclevel]) /* we can close old file */
606 fclose(ngp_fp[ngp_inclevel]);
607
608 ngp_fp[ngp_inclevel] = NULL;
609 if (ngp_inclevel <= 0)
610 { ngp_keyidx = NGP_TOKEN_EOF; /* no parents, so report error */
611 return(NGP_OK);
612 }
613 continue;
614
615 case NGP_OK:
616 if (ngp_curline.flags & NGP_LINE_REREAD) return(r);
617 break;
618 default:
619 return(r);
620 }
621
622 switch (ngp_curline.line[0])
623 { case 0: if (0 == ignore_blank_lines) break; /* ignore empty lines if told so */
624 case '#': continue; /* ignore comment lines */
625 }
626
627 r = ngp_extract_tokens(&ngp_curline); /* analyse line, extract tokens and comment */
628 if (NGP_OK != r) return(r);
629
630 if (NULL == ngp_curline.name) continue; /* skip lines consisting only of whitespaces */
631
632 for (k = 0; k < strlen(ngp_curline.name); k++)
633 { if ((ngp_curline.name[k] >= 'a') && (ngp_curline.name[k] <= 'z'))
634 ngp_curline.name[k] += 'A' - 'a'; /* force keyword to be upper case */
635 if (k == 7) break; /* only first 8 chars are required to be upper case */
636 }
637
638 for (k=0;; k++) /* find index of keyword in keyword table */
639 { if (NGP_TOKEN_UNKNOWN == ngp_tkdef[k].code) break;
640 if (0 == strcmp(ngp_curline.name, ngp_tkdef[k].name)) break;
641 }
642
643 ngp_keyidx = ngp_tkdef[k].code; /* save this index, grammar parser will need this */
644
645 if (NGP_TOKEN_INCLUDE == ngp_keyidx) /* if this is \INCLUDE keyword, try to include file */
646 { if (NGP_OK != (r = ngp_include_file(ngp_curline.value))) return(r);
647 continue; /* and read next line */
648 }
649
650 ngp_linkey.type = NGP_TTYPE_UNKNOWN; /* now, get the keyword type, it's a long story ... */
651
652 if (NULL != ngp_curline.value) /* if no value given signal it */
653 { if (NGP_TTYPE_STRING == ngp_curline.type) /* string type test */
654 { ngp_linkey.type = NGP_TTYPE_STRING;
655 ngp_linkey.value.s = ngp_curline.value;
656 }
657 if (NGP_TTYPE_UNKNOWN == ngp_linkey.type) /* bool type test */
658 { if ((!fits_strcasecmp("T", ngp_curline.value)) || (!fits_strcasecmp("F", ngp_curline.value)))
659 { ngp_linkey.type = NGP_TTYPE_BOOL;
660 ngp_linkey.value.b = (fits_strcasecmp("T", ngp_curline.value) ? 0 : 1);
661 }
662 }
663 if (NGP_TTYPE_UNKNOWN == ngp_linkey.type) /* complex type test */
664 { if (2 == sscanf(ngp_curline.value, "(%lg,%lg)%n", &(ngp_linkey.value.c.re), &(ngp_linkey.value.c.im), &nc))
665 { if ((' ' == ngp_curline.value[nc]) || ('\t' == ngp_curline.value[nc])
666 || ('\n' == ngp_curline.value[nc]) || (0 == ngp_curline.value[nc]))
667 { ngp_linkey.type = NGP_TTYPE_COMPLEX;
668 }
669 }
670 }
671 if (NGP_TTYPE_UNKNOWN == ngp_linkey.type) /* real type test */
672 { if (strchr(ngp_curline.value, '.') && (1 == sscanf(ngp_curline.value, "%lg%n", &(ngp_linkey.value.d), &nc)))
673 {
674 if ('D' == ngp_curline.value[nc]) {
675 /* test if template used a 'D' rather than an 'E' as the exponent character (added by WDP in 12/2010) */
676 savec = nc;
677 ngp_curline.value[nc] = 'E';
678 sscanf(ngp_curline.value, "%lg%n", &(ngp_linkey.value.d), &nc);
679 if ((' ' == ngp_curline.value[nc]) || ('\t' == ngp_curline.value[nc])
680 || ('\n' == ngp_curline.value[nc]) || (0 == ngp_curline.value[nc])) {
681 ngp_linkey.type = NGP_TTYPE_REAL;
682 } else { /* no, this is not a real value */
683 ngp_curline.value[savec] = 'D'; /* restore the original D character */
684 }
685 } else {
686 if ((' ' == ngp_curline.value[nc]) || ('\t' == ngp_curline.value[nc])
687 || ('\n' == ngp_curline.value[nc]) || (0 == ngp_curline.value[nc]))
688 { ngp_linkey.type = NGP_TTYPE_REAL;
689 }
690 }
691 }
692 }
693 if (NGP_TTYPE_UNKNOWN == ngp_linkey.type) /* integer type test */
694 { if (1 == sscanf(ngp_curline.value, "%d%n", &(ngp_linkey.value.i), &nc))
695 { if ((' ' == ngp_curline.value[nc]) || ('\t' == ngp_curline.value[nc])
696 || ('\n' == ngp_curline.value[nc]) || (0 == ngp_curline.value[nc]))
697 { ngp_linkey.type = NGP_TTYPE_INT;
698 }
699 }
700 }
701 if (NGP_TTYPE_UNKNOWN == ngp_linkey.type) /* force string type */
702 { ngp_linkey.type = NGP_TTYPE_STRING;
703 ngp_linkey.value.s = ngp_curline.value;
704 }
705 }
706 else
707 { if (NGP_TTYPE_RAW == ngp_curline.type) ngp_linkey.type = NGP_TTYPE_RAW;
708 else ngp_linkey.type = NGP_TTYPE_NULL;
709 }
710
711 if (NULL != ngp_curline.comment)
712 { strncpy(ngp_linkey.comment, ngp_curline.comment, NGP_MAX_COMMENT); /* store comment */
713 ngp_linkey.comment[NGP_MAX_COMMENT - 1] = 0;
714 }
715 else
716 { ngp_linkey.comment[0] = 0;
717 }
718
719 strncpy(ngp_linkey.name, ngp_curline.name, NGP_MAX_NAME); /* and keyword's name */
720 ngp_linkey.name[NGP_MAX_NAME - 1] = 0;
721
722 if (strlen(ngp_linkey.name) > FLEN_KEYWORD) /* WDP: 20-Jun-2002: mod to support HIERARCH */
723 {
724 return(NGP_BAD_ARG); /* cfitsio does not allow names > 8 chars */
725 }
726
727 return(NGP_OK); /* we have valid non empty line, so return success */
728 }
729 }
730
731 /* check whether keyword can be written as is */
732
ngp_keyword_is_write(NGP_TOKEN * ngp_tok)733 int ngp_keyword_is_write(NGP_TOKEN *ngp_tok)
734 { int i, j, l, spc;
735 /* indexed variables not to write */
736
737 static char *nm[] = { "NAXIS", "TFORM", "TTYPE", NULL } ;
738
739 /* non indexed variables not allowed to write */
740
741 static char *nmni[] = { "SIMPLE", "XTENSION", "BITPIX", "NAXIS", "PCOUNT",
742 "GCOUNT", "TFIELDS", "THEAP", "EXTEND", "EXTVER",
743 NULL } ;
744
745 if (NULL == ngp_tok) return(NGP_NUL_PTR);
746
747 for (j = 0; ; j++) /* first check non indexed */
748 { if (NULL == nmni[j]) break;
749 if (0 == strcmp(nmni[j], ngp_tok->name)) return(NGP_BAD_ARG);
750 }
751
752 for (j = 0; ; j++) /* now check indexed */
753 { if (NULL == nm[j]) return(NGP_OK);
754 l = strlen(nm[j]);
755 if ((l < 1) || (l > 5)) continue;
756 if (0 == strncmp(nm[j], ngp_tok->name, l)) break;
757 }
758
759 if ((ngp_tok->name[l] < '1') || (ngp_tok->name[l] > '9')) return(NGP_OK);
760 spc = 0;
761 for (i = l + 1; i < 8; i++)
762 { if (spc) { if (' ' != ngp_tok->name[i]) return(NGP_OK); }
763 else
764 { if ((ngp_tok->name[i] >= '0') && (ngp_tok->name[i] <= '9')) continue;
765 if (' ' == ngp_tok->name[i]) { spc = 1; continue; }
766 if (0 == ngp_tok->name[i]) break;
767 return(NGP_OK);
768 }
769 }
770 return(NGP_BAD_ARG);
771 }
772
773 /* write (almost) all keywords from given HDU to disk */
774
ngp_keyword_all_write(NGP_HDU * ngph,fitsfile * ffp,int mode)775 int ngp_keyword_all_write(NGP_HDU *ngph, fitsfile *ffp, int mode)
776 { int i, r, ib;
777 char buf[200];
778 long l;
779
780
781 if (NULL == ngph) return(NGP_NUL_PTR);
782 if (NULL == ffp) return(NGP_NUL_PTR);
783 r = NGP_OK;
784
785 for (i=0; i<ngph->tokcnt; i++)
786 { r = ngp_keyword_is_write(&(ngph->tok[i]));
787 if ((NGP_REALLY_ALL & mode) || (NGP_OK == r))
788 { switch (ngph->tok[i].type)
789 { case NGP_TTYPE_BOOL:
790 ib = ngph->tok[i].value.b;
791 fits_write_key(ffp, TLOGICAL, ngph->tok[i].name, &ib, ngph->tok[i].comment, &r);
792 break;
793 case NGP_TTYPE_STRING:
794 fits_write_key_longstr(ffp, ngph->tok[i].name, ngph->tok[i].value.s, ngph->tok[i].comment, &r);
795 break;
796 case NGP_TTYPE_INT:
797 l = ngph->tok[i].value.i; /* bugfix - 22-Jan-99, BO - nonalignment of OSF/Alpha */
798 fits_write_key(ffp, TLONG, ngph->tok[i].name, &l, ngph->tok[i].comment, &r);
799 break;
800 case NGP_TTYPE_REAL:
801 fits_write_key(ffp, TDOUBLE, ngph->tok[i].name, &(ngph->tok[i].value.d), ngph->tok[i].comment, &r);
802 break;
803 case NGP_TTYPE_COMPLEX:
804 fits_write_key(ffp, TDBLCOMPLEX, ngph->tok[i].name, &(ngph->tok[i].value.c), ngph->tok[i].comment, &r);
805 break;
806 case NGP_TTYPE_NULL:
807 fits_write_key_null(ffp, ngph->tok[i].name, ngph->tok[i].comment, &r);
808 break;
809 case NGP_TTYPE_RAW:
810 if (0 == strcmp("HISTORY", ngph->tok[i].name))
811 { fits_write_history(ffp, ngph->tok[i].comment, &r);
812 break;
813 }
814 if (0 == strcmp("COMMENT", ngph->tok[i].name))
815 { fits_write_comment(ffp, ngph->tok[i].comment, &r);
816 break;
817 }
818 snprintf(buf,200, "%-8.8s%s", ngph->tok[i].name, ngph->tok[i].comment);
819 fits_write_record(ffp, buf, &r);
820 break;
821 }
822 }
823 else if (NGP_BAD_ARG == r) /* enhancement 10 dec 2003, James Peachey: template comments replace defaults */
824 { r = NGP_OK; /* update comments of special keywords like TFORM */
825 if (ngph->tok[i].comment && *ngph->tok[i].comment) /* do not update with a blank comment */
826 { fits_modify_comment(ffp, ngph->tok[i].name, ngph->tok[i].comment, &r);
827 }
828 }
829 else /* other problem, typically a blank token */
830 { r = NGP_OK; /* skip this token, but continue */
831 }
832 if (r) return(r);
833 }
834
835 fits_set_hdustruc(ffp, &r); /* resync cfitsio */
836 return(r);
837 }
838
839 /* init HDU structure */
840
ngp_hdu_init(NGP_HDU * ngph)841 int ngp_hdu_init(NGP_HDU *ngph)
842 { if (NULL == ngph) return(NGP_NUL_PTR);
843 ngph->tok = NULL;
844 ngph->tokcnt = 0;
845 return(NGP_OK);
846 }
847
848 /* clear HDU structure */
849
ngp_hdu_clear(NGP_HDU * ngph)850 int ngp_hdu_clear(NGP_HDU *ngph)
851 { int i;
852
853 if (NULL == ngph) return(NGP_NUL_PTR);
854
855 for (i=0; i<ngph->tokcnt; i++)
856 { if (NGP_TTYPE_STRING == ngph->tok[i].type)
857 if (NULL != ngph->tok[i].value.s)
858 { ngp_free(ngph->tok[i].value.s);
859 ngph->tok[i].value.s = NULL;
860 }
861 }
862
863 if (NULL != ngph->tok) ngp_free(ngph->tok);
864
865 ngph->tok = NULL;
866 ngph->tokcnt = 0;
867
868 return(NGP_OK);
869 }
870
871 /* insert new token to HDU structure */
872
ngp_hdu_insert_token(NGP_HDU * ngph,NGP_TOKEN * newtok)873 int ngp_hdu_insert_token(NGP_HDU *ngph, NGP_TOKEN *newtok)
874 { NGP_TOKEN *tkp;
875
876 if (NULL == ngph) return(NGP_NUL_PTR);
877 if (NULL == newtok) return(NGP_NUL_PTR);
878
879 if (0 == ngph->tokcnt)
880 tkp = (NGP_TOKEN *)ngp_alloc((ngph->tokcnt + 1) * sizeof(NGP_TOKEN));
881 else
882 tkp = (NGP_TOKEN *)ngp_realloc(ngph->tok, (ngph->tokcnt + 1) * sizeof(NGP_TOKEN));
883
884 if (NULL == tkp) return(NGP_NO_MEMORY);
885
886 ngph->tok = tkp;
887 ngph->tok[ngph->tokcnt] = *newtok;
888
889 if (NGP_TTYPE_STRING == newtok->type)
890 { if (NULL != newtok->value.s)
891 { ngph->tok[ngph->tokcnt].value.s = (char *)ngp_alloc(1 + strlen(newtok->value.s));
892 if (NULL == ngph->tok[ngph->tokcnt].value.s) return(NGP_NO_MEMORY);
893 strcpy(ngph->tok[ngph->tokcnt].value.s, newtok->value.s);
894 }
895 }
896
897 ngph->tokcnt++;
898 return(NGP_OK);
899 }
900
901
ngp_append_columns(fitsfile * ff,NGP_HDU * ngph,int aftercol)902 int ngp_append_columns(fitsfile *ff, NGP_HDU *ngph, int aftercol)
903 { int r, i, j, exitflg, ngph_i;
904 char *my_tform, *my_ttype;
905 char ngph_ctmp;
906
907
908 if (NULL == ff) return(NGP_NUL_PTR);
909 if (NULL == ngph) return(NGP_NUL_PTR);
910 if (0 == ngph->tokcnt) return(NGP_OK); /* nothing to do ! */
911
912 r = NGP_OK;
913 exitflg = 0;
914
915 for (j=aftercol; j<NGP_MAX_ARRAY_DIM; j++) /* 0 for table, 6 for group */
916 {
917 my_tform = NULL;
918 my_ttype = "";
919
920 for (i=0; ; i++)
921 { if (1 == sscanf(ngph->tok[i].name, "TFORM%d%c", &ngph_i, &ngph_ctmp))
922 { if ((NGP_TTYPE_STRING == ngph->tok[i].type) && (ngph_i == (j + 1)))
923 { my_tform = ngph->tok[i].value.s;
924 }
925 }
926 else if (1 == sscanf(ngph->tok[i].name, "TTYPE%d%c", &ngph_i, &ngph_ctmp))
927 { if ((NGP_TTYPE_STRING == ngph->tok[i].type) && (ngph_i == (j + 1)))
928 { my_ttype = ngph->tok[i].value.s;
929 }
930 }
931
932 if ((NULL != my_tform) && (my_ttype[0])) break;
933
934 if (i < (ngph->tokcnt - 1)) continue;
935 exitflg = 1;
936 break;
937 }
938 if ((NGP_OK == r) && (NULL != my_tform))
939 fits_insert_col(ff, j + 1, my_ttype, my_tform, &r);
940
941 if ((NGP_OK != r) || exitflg) break;
942 }
943 return(r);
944 }
945
946 /* read complete HDU */
947
ngp_read_xtension(fitsfile * ff,int parent_hn,int simple_mode)948 int ngp_read_xtension(fitsfile *ff, int parent_hn, int simple_mode)
949 { int r, exflg, l, my_hn, tmp0, incrementor_index, i, j;
950 int ngph_dim, ngph_bitpix, ngph_node_type, my_version;
951 char incrementor_name[NGP_MAX_STRING], ngph_ctmp;
952 char *ngph_extname = 0;
953 long ngph_size[NGP_MAX_ARRAY_DIM];
954 NGP_HDU ngph;
955 long lv;
956
957 incrementor_name[0] = 0; /* signal no keyword+'#' found yet */
958 incrementor_index = 0;
959
960 if (NGP_OK != (r = ngp_hdu_init(&ngph))) return(r);
961
962 if (NGP_OK != (r = ngp_read_line(0))) return(r); /* EOF always means error here */
963 switch (NGP_XTENSION_SIMPLE & simple_mode)
964 {
965 case 0: if (NGP_TOKEN_XTENSION != ngp_keyidx) return(NGP_TOKEN_NOT_EXPECT);
966 break;
967 default: if (NGP_TOKEN_SIMPLE != ngp_keyidx) return(NGP_TOKEN_NOT_EXPECT);
968 break;
969 }
970
971 if (NGP_OK != (r = ngp_hdu_insert_token(&ngph, &ngp_linkey))) return(r);
972
973 for (;;)
974 { if (NGP_OK != (r = ngp_read_line(0))) return(r); /* EOF always means error here */
975 exflg = 0;
976 switch (ngp_keyidx)
977 {
978 case NGP_TOKEN_SIMPLE:
979 r = NGP_TOKEN_NOT_EXPECT;
980 break;
981
982 case NGP_TOKEN_END:
983 case NGP_TOKEN_XTENSION:
984 case NGP_TOKEN_GROUP:
985 r = ngp_unread_line(); /* WARNING - not break here .... */
986 case NGP_TOKEN_EOF:
987 exflg = 1;
988 break;
989
990 default: l = strlen(ngp_linkey.name);
991 if ((l >= 2) && (l <= 6))
992 { if ('#' == ngp_linkey.name[l - 1])
993 { if (0 == incrementor_name[0])
994 { memcpy(incrementor_name, ngp_linkey.name, l - 1);
995 incrementor_name[l - 1] = 0;
996 }
997 if (((l - 1) == (int)strlen(incrementor_name)) && (0 == memcmp(incrementor_name, ngp_linkey.name, l - 1)))
998 { incrementor_index++;
999 }
1000 snprintf(ngp_linkey.name + l - 1, NGP_MAX_NAME-l+1,"%d", incrementor_index);
1001 }
1002 }
1003 r = ngp_hdu_insert_token(&ngph, &ngp_linkey);
1004 break;
1005 }
1006 if ((NGP_OK != r) || exflg) break;
1007 }
1008
1009 if (NGP_OK == r)
1010 { /* we should scan keywords, and calculate HDU's */
1011 /* structure ourselves .... */
1012
1013 ngph_node_type = NGP_NODE_INVALID; /* init variables */
1014 ngph_bitpix = 0;
1015 ngph_extname = NULL;
1016 for (i=0; i<NGP_MAX_ARRAY_DIM; i++) ngph_size[i] = 0;
1017 ngph_dim = 0;
1018
1019 for (i=0; i<ngph.tokcnt; i++)
1020 { if (!strcmp("XTENSION", ngph.tok[i].name))
1021 { if (NGP_TTYPE_STRING == ngph.tok[i].type)
1022 { if (!fits_strncasecmp("BINTABLE", ngph.tok[i].value.s,8)) ngph_node_type = NGP_NODE_BTABLE;
1023 if (!fits_strncasecmp("TABLE", ngph.tok[i].value.s,5)) ngph_node_type = NGP_NODE_ATABLE;
1024 if (!fits_strncasecmp("IMAGE", ngph.tok[i].value.s,5)) ngph_node_type = NGP_NODE_IMAGE;
1025 }
1026 }
1027 else if (!strcmp("SIMPLE", ngph.tok[i].name))
1028 { if (NGP_TTYPE_BOOL == ngph.tok[i].type)
1029 { if (ngph.tok[i].value.b) ngph_node_type = NGP_NODE_IMAGE;
1030 }
1031 }
1032 else if (!strcmp("BITPIX", ngph.tok[i].name))
1033 { if (NGP_TTYPE_INT == ngph.tok[i].type) ngph_bitpix = ngph.tok[i].value.i;
1034 }
1035 else if (!strcmp("NAXIS", ngph.tok[i].name))
1036 { if (NGP_TTYPE_INT == ngph.tok[i].type) ngph_dim = ngph.tok[i].value.i;
1037 }
1038 else if (!strcmp("EXTNAME", ngph.tok[i].name)) /* assign EXTNAME, I hope struct does not move */
1039 { if (NGP_TTYPE_STRING == ngph.tok[i].type) ngph_extname = ngph.tok[i].value.s;
1040 }
1041 else if (1 == sscanf(ngph.tok[i].name, "NAXIS%d%c", &j, &ngph_ctmp))
1042 { if (NGP_TTYPE_INT == ngph.tok[i].type)
1043 if ((j>=1) && (j <= NGP_MAX_ARRAY_DIM))
1044 { ngph_size[j - 1] = ngph.tok[i].value.i;
1045 }
1046 }
1047 }
1048
1049 switch (ngph_node_type)
1050 { case NGP_NODE_IMAGE:
1051 if (NGP_XTENSION_FIRST == ((NGP_XTENSION_FIRST | NGP_XTENSION_SIMPLE) & simple_mode))
1052 { /* if caller signals that this is 1st HDU in file */
1053 /* and it is IMAGE defined with XTENSION, then we */
1054 /* need create dummy Primary HDU */
1055 fits_create_img(ff, 16, 0, NULL, &r);
1056 }
1057 /* create image */
1058 fits_create_img(ff, ngph_bitpix, ngph_dim, ngph_size, &r);
1059
1060 /* update keywords */
1061 if (NGP_OK == r) r = ngp_keyword_all_write(&ngph, ff, NGP_NON_SYSTEM_ONLY);
1062 break;
1063
1064 case NGP_NODE_ATABLE:
1065 case NGP_NODE_BTABLE:
1066 /* create table, 0 rows and 0 columns for the moment */
1067 fits_create_tbl(ff, ((NGP_NODE_ATABLE == ngph_node_type)
1068 ? ASCII_TBL : BINARY_TBL),
1069 0, 0, NULL, NULL, NULL, NULL, &r);
1070 if (NGP_OK != r) break;
1071
1072 /* add columns ... */
1073 r = ngp_append_columns(ff, &ngph, 0);
1074 if (NGP_OK != r) break;
1075
1076 /* add remaining keywords */
1077 r = ngp_keyword_all_write(&ngph, ff, NGP_NON_SYSTEM_ONLY);
1078 if (NGP_OK != r) break;
1079
1080 /* if requested add rows */
1081 if (ngph_size[1] > 0) fits_insert_rows(ff, 0, ngph_size[1], &r);
1082 break;
1083
1084 default: r = NGP_BAD_ARG;
1085 break;
1086 }
1087
1088 }
1089
1090 if ((NGP_OK == r) && (NULL != ngph_extname))
1091 { r = ngp_get_extver(ngph_extname, &my_version); /* write correct ext version number */
1092 lv = my_version; /* bugfix - 22-Jan-99, BO - nonalignment of OSF/Alpha */
1093 fits_write_key(ff, TLONG, "EXTVER", &lv, "auto assigned by template parser", &r);
1094 }
1095
1096 if (NGP_OK == r)
1097 { if (parent_hn > 0)
1098 { fits_get_hdu_num(ff, &my_hn);
1099 fits_movabs_hdu(ff, parent_hn, &tmp0, &r); /* link us to parent */
1100 fits_add_group_member(ff, NULL, my_hn, &r);
1101 fits_movabs_hdu(ff, my_hn, &tmp0, &r);
1102 if (NGP_OK != r) return(r);
1103 }
1104 }
1105
1106 if (NGP_OK != r) /* in case of error - delete hdu */
1107 { tmp0 = 0;
1108 fits_delete_hdu(ff, NULL, &tmp0);
1109 }
1110
1111 ngp_hdu_clear(&ngph);
1112 return(r);
1113 }
1114
1115 /* read complete GROUP */
1116
ngp_read_group(fitsfile * ff,char * grpname,int parent_hn)1117 int ngp_read_group(fitsfile *ff, char *grpname, int parent_hn)
1118 { int r, exitflg, l, my_hn, tmp0, incrementor_index;
1119 char grnm[NGP_MAX_STRING]; /* keyword holding group name */
1120 char incrementor_name[NGP_MAX_STRING];
1121 NGP_HDU ngph;
1122
1123 incrementor_name[0] = 0; /* signal no keyword+'#' found yet */
1124 incrementor_index = 6; /* first 6 cols are used by group */
1125
1126 ngp_grplevel++;
1127 if (NGP_OK != (r = ngp_hdu_init(&ngph))) return(r);
1128
1129 r = NGP_OK;
1130 if (NGP_OK != (r = fits_create_group(ff, grpname, GT_ID_ALL_URI, &r))) return(r);
1131 fits_get_hdu_num(ff, &my_hn);
1132 if (parent_hn > 0)
1133 { fits_movabs_hdu(ff, parent_hn, &tmp0, &r); /* link us to parent */
1134 fits_add_group_member(ff, NULL, my_hn, &r);
1135 fits_movabs_hdu(ff, my_hn, &tmp0, &r);
1136 if (NGP_OK != r) return(r);
1137 }
1138
1139 for (exitflg = 0; 0 == exitflg;)
1140 { if (NGP_OK != (r = ngp_read_line(0))) break; /* EOF always means error here */
1141 switch (ngp_keyidx)
1142 {
1143 case NGP_TOKEN_SIMPLE:
1144 case NGP_TOKEN_EOF:
1145 r = NGP_TOKEN_NOT_EXPECT;
1146 break;
1147
1148 case NGP_TOKEN_END:
1149 ngp_grplevel--;
1150 exitflg = 1;
1151 break;
1152
1153 case NGP_TOKEN_GROUP:
1154 if (NGP_TTYPE_STRING == ngp_linkey.type)
1155 { strncpy(grnm, ngp_linkey.value.s, NGP_MAX_STRING);
1156 }
1157 else
1158 { snprintf(grnm, NGP_MAX_STRING,"DEFAULT_GROUP_%d", master_grp_idx++);
1159 }
1160 grnm[NGP_MAX_STRING - 1] = 0;
1161 r = ngp_read_group(ff, grnm, my_hn);
1162 break; /* we can have many subsequent GROUP defs */
1163
1164 case NGP_TOKEN_XTENSION:
1165 r = ngp_unread_line();
1166 if (NGP_OK != r) break;
1167 r = ngp_read_xtension(ff, my_hn, 0);
1168 break; /* we can have many subsequent HDU defs */
1169
1170 default: l = strlen(ngp_linkey.name);
1171 if ((l >= 2) && (l <= 6))
1172 { if ('#' == ngp_linkey.name[l - 1])
1173 { if (0 == incrementor_name[0])
1174 { memcpy(incrementor_name, ngp_linkey.name, l - 1);
1175 incrementor_name[l - 1] = 0;
1176 }
1177 if (((l - 1) == (int)strlen(incrementor_name)) && (0 == memcmp(incrementor_name, ngp_linkey.name, l - 1)))
1178 { incrementor_index++;
1179 }
1180 snprintf(ngp_linkey.name + l - 1, NGP_MAX_NAME-l+1,"%d", incrementor_index);
1181 }
1182 }
1183 r = ngp_hdu_insert_token(&ngph, &ngp_linkey);
1184 break; /* here we can add keyword */
1185 }
1186 if (NGP_OK != r) break;
1187 }
1188
1189 fits_movabs_hdu(ff, my_hn, &tmp0, &r); /* back to our HDU */
1190
1191 if (NGP_OK == r) /* create additional columns, if requested */
1192 r = ngp_append_columns(ff, &ngph, 6);
1193
1194 if (NGP_OK == r) /* and write keywords */
1195 r = ngp_keyword_all_write(&ngph, ff, NGP_NON_SYSTEM_ONLY);
1196
1197 if (NGP_OK != r) /* delete group in case of error */
1198 { tmp0 = 0;
1199 fits_remove_group(ff, OPT_RM_GPT, &tmp0);
1200 }
1201
1202 ngp_hdu_clear(&ngph); /* we are done with this HDU, so delete it */
1203 return(r);
1204 }
1205
1206 /* top level API functions */
1207
1208 /* read whole template. ff should point to the opened empty fits file. */
1209
fits_execute_template(fitsfile * ff,char * ngp_template,int * status)1210 int fits_execute_template(fitsfile *ff, char *ngp_template, int *status)
1211 { int r, exit_flg, first_extension, i, my_hn, tmp0, keys_exist, more_keys, used_ver;
1212 char grnm[NGP_MAX_STRING], used_name[NGP_MAX_STRING];
1213 long luv;
1214
1215 if (NULL == status) return(NGP_NUL_PTR);
1216 if (NGP_OK != *status) return(*status);
1217
1218 /* This function uses many global variables (local to this file) and
1219 therefore is not thread-safe. */
1220 FFLOCK;
1221
1222 if ((NULL == ff) || (NULL == ngp_template))
1223 { *status = NGP_NUL_PTR;
1224 FFUNLOCK;
1225 return(*status);
1226 }
1227
1228 ngp_inclevel = 0; /* initialize things, not all should be zero */
1229 ngp_grplevel = 0;
1230 master_grp_idx = 1;
1231 exit_flg = 0;
1232 ngp_master_dir[0] = 0; /* this should be before 1st call to ngp_include_file */
1233 first_extension = 1; /* we need to create PHDU */
1234
1235 if (NGP_OK != (r = ngp_delete_extver_tab()))
1236 { *status = r;
1237 FFUNLOCK;
1238 return(r);
1239 }
1240
1241 fits_get_hdu_num(ff, &my_hn); /* our HDU position */
1242 if (my_hn <= 1) /* check whether we really need to create PHDU */
1243 { fits_movabs_hdu(ff, 1, &tmp0, status);
1244 fits_get_hdrspace(ff, &keys_exist, &more_keys, status);
1245 fits_movabs_hdu(ff, my_hn, &tmp0, status);
1246 if (NGP_OK != *status) /* error here means file is corrupted */
1247 {
1248 FFUNLOCK;
1249 return(*status);
1250 }
1251 if (keys_exist > 0) first_extension = 0; /* if keywords exist assume PHDU already exist */
1252 }
1253 else
1254 { first_extension = 0; /* PHDU (followed by 1+ extensions) exist */
1255
1256 for (i = 2; i<= my_hn; i++)
1257 { *status = NGP_OK;
1258 fits_movabs_hdu(ff, 1, &tmp0, status);
1259 if (NGP_OK != *status) break;
1260
1261 fits_read_key(ff, TSTRING, "EXTNAME", used_name, NULL, status);
1262 if (NGP_OK != *status) continue;
1263
1264 fits_read_key(ff, TLONG, "EXTVER", &luv, NULL, status);
1265 used_ver = luv; /* bugfix - 22-Jan-99, BO - nonalignment of OSF/Alpha */
1266 if (VALUE_UNDEFINED == *status)
1267 { used_ver = 1;
1268 *status = NGP_OK;
1269 }
1270
1271 if (NGP_OK == *status) *status = ngp_set_extver(used_name, used_ver);
1272 }
1273
1274 fits_movabs_hdu(ff, my_hn, &tmp0, status);
1275 }
1276
1277 if (NGP_OK != *status) {
1278 FFUNLOCK;
1279 return(*status);
1280 }
1281 if (NGP_OK != (*status = ngp_include_file(ngp_template))) {
1282 FFUNLOCK;
1283 return(*status);
1284 }
1285
1286 for (i = strlen(ngp_template) - 1; i >= 0; i--) /* strlen is > 0, otherwise fopen failed */
1287 {
1288 #ifdef MSDOS
1289 if ('\\' == ngp_template[i]) break;
1290 #else
1291 if ('/' == ngp_template[i]) break;
1292 #endif
1293 }
1294
1295 i++;
1296 if (i > (NGP_MAX_FNAME - 1)) i = NGP_MAX_FNAME - 1;
1297
1298 if (i > 0)
1299 { memcpy(ngp_master_dir, ngp_template, i);
1300 ngp_master_dir[i] = 0;
1301 }
1302
1303
1304 for (;;)
1305 { if (NGP_OK != (r = ngp_read_line(1))) break; /* EOF always means error here */
1306 switch (ngp_keyidx)
1307 {
1308 case NGP_TOKEN_SIMPLE:
1309 if (0 == first_extension) /* simple only allowed in first HDU */
1310 { r = NGP_TOKEN_NOT_EXPECT;
1311 break;
1312 }
1313 if (NGP_OK != (r = ngp_unread_line())) break;
1314 r = ngp_read_xtension(ff, 0, NGP_XTENSION_SIMPLE | NGP_XTENSION_FIRST);
1315 first_extension = 0;
1316 break;
1317
1318 case NGP_TOKEN_XTENSION:
1319 if (NGP_OK != (r = ngp_unread_line())) break;
1320 r = ngp_read_xtension(ff, 0, (first_extension ? NGP_XTENSION_FIRST : 0));
1321 first_extension = 0;
1322 break;
1323
1324 case NGP_TOKEN_GROUP:
1325 if (NGP_TTYPE_STRING == ngp_linkey.type)
1326 { strncpy(grnm, ngp_linkey.value.s, NGP_MAX_STRING); }
1327 else
1328 { snprintf(grnm,NGP_MAX_STRING, "DEFAULT_GROUP_%d", master_grp_idx++); }
1329 grnm[NGP_MAX_STRING - 1] = 0;
1330 r = ngp_read_group(ff, grnm, 0);
1331 first_extension = 0;
1332 break;
1333
1334 case NGP_TOKEN_EOF:
1335 exit_flg = 1;
1336 break;
1337
1338 default: r = NGP_TOKEN_NOT_EXPECT;
1339 break;
1340 }
1341 if (exit_flg || (NGP_OK != r)) break;
1342 }
1343
1344 /* all top level HDUs up to faulty one are left intact in case of i/o error. It is up
1345 to the caller to call fits_close_file or fits_delete_file when this function returns
1346 error. */
1347
1348 ngp_free_line(); /* deallocate last line (if any) */
1349 ngp_free_prevline(); /* deallocate cached line (if any) */
1350 ngp_delete_extver_tab(); /* delete extver table (if present), error ignored */
1351
1352 *status = r;
1353 FFUNLOCK;
1354 return(r);
1355 }
1356