gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 4d36efe9: Make extensions: first draft impleme


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 4d36efe9: Make extensions: first draft implementation
Date: Wed, 24 Aug 2022 20:32:33 -0400 (EDT)

branch: master
commit 4d36efe9663cbcd017309f00bd98469b3c66a7e0
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Make extensions: first draft implementation
    
    Until now, to use Gnuastro in Makefiles, it was necessary to use the
    'shell' function of Make. But 'shell' requires a system call by Make, and
    the called programs sometimes have a lot of overhead (when called many
    times for example).
    
    With this commit, the infrastructure has been put in place to add GNU Make
    functions in Gnuastro, with one function to start with. In time, we'll be
    adding more functions.
---
 NEWS                |  17 +++++
 bin/fits/fits.c     |   8 +--
 bin/fits/keywords.c |   6 +-
 configure.ac        |  16 +++++
 doc/gnuastro.texi   | 200 ++++++++++++++++++++++++++++++++++++++--------------
 lib/Makefile.am     |  34 +++++----
 lib/fits.c          | 109 ++++++++++++++++------------
 lib/gnuastro/fits.h |   3 +-
 lib/gnuastro/list.h |   6 ++
 lib/gnumake.c       | 168 +++++++++++++++++++++++++++++++++++++++++++
 lib/list.c          |  91 ++++++++++++++++++++++++
 lib/options.c       |   4 +-
 lib/wcs.c           |   2 +-
 13 files changed, 543 insertions(+), 121 deletions(-)

diff --git a/NEWS b/NEWS
index 67ee0093..cd4046f7 100644
--- a/NEWS
+++ b/NEWS
@@ -89,6 +89,19 @@ See the end of the file for license conditions.
     inner parts of the star, they degrade the stack's outer parts. With
     this option, the fainter stars won't harm the outer parts.
 
+  GNU Make extensions:
+  - It is now possible to use custom Gnuastro functions in GNU Make, using
+    its extension facilities with Dynamic libraries. GNU Make is a very
+    powerful workflow manager that is also used for data analysis (not just
+    for compilation). With the Gnaustro Make functions, (astronomical) data
+    analysis becomes even more easier and faster. In the following, you can
+    see the first set of such functions (they all begin with 'astgmk' for
+    "Astronomical GNU Make"):
+    - astgmk-fits-with-keyvalue: Takes a list of files, a HDU, a keyword
+      name and a list of keyword values. It will return only those FITS
+      files that have the requested value(s) in the requested keyword of
+      the requested HDU.
+
   Library functions:
   - gal_box_border_rotate_around_center: width of box after rotation.
   - gal_color_name_to_id: return the ID of a color from its name.
@@ -97,6 +110,8 @@ See the end of the file for license conditions.
   - gal_eps_shape_name_to_id: return the ID of a shape from its name.
   - gal_eps_shape_id_to_name: return the name of a shape from its ID.
   - gal_list_data_select_by_name: select a dataset from a list by its name.
+  - gal_list_str_cat: Concatenate (append) list to a space-separated string.
+  - gal_list_str_extract: Extract space-separated tokens to a list.
   - gal_units_mag_to_sb: surface brightness (SB) from magnitude and area.
   - gal_units_sb_to_mag: magnitude from SB and area.
   - gal_units_counts_to_sb: SB from counts, zeropoint and area.
@@ -126,6 +141,8 @@ See the end of the file for license conditions.
   Library
   - gal_eps_write: an extra argument has been added to draw marks.
   - gal_pdf_write: similar to 'gal_eps_write'.
+  - gal_fits_hdu_open: new argument to optionally exit program if HDU
+        couldn't be opened.
 
 ** Bugs fixed
   bug #62861: '--printkeynames' of Fits program gets caught in an infinite
diff --git a/bin/fits/fits.c b/bin/fits/fits.c
index f7dbd2c4..976666b7 100644
--- a/bin/fits/fits.c
+++ b/bin/fits/fits.c
@@ -101,7 +101,7 @@ fits_print_extension_info(struct fitsparams *p)
   /* Open the FITS file and read the first extension type, upon moving to
      the next extension, we will read its type, so for the first we will
      need to do it explicitly. */
-  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
+  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY, 1);
   if (fits_get_hdu_type(fptr, &hdutype, &status) )
     gal_fits_io_error(status, "reading first extension");
 
@@ -325,7 +325,7 @@ fits_hdu_number(struct fitsparams *p)
   int numhdu, status=0;
 
   /* Read the first extension (necessary for reading the rest). */
-  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
+  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY, 1);
 
   /* Get the number of HDUs. */
   if( fits_get_num_hdus(fptr, &numhdu, &status) )
@@ -758,7 +758,7 @@ fits_hdu_remove(struct fitsparams *p, int *r)
       hdu=gal_list_str_pop(&p->remove);
 
       /* Open the FITS file at the specified HDU. */
-      fptr=gal_fits_hdu_open(p->input->v, hdu, READWRITE);
+      fptr=gal_fits_hdu_open(p->input->v, hdu, READWRITE, 1);
 
       /* Delete the extension. */
       if( fits_delete_hdu(fptr, &hdutype, &status) )
@@ -821,7 +821,7 @@ fits_hdu_copy(struct fitsparams *p, int cut1_copy0, int *r)
 
       /* Open the FITS file at the specified HDU. */
       in=gal_fits_hdu_open(p->input->v, hdu,
-                           cut1_copy0 ? READWRITE : READONLY);
+                           cut1_copy0 ? READWRITE : READONLY, 1);
 
       /* If the output isn't opened yet, open it.  */
       if(out==NULL)
diff --git a/bin/fits/keywords.c b/bin/fits/keywords.c
index 9eca698a..7c08dcd8 100644
--- a/bin/fits/keywords.c
+++ b/bin/fits/keywords.c
@@ -53,7 +53,7 @@ static void
 keywords_open(struct fitsparams *p, fitsfile **fptr, int iomode)
 {
   if(*fptr==NULL)
-    *fptr=gal_fits_hdu_open(p->input->v, p->cp.hdu, iomode);
+    *fptr=gal_fits_hdu_open(p->input->v, p->cp.hdu, iomode, 1);
 }
 
 
