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