1 /*** File libwcs/hput.c
2  *** September 9, 2011
3  *** By Jessica Mink, jmink@cfa.harvard.edu
4  *** Harvard-Smithsonian Center for Astrophysics
5  *** Copyright (C) 1995-2011
6  *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
7 
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12 
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with this library; if not, write to the Free Software
20     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22     Correspondence concerning WCSTools should be addressed as follows:
23            Internet email: jmink@cfa.harvard.edu
24            Postal address: Jessica Mink
25                            Smithsonian Astrophysical Observatory
26                            60 Garden St.
27                            Cambridge, MA 02138 USA
28 
29  * Module:	hput.c (Put FITS Header parameter values)
30  * Purpose:	Implant values for parameters into FITS header string
31  * Subroutine:	hputi4 (hstring,keyword,ival) sets int ival
32  * Subroutine:	hputr4 (hstring,keyword,rval) sets real*4 rval
33  * Subroutine:	hputr8 (hstring,keyword,dval) sets real*8 dval
34  * Subroutine:	hputnr8 (hstring,keyword,ndec,dval) sets real*8 dval
35  * Subroutine:	hputra (hstring,keyword,lval) sets right ascension as string
36  * Subroutine:	hputdec (hstring,keyword,lval) sets declination as string
37  * Subroutine:	hputl  (hstring,keyword,lval) sets logical lval
38  * Subroutine:	hputs  (hstring,keyword,cval) sets character string adding ''
39  * Subroutine:	hputm  (hstring,keyword,cval) sets multi-line character string
40  * Subroutine:	hputc  (hstring,keyword,cval) sets character string cval
41  * Subroutine:	hdel   (hstring,keyword) deletes entry for keyword keyword
42  * Subroutine:	hadd   (hplace,keyword) adds entry for keyword at hplace
43  * Subroutine:	hchange (hstring,keyword1,keyword2) changes keyword for entry
44  * Subroutine:	hputcom (hstring,keyword,comment) sets comment for parameter keyword
45  * Subroutine:	ra2str (out, lstr, ra, ndec) converts RA from degrees to string
46  * Subroutine:	dec2str (out, lstr, dec, ndec) converts Dec from degrees to string
47  * Subroutine:	deg2str (out, lstr, deg, ndec) converts degrees to string
48  * Subroutine:	num2str (out, num, field, ndec) converts number to string
49  * Subroutine:  getltime () returns current local time as ISO-style string
50  * Subroutine:  getutime () returns current UT as ISO-style string
51  */
52 #include <sys/time.h>
53 #include <string.h>             /* NULL, strlen, strstr, strcpy */
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <math.h>
57 #include "fitshead.h"
58 
59 static int verbose=0;	/* Set to 1 to print error messages and other info */
60 
61 static void fixnegzero();
62 
63 
64 /*  HPUTI4 - Set int keyword = ival in FITS header string */
65 
66 int
hputi4(hstring,keyword,ival)67 hputi4 (hstring,keyword,ival)
68 
69 char *hstring;		/* FITS-style header information in the format
70 			   <keyword>= <value> {/ <comment>}
71 			   each entry is padded with spaces to 80 characters */
72 
73 const char *keyword;	/* Name of the variable in header to be returned.
74 			   If no line begins with this string, one is created.
75 		   	   The first 8 characters of keyword must be unique. */
76 int ival;		/* int number */
77 {
78     char value[30];
79 
80     /* Translate value from binary to ASCII */
81     sprintf (value,"%d",ival);
82 
83     /* Put value into header string */
84     return (hputc (hstring,keyword,value));
85 }
86 
87 
88 /*  HPUTR4 - Set float keyword = rval in FITS header string */
89 
90 int
hputr4(hstring,keyword,rval)91 hputr4 (hstring, keyword, rval)
92 
93 char *hstring;		/* FITS header string */
94 const char *keyword;	/* Keyword name */
95 const float *rval;	/* float number */
96 
97 {
98     char value[30];
99 
100     /* Translate value from binary to ASCII */
101     sprintf (value, "%f", *rval);
102 
103     /* Remove sign if string is -0 or extension thereof */
104     fixnegzero (value);
105 
106     /* Put value into header string */
107     return (hputc (hstring, keyword, value));
108 }
109 
110 
111 /*  HPUTR8 - Set double keyword = dval in FITS header string */
112 
113 int
hputr8(hstring,keyword,dval)114 hputr8 (hstring, keyword, dval)
115 
116 char	*hstring;	/* FITS header string */
117 const char *keyword;	/* Keyword name */
118 const double dval;	/* double number */
119 {
120     char value[30];
121 
122     /* Translate value from binary to ASCII */
123     sprintf (value, "%g", dval);
124 
125     /* Remove sign if string is -0 or extension thereof */
126     fixnegzero (value);
127 
128     /* Put value into header string */
129     return (hputc (hstring, keyword, value));
130 }
131 
132 
133 /*  HPUTNR8 - Set double keyword = dval in FITS header string */
134 
135 int
hputnr8(hstring,keyword,ndec,dval)136 hputnr8 (hstring, keyword, ndec, dval)
137 
138 char	*hstring;	/* FITS header string */
139 const char *keyword;	/* Keyword name */
140 const int ndec;		/* Number of decimal places to print */
141 const double dval;	/* double number */
142 {
143     char value[30];
144     char format[8];
145     int i, lval;
146 
147     /* Translate value from binary to ASCII */
148     if (ndec < 0) {
149 	sprintf (format, "%%.%dg", -ndec);
150 	sprintf (value, format, dval);
151 	lval = (int) strlen (value);
152 	for (i = 0; i < lval; i++)
153 	    if (value[i] == 'e') value[i] = 'E';
154 	}
155     else {
156 	sprintf (format, "%%.%df", ndec);
157 	sprintf (value, format, dval);
158 	}
159 
160     /* Remove sign if string is -0 or extension thereof */
161     fixnegzero (value);
162 
163     /* Put value into header string */
164     return (hputc (hstring, keyword, value));
165 }
166 
167 
168 /*  HPUTRA - Set double keyword = hh:mm:ss.sss in FITS header string */
169 
170 int
hputra(hstring,keyword,ra)171 hputra (hstring, keyword, ra)
172 
173 char *hstring;		/* FITS header string */
174 const char *keyword;	/* Keyword name */
175 const double ra;		/* Right ascension in degrees */
176 {
177     char value[30];
178 
179     /* Translate value from binary to ASCII */
180     ra2str (value, 30, ra, 3);
181 
182     /* Remove sign if string is -0 or extension thereof */
183     fixnegzero (value);
184 
185     /* Put value into header string */
186     return (hputs (hstring, keyword, value));
187 }
188 
189 
190 /*  HPUTDEC - Set double keyword = dd:mm:ss.sss in FITS header string */
191 
192 int
hputdec(hstring,keyword,dec)193 hputdec (hstring, keyword, dec)
194 
195 char *hstring;		/* FITS header string */
196 const char *keyword;	/* Keyword name */
197 const double dec;		/* Declination in degrees */
198 {
199     char value[30];
200 
201     /* Translate value from binary to ASCII */
202     dec2str (value, 30, dec, 2);
203 
204     /* Remove sign if string is -0 or extension thereof */
205     fixnegzero (value);
206 
207     /* Put value into header string */
208     return (hputs (hstring, keyword, value));
209 }
210 
211 
212 /* FIXNEGZERO -- Drop - sign from beginning of any string which is all zeros */
213 
214 static void
fixnegzero(string)215 fixnegzero (string)
216 
217 char *string;
218 {
219     int i, lstr;
220 
221     if (string[0] != '-')
222 	return;
223 
224     /* Drop out if any non-zero digits in this string */
225     lstr = (int) strlen (string);
226     for (i = 1; i < lstr; i++) {
227 	if (string[i] > '0' && string[i] <= '9')
228 	    return;
229 	if (string[i] == 'd' || string[i] == 'e' || string[i] == ' ')
230 	    break;
231 	}
232 
233     /* Drop - from start of string; overwrite string in place */
234     for (i = 1; i < lstr; i++)
235 	string[i-1] = string[i];
236     string[lstr-1] = (char) 0;
237 
238     return;
239 }
240 
241 
242 
243 /*  HPUTL - Set keyword = F if lval=0, else T, in FITS header string */
244 
245 int
hputl(hstring,keyword,lval)246 hputl (hstring, keyword,lval)
247 
248 char *hstring;		/* FITS header */
249 const char *keyword;	/* Keyword name */
250 const int lval;		/* logical variable (0=false, else true) */
251 {
252     char value[8];
253 
254     /* Translate value from binary to ASCII */
255     if (lval)
256 	strcpy (value, "T");
257     else
258 	strcpy (value, "F");
259 
260     /* Put value into header string */
261     return (hputc (hstring,keyword,value));
262 }
263 
264 
265 /*  HPUTM - Set multi-line character string in FITS header string */
266 /*          return number of keywords written */
267 
268 int
hputm(hstring,keyword,cval)269 hputm (hstring,keyword,cval)
270 
271 char *hstring;	/* FITS header */
272 const char *keyword;	/* Keyword name root (6 characters or less) */
273 const char *cval;	/* character string containing the value for variable
274 		   keyword.  trailing and leading blanks are removed.  */
275 {
276     int lroot, lcv, i, ii, nkw, lkw, lval;
277     int comment = 0;
278     const char *v;
279     char keyroot[8], newkey[12], value[80];
280     char squot = 39;
281 
282     /*  If COMMENT or HISTORY, use the same keyword on every line */
283     lkw = (int) strlen (keyword);
284     if (lkw == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
285 	strncmp (keyword,"HISTORY",7) == 0)) {
286 	comment = 1;
287 	lroot = 0;
288 	}
289 
290     /* Set up keyword root, shortening it to 6 characters, if necessary */
291     else {
292 	comment = 0;
293 	strcpy (keyroot, keyword);
294 	lroot = (int) strlen (keyroot);
295 	if (lroot > 6) {
296 	    keyroot[6] = (char) 0;
297 	    lroot = 6;
298 	    }
299 	}
300 
301     /* Write keyword value one line of up to 67 characters at a time */
302     ii = '1';
303     nkw = 0;
304     lcv = (int) strlen (cval);
305     if (!comment) {
306 	strcpy (newkey, keyroot);
307 	strcat (newkey, "_");
308 	newkey[lroot+2] = (char) 0;
309 	}
310     v = cval;
311     while (lcv > 0) {
312 	if (lcv > 67)
313 	    lval = 67;
314 	else
315 	    lval = lcv;
316 	value[0] = squot;
317 	for (i = 1; i <= lval; i++)
318 	    value[i] = *v++;
319 
320 	/* Pad short strings to 8 characters */
321 	if (lval < 8) {
322 	    for (i = lval+1; i < 9; i++)
323 		value[i] = ' ';
324 	    lval = 8;
325 	    }
326 	value[lval+1] = squot;
327 	value[lval+2] = (char) 0;
328 
329 	/* Add this line to the header */
330 	if (comment)
331 	    i = hputc (hstring, keyroot, value);
332 	else {
333 	    newkey[lroot+1] = ii;
334 	    ii++;
335 	    i = hputc (hstring, newkey, value);
336 	    }
337 	if (i != 0) return (i);
338 	nkw++;
339 	if (lcv > 67)
340 	    lcv = lcv - 67;
341 	else
342 	    break;
343 	}
344     return (nkw);
345 }
346 
347 
348 /*  HPUTS - Set character string keyword = 'cval' in FITS header string */
349 
350 int
hputs(hstring,keyword,cval)351 hputs (hstring,keyword,cval)
352 
353 char *hstring;	/* FITS header */
354 const char *keyword; /* Keyword name */
355 const char *cval; /* character string containing the value for variable
356 		   keyword.  trailing and leading blanks are removed.  */
357 {
358     char squot = 39;
359     char value[80];
360     int lcval, i, lkeyword;
361 
362     /*  If COMMENT or HISTORY, just add it as is */
363     lkeyword = (int) strlen (keyword);
364     if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
365 	strncmp (keyword,"HISTORY",7) == 0))
366 	return (hputc (hstring,keyword,cval));
367 
368     /*  find length of variable string */
369     lcval = (int) strlen (cval);
370     if (lcval > 67)
371 	lcval = 67;
372 
373     /* Put single quote at start of string */
374     value[0] = squot;
375     strncpy (&value[1],cval,lcval);
376 
377     /* If string is less than eight characters, pad it with spaces */
378     if (lcval < 8) {
379 	for (i = lcval; i < 8; i++) {
380 	    value[i+1] = ' ';
381 	    }
382 	lcval = 8;
383 	}
384 
385     /* Add single quote and null to end of string */
386     value[lcval+1] = squot;
387     value[lcval+2] = (char) 0;
388 
389     /* Put value into header string */
390     return (hputc (hstring,keyword,value));
391 }
392 
393 
394 /*  HPUTC - Set character string keyword = value in FITS header string */
395 /*          Return -1 if error, 0 if OK */
396 
397 int
hputc(hstring,keyword,value)398 hputc (hstring,keyword,value)
399 
400 char *hstring;
401 const char *keyword;
402 const char *value; /* character string containing the value for variable
403 		   keyword.  trailing and leading blanks are removed.  */
404 {
405     char squot = 39;
406     char line[100];
407     char newcom[50];
408     char *vp, *v1, *v2, *q1, *q2, *c1, *ve;
409     int lkeyword, lcom, lval, lc, lv1, lhead, lblank, ln, nc, i;
410 
411     /* Find length of keyword, value, and header */
412     lkeyword = (int) strlen (keyword);
413     lval = (int) strlen (value);
414     lhead = gethlength (hstring);
415 
416     /*  If COMMENT or HISTORY, always add it just before the END */
417     if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
418 	strncmp (keyword,"HISTORY",7) == 0)) {
419 
420 	/* First look for blank lines before END */
421         v1 = blsearch (hstring, "END");
422 
423 	/*  Otherwise, create a space for it at the end of the header */
424 	if (v1 == NULL) {
425 
426 	    /* Find end of header */
427 	    v1 = ksearch (hstring,"END");
428 
429 	    /* Align pointer at start of 80-character line */
430 	    lc = v1 - hstring;
431 	    ln = lc / 80;
432 	    nc = ln * 80;
433 	    v1 = hstring + nc;
434 	    v2 = v1 + 80;
435 
436 	    /* If header length is exceeded, return error code */
437 	    if (v2 - hstring > lhead) {
438 		return (-1);
439 		}
440 
441 	    /* Move END down 80 characters */
442 	    strncpy (v2, v1, 80);
443 	    }
444 	else
445 	    v2 = v1 + 80;
446 
447 	/* Insert keyword */
448 	strncpy (v1,keyword,7);
449 
450 	/* Pad with spaces */
451 	for (vp = v1+lkeyword; vp < v2; vp++)
452 	    *vp = ' ';
453 
454 	if (lval > 71)
455 	    lv1 = 71;
456 	else
457 	    lv1 = lval;
458 
459 	/* Insert comment */
460 	strncpy (v1+9,value,lv1);
461 	return (0);
462 	}
463 
464     /* Otherwise search for keyword */
465     else
466 	v1 = ksearch (hstring,keyword);
467 
468     /*  If parameter is not found, find a place to put it */
469     if (v1 == NULL) {
470 
471 	/* First look for blank lines before END */
472         v1 = blsearch (hstring, "END");
473 
474 	/*  Otherwise, create a space for it at the end of the header */
475 	if (v1 == NULL) {
476 	    ve = ksearch (hstring,"END");
477 	    v1 = ve;
478 
479 	    /* Align pointer at start of 80-character line */
480 	    lc = v1 - hstring;
481 	    ln = lc / 80;
482 	    nc = ln * 80;
483 	    v1 = hstring + nc;
484 	    v2 = v1 + 80;
485 
486 	    /* If header length is exceeded, return error code */
487 	    if (v2 - hstring > lhead) {
488 		return (-1);
489 		}
490 
491 	    strncpy (v2, ve, 80);
492 	    }
493 	else
494 	    v2 = v1 + 80;
495 	lcom = 0;
496 	newcom[0] = 0;
497 	}
498 
499     /*  Otherwise, extract the entry for this keyword from the header */
500     else {
501 
502 	/* Align pointer at start of 80-character line */
503 	lc = v1 - hstring;
504 	ln = lc / 80;
505 	nc = ln * 80;
506 	v1 = hstring + nc;
507 	v2 = v1 + 80;
508 
509 	strncpy (line, v1, 80);
510 	line[80] = 0;
511 	v2 = v1 + 80;
512 
513 	/*  check for quoted value */
514 	q1 = strchr (line, squot);
515 	if (q1 != NULL) {
516 	    q2 = strchr (q1+1,squot);
517 	    if (q2 != NULL)
518 		c1 = strchr (q2,'/');
519 	    else
520 		c1 = strrchr (line+79,'/');
521 	    }
522 	else
523 	    c1 = strchr (line,'/');
524 
525 	/*  extract comment and discount trailing spaces */
526 	if (c1 != NULL) {
527 	    lcom = 80 - (c1 + 2 - line);
528 	    strncpy (newcom, c1+2, lcom);
529 	    vp = newcom + lcom - 1;
530 	    while (vp-- > newcom && *vp == ' ')
531 		lcom--;
532 	    }
533 	else {
534 	    newcom[0] = 0;
535 	    lcom = 0;
536 	    }
537 	}
538 
539     /* Fill new entry with spaces */
540     for (vp = v1; vp < v2; vp++)
541 	*vp = ' ';
542 
543     /*  Copy keyword to new entry */
544     strncpy (v1, keyword, lkeyword);
545 
546     /*  Add parameter value in the appropriate place */
547     vp = v1 + 8;
548     *vp = '=';
549     vp = v1 + 9;
550     *vp = ' ';
551     vp = vp + 1;
552     if (*value == squot) {
553 	strncpy (vp, value, lval);
554 	if (lval+12 > 31)
555 	    lc = lval + 12;
556 	else
557 	    lc = 30;
558 	}
559     else {
560 	vp = v1 + 30 - lval;
561 	strncpy (vp, value, lval);
562 	lc = 30;
563 	}
564 
565     /* Add comment in the appropriate place */
566 	if (lcom > 0) {
567 	    if (lc+2+lcom > 80)
568 		lcom = 77 - lc;
569 	    vp = v1 + lc;     /* Jul 16 1997: was vp = v1 + lc * 2 */
570 	    *vp++ = ' ';
571 	    *vp++ = '/';
572 	    *vp++ = ' ';
573 	    lblank = v2 - vp;
574 	    for (i = 0; i < lblank; i++)
575 		vp[i] = ' ';
576 	    if (lcom > lblank)
577 		lcom = lblank;
578 	    strncpy (vp, newcom, lcom);
579 	    }
580 
581 	if (verbose) {
582 	    if (lcom > 0)
583 		fprintf (stderr,"HPUT: %s  = %s  / %s\n",keyword, value, newcom);
584 	    else
585 		fprintf (stderr,"HPUT: %s  = %s\n",keyword, value);
586 	    }
587 
588 	return (0);
589 }
590 
591 
592 /*  HPUTCOM - Set comment for keyword or on line in FITS header string */
593 
594 int
hputcom(hstring,keyword,comment)595 hputcom (hstring,keyword,comment)
596 
597   char *hstring;
598   const char *keyword;
599   const char *comment;
600 {
601     char squot, slash, space;
602     char line[100];
603     int lkeyword, lcom, lhead, i, lblank, ln, nc, lc;
604     char *vp, *v1, *v2, *c0, *c1, *q1, *q2;
605 
606     squot = (char) 39;
607     slash = (char) 47;
608     space = (char) 32;
609 
610     /*  Find length of variable name */
611     lkeyword = (int) strlen (keyword);
612     lhead = gethlength (hstring);
613     lcom = (int) strlen (comment);
614 
615     /*  If COMMENT or HISTORY, always add it just before the END */
616     if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
617 	strncmp (keyword,"HISTORY",7) == 0)) {
618 
619 	/* Find end of header */
620 	v1 = ksearch (hstring,"END");
621 
622 	/* Align pointer at start of 80-character line */
623 	lc = v1 - hstring;
624 	ln = lc / 80;
625 	nc = ln * 80;
626 	v1 = hstring + nc;
627 	v2 = v1 + 80;
628 
629 	/* If header length is exceeded, return error code */
630 	if (v2 - hstring > lhead) {
631 	    return (-1);
632 	    }
633 
634 	/* Move END down 80 characters */
635 	strncpy (v2, v1, 80);
636 
637 	/*  blank out new line and insert keyword */
638 	for (vp = v1; vp < v2; vp++)
639 	    *vp = ' ';
640 	strncpy (v1, keyword, lkeyword);
641 	c0 = v1 + lkeyword;
642 	}
643 
644     /* Search header string for variable name */
645     else {
646 	v1 = ksearch (hstring,keyword);
647 
648 	/* If parameter is not found, return without doing anything */
649 	if (v1 == NULL) {
650 	    if (verbose)
651 		fprintf (stderr,"HPUTCOM: %s not found\n",keyword);
652 	    return (-1);
653 	    }
654 
655 	/* Align pointer at start of 80-character line */
656 	lc = v1 - hstring;
657 	ln = lc / 80;
658 	nc = ln * 80;
659 	v1 = hstring + nc;
660 	v2 = v1 + 80;
661 
662 	/* Extract entry for this variable from the header */
663 	strncpy (line, v1, 80);
664 	line[80] = '\0'; /* Null-terminate line before strchr call */
665 
666 	/* check for quoted value */
667 	q1 = strchr (line,squot);
668 	c1 = strchr (line,slash);
669 	if (q1 != NULL) {
670 	    if (c1 != NULL && q1 < c1) {
671 		q2 = strchr (q1+1, squot);
672 		if (q2 == NULL) {
673 		    q2 = c1 - 1;
674 		    while (*q2 == space)
675 			q2--;
676 		    q2++;
677 		    }
678 		else if (c1 < q2)
679 		    c1 = strchr (q2, slash);
680 		}
681 	    else if (c1 == NULL) {
682 		q2 = strchr (q1+1, squot);
683 		if (q2 == NULL) {
684 		    q2 = line + 79;
685 		    while (*q2 == space)
686 			q2--;
687 		    q2++;
688 		    }
689 		}
690 	    else
691 		q1 = NULL;
692 		q2 = NULL;
693 	    }
694 
695 	else
696 	    q2 = NULL;
697 
698 	if (c1 != NULL)
699 	    c0 = v1 + (c1 - line) - 1;
700 	else if (q2 == NULL || q2-line < 30)
701 	    c0 = v1 + 30;
702 	else
703 	    c0 = v1 + (q2 - line) + 1; /* allan: 1997-09-30, was c0=q2+2 */
704 
705 	/* If comment will not fit at all, return */
706 	if (c0 - v1 > 77)
707 	    return (-1);
708 	strncpy (c0, " / ",3);
709 	}
710 
711     /* Create new entry */
712     if (lcom > 0) {
713 	c1 = c0 + 3;
714 	lblank = v1 + 79 - c1;
715 	if (lcom > lblank)
716 	    lcom = lblank;
717 	for (i = 0; i < lblank; i++)
718 	    c1[i] = ' ';
719 	strncpy (c1, comment, lcom);
720 	}
721 
722     if (verbose) {
723 	fprintf (stderr,"HPUTCOM: %s / %s\n",keyword,comment);
724 	}
725     return (0);
726 }
727 
728 
729 static int leaveblank = 0;	/* If 1, leave blank line when deleting */
730 void
setleaveblank(lb)731 setleaveblank (lb)
732 int lb; { leaveblank = lb; return; }
733 
734 static int headshrink=1; /* Set to 1 to drop line after deleting keyword */
735 void
setheadshrink(hsh)736 setheadshrink (hsh)
737 int hsh;
738 {headshrink = hsh; return;}
739 
740 /*  HDEL - Set character string keyword = value in FITS header string
741  *	    returns 1 if entry deleted, else 0
742  */
743 
744 int
hdel(hstring,keyword)745 hdel (hstring,keyword)
746 
747 char *hstring;		/* FITS header */
748 const char *keyword;	/* Keyword of entry to be deleted */
749 {
750     char *v, *v1, *v2, *ve;
751 
752     /* Search for keyword */
753     v1 = ksearch (hstring,keyword);
754 
755     /*  If keyword is not found, return header unchanged */
756     if (v1 == NULL) {
757 	return (0);
758 	}
759 
760     /*  Find end of header */
761     ve = ksearch (hstring,"END");
762 
763     /* If headshrink is 0, leave END where it is */
764     if (!leaveblank && !headshrink)
765 	ve = ve - 80;
766 
767     /* Cover deleted keyword line with spaces */
768     if (leaveblank) {
769 	v2 = v1 + 80;
770 	for (v = ve; v < v2; v++)
771 	    *v = ' ';
772 	}
773 
774     /* Shift rest of header up one line */
775     else {
776 	for (v = v1; v < ve; v = v + 80) {
777 	    v2 = v + 80;
778 	    strncpy (v, v2, 80);
779 	    }
780 
781 	/* Cover former last line with spaces */
782 	v2 = ve + 80;
783 	for (v = ve; v < v2; v++)
784 	    *v = ' ';
785 	}
786 
787     return (1);
788 }
789 
790 
791 /*  HADD - Add character string keyword = value to FITS header string
792  *	    returns 1 if entry added, else 0
793  *	    Call hputx() to put value into entry
794  */
795 
796 int
hadd(hplace,keyword)797 hadd (hplace, keyword)
798 
799 char *hplace;		/* FITS header position for new keyword */
800 const char *keyword;	/* Keyword of entry to be deleted */
801 {
802     char *v, *v1, *v2, *ve;
803     int i, lkey;
804 
805     /*  Find end of header */
806     ve = ksearch (hplace,"END");
807 
808     /*  If END is not found, return header unchanged */
809     if (ve == NULL) {
810 	return (0);
811 	}
812 
813     v1 = hplace;
814 
815     /* Shift rest of header down one line */
816     /* limit bug found by Paolo Montegriffo fixed 2000-04-19 */
817     for (v = ve; v >= v1; v = v - 80) {
818 	v2 = v + 80;
819 	strncpy (v2, v, 80);
820 	}
821 
822     /* Cover former first line with new keyword */
823     lkey = (int) strlen (keyword);
824     strncpy (hplace, keyword, lkey);
825     if (lkey < 8) {
826 	for (i = lkey; i < 8; i++)
827 	    hplace[i] = ' ';
828 	hplace[8] = '=';
829 	}
830     for (i = 9; i < 80; i++)
831 	hplace[i] = ' ';
832 
833     return (1);
834 }
835 
836 
837 /*  HCHANGE - Changes keyword for entry from keyword1 to keyword2 in FITS
838               header string
839  *	      returns 1 if entry changed, else 0
840  */
841 
842 int
hchange(hstring,keyword1,keyword2)843 hchange (hstring, keyword1, keyword2)
844 
845 char *hstring;		/* FITS header */
846 const char *keyword1;	/* Keyword to be changed */
847 const char *keyword2;	/* New keyword name */
848 {
849     char *v, *v1;
850     const char *v2;
851     int lv2, i;
852 
853     /* Search for keyword */
854     v1 = ksearch (hstring,keyword1);
855 
856     /*  If keyword is not found, return header unchanged */
857     if (!v1)
858 	return (0);
859 
860     else {
861 	lv2 = (int) strlen (keyword2);
862 	v = v1;
863 	v2 = keyword2;
864 	for (i = 0; i < 8; i++) {
865 	    if (i < lv2)
866 		v[i] = v2[i];
867 	    else
868 		v[i] = ' ';
869 	    }
870 	}
871 
872     return (1);
873 }
874 
875 
876 /* Write the right ascension ra in sexagesimal format into string*/
877 
878 void
ra2str(string,lstr,ra,ndec)879 ra2str (string, lstr, ra, ndec)
880 
881 char	*string;	/* Character string (returned) */
882 int	lstr;		/* Maximum number of characters in string */
883 double	ra;		/* Right ascension in degrees */
884 int	ndec;		/* Number of decimal places in seconds */
885 
886 {
887     double a,b;
888     double seconds;
889     char tstring[64];
890     int hours;
891     int minutes;
892     int isec, ltstr;
893     double dsgn;
894 
895     /* Keep RA between 0 and 360 */
896     if (ra < 0.0 ) {
897 	ra = -ra;
898 	dsgn = -1.0;
899 	}
900     else
901 	dsgn = 1.0;
902     ra = fmod(ra, 360.0);
903     ra *= dsgn;
904     if (ra < 0.0)
905 	ra = ra + 360.0;
906 
907     a = ra / 15.0;
908 
909     /* Convert to hours */
910     hours = (int) a;
911 
912     /* Compute minutes */
913     b =  (a - (double)hours) * 60.0;
914     minutes = (int) b;
915 
916     /* Compute seconds */
917     seconds = (b - (double)minutes) * 60.0;
918 
919     if (ndec > 5) {
920 	if (seconds > 59.999999) {
921 	    seconds = 0.0;
922 	    minutes = minutes + 1;
923 	    }
924 	if (minutes > 59) {
925 	    minutes = 0;
926 	    hours = hours + 1;
927 	    }
928 	hours = hours % 24;
929 	(void) sprintf (tstring,"%02d:%02d:%09.6f",hours,minutes,seconds);
930 	}
931     else if (ndec > 4) {
932 	if (seconds > 59.99999) {
933 	    seconds = 0.0;
934 	    minutes = minutes + 1;
935 	    }
936 	if (minutes > 59) {
937 	    minutes = 0;
938 	    hours = hours + 1;
939 	    }
940 	hours = hours % 24;
941 	(void) sprintf (tstring,"%02d:%02d:%08.5f",hours,minutes,seconds);
942 	}
943     else if (ndec > 3) {
944 	if (seconds > 59.9999) {
945 	    seconds = 0.0;
946 	    minutes = minutes + 1;
947 	    }
948 	if (minutes > 59) {
949 	    minutes = 0;
950 	    hours = hours + 1;
951 	    }
952 	hours = hours % 24;
953 	(void) sprintf (tstring,"%02d:%02d:%07.4f",hours,minutes,seconds);
954 	}
955     else if (ndec > 2) {
956 	if (seconds > 59.999) {
957 	    seconds = 0.0;
958 	    minutes = minutes + 1;
959 	    }
960 	if (minutes > 59) {
961 	    minutes = 0;
962 	    hours = hours + 1;
963 	    }
964 	hours = hours % 24;
965 	(void) sprintf (tstring,"%02d:%02d:%06.3f",hours,minutes,seconds);
966 	}
967     else if (ndec > 1) {
968 	if (seconds > 59.99) {
969 	    seconds = 0.0;
970 	    minutes = minutes + 1;
971 	    }
972 	if (minutes > 59) {
973 	    minutes = 0;
974 	    hours = hours + 1;
975 	    }
976 	hours = hours % 24;
977 	(void) sprintf (tstring,"%02d:%02d:%05.2f",hours,minutes,seconds);
978 	}
979     else if (ndec > 0) {
980 	if (seconds > 59.9) {
981 	    seconds = 0.0;
982 	    minutes = minutes + 1;
983 	    }
984 	if (minutes > 59) {
985 	    minutes = 0;
986 	    hours = hours + 1;
987 	    }
988 	hours = hours % 24;
989 	(void) sprintf (tstring,"%02d:%02d:%04.1f",hours,minutes,seconds);
990 	}
991     else {
992 	isec = (int)(seconds + 0.5);
993 	if (isec > 59) {
994 	    isec = 0;
995 	    minutes = minutes + 1;
996 	    }
997 	if (minutes > 59) {
998 	    minutes = 0;
999 	    hours = hours + 1;
1000 	    }
1001 	hours = hours % 24;
1002 	(void) sprintf (tstring,"%02d:%02d:%02d",hours,minutes,isec);
1003 	}
1004 
1005     /* Move formatted string to returned string */
1006     ltstr = (int) strlen (tstring);
1007     if (ltstr < lstr-1)
1008 	strcpy (string, tstring);
1009     else {
1010 	strncpy (string, tstring, lstr-1);
1011 	string[lstr-1] = 0;
1012 	}
1013     return;
1014 }
1015 
1016 
1017 /* Write the variable a in sexagesimal format into string */
1018 
1019 void
dec2str(string,lstr,dec,ndec)1020 dec2str (string, lstr, dec, ndec)
1021 
1022 char	*string;	/* Character string (returned) */
1023 int	lstr;		/* Maximum number of characters in string */
1024 double	dec;		/* Declination in degrees */
1025 int	ndec;		/* Number of decimal places in arcseconds */
1026 
1027 {
1028     double a, b, dsgn, deg1;
1029     double seconds;
1030     char sign;
1031     int degrees;
1032     int minutes;
1033     int isec, ltstr;
1034     char tstring[64];
1035 
1036     /* Keep angle between -180 and 360 degrees */
1037     deg1 = dec;
1038     if (deg1 < 0.0 ) {
1039 	deg1 = -deg1;
1040 	dsgn = -1.0;
1041 	}
1042     else
1043 	dsgn = 1.0;
1044     deg1 = fmod(deg1, 360.0);
1045     deg1 *= dsgn;
1046     if (deg1 <= -180.0)
1047 	deg1 = deg1 + 360.0;
1048 
1049     a = deg1;
1050 
1051     /* Set sign and do all the rest with a positive */
1052     if (a < 0) {
1053 	sign = '-';
1054 	a = -a;
1055 	}
1056     else
1057 	sign = '+';
1058 
1059     /* Convert to degrees */
1060     degrees = (int) a;
1061 
1062     /* Compute minutes */
1063     b =  (a - (double)degrees) * 60.0;
1064     minutes = (int) b;
1065 
1066     /* Compute seconds */
1067     seconds = (b - (double)minutes) * 60.0;
1068 
1069     if (ndec > 5) {
1070 	if (seconds > 59.999999) {
1071 	    seconds = 0.0;
1072 	    minutes = minutes + 1;
1073 	    }
1074 	if (minutes > 59) {
1075 	    minutes = 0;
1076 	    degrees = degrees + 1;
1077 	    }
1078 	(void) sprintf (tstring,"%c%02d:%02d:%09.6f",sign,degrees,minutes,seconds);
1079 	}
1080     else if (ndec > 4) {
1081 	if (seconds > 59.99999) {
1082 	    seconds = 0.0;
1083 	    minutes = minutes + 1;
1084 	    }
1085 	if (minutes > 59) {
1086 	    minutes = 0;
1087 	    degrees = degrees + 1;
1088 	    }
1089 	(void) sprintf (tstring,"%c%02d:%02d:%08.5f",sign,degrees,minutes,seconds);
1090 	}
1091     else if (ndec > 3) {
1092 	if (seconds > 59.9999) {
1093 	    seconds = 0.0;
1094 	    minutes = minutes + 1;
1095 	    }
1096 	if (minutes > 59) {
1097 	    minutes = 0;
1098 	    degrees = degrees + 1;
1099 	    }
1100 	(void) sprintf (tstring,"%c%02d:%02d:%07.4f",sign,degrees,minutes,seconds);
1101 	}
1102     else if (ndec > 2) {
1103 	if (seconds > 59.999) {
1104 	    seconds = 0.0;
1105 	    minutes = minutes + 1;
1106 	    }
1107 	if (minutes > 59) {
1108 	    minutes = 0;
1109 	    degrees = degrees + 1;
1110 	    }
1111 	(void) sprintf (tstring,"%c%02d:%02d:%06.3f",sign,degrees,minutes,seconds);
1112 	}
1113     else if (ndec > 1) {
1114 	if (seconds > 59.99) {
1115 	    seconds = 0.0;
1116 	    minutes = minutes + 1;
1117 	    }
1118 	if (minutes > 59) {
1119 	    minutes = 0;
1120 	    degrees = degrees + 1;
1121 	    }
1122 	(void) sprintf (tstring,"%c%02d:%02d:%05.2f",sign,degrees,minutes,seconds);
1123 	}
1124     else if (ndec > 0) {
1125 	if (seconds > 59.9) {
1126 	    seconds = 0.0;
1127 	    minutes = minutes + 1;
1128 	    }
1129 	if (minutes > 59) {
1130 	    minutes = 0;
1131 	    degrees = degrees + 1;
1132 	    }
1133 	(void) sprintf (tstring,"%c%02d:%02d:%04.1f",sign,degrees,minutes,seconds);
1134 	}
1135     else {
1136 	isec = (int)(seconds + 0.5);
1137 	if (isec > 59) {
1138 	    isec = 0;
1139 	    minutes = minutes + 1;
1140 	    }
1141 	if (minutes > 59) {
1142 	    minutes = 0;
1143 	    degrees = degrees + 1;
1144 	    }
1145 	(void) sprintf (tstring,"%c%02d:%02d:%02d",sign,degrees,minutes,isec);
1146 	}
1147 
1148     /* Move formatted string to returned string */
1149     ltstr = (int) strlen (tstring);
1150     if (ltstr < lstr-1)
1151 	strcpy (string, tstring);
1152     else {
1153 	strncpy (string, tstring, lstr-1);
1154 	string[lstr-1] = 0;
1155 	}
1156    return;
1157 }
1158 
1159 
1160 /* Write the angle a in decimal format into string */
1161 
1162 void
deg2str(string,lstr,deg,ndec)1163 deg2str (string, lstr, deg, ndec)
1164 
1165 char	*string;	/* Character string (returned) */
1166 int	lstr;		/* Maximum number of characters in string */
1167 double	deg;		/* Angle in degrees */
1168 int	ndec;		/* Number of decimal places in degree string */
1169 
1170 {
1171     char degform[8];
1172     int field, ltstr;
1173     char tstring[64];
1174     double deg1;
1175     double dsgn;
1176 
1177     /* Keep angle between -180 and 360 degrees */
1178     deg1 = deg;
1179     if (deg1 < 0.0 ) {
1180 	deg1 = -deg1;
1181 	dsgn = -1.0;
1182 	}
1183     else
1184 	dsgn = 1.0;
1185     deg1 = fmod(deg1, 360.0);
1186     deg1 *= dsgn;
1187     if (deg1 <= -180.0)
1188 	deg1 = deg1 + 360.0;
1189 
1190     /* Write angle to string, adding 4 digits to number of decimal places */
1191     field = ndec + 4;
1192     if (ndec > 0) {
1193 	sprintf (degform, "%%%d.%df", field, ndec);
1194 	sprintf (tstring, degform, deg1);
1195 	}
1196     else {
1197 	sprintf (degform, "%%%4d", field);
1198 	sprintf (tstring, degform, (int)deg1);
1199 	}
1200 
1201     /* Move formatted string to returned string */
1202     ltstr = (int) strlen (tstring);
1203     if (ltstr < lstr-1)
1204 	strcpy (string, tstring);
1205     else {
1206 	strncpy (string, tstring, lstr-1);
1207 	string[lstr-1] = 0;
1208 	}
1209     return;
1210 }
1211 
1212 
1213 /* Write the variable a in decimal format into field-character string  */
1214 
1215 void
num2str(string,num,field,ndec)1216 num2str (string, num, field, ndec)
1217 
1218 char	*string;	/* Character string (returned) */
1219 double	num;		/* Number */
1220 int	field;		/* Number of characters in output field (0=any) */
1221 int	ndec;		/* Number of decimal places in degree string */
1222 
1223 {
1224     char numform[8];
1225 
1226     if (field > 0) {
1227 	if (ndec > 0) {
1228 	    sprintf (numform, "%%%d.%df", field, ndec);
1229 	    sprintf (string, numform, num);
1230 	    }
1231 	else {
1232 	    sprintf (numform, "%%%dd", field);
1233 	    sprintf (string, numform, (int)num);
1234 	    }
1235 	}
1236     else {
1237 	if (ndec > 0) {
1238 	    sprintf (numform, "%%.%df", ndec);
1239 	    sprintf (string, numform, num);
1240 	    }
1241 	else {
1242 	    sprintf (string, "%d", (int)num);
1243 	    }
1244 	}
1245     return;
1246 }
1247 
1248 /* Dec 14 1995	Original subroutines
1249 
1250  * Feb  5 1996	Added HDEL to delete keyword entry from FITS header
1251  * Feb  7 1996	Add EOS to LINE in HPUTC
1252  * Feb 21 1996	Add RA2STR and DEC2STR string routines
1253  * Jul 19 1996	Add HPUTRA and HPUTDEC
1254  * Jul 22 1996	Add HCHANGE to change keywords
1255  * Aug  5 1996	Add HPUTNR8 to save specific number of decimal places
1256  * Oct 15 1996	Fix spelling
1257  * Nov  1 1996	Add DEG2STR to set specific number of decimal places
1258  * Nov  1 1996	Allow DEC2STR to handle upt to 6 decimal places
1259  *
1260  * Mar 20 1997	Fix format error in DEG2STR
1261  * Jul  7 1997	Fix 2 errors in HPUTCOM found by Allan Brighton
1262  * Jul 16 1997	Fix error in HPUTC found by Allan Brighton
1263  * Jul 17 1997	Fix error in HPUTC found by Allan Brighton
1264  * Sep 30 1997	Fix error in HPUTCOM found by Allan Brighton
1265  * Dec 15 1997	Fix minor bugs after lint
1266  * Dec 31 1997	Always put two hour digits in RA2STR
1267  *
1268  * Feb 25 1998	Add HADD to insert keywords at specific locations
1269  * Mar 27 1998	If n is negative, write g format in HPUTNR8()
1270  * Apr 24 1998	Add NUM2STR() for easy output formatting
1271  * Apr 30 1998	Use BLSEARCH() to overwrite blank lines before END
1272  * May 27 1998	Keep Dec between -90 and +90 in DEC2STR()
1273  * May 28 1998	Keep RA between 0 and 360 in RA2STR()
1274  * Jun  2 1998	Fix bug when filling in blank lines before END
1275  * Jun 24 1998	Add string length to ra2str(), dec2str(), and deg2str()
1276  * Jun 25 1998	Make string converstion subroutines more robust
1277  * Aug 31 1998	Add getltime() and getutime()
1278  * Sep 28 1998	Null-terminate comment in HPUTCOM (Allan Brighton)
1279  * Oct  1 1998	Change clock declaration in getltime() from int (Allan Brighton)
1280  *
1281  * Jan 28 1999	Fix bug to avoid writing HISTORY or COMMENT past 80 characters
1282  * Jul 14 1999	Pad string in hputs() to minimum of 8 characters
1283  * Aug 16 1999	Keep angle between -180 and +360 in dec2str()
1284  * Oct  6 1999	Reallocate header buffer if it is too small in hputc()
1285  * Oct 14 1999	Do not reallocate header; return error if not successful
1286  *
1287  * Mar  2 2000	Do not add quotes if adding HISTORY or COMMENT with hputs()
1288  * Mar 22 2000	Move getutime() and getltime() to dateutil.c
1289  * Mar 27 2000	Add hputm() for muti-line keywords
1290  * Mar 27 2000	Fix bug testing for space to fit comment in hputcom()
1291  * Apr 19 2000	Fix bug in hadd() which overwrote line
1292  * Jun  2 2000	Dropped unused variable lv in hputm() after lint
1293  * Jul 20 2000	Drop unused variables blank and i in hputc()
1294  *
1295  * Jan 11 2001	Print all messages to stderr
1296  * Jan 18 2001	Drop declaration of blsearch(); it is in fitshead.h
1297  *
1298  * Jan  4 2002	Fix placement of comments
1299  *
1300  * Jul  1 2004	Add headshrink to optionally keep blank lines in header
1301  * Sep  3 2004	Fix bug so comments are not pushed onto next line if long value
1302  * Sep 16 2004	Add fixnegzero() to avoid putting signed zero values in header
1303  *
1304  * May 22 2006	Add option to leave blank line when deleting a keyword
1305  * Jun 15 2006	Fix comment alignment in hputc() and hputcom()
1306  * Jun 20 2006	Initialized uninitialized variables in hputm() and hputcom()
1307  *
1308  * Jan  4 2007	Declare keyword to be const
1309  * Jan  4 2007	Drop unused subroutine hputi2()
1310  * Jan  5 2007	Drop ksearch() declarations; it is now in fitshead.h
1311  * Jan 16 2007	Fix bugs in ra2str() and dec2str() so ndec=0 works
1312  * Aug 20 2007	Fix bug so comments after quoted keywords work
1313  * Aug 22 2007	If closing quote not found, make one up
1314  *
1315  * Sep  9 2011	Always initialize q2 and lroot
1316  */
1317