@@ -507,7 +507,7 @@ keywords_copykeys(struct fitsparams *p, char *inkeys, 
size_t numinkeys)
   int updatechecksum=0, checksumexists=0;
 
   /* Open the output HDU. */
-  fptr=gal_fits_hdu_open(p->cp.output, p->outhdu, READWRITE);
+  fptr=gal_fits_hdu_open(p->cp.output, p->outhdu, READWRITE, 1);
 
   /* See if a 'CHECKSUM' key exists in the HDU or not (to update in case we
      wrote anything). */
@@ -946,7 +946,7 @@ keywords_value(struct fitsparams *p)
   for(input=p->input; input!=NULL; input=input->next)
     {
       /* Open the input FITS file. */
-      fptr=gal_fits_hdu_open(input->v, p->cp.hdu, READONLY);
+      fptr=gal_fits_hdu_open(input->v, p->cp.hdu, READONLY, 1);
 
       /* Allocate the array to keep the keys. */
       i=0;
diff --git a/configure.ac b/configure.ac
index c5a2e0fa..58c648ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -611,6 +611,14 @@ AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_PTHREAD_BARRIER], 
[$has_pthread_barrier],
                    [System has pthread_barrier])
 AC_SUBST(HAVE_PTHREAD_BARRIER, [$has_pthread_barrier])
 
+# If a GNU Make header can be found (for Gnuastro's GNU Make extensions)
+AC_CHECK_HEADER([gnumake.h], [has_gnumake_h=1],
+                [has_gnumake_h=0; anywarnings=yes])
+AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_GNUMAKE_H], [$has_gnumake_h],
+                   [GNU Make's extension header is present.])
+AC_SUBST(HAVE_GNUMAKE_H, [$has_gnumake_h])
+AM_CONDITIONAL([COND_HASGNUMAKE_H], [test "x$has_gnumake_h" = "x1"])
+
 
 
 
@@ -1287,6 +1295,14 @@ AS_IF([test x$enable_guide_message = xyes],
                AS_ECHO(["    first time)."])
                AS_ECHO([]) ])
 
