1 /*
2  				fitsutil.c
3 
4 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 *
6 *	Part of:	The LDAC Tools
7 *
8 *	Author:		E.BERTIN, DeNIS/LDAC
9 *
10 *	Contents:	functions for handling FITS keywords.
11 *
12 *	Last modify:	17/11/2004
13 *
14 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include	"config.h"
19 #endif
20 
21 #include	<stdio.h>
22 #include	<stdlib.h>
23 #include	<string.h>
24 
25 #include	"fitscat_defs.h"
26 #include	"fitscat.h"
27 
28 char	histokeys[][12] = {"COMMENT ", "HISTORY ", "        ", ""};
29 
30 /****** fitsadd ***************************************************************
31 PROTO	int fitsadd(char *fitsbuf, char *keyword, char *comment)
32 PURPOSE	Write a FITS keyword in a fits header.
33 INPUT	pointer to the FITS buffer,
34 	name of the keyword to be created,
35 	a comment to put beyond the slash, or next to a COMMENT or HISTORY.
36 OUTPUT	line position or RETURN_ERROR if the keyword is invalid.
37 NOTES	For all keywords except commentary ones (like COMMENT, HISTORY or
38 	blank), it is checked that they do not exist already.
39 	Enough memory should be provided for the FITS header to contain one
40 	more line of 80 char.
41 AUTHOR	E. Bertin (IAP & Leiden observatory)
42 VERSION	13/06/2004
43  ***/
fitsadd(char * fitsbuf,char * keyword,char * comment)44 int	fitsadd(char *fitsbuf, char *keyword, char *comment)
45 
46   {
47    char    	*key_ptr;
48    char		str[82];
49    int     	headpos, headpos2, commentflag,
50 		i, n;
51 
52 
53   if (strcspn(keyword, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -_"))
54     return RETURN_ERROR;
55   commentflag = findkey(keyword, (char *)histokeys, 12)==RETURN_ERROR?0:1;
56   if (commentflag || (headpos = fitsfind(fitsbuf, keyword))==RETURN_ERROR)
57     {
58     headpos2 = headpos = fitsfind(fitsbuf, "END     ");
59 /*-- Special case of NAXIS parameters */
60     if (!strncmp(keyword, "NAXIS", 5) && keyword[5] && keyword[5] != ' ')
61       {
62       sscanf(keyword, "NAXIS%d", &n);
63 /*---- Look for all previous NAXIS parameters */
64       for (i=n; i--;)
65         {
66         sprintf(str, "NAXIS%-3d", i);
67         headpos=fitsfind(fitsbuf, str);
68         if (headpos>0)
69           break;
70         }
71       if (headpos<0)
72 /*---- Most likely keyword is NAXIS1 */
73         headpos=fitsfind(fitsbuf, "NAXIS   ");
74       if (headpos>0)
75         headpos++;
76       else
77         return RETURN_ERROR;
78       }
79     key_ptr = fitsbuf+80*headpos;
80     memmove(key_ptr+80, key_ptr, 80*(headpos2-headpos+1));
81 
82     if (commentflag)
83       sprintf(str, "%-8.8s %-71.71s",
84 	keyword, comment?comment:" ");
85     else if (comment && *comment)
86       sprintf(str, "%-8.8s=                      / %-47.47s",
87 	keyword, comment);
88     else
89       sprintf(str, "%-8.8s=                        %-47.47s",
90 	      keyword, " ");
91 
92     memcpy(key_ptr, str, 80);
93     }
94 
95   return headpos;
96   }
97 
98 
99 /****** fitsfind **************************************************************
100 PROTO	int fitsfind(char *fitsbuf, char *keyword)
101 PURPOSE	Search for a FITS keyword in a FITS header.
102 INPUT	pointer to the FITS buffer,
103 	name of the keyword to search for.
104 OUTPUT	position in lines  of 80 char (0=first) of the keyword if it was
105 	found, RETURN_ERROR otherwise.
106 NOTES	The buffer MUST contain the ``END     '' keyword.
107 AUTHOR	E. Bertin (IAP & Leiden observatory)
108 VERSION	15/02/96
109  ***/
fitsfind(char * fitsbuf,char * keyword)110 int	fitsfind(char *fitsbuf, char *keyword)
111 
112   {
113    char	*ptr;
114    int	i, len;
115 
116   len = strlen(keyword);
117   for (i=0; strncmp(ptr=&fitsbuf[80*i], "END     ", 8); i++)
118     if (!wstrncmp(ptr, keyword, len))
119       return i;
120   if (strncmp(keyword, "END     ", 8))
121     return RETURN_ERROR;
122   else
123     return i;
124   }
125 
126 
127 /****** fitsnfind *************************************************************
128 PROTO	char    *fitsnfind(char *fitsbuf, char *str, int nblock)
129 PURPOSE	Search for a FITS keyword in a fits header of nblock blocks.
130 INPUT	pointer to the FITS buffer,
131 	name of the keyword to search for,
132 	number of FITS blocks (2880 bytes each).
133 OUTPUT	pointer at the keyword position if it was found, NULL otherwise.
134 NOTES	No need for an ``END     '' keyword.
135 AUTHOR	E. Bertin (IAP & Leiden observatory)
136 VERSION	25/04/97
137  ***/
fitsnfind(char * fitsbuf,char * str,int nblock)138 char    *fitsnfind(char *fitsbuf, char *str, int nblock)
139   {
140    int  i;
141 
142   for (i=36*nblock;i--; fitsbuf+=80)
143     if (!strncmp(fitsbuf, str, strlen(str)))
144       return fitsbuf;
145 
146   return        (char *)NULL;
147   }
148 
149 
150 /****** fitspick **************************************************************
151 PROTO	int fitspick(char *fitsline, char *keyword, void *ptr, h_type *htype,
152 			t_type *ttype, char *comment)
153 
154 PURPOSE	Pick up FITS keyword,content,type and comment in a fits header line.
155 INPUT	pointer to the current line of FITS buffer,
156 	pointer to a char * (where to put the keyword),
157 	pointer to ``where to put the data'',
158 	pointer to ``where to put the h_type'',
159 	pointer to ``where to put the t_type'',
160 	pointer to a char * (where to put the comment).
161 OUTPUT	RETURN_OK if something was found, RETURN_ERROR otherwise.
162 NOTES	-.
163 AUTHOR	E. Bertin (IAP),
164         E.R. Deul - Handling of NaN
165 VERSION	04/08/2004
166  ***/
fitspick(char * fitsline,char * keyword,void * ptr,h_type * htype,t_type * ttype,char * comment)167 int	fitspick(char *fitsline, char *keyword, void *ptr, h_type *htype,
168 		t_type *ttype, char *comment)
169 
170   {
171    char *fptr, *cptr, c, *lastspace;
172    int	i,j, toggle;
173 
174   *((char *)ptr) = 0;
175 /*First, get the keyword*/
176   memcpy(keyword, fitsline, 8);
177   keyword[8] = 0;
178 
179 /*Handle comments*/
180   if ((int)fitsline[8] != '=')
181     {
182     if (strncmp(keyword, "COMMENT ", 8)
183 	&& strncmp(keyword, "HISTORY ", 8)
184 	&& strncmp(keyword, "HIERARCH", 8)
185 	&& strncmp(keyword, "        ", 8))
186       return RETURN_ERROR;
187     memcpy(comment, fitsline+9, 71);
188     comment[71] = 0;
189     *htype = H_COMMENT;
190     *ttype = T_STRING;
191     return RETURN_OK;
192     }
193 
194   for (j=10; j<80 && fitsline[j] == (char)' '; j++);
195   if (j==80 || fitsline[j] == '/')
196     {
197     *htype = H_COMMENT;
198     *ttype = T_STRING;
199     return RETURN_ERROR;
200     }
201   if ((int)fitsline[j] == '\'')
202     {
203     cptr = ptr;
204     for (fptr = fitsline + (i=j+1); i<80; i++)
205       {
206       if (*fptr==(char)'\'')
207         {
208         if (i++>=79 || *(fptr+1)!=(char)'\'')
209           break;
210         else
211           fptr++;
212         }
213       *cptr++ = *fptr++;
214       }
215     *cptr = 0;
216 /*-- Check if there is a trailing space */
217     *htype = (cptr != ptr && *(cptr-1)==' ') ? H_STRINGS: H_STRING;
218     *ttype = T_STRING;
219     }
220   else if (fitsline[j] == (char)'T' || fitsline[j] == (char)'F')
221     {
222     *((BYTE *)ptr) = fitsline[j]==(char)'T'?1:0;
223     *htype = H_BOOL;
224     *ttype = T_BYTE;
225     }
226   else if (!strncmp(fitsline+j, "NaN", 3))
227     {
228     *((double *)ptr) = BIG;
229     *htype = H_EXPO;
230     *ttype = T_DOUBLE;
231     }
232   else
233     {
234     for (i=j; i<80 && fitsline[i]!=(char)'/' && fitsline[i]!=(char)'.'; i++);
235 /*-- Handle floats*/
236     if (i==80)
237       {
238       *((int *)ptr) = 0;
239       *htype = H_INT;
240       *ttype = T_LONG;
241       }
242     else if (fitsline[i]==(char)'.')
243       {
244       fixexponent(fitsline+j);
245       *((double *)ptr) = atof(fitsline+j);
246       *htype = H_EXPO;
247       *ttype = T_DOUBLE;
248       }
249     else
250 /*---- Handle ints*/
251       {
252       *((int *)ptr) = atoi(fitsline+j);
253       *htype = H_INT;
254       *ttype = T_LONG;
255       }
256     }
257 
258 /*Store comment if it is found*/
259   toggle = 0;
260   lastspace = NULL;
261   for (fptr = fitsline + (i=j); i<80; i++)
262     {
263     if (*fptr == (char)'\'')
264       toggle^=toggle;
265     if (*(fptr++) == (char)'/' && !toggle)
266       {
267       while (++i<80 && *fptr<=' ')
268         fptr++;
269       i--;
270       while (++i<80)
271         if ((c=*(fptr++))>= ' ')
272 	  {
273           *(comment++) = c;
274           if (c>' ')
275             lastspace = comment;
276           }
277       }
278     }
279   if (lastspace)
280     *lastspace = '\0';
281   else
282     *comment = '\0';
283 
284   return RETURN_OK;
285   }
286 
287 
288 /****** fitsread **************************************************************
289 PROTO	int fitsread(char *fitsbuf, char *keyword, void *ptr, h_type htype,
290 			t_type ttype)
291 PURPOSE	Read a FITS keyword in a fits header.
292 INPUT	pointer to the FITS buffer,
293 	name of the keyword to be read,
294 	pointer where to put the read data,
295 	h_type of the data to be read (see fitscat.h),
296 	t_type of the data to be read (see fitscat.h).
297 OUTPUT	RETURN_OK if the keyword was found, RETURN_ERROR otherwise.
298 NOTES	The buffer MUST contain the ``END     '' keyword.
299 AUTHOR	E. Bertin (IAP & Leiden observatory)
300 VERSION	04/08/2004
301  ***/
fitsread(char * fitsbuf,char * keyword,void * ptr,h_type htype,t_type ttype)302 int	fitsread(char *fitsbuf, char *keyword, void *ptr, h_type htype,
303 		t_type ttype)
304 
305   {
306    int		i,pos;
307    char		s[4], str[82];
308    char		*st, *st2;
309 
310   if ((pos = fitsfind(fitsbuf, keyword)) < 0)
311     return RETURN_ERROR;
312 
313   strncpy(str,fitsbuf+80*pos,80);
314   str[80] = '\0';
315 
316   switch(htype)
317     {
318     case H_INT:		if (ttype == T_SHORT)
319 			  sscanf(str+10, "    %hd", (short *)ptr);
320 			else
321 			  sscanf(str+10, "    %d", (LONG *)ptr);
322 			break;
323 
324     case H_FLOAT:
325     case H_EXPO:	fixexponent(str);
326 			if (ttype == T_DOUBLE)
327 			  sscanf(str+10, "    %lf", (double *)ptr);
328 			else
329 			  sscanf(str+10, "    %f", (float *)ptr);
330 			break;
331 
332     case H_BOOL:	sscanf(str+10, "%1s", s);
333                         if (ttype == T_BYTE)
334 			  *(BYTE *)ptr = ((int)s[0] == 'T') ? 1 : 0;
335                         else if (ttype == T_SHORT)
336 			  *(short *)ptr = ((int)s[0] == 'T') ? 1 : 0;
337                         else
338 			  *(LONG *)ptr = ((int)s[0] == 'T') ? 1 : 0;
339 			break;
340 
341     case H_STRING:	st = ptr;
342 			st2= str+10;
343 			for (i=70; i-- && *(st2++)!=(char)'\'';);
344 			while (i-->0)
345 			  {
346 			  if (*st2 == '\'' && *(++st2) != '\'')
347 			    break;
348 			  *(st++) = *(st2++);
349 			  }
350 			do
351 			  {
352 			  *(st--) = (char)'\0';
353 			  } while (st>(char *)ptr && (*st == (char)' '));
354 			break;
355 
356     case H_STRINGS:	st = ptr;
357 			st2= str+10;
358 			for (i=70; i-- && *(st2++)!=(char)'\'';);
359 			while (i-->0)
360 			  {
361 			  if (*st2 == '\'' && *(++st2) != '\'')
362 			    break;
363 			  *(st++) = *(st2++);
364 			  }
365 			*st = (char)'\0';
366 			break;
367 
368     case H_COMMENT:	strcpy(ptr,str+9);
369 			break;
370 
371     case H_HCOMMENT:	strcpy(ptr,str+33);
372 			break;
373 
374     default:		error(EXIT_FAILURE,
375 				"*Internal Error*: Unknown FITS type in ",
376 				"fitsread()");
377 			break;
378     }
379 
380   return RETURN_OK;
381   }
382 
383 
384 /****** fitsremove ************************************************************
385 PROTO	int fitsremove(char *fitsbuf, char *keyword)
386 PURPOSE	Remove one (or more) FITS keyword from a fits header.
387 INPUT	pointer to the FITS buffer,
388 	name of the keyword to be created.
389 OUTPUT	RETURN_OK if the keyword was found, RETURN_ERROR otherwise.
390 NOTES	'?' wildcard allowed;
391 	Don't remove the ``END'' keyword with this!!!
392 AUTHOR	E. Bertin (IAP & Leiden observatory)
393 VERSION	08/04/99
394  ***/
395 
fitsremove(char * fitsbuf,char * keyword)396 int	fitsremove(char *fitsbuf, char *keyword)
397 
398   {
399    char	*cp1,*cp2;
400    int	endpos,pos, i,n;
401 
402   endpos = fitsfind(fitsbuf, "END     ");
403   for (n=0; (pos = fitsfind(fitsbuf, keyword))>=0; n++, endpos--)
404     for (cp1=fitsbuf+80*(pos+1), cp2=fitsbuf+80*pos, i=80*(endpos - pos); i--;)
405       *(cp2++) = *(cp1++);
406 
407   if (!n)
408     return RETURN_ERROR;
409 
410   memset(fitsbuf+80*(endpos+1), ' ', 80*n);
411 
412   return RETURN_OK;
413   }
414 
415 
416 /****** fitswrite *************************************************************
417 PROTO	int fitswrite(char *fitsbuf, char *keyword, void *ptr, h_type htype,
418 			t_type ttype)
419 PURPOSE	Write a FITS keyword in a fits header.
420 INPUT	pointer to the FITS buffer,
421 	name of the keyword to be written,
422 	pointer where to retrieve the  data,
423 	h_type of the data to be written (see fitscat.h),
424 	t_type of the data to be written (see fitscat.h).
425 OUTPUT	RETURN_OK if the keyword was found, RETURN_ERROR otherwise.
426 NOTES	The buffer MUST contain the ``END     '' keyword.
427 	The keyword must already exist in the buffer (use fitsadd()).
428 AUTHOR	E. Bertin (IAP & Leiden observatory)
429 VERSION	17/11/2004
430  ***/
fitswrite(char * fitsbuf,char * keyword,void * ptr,h_type htype,t_type ttype)431 int	fitswrite(char *fitsbuf, char *keyword, void *ptr, h_type htype,
432 		t_type ttype)
433 
434   {
435    int		i, l, pos, posoff, flag;
436    char		str[81],str2[81];
437    char		*cstr, *cstr1,*cstr2,
438 		c;
439 
440 /* Ignore HISTORY and COMMENTS */
441   if (findkey(keyword, (char *)histokeys, 12)!=RETURN_ERROR
442 	|| (pos = fitsfind(fitsbuf, keyword)) < 0)
443     return RETURN_ERROR;
444   posoff = 10;
445   fitsbuf += 80*pos;
446   switch(htype)
447     {
448     case H_INT:	sprintf(str, "%20d", (ttype==T_SHORT)?
449 				*(short *)ptr: *(int *)ptr);
450 			break;
451 
452     case H_FLOAT:	sprintf(str, "        %12.4f", (ttype==T_DOUBLE)?
453 				*(double *)ptr: *(float *)ptr);
454 			break;
455 
456     case H_EXPO:	sprintf(str, "    %16.9E", (ttype==T_DOUBLE)?
457 				*(double *)ptr: *(float *)ptr);
458 			break;
459 
460     case H_BOOL:	if (((ttype==T_SHORT)? *(short *)ptr : *(LONG *)ptr))
461 			  sprintf(str, "                   T");
462 			else
463 			  sprintf(str, "                   F");
464 			break;
465 
466     case H_STRING:	/* Handle the famous quote */
467 			cstr1 = (char *)ptr;
468 			cstr2 = str2;
469 			for (i=0; i<80; i++)
470 			  if (!(c=*(cstr2++) = *(cstr1++)))
471 			    break;
472 			  else if (c == '\'')
473 			    {
474 			    *(cstr2++) = '\'';
475 			    i++;
476 			    }
477 			if (strlen(str2)<=18)
478 			  {
479 			  sprintf(str, "'%-18.18s ", str2);
480 			  cstr = str+18;
481 			  i = 10;
482 			  }
483 			else
484 			  {
485 			  sprintf(str, "'%-68.68s ", str2);
486 			  cstr = str+68;
487 			  i = 60;
488 			  }
489                         for (; i-- && *cstr==(char)' '; cstr--);
490                         *(++cstr) = (char)'\'';
491                         if (i>9)
492                           *(++cstr) = 0;
493 			break;
494 
495     case H_STRINGS:	/* Handle the famous quote */
496 			cstr1 = (char *)ptr;
497 			cstr2 = str2;
498 			for (i=0; i<80; i++)
499 			  if (!(c=*(cstr2++) = *(cstr1++)))
500 			    break;
501 			  else if (c == '\'')
502 			    {
503 			    *(cstr2++) = '\'';
504 			    i++;
505 			    }
506 		        sprintf(str, "'%s'", str2);
507                         for (i+=2;i<20; i++)
508                           str[i]=' ';
509                         str[i] = '\0';
510 			break;
511 
512     case H_COMMENT:	sprintf(str, "%-70s", (char *)ptr);
513 			posoff = 9;
514 			break;
515 
516 			/* Special case of ``half-comments'' */
517     case H_HCOMMENT:	sprintf(str, " / %-47s", (char *)ptr);
518 			posoff = 30;
519 			break;
520 
521     default:		error(EXIT_FAILURE,
522 				"*FATAL ERROR*: Unknown FITS type in ",
523 				"fitswrite()");
524 			break;
525     }
526 
527 
528 /* Now the tricky problem of (former) comments */
529   flag=1;
530   cstr = fitsbuf+10;
531   for (i=71; --i; cstr++)
532     {
533     if (*cstr=='\'')
534       flag ^= 1;
535     else if (flag && *cstr=='/')
536       break;
537     }
538   if (posoff==10 && i && (l=69-strlen(str))>0)
539     {
540     strncpy(str2, cstr, i);
541     str2[i+1] = 0;
542     strcat(str, " ");
543     strncat(str, str2, l);
544     }
545 
546   memset(fitsbuf+9, ' ', 71);
547   fitsbuf += posoff;
548 
549 /* Finally copy the result to the right place (except the trailing zero) */
550   for (cstr = str; *cstr; *(fitsbuf++) = *(cstr++));
551 
552   return RETURN_OK;
553   }
554 
555 
556 /****** fixexponent ***********************************************************
557 PROTO	void fixexponent(char *s)
558 PURPOSE	Replaces the FORTRAN 'D' exponent sign to 'E' in a FITS line.
559 INPUT	FITS line
560 OUTPUT	-.
561 NOTES	-.
562 AUTHOR	E. Bertin (IAP & Leiden observatory)
563 VERSION	25/04/97
564  ***/
fixexponent(char * s)565 void	fixexponent(char *s)
566 
567   {
568    int	i;
569 
570   s += 9;
571   for (i=71; (int)*s != '/' && i--; s++)
572     if ((int)*s == 'D' || (int)*s == 'd')
573       *s = (char)'E';
574 
575   return;
576   }
577 
578 
579