1 /*********************************************************************
2 Fits - View and manipulate FITS extensions and/or headers.
3 Fits is part of GNU Astronomy Utilities (Gnuastro) package.
4 
5 Original author:
6      Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2017-2021, Free Software Foundation, Inc.
9 
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14 
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24 
25 #include <errno.h>
26 #include <error.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include <gnuastro/wcs.h>
31 #include <gnuastro/list.h>
32 #include <gnuastro/fits.h>
33 #include <gnuastro/blank.h>
34 #include <gnuastro/pointer.h>
35 #include <gnuastro/statistics.h>
36 
37 #include <gnuastro-internal/timing.h>
38 #include <gnuastro-internal/checkset.h>
39 
40 #include "main.h"
41 
42 #include "fits.h"
43 #include "keywords.h"
44 
45 
46 
47 int
fits_has_error(struct fitsparams * p,int actioncode,char * string,int status)48 fits_has_error(struct fitsparams *p, int actioncode, char *string, int status)
49 {
50   char *action=NULL;
51   int r=EXIT_SUCCESS;
52 
53   switch(actioncode)
54     {
55     case FITS_ACTION_DELETE:        action="deleted";      break;
56     case FITS_ACTION_RENAME:        action="renamed";      break;
57     case FITS_ACTION_UPDATE:        action="updated";      break;
58     case FITS_ACTION_WRITE:         action="written";      break;
59     case FITS_ACTION_COPY:          action="copied";       break;
60     case FITS_ACTION_REMOVE:        action="removed";      break;
61 
62     default:
63       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' so we "
64             "can fix this problem. The value of 'actioncode' must not be %d",
65             __func__, PACKAGE_BUGREPORT, actioncode);
66     }
67 
68   if(p->quitonerror)
69     {
70       fits_report_error(stderr, status);
71       error(EXIT_FAILURE, 0, "%s: %s: not %s\n", __func__, string, action);
72     }
73   else
74     {
75       fprintf(stderr, "%s: Not %s.\n", string, action);
76       r=EXIT_FAILURE;
77     }
78   return r;
79 }
80 
81 
82 
83 
84 
85 /* Print all the extension informations. */
86 void
fits_print_extension_info(struct fitsparams * p)87 fits_print_extension_info(struct fitsparams *p)
88 {
89   uint16_t *ui16;
90   fitsfile *fptr;
91   size_t i, numext, *dsize, ndim;
92   int hascomments=0, hasblankname=0;
93   char **tstra, **estra, **sstra, **cmstra;
94   int j, nc, numhdu, hdutype, status=0, type;
95   gal_data_t *tmp, *cols=NULL, *sizecol, *commentscol;
96   char *msg, *tstr=NULL, sstr[1000], extname[FLEN_VALUE];
97 
98 
99   /* Open the FITS file and read the first extension type, upon moving to
100      the next extension, we will read its type, so for the first we will
101      need to do it explicitly. */
102   fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
103   if (fits_get_hdu_type(fptr, &hdutype, &status) )
104     gal_fits_io_error(status, "reading first extension");
105 
106 
107   /* Get the number of HDUs. */
108   if( fits_get_num_hdus(fptr, &numhdu, &status) )
109     gal_fits_io_error(status, "finding number of HDUs");
110   numext=numhdu;
111 
112 
113   /* Allocate all the columns (in reverse order, since this is a simple
114      linked list). */
115   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
116                           -1, 1, "HDU_COMMENT", "note", "Possible comment");
117   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
118                           -1, 1, "HDU_SIZE", "name", "Size of image or table "
119                           "number of rows and columns.");
120   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
121                           -1, 1, "HDU_TYPE", "name", "Image data type or "
122                           "'table' format (ASCII or binary).");
123   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
124                           -1, 1, "EXTNAME", "name", "Extension name of this "
125                           "HDU (EXTNAME in FITS).");
126   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_UINT16, 1, &numext, NULL, 1,
127                           -1, 1, "HDU_INDEX", "count", "Index (starting from "
128                           "zero) of each HDU (extension).");
129 
130 
131   /* Keep pointers to the array of each column for easy writing. */
132   ui16  = cols->array;
133   estra = cols->next->array;
134   tstra = cols->next->next->array;
135 
136   sizecol = cols->next->next->next;
137   sstra = sizecol->array;
138 
139   commentscol = cols->next->next->next->next;
140   cmstra = commentscol->array;
141 
142   cols->next->disp_width=15;
143   cols->next->next->disp_width=15;
144 
145 
146   /* Fill in each column. */
147   for(i=0;i<numext;++i)
148     {
149       /* Work based on the type of the extension. */
150       switch(hdutype)
151         {
152         case IMAGE_HDU:
153           gal_fits_img_info(fptr, &type, &ndim, &dsize, NULL, NULL);
154           tstr = ndim==0 ? "no-data" : gal_type_name(type , 1);
155           break;
156 
157         case ASCII_TBL:
158         case BINARY_TBL:
159           ndim=2;
160           tstr = hdutype==ASCII_TBL ? "table_ascii" : "table_binary";
161           dsize=gal_pointer_allocate(GAL_TYPE_SIZE_T, 2, 0, __func__,
162                                      "dsize");
163           gal_fits_tab_size(fptr, dsize+1, dsize);
164           break;
165 
166         default:
167           error(EXIT_FAILURE, 0, "%s: a bug! the 'hdutype' code %d not "
168                 "recognized", __func__, hdutype);
169         }
170 
171 
172       /* Read the extension name*/
173       fits_read_keyword(fptr, "EXTNAME", extname, NULL, &status);
174       switch(status)
175         {
176         case 0:
177           gal_fits_key_clean_str_value(extname);
178           break;
179 
180         case KEY_NO_EXIST:
181           sprintf(extname, "%s", GAL_BLANK_STRING);
182           hasblankname=1;
183           status=0;
184           break;
185 
186         default:
187           gal_fits_io_error(status, "reading EXTNAME keyword");
188         }
189       status=0;
190 
191 
192       /* Check if its a healpix grid. */
193       if( gal_fits_hdu_is_healpix(fptr) )
194         {
195           hascomments=1;
196           gal_checkset_allocate_copy("HEALpix", cmstra+i);
197         }
198       else
199         gal_checkset_allocate_copy(GAL_BLANK_STRING, cmstra+i);
200 
201 
202       /* Write the size into a string. 'sprintf' returns the number of
203          written characters (excluding the '\0'). So for each dimension's
204          size that is written, we add to 'nc' (the number of
205          characters). Note that FITS allows blank extensions, in those
206          cases, return "0". */
207       if(ndim>0)
208         {
209           nc=0;
210           for(j=ndim-1;j>=0;--j)
211             nc += sprintf(sstr+nc, "%zux", dsize[j]);
212           sstr[nc-1]='\0';
213           free(dsize);
214         }
215       else
216         {
217           sstr[0]='0';
218           sstr[1]='\0';
219         }
220 
221 
222       /* Write the strings into the columns. */
223       j=0;
224       for(tmp=cols; tmp!=NULL; tmp=tmp->next)
225         {
226           switch(j)
227             {
228             case 0: ui16[i]=i;                                    break;
229             case 1: gal_checkset_allocate_copy(extname, estra+i); break;
230             case 2: gal_checkset_allocate_copy(tstr, tstra+i);    break;
231             case 3: gal_checkset_allocate_copy(sstr, sstra+i);    break;
232             }
233           ++j;
234         }
235 
236 
237       /* Move to the next extension if we aren't on the last extension. */
238       if( i!=numext-1 && fits_movrel_hdu(fptr, 1, &hdutype, &status) )
239         {
240           if( asprintf(&msg, "moving to hdu %zu", i+1)<0 )
241             error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
242           gal_fits_io_error(status, msg);
243         }
244     }
245 
246   /* Close the file. */
247   fits_close_file(fptr, &status);
248 
249   /* If there weren't any comments, don't print the comment column. */
250   if(hascomments==0)
251     {
252       sizecol->next=NULL;
253       gal_data_free(commentscol);
254     }
255 
256   /* Print the results. */
257   if(!p->cp.quiet)
258     {
259       printf("%s\nRun on %s-----\n", PROGRAM_STRING, ctime(&p->rawtime));
260       printf("HDU (extension) information: '%s'.\n", p->input->v);
261       printf(" Column 1: Index (counting from 0, usable with '--hdu').\n");
262       printf(" Column 2: Name ('EXTNAME' in FITS standard, usable with "
263              "'--hdu').\n");
264       if(hasblankname)
265         printf("           ('%s' means that no name is specified for this "
266                "HDU)\n", GAL_BLANK_STRING);
267       printf(" Column 3: Image data type or 'table' format (ASCII or "
268              "binary).\n");
269       printf(" Column 4: Size of data in HDU.\n");
270       if(hascomments)
271         printf(" Column 5: Comments about the HDU (e.g., if its HEALpix, or "
272                "etc).\n");
273       printf("-----\n");
274     }
275   gal_table_write(cols, NULL, NULL, GAL_TABLE_FORMAT_TXT, NULL, NULL, 0);
276   gal_list_data_free(cols);
277 }
278 
279 
280 
281 
282 
283 static void
fits_hdu_number(struct fitsparams * p)284 fits_hdu_number(struct fitsparams *p)
285 {
286   fitsfile *fptr;
287   int numhdu, status=0;
288 
289   /* Read the first extension (necessary for reading the rest). */
290   fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
291 
292   /* Get the number of HDUs. */
293   if( fits_get_num_hdus(fptr, &numhdu, &status) )
294     gal_fits_io_error(status, "finding number of HDUs");
295 
296   /* Close the file. */
297   fits_close_file(fptr, &status);
298 
299   /* Print the result. */
300   printf("%d\n", numhdu);
301 }
302 
303 
304 
305 
306 
307 static void
fits_datasum(struct fitsparams * p)308 fits_datasum(struct fitsparams *p)
309 {
310   printf("%ld\n", gal_fits_hdu_datasum(p->input->v, p->cp.hdu));
311 }
312 
313 
314 
315 
316 
317 static void
fits_pixelscale(struct fitsparams * p)318 fits_pixelscale(struct fitsparams *p)
319 {
320   int nwcs=0;
321   size_t i, ndim=0;
322   struct wcsprm *wcs;
323   double multip, *pixelscale;
324 
325   /* Read the desired WCS. */
326   wcs=gal_wcs_read(p->input->v, p->cp.hdu, p->cp.wcslinearmatrix,
327                    0, 0, &nwcs);
328 
329   /* If a WCS doesn't exist, let the user know and return. */
330   if(wcs)
331     ndim=wcs->naxis;
332   else
333     error(EXIT_FAILURE, 0, "%s (hdu %s): no WCS could be read by WCSLIB, "
334           "hence the pixel-scale cannot be determined", p->input->v,
335           p->cp.hdu);
336 
337   /* Calculate the pixel-scale in each dimension. */
338   pixelscale=gal_wcs_pixel_scale(wcs);
339 
340   /* If not in quiet-mode, print some extra information. We don't want the
341      last number to have a space after it, so we'll write the last one
342      outside the loop.*/
343   if(p->cp.quiet==0)
344     {
345       printf("Basic information for --pixelscale (remove extra info "
346              "with '--quiet' or '-q')\n");
347       printf("  Input: %s (hdu %s) has %zu dimensions.\n", p->input->v,
348              p->cp.hdu, ndim);
349       printf("  Pixel scale in each FITS dimension:\n");
350       for(i=0;i<ndim;++i)
351         {
352           if( !strcmp(wcs->cunit[i], "deg") )
353             printf("    %zu: %g (%s/pixel) = %g (arcsec/pixel)\n", i+1,
354                    pixelscale[i], wcs->cunit[i], pixelscale[i]*3600);
355           else
356             printf("    %zu: %g (%s/slice)\n", i+1,
357                    pixelscale[i], wcs->cunit[i]);
358         }
359 
360       /* Pixel area/volume. */
361       if(ndim>=2)
362         {
363           /* Multiply the values in each dimension. */
364           multip=pixelscale[0]*pixelscale[1];
365 
366           /* Pixel scale (applicable to 2 or 3 dimensions). */
367           printf("  Pixel area%s:\n", ndim==2?"":" (on each 2D slice) ");
368           if( strcmp(wcs->cunit[0], wcs->cunit[1]) )
369             printf("    %g (%s*%s)\n", multip, wcs->cunit[0],
370                    wcs->cunit[1]);
371           else
372             if( strcmp(wcs->cunit[0], "deg") )
373               printf("    %g (%s^2)\n", multip, wcs->cunit[0]);
374             else
375               printf("    %g (deg^2) = %g (arcsec^2)\n",
376                      multip, multip*3600*3600 );
377 
378           /* For a 3 dimensional dataset, we need need extra info. */
379           if(ndim>=3)
380             {
381               multip*=pixelscale[2];
382               printf("  Voxel volume:\n");
383               if( strcmp(wcs->cunit[0], wcs->cunit[1]) )
384                 printf("    %g (%s*%s*%s)\n", multip, wcs->cunit[0],
385                        wcs->cunit[1], wcs->cunit[2]);
386               else
387                 if( strcmp(wcs->cunit[0], "deg") )
388                   printf("    %g (%s^2*%s)\n", multip, wcs->cunit[0],
389                          wcs->cunit[2]);
390                 else
391                   {
392                     if( strcmp(wcs->cunit[2], "m") )
393                       printf("    %g (deg^2*%s) = %g (arcsec^2*%s)\n",
394                              multip, wcs->cunit[2], multip*3600*3600,
395                              wcs->cunit[2]);
396                     else
397                       printf("    %g (deg^2*m) = %g (arcsec^2*m) = "
398                              "%g (arcsec^2*A)\n", multip, multip*3600*3600,
399                              multip*3600*3600*1e10);
400                   }
401 
402               /* Higher-dimensional data are not yet supported. */
403               if(ndim>=4 && p->cp.quiet==0)
404                 error(EXIT_SUCCESS, 0, "WARNING: the '--pixelscale' "
405                       "option only prints area/volume information for "
406                       "2 or 3 dimensional datasets, this dataset is "
407                       "%zu dimensions! If you need this feature, please "
408                       "contact '%s'. To suppress this warning, please "
409                       "run with '--quiet'", ndim, PACKAGE_BUGREPORT);
410             }
411         }
412     }
413   else
414     {
415       multip=1;
416       for(i=0;i<ndim;++i)
417         {
418           multip*=pixelscale[i];
419           printf("%g ", pixelscale[i]);
420         }
421       switch(ndim)
422         {
423         case 2: printf("%g\n", multip); break;
424         case 3: printf("%g %g\n", pixelscale[0]*pixelscale[1], multip); break;
425         default: printf("\n");
426         }
427     }
428 
429   /* Clean up. */
430   wcsfree(wcs);
431   free(pixelscale);
432 }
433 
434 
435 
436 
437 
438 static void
fits_skycoverage(struct fitsparams * p)439 fits_skycoverage(struct fitsparams *p)
440 {
441   int nwcs=0;
442   size_t i, ndim;
443   struct wcsprm *wcs;
444   double *center, *width, *min, *max;
445 
446   /* Find the coverage. */
447   if( gal_wcs_coverage(p->input->v, p->cp.hdu, &ndim,
448                        &center, &width, &min, &max)==0 )
449     error(EXIT_FAILURE, 0, "%s (hdu %s): is not usable for finding "
450           "sky coverage (either doesn't have a WCS, or isn't an image "
451           "or cube HDU with 2 or 3 dimensions", p->input->v, p->cp.hdu);
452 
453   /* Inform the user. */
454   if(p->cp.quiet)
455     {
456       /* First print the center and full-width. */
457       for(i=0;i<ndim;++i) printf("%-15.10g ", center[i]);
458       for(i=0;i<ndim;++i) printf("%-15.10g ", width[i]);
459       printf("\n");
460 
461       /* Then print the range in coordinates. */
462       for(i=0;i<ndim;++i) printf("%-15.10g %-15.10g ", min[i], max[i]);
463       printf("\n");
464     }
465   else
466     {
467       printf("Input file: %s (hdu: %s)\n", p->input->v, p->cp.hdu);
468       printf("\nSky coverage by center and (full) width:\n");
469       switch(ndim)
470         {
471         case 2:
472           printf("  Center: %-15.10g%-15.10g\n", center[0], center[1]);
473           printf("  Width:  %-15.10g%-15.10g\n", width[0],  width[1]);
474           break;
475         case 3:
476           printf("  Center: %-15.10g%-15.10g%-15.10g\n", center[0],
477                  center[1], center[2]);
478           printf("  width:  %-15.10g%-15.10g%-15.10g\n", width[0],
479                  width[1], width[2]);
480           break;
481         default:
482           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
483                 "the problem. 'ndim' value %zu is not recognized", __func__,
484                 PACKAGE_BUGREPORT, ndim);
485         }
486 
487       /* For the range type of coverage. */
488       wcs=gal_wcs_read(p->input->v, p->cp.hdu, p->cp.wcslinearmatrix,
489                        0, 0, &nwcs);
490       printf("\nSky coverage by range along dimensions:\n");
491       for(i=0;i<ndim;++i)
492         printf("  %-8s %-15.10g%-15.10g\n", gal_wcs_dimension_name(wcs, i),
493                min[i], max[i]);
494       wcsfree(wcs);
495     }
496 
497   /* Clean up. */
498   free(min);
499   free(max);
500   free(width);
501   free(center);
502 }
503 
504 
505 
506 
507 
508 static char *
fits_list_certain_hdu_string(fitsfile * fptr,int hduindex)509 fits_list_certain_hdu_string(fitsfile *fptr, int hduindex)
510 {
511   size_t i;
512   int status=0;
513   char *out, extname[80];
514 
515   /* See if an 'EXTNAME' keyword exists for this HDU. */
516   fits_read_keyword(fptr, "EXTNAME", extname, NULL, &status);
517 
518   /* If EXTNAME doesn't exist, write the HDU index (counting from zero),
519      but if it does, remove the ending single quotes and possible extra
520      space characters (the staring single quote will be removed when
521      copying the name into 'out'). */
522   if(status) sprintf(extname, "%d", hduindex);
523   else
524     {
525       for(i=strlen(extname)-1; i>0; --i)
526         if(extname[i]!='\'' && extname[i]!=' ')
527           { extname[i+1]='\0'; break; }
528     }
529 
530   /* Put the value into the allocated 'out' string. */
531   gal_checkset_allocate_copy(status ? extname : extname+1, &out);
532   return out;
533 }
534 
535 
536 
537 
538 static void
fits_certain_hdu(struct fitsparams * p,int list1has0,int table1image0)539 fits_certain_hdu(struct fitsparams *p, int list1has0,
540                  int table1image0)
541 {
542   fitsfile *fptr;
543   gal_list_str_t *names=NULL;
544   int has=0, naxis, hducounter=1, hdutype, status=0;
545 
546   /* Open the FITS file. */
547   fits_open_file(&fptr, p->input->v, READONLY, &status);
548   gal_fits_io_error(status, NULL);
549 
550   /* Start by moving to the first HDU (counting from 1) and then parsing
551      through them. */
552   fits_movabs_hdu(fptr, hducounter, &hdutype, &status);
553   while(status==0)
554     {
555       /* Check the HDU type. */
556       switch(hdutype)
557         {
558 
559         /* Tables are easy. */
560         case BINARY_TBL:
561         case ASCII_TBL:
562           if(table1image0)
563             {
564               if(list1has0)
565                 gal_list_str_add(&names,
566                                  fits_list_certain_hdu_string(fptr,
567                                                               hducounter-1),
568                                  0);
569               else has=1;
570             }
571           break;
572 
573         /* For images, we need to check there is actually any data. */
574         case IMAGE_HDU:
575           if(table1image0==0)
576             {
577               fits_get_img_dim(fptr, &naxis, &status);
578               gal_fits_io_error(status, NULL);
579               if(naxis>0)
580                 {
581                   if(list1has0)
582                     gal_list_str_add(&names,
583                                      fits_list_certain_hdu_string(fptr,
584                                                                   hducounter-1),
585                                      0);
586                   else has=1;
587                 }
588             }
589           break;
590 
591         /* Just to avoid bad bugs. */
592         default:
593           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
594                 "to fix the problem. The value %d is not recognized "
595                 "for 'hdutype'", __func__, PACKAGE_BUGREPORT, hdutype);
596         }
597 
598       /* Move to the next HDU. */
599       fits_movabs_hdu(fptr, ++hducounter, &hdutype, &status);
600 
601       /* If we are in "has" mode and the first HDU has already been found,
602          then there is no need to continue parsing the HDUs, so set
603          'status' to 1 to stop the 'while' above. */
604       if( list1has0==0 && has==1 ) status=1;
605     }
606 
607   /* Close the file. */
608   status=0;
609   fits_close_file(fptr, &status);
610 
611   /* Print the result. */
612   if( list1has0 )
613     {
614       gal_list_str_print(names);
615       gal_list_str_free(names, 1);
616     }
617   else printf("%d\n", has);
618 }
619 
620 
621 
622 
623 
624 static void
fits_list_all_hdus(struct fitsparams * p)625 fits_list_all_hdus(struct fitsparams *p)
626 {
627   fitsfile *fptr;
628   gal_list_str_t *names=NULL;
629   int hducounter=1, hdutype, status=0;
630 
631   /* Open the FITS file. */
632   fits_open_file(&fptr, p->input->v, READONLY, &status);
633   gal_fits_io_error(status, NULL);
634 
635   /* Start by moving to the first HDU (counting from 1) and then parsing
636      through them. */
637   fits_movabs_hdu(fptr, hducounter, &hdutype, &status);
638   while(status==0)
639     {
640       gal_list_str_add(&names,
641                        fits_list_certain_hdu_string(fptr,
642                                                     hducounter-1),
643                        0);
644       fits_movabs_hdu(fptr, ++hducounter, &hdutype, &status);
645     }
646 
647   /* Close the file. */
648   status=0;
649   fits_close_file(fptr, &status);
650 
651   /* Print the result. */
652   gal_list_str_print(names);
653   gal_list_str_free(names, 1);
654 }
655 
656 
657 
658 
659 
660 static void
fits_hdu_remove(struct fitsparams * p,int * r)661 fits_hdu_remove(struct fitsparams *p, int *r)
662 {
663   char *hdu;
664   fitsfile *fptr;
665   int status=0, hdutype;
666 
667   while(p->remove)
668     {
669       /* Pop-out the top element. */
670       hdu=gal_list_str_pop(&p->remove);
671 
672       /* Open the FITS file at the specified HDU. */
673       fptr=gal_fits_hdu_open(p->input->v, hdu, READWRITE);
674 
675       /* Delete the extension. */
676       if( fits_delete_hdu(fptr, &hdutype, &status) )
677         *r=fits_has_error(p, FITS_ACTION_REMOVE, hdu, status);
678       status=0;
679 
680       /* Close the file. */
681       fits_close_file(fptr, &status);
682     }
683 }
684 
685 
686 
687 
688 
689 /* This is similar to the library's 'gal_fits_open_to_write', except that
690    it won't create an empty first extension.*/
691 fitsfile *
fits_open_to_write_no_blank(char * filename)692 fits_open_to_write_no_blank(char *filename)
693 {
694   int status=0;
695   fitsfile *fptr;
696 
697   /* When the file exists just open it. Otherwise, create the file. But we
698      want to leave the first extension as a blank extension and put the
699      image in the next extension to be consistent between tables and
700      images. */
701   if(access(filename,F_OK) == -1 )
702     {
703       /* Create the file. */
704       if( fits_create_file(&fptr, filename, &status) )
705         gal_fits_io_error(status, NULL);
706     }
707 
708   /* Open the file, ready for later steps. */
709   if( fits_open_file(&fptr, filename, READWRITE, &status) )
710     gal_fits_io_error(status, NULL);
711 
712   /* Return the pointer. */
713   return fptr;
714 }
715 
716 
717 
718 
719 
720 static void
fits_hdu_copy(struct fitsparams * p,int cut1_copy0,int * r)721 fits_hdu_copy(struct fitsparams *p, int cut1_copy0, int *r)
722 {
723   char *hdu;
724   int status=0, hdutype;
725   fitsfile *in, *out=NULL;
726   gal_list_str_t *list = cut1_copy0 ? p->cut : p->copy;
727 
728   /* Copy all the given extensions. */
729   while(list)
730     {
731       /* Pop-out the top element. */
732       hdu=gal_list_str_pop(&list);
733 
734       /* Open the FITS file at the specified HDU. */
735       in=gal_fits_hdu_open(p->input->v, hdu,
736                            cut1_copy0 ? READWRITE : READONLY);
737 
738       /* If the output isn't opened yet, open it.  */
739       if(out==NULL)
740         out = ( ( gal_fits_hdu_format(p->input->v, hdu)==IMAGE_HDU
741                   && p->primaryimghdu )
742                 ? fits_open_to_write_no_blank(p->cp.output)
743                 : gal_fits_open_to_write(p->cp.output) );
744 
745 
746       /* Copy to the extension. */
747       if( fits_copy_hdu(in, out, 0, &status) )
748         *r=fits_has_error(p, FITS_ACTION_COPY, hdu, status);
749       status=0;
750 
751       /* If this is a 'cut' operation, then remove the extension. */
752       if(cut1_copy0)
753         {
754           if( fits_delete_hdu(in, &hdutype, &status) )
755             *r=fits_has_error(p, FITS_ACTION_REMOVE, hdu, status);
756           status=0;
757         }
758 
759       /* Close the input file. */
760       fits_close_file(in, &status);
761     }
762 
763   /* Close the output file. */
764   if(out) fits_close_file(out, &status);
765 }
766 
767 
768 
769 
770 
771 int
fits(struct fitsparams * p)772 fits(struct fitsparams *p)
773 {
774   int r=EXIT_SUCCESS, printhduinfo=1;
775 
776   switch(p->mode)
777     {
778     /* Keywords, we have a separate set of functions in 'keywords.c'. */
779     case FITS_MODE_KEY:
780       r=keywords(p);
781       break;
782 
783     /* HDU, functions defined here. */
784     case FITS_MODE_HDU:
785 
786       /* Options that must be called alone. */
787       if(p->numhdus) fits_hdu_number(p);
788       else if(p->datasum)       fits_datasum(p);
789       else if(p->pixelscale)    fits_pixelscale(p);
790       else if(p->skycoverage)   fits_skycoverage(p);
791       else if(p->hasimagehdu)   fits_certain_hdu(p, 0, 0);
792       else if(p->hastablehdu)   fits_certain_hdu(p, 0, 1);
793       else if(p->listimagehdus) fits_certain_hdu(p, 1, 0);
794       else if(p->listtablehdus) fits_certain_hdu(p, 1, 1);
795       else if(p->listallhdus)   fits_list_all_hdus(p);
796 
797       /* Options that can be called together. */
798       else
799         {
800           if(p->copy)
801             {
802               fits_hdu_copy(p, 0, &r);
803               printhduinfo=0;
804             }
805           if(p->cut)
806             {
807               fits_hdu_copy(p, 1, &r);
808               printhduinfo=0;
809             }
810           if(p->remove)
811             {
812               fits_hdu_remove(p, &r);
813               printhduinfo=0;
814             }
815 
816           if(printhduinfo)
817             fits_print_extension_info(p);
818         }
819       break;
820 
821     /* Not recognized. */
822     default:
823       error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to address "
824             "the problem. The code %d is not recognized for p->mode",
825             __func__, PACKAGE_BUGREPORT, p->mode);
826     }
827 
828   return r;
829 }
830