+        AS_IF([test "x$has_gnumake_h" = "x0"],
+              [dependency_notice=yes
+               AS_ECHO(["  - GNU Make (https://www.gnu.org/software/make) 
extension headers "])
+               AS_ECHO(["    'gnumake.h' couldn't be found by the C compiler. 
If available, "])
+               AS_ECHO(["    Gnuastro can use those headers to add custom GNU 
Make functions "])
+               AS_ECHO(["    to improve your Makefiles for data analysis 
workflows."])
+               AS_ECHO([]) ])
+
         AS_IF([test "x$has_ds9" = "xno"],
               [dependency_notice=yes
                AS_ECHO(["  - (GUI only) SAO ds9 
(https://sites.google.com/cfa.harvard.edu/saoimageds9)"])
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index afecb5cd..13b2eae0 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -231,6 +231,7 @@ To go to the sections or subsections, you have to click on 
the menu entries that
 * Modeling and fittings::       Make and fit models.
 * High-level calculations::     Physical calculations.
 * Installed scripts::           Installed scripts that operate like programs.
+* Makefile extensions::         Use Gnuastro's features as GNU Make functions.
 * Library::                     Gnuastro's library of useful functions.
 * Developing::                  The development environment.
 * Gnuastro programs list::      List and short summary of Gnuastro.
@@ -724,6 +725,11 @@ PSF construction and subtraction
 * Invoking astscript-psf-scale-factor::  Calculate factor to scale PSF to star.
 * Invoking astscript-psf-subtract::  Put the PSF in the image to subtract.
 
+Makefile extensions (for GNU Make)
+
+* Loading the Gnuastro Make functions::
+* Gnuastro Make functions::
+
 Library
 
 * Review of library fundamentals::  Guide on libraries and linking.
@@ -7753,6 +7759,13 @@ However, this internal Libtool does not get installed.
 Therefore, after Gnuastro's installation, if you want to use 
@ref{BuildProgram} to compile and link your own C source code which uses the 
@ref{Gnuastro library}, you need to have GNU Libtool available on your system 
(independent of Gnuastro).
 See @ref{Review of library fundamentals} to learn more about libraries.
 
+@item GNU Make extension headers
+@cindex GNU Make
+GNU Make is a workflow management system that can be used to run a series of 
commands in a specific order, and in parallel if you want.
+GNU Make offers special features to extend it with custom functions within a 
dynamic library.
+They are defined in the @file{gnumake.h} header.
+If @file{gnumake.h} can be found on your system at configuartion time, 
Gnuastro will build a custom library that GNU Make can use for extended 
functionality in (astronomical) data analysis scenarios.
+
 @item libgit2
 @cindex Git
 @pindex libgit2
@@ -26246,7 +26259,7 @@ In the latter (when a number is given), the returned 
value is the same units of
 
 
 
-@node Installed scripts, Library, High-level calculations, Top
+@node Installed scripts, Makefile extensions, High-level calculations, Top
 @chapter Installed scripts
 
 Gnuastro's programs (introduced in previous chapters) are designed to be 
highly modular and thus contain lower-level operations on the data.
@@ -27876,12 +27889,87 @@ This option is useful when the user wants to obtain 
the scattered light field gi
 
 
 
+@node Makefile extensions, Library, Installed scripts, Top
+@chapter Makefile extensions (for GNU Make)
+
+@cindex Make
+@url{https://en.wikipedia.org/wiki/Make_(software), Make} is a build 
automation tool.
+It can greatly help manage your analysis workflow, even very complex projects 
with thousands of files and hundreds of processing steps.
+In this book, we have discussed Make previously in the context of 
parallelization (see @ref{How to run simultaneous operations}).
+It has also been used in
+
+@cindex GNU Make
+GNU Make is the most common and powerful implementation of Make, with many 
unique additions to the core POSIX standard of Make.
+One of those features is the ability to add extensions using a dynamic library 
(that Gnuastro provides).
+For the details of this feature from GNU Make's own manual, see its 
@url{https://www.gnu.org/software/make/manual/html_node/Loading-Objects.html, 
Loading dynamic objects} section.
+Through this feature, Gnuastro provides additional Make functions that are 
useful in the context of data analysis.
+
+To use this feature, Gnuastro has to be built in shared library more.
+Gnuastro's Make extensions won't work if you build Gnuastro without shared 
libraries (for example when you configure Gnuastro with 
@option{--disable-shared} or @option{--debug}).
+
+@menu
+* Loading the Gnuastro Make functions:: How to find and load Gnuastro's Make 
library.
+* Gnuastro Make functions::             The available functions.
+@end menu
+
+@node Loading the Gnuastro Make functions, Gnuastro Make functions, Makefile 
extensions, Makefile extensions
+@section Loading the Gnuastro Make functions
+
+To load Gnuastro's Make functions in your Makefile, you should use the 
@command{load} command of GNU Make in your Makefile.
+The load command should be given Gnuastro's @file{libgnuastro_make.so} dynamic 
library, which has been specifically written for being called by GNU Make.
+The generic command looks like this (the @file{/PATH/TO} part should be 
changed):
+
+@example
+load /PATH/TO/lib/libgnuastro_make.so
+@end example
+
+@noindent
+Here are the possible replacements of the @file{/PATH/TO} component:
+@table @file
+@item /usr/local
+If you installed Gnuastro from source and didn't use the @option{--prefix} 
option at configuration time, you should use this base directory.
+@item /usr/
+If you installed Gnuastro through your operting system's package manager, it 
is highly likely that Gnuastro's library is here.
+@item ~/.local
+If you installed Gnuastro from source, but used @option{--prefix} to install 
Gnuastro in your home directory (as described in @ref{Installation directory}).
+@end table
+
+If you can't find @file{libgnuastro_make.so} in the locations above, the 
command below should give you its location.
+It assumes that the libraries are in the same base directory as the programs 
(which is usually the case).
+
+@example
+$ which astfits | sed -e's|bin/astfits|lib/libgnuastro_make.so|'
+@end example
+
+@node Gnuastro Make functions,  , Loading the Gnuastro Make functions, 
Makefile extensions
+@section Gnuastro Make functions
+
+All Gnuastro Make functions start with the @command{astgmk-} prefix, which is 
short for ``@emph{Astronomical GNU Make}''.
+After you have loaded Gnuastro's shared library for Makefiles within your 
Makefile, you can call these functions just like any Make function.
+
+The Make functions in Gnuastro have been recently added (in August 2022), and 
will be gradually incrasing, as we find the need for more specialized functions.
 
 
+@table @code
+@item $(astgmk-fits-with-keyvalue FITS_FILES, HDU, KEYNAME, KEYVALUES)
+Will select only the FITS files (from a list of many in @code{FITS_FILES}), 
where the @code{KEYNAME} keyword has the value(s) given in @code{KEYVALUES}.
+Only the HDU given in the @code{HDU} argument will be checked.
+According to the FITS standard, the keyword name is not case sensitive, but 
the keyword value is.
 
+For example if you have many FITS files in the @file{/datasets/images} 
directory, the minimal Makefile below will put those with a value of @code{BAR} 
or @code{BAZ} for the @code{FOO} keyword in HDU number @code{1} in the 
@code{selected} Make variable.
+Notice how there is no comma between @code{BAR} and @code{BAZ}: you can 
specify any series of values.
 
+@verbatim
+load /usr/local/lib/libgnuastro_make.so
 
+files := $(wildcard /datasets/images/*.fits)
+selected := $(astgmk-with-keyvalue $(files), 1, FOO, BAR BAZ)
 
+all:
+       echo "Full:     $(words $(files)) files";
+       echo "Selected: $(words $(selected)) files"
+@end verbatim
+@end table
 
 
 
@@ -27891,7 +27979,12 @@ This option is useful when the user wants to obtain 
the scattered light field gi
 
 
 
-@node Library, Developing, Installed scripts, Top
+
+
+
+
+
+@node Library, Developing, Makefile extensions, Top
 @chapter Library
 
 Each program in Gnuastro that was discussed in the prior chapters (or any 
program in general) is a collection of functions that is compiled into one 
executable file which can communicate directly with the outside world.
@@ -30301,11 +30394,9 @@ structure. All these structures and functions are 
defined and declared in
 @node List of strings, List of int32_t, Linked lists, Linked lists
 @subsubsection List of strings
 
-Probably one of the most common lists you will be using are lists of
-strings. They are the best tools when you are reading the user's inputs, or
-when adding comments to the output files. Below you can see Gnuastro's
-string list type and several functions to help in adding, removing/popping,
-reversing and freeing the list.
+Probably one of the most common lists you will be using are lists of strings.
+They are the best tools when you are reading the user's inputs, or when adding 
comments to the output files.
+Below you can see Gnuastro's string list type and several functions to help in 
adding, removing/popping, reversing and freeing the list.
 
 @deftp {Type (C @code{struct})} gal_list_str_t
 A single node in a list containing a string of characters.
@@ -30319,13 +30410,11 @@ typedef struct gal_list_str_t
 @end deftp
 
 @deftypefun void gal_list_str_add (gal_list_str_t @code{**list}, char 
@code{*value}, int @code{allocate})
-Add a new node to the list of strings (@code{list}) and update it. The new
-node will contain the string @code{value}. If @code{allocate} is not zero,
-space will be allocated specifically for the string of the new node and the
-contents of @code{value} will be copied into it. This can be useful when
-your string may be changed later in the program, but you want your list to
-remain. Here is one short/simple example of initializing and adding
-elements to a string list:
+Add a new node to the list of strings (@code{list}) and update it.
+The new node will contain the string @code{value}.
+If @code{allocate} is not zero, space will be allocated specifically for the 
string of the new node and the contents of @code{value} will be copied into it.
+This can be useful when your string may be changed later in the program, but 
you want your list to remain.
+Here is one short/simple example of initializing and adding elements to a 
string list:
 
 @example
 gal_list_str_t *strlist=NULL;
@@ -30336,10 +30425,8 @@ gal_list_str_add(&strlist, "second last element of 
list.");
 @end deftypefun
 
 @deftypefun {char *} gal_list_str_pop (gal_list_str_t @code{**list})
-Pop the top element of @code{list}, change @code{list} to point to the next
-node in the list, and return the string that was in the popped node. If
-@code{*list==NULL}, then this function will also return a @code{NULL}
-pointer.
+Pop the top element of @code{list}, change @code{list} to point to the next 
node in the list, and return the string that was in the popped node.
+If @code{*list==NULL}, then this function will also return a @code{NULL} 
pointer.
 @end deftypefun
 
 @deftypefun size_t gal_list_str_number (gal_list_str_t @code{*list})
@@ -30351,11 +30438,11 @@ Return a pointer to the last node in @code{list}.
 @end deftypefun
 
 @deftypefun void gal_list_str_print (gal_list_str_t @code{*list})
-Print the strings within each node of @code{*list} on the standard output
-in the same order that they are stored. Each string is printed on one
-line. This function is mainly good for checking/debugging your program. For
-program outputs, its best to make your own implementation with a better,
-more user-friendly, format. For example the following code snippet.
+Print the strings within each node of @code{*list} on the standard outputin 
the same order that they are stored.
+Each string is printed on one line.
+This function is mainly good for checking/debugging your program.
+For program outputs, its best to make your own implementation with a better, 
more user-friendly, format.
+For example the following code snippet.
 
 @example
 size_t i;
@@ -30366,18 +30453,31 @@ for(tmp=list; tmp!=NULL; tmp=tmp->next)
 @end deftypefun
 
 @deftypefun void gal_list_str_reverse (gal_list_str_t @code{**list})
-Reverse the order of the list such that the top node in the list before
-calling this function becomes the bottom node after it.
+Reverse the order of the list such that the top node in the list before 
calling this function becomes the bottom node after it.
 @end deftypefun
 
 @deftypefun void gal_list_str_free (gal_list_str_t @code{*list}, int 
@code{freevalue})
-Free every node in @code{list}. If @code{freevalue} is not zero, also free
-the string within the nodes.
+Free every node in @code{list}.
+If @code{freevalue} is not zero, also free the string within the nodes.
+@end deftypefun
+
+@deftypefun {gal_list_str_t *} gal_list_str_extract (char @code{*string})
+Extract space-separated components of the input string.
+If any space element should be kept (and not considered as a delimiter betwen 
two tokens), precede it with a back-slash (@code{\}).
+@end deftypefun
+
+@deftypefun {char *} gal_list_str_cat (gal_list_str_t @code{*list})
+Concatenate (append) the input list of strings into a single space-separated 
string.
+The space for the output string is allocated by this function and should be 
freed when you have finished with it.
+
+If there is any SPACE characters in any of the elements, a back-slash 
(@code{\}) will be printed before the SPACE character.
+This is necessary, otherwise, a function like @code{gal_list_str_extract} 
won't be able to extract the elements back into separate elements in a list.
 @end deftypefun
 
 
 
 
+
 @node List of int32_t, List of size_t, List of strings, Linked lists
 @subsubsection List of @code{int32_t}
 
@@ -31467,16 +31567,13 @@ dataset is a table column, put a non-zero value into
 @node FITS HDUs, FITS header keywords, CFITSIO and Gnuastro types, FITS files
 @subsubsection FITS HDUs
 
-A FITS file can contain multiple HDUs/extensions. The functions in this
-section can be used to get basic information about the extensions or open
-them. Note that @code{fitsfile} is defined in CFITSIO's @code{fitsio.h}
-which is automatically included by Gnuastro's @file{gnuastro/fits.h}.
+A FITS file can contain multiple HDUs/extensions.
+The functions in this section can be used to get basic information about the 
extensions or open them.
+Note that @code{fitsfile} is defined in CFITSIO's @code{fitsio.h} which is 
automatically included by Gnuastro's @file{gnuastro/fits.h}.
 
 @deftypefun {fitsfile *} gal_fits_open_to_write (char @code{*filename})
-If @file{filename} exists, open it and return the @code{fitsfile} pointer
-that corresponds to it. If @file{filename} doesn't exist, the file will be
-created which contains a blank first extension and the pointer to its next
-extension will be returned.
+If @file{filename} exists, open it and return the @code{fitsfile} pointer that 
corresponds to it.
+If @file{filename} doesn't exist, the file will be created which contains a 
blank first extension and the pointer to its next extension will be returned.
 @end deftypefun
 
 @deftypefun size_t gal_fits_hdu_num (char @code{*filename})
@@ -31506,28 +31603,25 @@ Return @code{1} if the dataset may be a HEALpix grid 
and @code{0} otherwise.
 Technically, it is considered to be a HEALPix if the HDU isn't an ASCII table, 
and has the @code{NSIDE}, @code{FIRSTPIX} and @code{LASTPIX}.
 @end deftypefun
 
-@deftypefun {fitsfile *} gal_fits_hdu_open (char @code{*filename}, char 
@code{*hdu}, int @code{iomode})
-Open the HDU/extension @code{hdu} from @file{filename} and return a pointer
-to CFITSIO's @code{fitsfile}. @code{iomode} determines how the FITS file
-will be opened using CFITSIO's macros: @code{READONLY} or @code{READWRITE}.
+@deftypefun {fitsfile *} gal_fits_hdu_open (char @code{*filename}, char 
@code{*hdu}, int @code{iomode}, int @code{exitonerror})
+Open the HDU/extension @code{hdu} from @file{filename} and return a pointer to 
CFITSIO's @code{fitsfile}.
+@code{iomode} determines how the FITS file will be opened using CFITSIO's 
macros: @code{READONLY} or @code{READWRITE}.
+
+The string in @code{hdu} will be appended to @file{filename} in square 
brackets so CFITSIO only opens this extension.
+You can use any formatting for the @code{hdu} that is acceptable to CFITSIO.
+See the description under @option{--hdu} in @ref{Input output options} for 
more.
 
-The string in @code{hdu} will be appended to @file{filename} in square
-brackets so CFITSIO only opens this extension. You can use any formatting
-for the @code{hdu} that is acceptable to CFITSIO. See the description under
-@option{--hdu} in @ref{Input output options} for more.
+If @code{exitonerror!=0} and the given HDU can't be opened for any reason, the 
function will exit the program, and print an informative message.
+Otherwise, when the HDU can't be opened, it will just return a NULL pointer.
 @end deftypefun
 
 @deftypefun {fitsfile *} gal_fits_hdu_open_format (char @code{*filename}, char 
@code{*hdu}, int @code{img0_tab1})
-Open (in read-only format) the @code{hdu} HDU/extension of @file{filename}
-as an image or table. When @code{img0_tab1} is @code{0}(zero) but the HDU
-is a table, this function will abort with an error. It will also abort with
-an error when @code{img0_tab1} is @code{1} (one), but the HDU is an
-image.
-
-A FITS HDU may contain both tables or images. When your program needs one
-of these formats, you can call this function so if the user provided the
-wrong HDU/file, it will abort and inform the user that the file/HDU is has
-the wrong format.
+Open (in read-only format) the @code{hdu} HDU/extension of @file{filename} as 
an image or table.
+When @code{img0_tab1} is @code{0}(zero) but the HDU is a table, this function 
will abort with an error.
+It will also abort with an error when @code{img0_tab1} is @code{1} (one), but 
the HDU is an image.
+
+A FITS HDU may contain both tables or images.
+When your program needs one of these formats, you can call this function so if 
the user provided the wrong HDU/file, it will abort and inform the user that 
the file/HDU is has the wrong format.
 @end deftypefun
 
 
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f11dcee7..f87b6c30 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -21,6 +21,17 @@
 
 
 
+# Conditional compilation
+if COND_HASWCSDIS_H
+  MAYBE_WCSDISTORTION = wcsdistortion.c
+endif
+if COND_HASGNUMAKE_H
+  MAYBE_GNUMAKE = libgnuastro_make.la
+endif
+
+
+
+
 
 ## Necessary flags.
 ##
@@ -45,25 +56,24 @@ AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
 # Gnuastro library functions can also benefit from Gnulib's many great
 # features. This also avoids the need for the programs to link separately
 # with Gnulib, they only need to link with the Gnuastro library.
-lib_LTLIBRARIES = libgnuastro.la
+lib_LTLIBRARIES = libgnuastro.la $(MAYBE_GNUMAKE)
+
+# Linking flags for the Gnuastro library.
+libgnuastro_la_LIBADD = $(top_builddir)/bootstrapped/lib/libgnu.la
 libgnuastro_la_LDFLAGS = -version-info $(GAL_LT_VERSION) $(CONFIG_LDADD) \
                          -lc -no-undefined
-libgnuastro_la_LIBADD = $(top_builddir)/bootstrapped/lib/libgnu.la
-
-
-
-
-# Conditional compilation
-if COND_HASWCSDIS_H
-  MAYBE_WCSDISTORTION = wcsdistortion.c
-endif
-
 
+# Gnuastro's GNU Make extensions
+libgnuastro_make_la: libgnuastro.la
+libgnuastro_make_la_LIBADD = libgnuastro.la
+libgnuastro_make_la_LDFLAGS = -version-info $(GAL_LT_VERSION)
 
 
 
 # Specify the library .c files
-libgnuastro_la_SOURCES = $(MAYBE_WCSDISTORTION) \
+libgnuastro_make_la_SOURCES = gnumake.c
+libgnuastro_la_SOURCES = \
+  $(MAYBE_WCSDISTORTION) \
   arithmetic.c \
   arithmetic-and.c \
   arithmetic-bitand.c \
diff --git a/lib/fits.c b/lib/fits.c
index 5833912a..41af504b 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -707,7 +707,7 @@ gal_fits_hdu_datasum(char *filename, char *hdu)
   unsigned long datasum;
 
   /* Read the desired extension (necessary for reading the rest). */
-  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY, 1);
 
   /* Calculate the datasum. */
   datasum=gal_fits_hdu_datasum_ptr(fptr);
@@ -755,7 +755,7 @@ gal_fits_hdu_format(char *filename, char *hdu)
   int hdutype, status=0;
 
   /* Open the HDU. */
-  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY, 1);
 
   /* Check the type of the given HDU: */
   if (fits_get_hdu_type(fptr, &hdutype, &status) )
@@ -802,7 +802,7 @@ gal_fits_hdu_is_healpix(fitsfile *fptr)
      READONLY:   read-only.
      READWRITE:  read and write.         */
 fitsfile *
-gal_fits_hdu_open(char *filename, char *hdu, int iomode)
+gal_fits_hdu_open(char *filename, char *hdu, int iomode, int exitonerror)
 {
   int status=0;
   char *ffname;
@@ -810,7 +810,11 @@ gal_fits_hdu_open(char *filename, char *hdu, int iomode)
 
   /* Add hdu to filename: */
   if( asprintf(&ffname, "%s[%s#]", filename, hdu)<0 )
-    error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+    {
+      if(exitonerror)
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+      else return NULL;
+    }
 
   /* Open the FITS file: */
   if( fits_open_file(&fptr, ffname, iomode, &status) )
@@ -822,31 +826,40 @@ gal_fits_hdu_open(char *filename, char *hdu, int iomode)
            notice. */
         case END_OF_FILE:
           if( hdu[0]=='1' && hdu[1]=='\0' )
-            error(EXIT_FAILURE, 0, "%s: only has one HDU.\n\n"
-                  "You should tell Gnuastro's command-line "
-                  "programs to look for data in the primary HDU with the "
-                  "'--hdu=0' option (or '-h0'). For library users, you can "
-                  "put \"0\" (a string literal) for the function's HDU "
-                  "argument. For more, see the FOOTNOTE below.\n\n"
-                  "Pro TIP: if your desired HDU has a name (value to "
-                  "'EXTNAME' keyword), it is best to just use that name "
-                  "with '--hdu' instead of relying on a counter. You can "
-                  "see the list of HDUs in a FITS file (with their data "
-                  "format, type, size and possibly HDU name) using "
-                  "Gnuastro's 'astfits' program, for example:\n\n"
-                  "    astfits %s\n\n"
-                  "FOOTNOTE -- When writing a new FITS file, Gnuastro leaves "
-                  "the pimary HDU only for metadata. The output datasets "
-                  "(tables, images or cubes) are written after the primary "
-                  "HDU. In this way the keywords of the the first HDU can be "
-                  "used as metadata of the whole file (which may contain many "
-                  "extensions, this is stipulated in the FITS standard). "
-                  "Usually the primary HDU keywords contains the option names "
-                  "and values that the program was run with. Because of this, "
-                  "Gnuastro's default HDU to read data in a FITS file is the "
-                  "second (or '--hdu=1'). This error is commonly caused when "
-                  "the FITS file wasn't created by Gnuastro or by a program "
-                  "respecting this convention.", filename, filename);
+            {
+              if(exitonerror)
+                error(EXIT_FAILURE, 0, "%s: only has one HDU.\n\n"
+                      "You should tell Gnuastro's command-line "
+                      "programs to look for data in the primary HDU "
+                      "with the '--hdu=0' option (or '-h0'). For library "
+                      "users, you can put \"0\" (a string literal) for "
+                      "the function's HDU argument. For more, see the "
+                      "FOOTNOTE below.\n\n"
+                      "Pro TIP: if your desired HDU has a name (value to "
+                      "'EXTNAME' keyword), it is best to just use that "
+                      "name with '--hdu' instead of relying on a "
+                      "counter. You can see the list of HDUs in a FITS "
+                      "file (with their data format, type, size and "
+                      "possibly HDU name) using Gnuastro's 'astfits' "
+                      "program, for example:\n\n"
+                      "    astfits %s\n\n"
+                      "FOOTNOTE -- When writing a new FITS file, "
+                      "Gnuastro leaves the pimary HDU only for metadata. "
+                      "The output datasets (tables, images or cubes) are "
+                      "written after the primary HDU. In this way the "
+                      "keywords of the the first HDU can be used as "
+                      "metadata of the whole file (which may contain "
+                      "many extensions, this is stipulated in the FITS "
+                      "standard). Usually the primary HDU keywords "
+                      "contains the option names and values that the "
+                      "program was run with. Because of this, Gnuastro's "
+                      "default HDU to read data in a FITS file is the "
+                      "second (or '--hdu=1'). This error is commonly "
+                      "caused when the FITS file wasn't created by "
+                      "Gnuastro or by a program respecting this "
+                      "convention.", filename, filename);
+              else return NULL;
+            }
           break;
 
         /* Generic error below is fine for this case */
@@ -856,20 +869,24 @@ gal_fits_hdu_open(char *filename, char *hdu, int iomode)
         /* In case an un-expected error occurs, use the general CFITSIO
            reporting that we have already prepared. */
         default:
-          gal_fits_io_error(status, "opening the given extension/HDU in "
-                            "the given file");
+          if(exitonerror)
+            gal_fits_io_error(status, "opening the given extension/HDU in "
+                              "the given file");
+          else return NULL;
         }
 
-      error(EXIT_FAILURE, 0, "%s: extension/HDU '%s' doesn't exist. Please "
-            "run the following command to see the extensions/HDUs in "
-            "'%s':\n\n"
-            "    $ astfits %s\n\n"
-            "The respective HDU number (or name, when present) may be used "
-            "with the '--hdu' option in Gnuastro's programs (or the 'hdu' "
-            "argument in Gnuastro's libraries) to open the respective HDU. "
-            "If you are using counters/numbers to identify your HDUs, note "
-            "that since Gnuastro uses CFITSIO for FITS input/output, HDU "
-            "counting starts from 0", filename, hdu, filename, filename);
+      if(exitonerror)
+        error(EXIT_FAILURE, 0, "%s: extension/HDU '%s' doesn't exist. Please "
+              "run the following command to see the extensions/HDUs in "
+              "'%s':\n\n"
+              "    $ astfits %s\n\n"
+              "The respective HDU number (or name, when present) may be used "
+              "with the '--hdu' option in Gnuastro's programs (or the 'hdu' "
+              "argument in Gnuastro's libraries) to open the respective HDU. "
+              "If you are using counters/numbers to identify your HDUs, note "
+              "that since Gnuastro uses CFITSIO for FITS input/output, HDU "
+              "counting starts from 0", filename, hdu, filename, filename);
+      else return NULL;
     }
 
   /* Clean up and the pointer. */
@@ -894,7 +911,7 @@ gal_fits_hdu_open_format(char *filename, char *hdu, int 
img0_tab1)
     error(EXIT_FAILURE, 0, "no HDU specified for %s", filename);
 
   /* Open the HDU. */
-  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY, 1);
 
   /* Check the type of the given HDU: */
   if (fits_get_hdu_type(fptr, &hdutype, &status) )
@@ -1988,7 +2005,7 @@ gal_fits_key_write(gal_fits_list_key_t **keylist, char 
*title,
                    char *filename, char *hdu)
 {
   int status=0;
-  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE);
+  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE, 1);
 
   /* Write the title */
   gal_fits_key_write_title_in_ptr(title, fptr);
@@ -2114,7 +2131,7 @@ gal_fits_key_write_version(gal_fits_list_key_t **keylist, 
char *title,
                            char *filename, char *hdu)
 {
   int status=0;
-  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE);
+  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE, 1);
 
   /* Write the given keys followed by the versions. */
   gal_fits_key_write_version_in_ptr(keylist, title, fptr);
@@ -2226,7 +2243,7 @@ gal_fits_key_write_config(gal_fits_list_key_t **keylist, 
char *title,
                           char *extname, char *filename, char *hdu)
 {
   int status=0;
-  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE);
+  fitsfile *fptr=gal_fits_hdu_open(filename, hdu, READWRITE, 1);
 
   /* Delete the two extra comment lines describing the FITS standard that
      CFITSIO puts in when it creates a new extension. We'll set status to 0
@@ -2369,7 +2386,7 @@ gal_fits_img_info_dim(char *filename, char *hdu, size_t 
*ndim)
 
   /* Open the given header, read the basic image information and close it
      again. */
-  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY, 1);
   gal_fits_img_info(fptr, &type, ndim, &dsize, NULL, NULL);
   if( fits_close_file(fptr, &status) ) gal_fits_io_error(status, NULL);
 
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index 4152d16a..8e6da2e6 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -172,7 +172,8 @@ int
 gal_fits_hdu_is_healpix(fitsfile *fptr);
 
 fitsfile *
-gal_fits_hdu_open(char *filename, char *hdu, int iomode);
+gal_fits_hdu_open(char *filename, char *hdu, int iomode,
+                  int exitonerror);
 
 fitsfile *
 gal_fits_hdu_open_format(char *filename, char *hdu, int img0_tab1);
diff --git a/lib/gnuastro/list.h b/lib/gnuastro/list.h
index 2561050f..9172e96c 100644
--- a/lib/gnuastro/list.h
+++ b/lib/gnuastro/list.h
@@ -80,6 +80,12 @@ gal_list_str_reverse(gal_list_str_t **list);
 void
 gal_list_str_free(gal_list_str_t *list, int freevalue);
 
+gal_list_str_t *
+gal_list_str_extract(char *string);
+
+char *
+gal_list_str_cat(gal_list_str_t *list);
+
 
 
 
diff --git a/lib/gnumake.c b/lib/gnumake.c
new file mode 100644
index 00000000..3a38916a
--- /dev/null
+++ b/lib/gnumake.c
@@ -0,0 +1,168 @@
+/*********************************************************************
+Extensions to GNU Make for working with FITS files.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2022 Free Software Foundation, Inc.
+
+Gnuastro is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <gnumake.h>
+
+#include <gnuastro/txt.h>
+
+#include <gnuastro-internal/options.h>
+#include <gnuastro-internal/checkset.h>
+
+
+
+
+
+/* Necessary for GNU Make. */
+int plugin_is_GPL_compatible=1;
+
+
+
+
+
+/* Names of the separate functions */
+#define GNUMAKE_FUNC_PREFIX "astgmk"
+char *with_keyvalue_name=GNUMAKE_FUNC_PREFIX"-fits-with-keyvalue";
+
+
+
+
+
+/* Select the input files that have the requested value(s) in the requested
+   keywords. */
+static int
+gnumake_goodinput(char **argv, size_t numargs, char *name)
+{
+  char *c;
+  size_t i;
+
+  /* If the HDU is empty, print a warning and don't continue. */
+  for(i=0;i<numargs;++i)
+    {
+      /* We need to skip white-space characters. */
+      c=argv[i]; do if(isspace(*c)) ++c; else break; while(1);
+      if(*c=='\0')
+        {
+          if(i>0) /* Message only necessary for first argument. */
+            error(EXIT_SUCCESS, 0, "%s: argument %zu is empty",
+                  name, i+1);
+          return 0;
+        }
+    }
+
+  /* If control reaches here, everything is good. */
+  return 1;
+}
+
+
+
+
+
+static char *
+gnumake_fits_with_keyvalue(const char *caller, unsigned int argc,
+                           char **argv)
+{
+  int status=0;
+  fitsfile *fptr;
+  char keyvalue[FLEN_VALUE];
+  gal_list_str_t *outlist=NULL;
+  char *name=gal_txt_trim_space(argv[2]);
+  char *out, *hdu=gal_txt_trim_space(argv[1]);
+  gal_list_str_t *f, *v, *files=NULL, *values=NULL;
+
+  /* If any of the inputs are empty, then don't bother continuing. */
+  if( gnumake_goodinput(argv, 4, with_keyvalue_name)==0 )
+    return NULL;
+
+  /* Extract the components in the arguments with possibly multiple
+     values.*/
+  files=gal_list_str_extract(argv[0]);
+  values=gal_list_str_extract(argv[3]);
+
+  /* Go over the list of files and see if they have the requested
+     keyword(s). */
+  for(f=files; f!=NULL; f=f->next)
+    {
+      /* Open the file. */
+      fptr=gal_fits_hdu_open(f->v, hdu, READONLY, 0);
+
+      /* Only attempt to read the value if the requested HDU could be
+         opened ('fptr!=NULL'). */
+      if(fptr)
+        {
+          /* Check if the keyword actually exists. */
+          if( gal_fits_key_exists_fptr(fptr, name) )
+            {
+              /* Read the keyword. */
+              if( fits_read_key(fptr, TSTRING, name, &keyvalue, NULL,
+                                &status) )
+                gal_fits_io_error(status, NULL);
+
+              /* If the value corresponds to any of the user's values for this
+                 keyword, add it to the list of output names. */
+              for(v=values; v!=NULL; v=v->next)
+                {
+                  if( strcmp(v->v, keyvalue)==0 )
+                    { gal_list_str_add(&outlist, f->v, 1); break; }
+                }
+            }
+
+          /* Close the file. */
+          if( fits_close_file(fptr, &status) )
+            gal_fits_io_error(status, NULL);
+        }
+    }
+
+  /* Write the output value. */
+  out=gal_list_str_cat(outlist);
+
+  /* Clean up and return. */
+  gal_list_str_free(files, 1);
+  gal_list_str_free(values, 1);
+  gal_list_str_free(outlist, 1);
+  return out;
+}
+
+
+
+
+/* Top-level function (that should have this name). */
+int
+libgnuastro_make_gmk_setup()
+{
+  /* Select files, were a certain keyword has a certain value. It takes
+     for arguments:
+       0. List of files.
+       1. HDU (fixed in all files).
+       2. Keyword name.
+       3. Keyword value(s). */
+  gmk_add_function(with_keyvalue_name, gnumake_fits_with_keyvalue,
+                   4, 4, GMK_FUNC_DEFAULT);
+  return 1;
+}
diff --git a/lib/list.c b/lib/list.c
index 21e9dcd7..f7844374 100644
--- a/lib/list.c
+++ b/lib/list.c
@@ -172,6 +172,97 @@ gal_list_str_free(gal_list_str_t *list, int freevalue)
 
 
 
+/* Replacement characters for commented space (ASCII code 14 for "Shift
+   out"). These are chosen as non-printable ASCII characters, that user's
+   will not be typing. Inspired from 'gal_options_parse_list_of_strings'.*/
+#define LIST_COMMENTED_SPACE 14
+gal_list_str_t *
+gal_list_str_extract(char *string)
+{
+  gal_list_str_t *list=NULL, *tmp;
+  char *c, *d, *cp, *token, *saveptr, delimiters[]=" \t";
+
+  /* Make a copy of the input string, remove all commented delimiters
+     (those with a preceding '\'), this was inspired from
+     'gal_options_parse_list_of_strings'. */
+  gal_checkset_allocate_copy(string, &cp);
+  for(c=cp; *c!='\0'; c++)
+    if(*c=='\\' && c[1]!='\0')
+      {
+        /* If the next character (after the '\') is a delimiter, we need to
+           replace it with a non-delimiter (and not-typed!) character and
+           shift the whole string back by one character to simplify future
+           steps. */
+        if(c[1]==' ')
+          {
+            *c=LIST_COMMENTED_SPACE;
+            for(d=c+2; *d!='\0'; ++d) {*(d-1)=*d;} *(d-1)='\0';
+          }
+      }
+
+  /* Tokenize the string. */
+  token=strtok_r(cp, delimiters, &saveptr);
+  gal_list_str_add(&list, token, 1);
+  while(token!=NULL)
+    {
+      token=strtok_r(NULL, delimiters, &saveptr);
+      if(token!=NULL)
+        gal_list_str_add(&list, token, 1);
+    }
+
+  /* Go over each token and change the temporarily replaced value to a
+     SPACE. */
+  for(tmp=list;tmp!=NULL;tmp=tmp->next)
+    for(c=tmp->v; *c!='\0'; ++c)
+      if(*c==LIST_COMMENTED_SPACE)
+        *c=' ';
+
+  /* Return the list. */
+  gal_list_str_reverse(&list);
+  return list;
+}
+
+
+
+
+
+char *
+gal_list_str_cat(gal_list_str_t *list)
+{
+  size_t bsize=0;
+  char *c, *o, *out;
+  gal_list_str_t *tmp;
+
+  /* If the list is empty, return a NULL pointer. */
+  if(list==NULL) return NULL;
+
+  /* Go over each element of the list and count how many characters there
+     are in it (add one for the space with the next). */
+  for(tmp=list; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Count the characters. If we have a SPACE, we need to add an extra
+         count for the back slash.*/
+      c=tmp->v; do {++bsize; if(*c==' ') ++bsize;} while(*(++c)!='\0');
+      ++bsize; /* For the extra space between characters */
+    }
+
+  /* Allocate the necessary space and write all the strings inside of it,
+     (while also commenting the space characters).*/
+  out=gal_pointer_allocate(GAL_TYPE_STRING, bsize, 0, __func__, "out");
+  o=out;
+  for(tmp=list; tmp!=NULL; tmp=tmp->next)
+    {
+      c=tmp->v;
+      do {if(*c==' ') *o++='\\'; *o++=*c;} while(*(++c)!='\0');
+      *o++=' ';
+    }
+  *o='\0';
+
+  return out;
+}
+
+
+
 
 
 
diff --git a/lib/options.c b/lib/options.c
index d8ad5094..34fed5b6 100644
--- a/lib/options.c
+++ b/lib/options.c
@@ -900,6 +900,8 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
 
 
 
+
+
 /* Replacement characters for commented comma (ASCII code 14 for "Shift
    out") or colon (ASCII code 15 for "Shift in"). These are chosen as
    non-printable ASCII characters, that user's will not be typing. */
@@ -943,7 +945,7 @@ gal_options_parse_list_of_strings(char *string, char 
*filename, size_t lineno)
           { for(d=c+2; *d!='\0'; ++d) {*(d-1)=*d;} *(d-1)='\0'; }
       }
 
-  /* Make a copy of the input string, and save the tokens */
+  /* Start separating the tokens */
   token=strtok(cp, delimiters);
   gal_list_str_add(&list, token, 1);
   while(token!=NULL)
diff --git a/lib/wcs.c b/lib/wcs.c
index e29bbeea..23e489de 100644
--- a/lib/wcs.c
+++ b/lib/wcs.c
@@ -2035,7 +2035,7 @@ gal_wcs_coverage(char *filename, char *hdu, size_t *ondim,
           filename, hdu);
 
   /* Get the array information of the image. */
-  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY, 1);
   gal_fits_img_info(fptr, &type, ondim, &dsize, &name, &unit);
   fits_close_file(fptr, &status);
   ndim=*ondim;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]