gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master b78b0c7 16/16: New Table utility merged


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master b78b0c7 16/16: New Table utility merged
Date: Wed, 24 Aug 2016 22:27:45 +0000 (UTC)

branch: master
commit b78b0c7e19d537f12f582a1f183a7548a946b182
Merge: 56bd4e5 b16c7a4
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    New Table utility merged
    
    The commits of the Table utility are now merged into the main history-line
    of Gnuastro.
---
 Makefile.am                   |    5 +-
 bootstrap.conf                |    1 +
 configure.ac                  |   12 +-
 doc/gnuastro.texi             |  263 ++++++++++-
 genauthors                    |    2 +
 lib/checkset.c                |   54 ++-
 lib/fits.c                    |  568 ++++++++++++++++++----
 lib/gnuastro/checkset.h       |    3 +
 lib/gnuastro/fits.h           |   55 ++-
 lib/gnuastro/linkedlist.h     |    4 +-
 lib/gnuastro/txtarray.h       |    4 +
 lib/linkedlist.c              |   13 +-
 lib/txtarray.c                |    7 +-
 src/imgcrop/crop.c            |   36 +-
 src/imgcrop/imgcrop.c         |    4 +-
 src/imgcrop/ui.c              |    4 +-
 src/mkcatalog/ui.c            |    2 +-
 src/table/Makefile.am         |   41 ++
 src/table/args.h              |  371 +++++++++++++++
 src/table/asttable.conf       |   32 ++
 src/table/cite.h              |   38 ++
 src/table/main.c              |   56 +++
 src/table/main.h              |  126 +++++
 src/table/table.c             |  437 +++++++++++++++++
 src/table/table.h             |   31 ++
 src/table/ui.c                | 1041 +++++++++++++++++++++++++++++++++++++++++
 src/table/ui.h                |   39 ++
 tests/Makefile.am             |    6 +-
 tests/during-dev.sh           |   22 +-
 tests/prepconf.sh             |    2 +-
 tests/table/asciitobinary.sh  |   51 ++
 tests/table/asciitobinary.txt |    2 +
 tests/table/binarytoascii.sh  |   51 ++
 33 files changed, 3211 insertions(+), 172 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 1559349..1c819fa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,6 +88,9 @@ endif
 if COND_SUBTRACTSKY
   MAYBE_SUBTRACTSKY = src/subtractsky
 endif
+if COND_TABLE
+  MAYBE_TABLE = src/table
+endif
 #if COND_TEMPLATE
 #  MAYBE_TEMPLATE = src/TEMPLATE
 #endif
@@ -105,7 +108,7 @@ SUBDIRS = bootstrapped/lib $(MAYBE_GNULIBCHECK) lib 
$(MAYBE_ARITHMETIC)     \
 $(MAYBE_CONVERTT) $(MAYBE_CONVOLVE) $(MAYBE_COSMICCAL) $(MAYBE_HEADER)      \
 $(MAYBE_IMGCROP) $(MAYBE_IMGSTAT) $(MAYBE_IMGWARP) $(MAYBE_MKCATALOG)       \
 $(MAYBE_MKNOISE) $(MAYBE_MKPROF) $(MAYBE_NOISECHISEL) $(MAYBE_SUBTRACTSKY)  \
-$(MAYBE_TEMPLATE) doc tests
+$(MAYBE_TABLE) $(MAYBE_TEMPLATE) doc tests
 
 
 
diff --git a/bootstrap.conf b/bootstrap.conf
index 88ca654..1783522 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -161,6 +161,7 @@ gnulib_modules="
     fdl
     math
     argp
+    regex
     error
     nproc
     strcase
diff --git a/configure.ac b/configure.ac
index 33b40cd..c3d54f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -299,10 +299,16 @@ AC_ARG_ENABLE([noisechisel],
               [enable_noisechisel=notset])
 AC_ARG_ENABLE([subtractsky],
               [AS_HELP_STRING([--enable-subtractsky],
-                   [Install SubtractSky and other enabled packages only.])],
+                    [Install SubtractSky and other enabled packages only.])],
              [AS_IF([test "x$enable_subtractsky" != xno],
                      [enable_subtractsky=yes; ayes=true])],
               [enable_subtractsky=notset])
+AC_ARG_ENABLE([table],
+              [AS_HELP_STRING([--enable-table],
+                    [Install Table and other enabled packages only.])],
+             [AS_IF([test "x$enable_table" != xno],
+                     [enable_table=yes; ayes=true])],
+              [enable_table=notset])
 #AC_ARG_ENABLE([TEMPLATE],
 #              [AS_HELP_STRING([--enable-TEMPLATE],
 #                  [Install TEMPLATE and other enabled packages only.])],
@@ -337,6 +343,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_mkprof = notset], [enable_mkprof=no])
        AS_IF([test $enable_noisechisel = notset], [enable_noisechisel=no])
        AS_IF([test $enable_subtractsky = notset], [enable_subtractsky=no])
+       AS_IF([test $enable_table = notset], [enable_table=no])
 #      AS_IF([test $enable_TEMPLATE = notset], [enable_TEMPLATE=no])
        ],
 
@@ -353,6 +360,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_mkprof = notset], [enable_mkprof=yes])
        AS_IF([test $enable_noisechisel = notset], [enable_noisechisel=yes])
        AS_IF([test $enable_subtractsky = notset], [enable_subtractsky=yes])
+       AS_IF([test $enable_table = notset], [enable_table=yes])
 #      AS_IF([test $enable_TEMPLATE = notset], [enable_TEMPLATE=yes])
        ]
      )
@@ -375,6 +383,7 @@ AM_CONDITIONAL([COND_MKNOISE], [test $enable_mknoise = yes])
 AM_CONDITIONAL([COND_MKPROF], [test $enable_mkprof = yes])
 AM_CONDITIONAL([COND_NOISECHISEL], [test $enable_noisechisel = yes])
 AM_CONDITIONAL([COND_SUBTRACTSKY], [test $enable_subtractsky = yes])
+AM_CONDITIONAL([COND_TABLE], [test $enable_table = yes])
 #AM_CONDITIONAL([COND_TEMPLATE], [test $enable_TEMPLATE = yes])
 
 
@@ -388,6 +397,7 @@ AC_CONFIG_FILES([Makefile
                  doc/Makefile
                  lib/Makefile
                 tests/Makefile
+                 src/table/Makefile
                  src/mkprof/Makefile
                  src/header/Makefile
                  src/mknoise/Makefile
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 87743aa..703d795 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -51,6 +51,9 @@ Texts.  A copy of the license is included in the section 
entitled
 * ConvertType: (gnuastro)ConvertType. Convert different file types.
 * astconvertt: (gnuastro)Invoking astconvertt. Options to ConvertType.
 
+* Table: (gnuastro)Table. Read and write FITS binary or ASCII tables.
+* asttable: (gnuastro)Invoking asttable. Options to Table.
+
 * Convolve: (gnuastro)Convolve. Convolve an input file with kernel.
 * astconvolve: (gnuastro)Invoking astconvolve. Options to Convolve.
 
@@ -186,7 +189,7 @@ sub-component to a title is present.
 * Tutorials::                   Tutorials or Cookbooks.
 * Installation::                Requirements and installation.
 * Common behavior::             Common behavior in all programs.
-* Files::                       File information and type conversion.
+* Extensions and Tables::       Tools to operate on extensions and tables.
 * Image manipulation::          Tools for basic image manipulation.
 * Image analysis::              Analyze images.
 * Modeling and fittings::       Make and fit models.
@@ -311,10 +314,11 @@ Getting help
 * Info::                        View complete book in terminal.
 * help-gnuastro mailing list::  Contacting experienced users.
 
-Files
+Extensions and Tables
 
 * Header::                      Print and manipulate data file header.
 * ConvertType::                 Convert data to various formats.
+* Table::                       Read and Write FITS tables to plain text.
 
 Header
 
@@ -326,6 +330,10 @@ ConvertType
 * Color::                       Some explanations on color.
 * Invoking astconvertt::        Options and arguments to ConvertType.
 
+Table
+
+* Invoking asttable::           Options and arguments to Table.
+
 Image manipulation
 
 * ImageCrop::                   Crop region(s) from FITS image(s).
@@ -3766,7 +3774,7 @@ If your problem was not listed above, please file a bug 
report
 
 
 
address@hidden Common behavior, Files, Installation, Top
address@hidden Common behavior, Extensions and Tables, Installation, Top
 @chapter Common behavior
 
 There are some facts that are common to all the programs in Gnuastro
@@ -5175,30 +5183,42 @@ END
 
 
 
address@hidden Files, Image manipulation, Common behavior, Top
address@hidden Files
address@hidden Extensions and Tables, Image manipulation, Common behavior, Top
address@hidden Extensions and Tables
 
 @cindex File operations
 @cindex Operations on files
 @cindex General file operations
-This chapter documents the programs in Gnuastro that are provided for
-getting information on the contents of a data file or converting a
-file format. Before working on a FITS file, it is commonly the case
-that you are not sure how many extensions it has within it and also
-what each extension is (image, table or blank). In other cases you
-want to use the data in a FITS file in other programs (for example in
-reports) that don't recognize the FITS format.
+This chapter documents those Gnuastro utilities that don't directly operate
+on the contents of the data file, they just print information, or convert
+from different data types. Before working on a FITS file, it is commonly
+the case that you are not sure how many extensions it has within it and
+also what each extension is (image, table or blank). In such cases, Header
+(see @ref{Header}) can be handy by printing the full header of a FITS file
+to the terminal for easy inspection.
+
+In other cases you want to use the data in a FITS file in other programs
+(for example in reports) that don't recognize the FITS format, for example
+converting a FITS image into a Jpeg, or PDF format image. ConvertType (see
address@hidden) was built for such situations. Finally, the FITS format
+is not just for images, it can also store tables. Binary tables in
+particular can be very useful in storing very large catalogs or tables
+compared to a plain text file. Table (see @ref{Table}) can be used to
+choose certain table columns in a FITS table and see them as a human
+readable output on the terminal, or to save them in another plain text or
+FITS table.
 
 @menu
 * Header::                      Print and manipulate data file header.
 * ConvertType::                 Convert data to various formats.
+* Table::                       Read and Write FITS tables to plain text.
 @end menu
 
 
 
 
 
address@hidden Header, ConvertType, Files, Files
address@hidden Header, ConvertType, Extensions and Tables, Extensions and Tables
 @section Header
 
 The FITS standard requires each extension of a FITS file to have a
@@ -5432,7 +5452,7 @@ continue with the rest of actions.
 
 
 
address@hidden ConvertType,  , Header, Files
address@hidden ConvertType, Table, Header, Extensions and Tables
 @section ConvertType
 
 @cindex Data format conversion
@@ -5922,9 +5942,212 @@ is best to call this option so the image is not 
inverted.
 
 @end table
 
address@hidden Table,  , ConvertType, Extensions and Tables
address@hidden Table
+
+The FITS standard is not just for storing astronomical images, from its
+early days, it also included tables. Tables are the products of processing
+astronomical images and spectra. For example in Gnuastro, MakeCatalog will
+process the defined pixels over an object and produce a catalog (see
address@hidden). For each identified object, MakeCatalog can print its
+position on the image or sky, its total brightness and many other
+information that is deducible from the given image. Each one of these
+properties is a column in its output catalog (or table) and for each input
+object, we have a row.
+
+When there are only a small number of objects (rows) and not too many
+properties (columns), then a simple plain text file is mainly enough to
+store, transfer, or even use the produced data. However, to be more
+efficient in all these aspects, astronomers have defined the FITS binary
+table standard to store data in a binary (0 and 1) format, not plain
+text. This can offer major advantages in all those aspects: the file size
+will be greatly reduced and the reading and writing will be faster (because
+the RAM and CPU also work in binary).
+
+The FITS standard also defines a standard for ASCII tables, where the data
+are stored in the human readable ASCII format, but within the FITS file
+structure. These are mainly useful for keeping ASCII data along with images
+and possibly binary data as multiple (conceptually related) extensions
+within a FITS file.
+
address@hidden AWK
address@hidden GNU AWK
+However, this comes at a cost: binary tables are not easily readable by
+human eyes. There is no standard on how the zero and ones should be
+interpretted. The Unix-like operating systems have flurished because of a
+simple fact: communication between the various tools is based on human
+readible address@hidden ``The art of Unix programming'', Eric
+Raymond makes this suggestion to programmers: ``When you feel the urge to
+design a complex binary file format, or a complex binary application
+protocol, it is generally wise to lie down until the feeling
+passes.''. This is a great book and strongly recommended, give it a look if
+you want to truely enjoy your work/life in this environment.}. So while the
+FITS table standards are very beneficial for the tools that recognize them,
+they are hard to use in the vast majority of available software. This cases
+some major limitations for their generic use.
+
+`Table' is Gnuastro's solution to this problem. With Table, FITS tables
+(ASCII or binary) are directly accessible to the Unix-like operating
+systems power-users (those working the command-line or shell, see
address@hidden interface}). With Table, a FITS table (in binary or ASCII
+formats) is only one command away from AWK (or any other tool you want to
+use). Just like a plain text file that you read with the @command{cat}
+command. You can pipe the output of Table into any other tool for
+higher-level processing, see the examples in @ref{Invoking asttable} for
+some very simple examples.
+
address@hidden
+* Invoking asttable::           Options and arguments to Table.
address@hidden menu
+
address@hidden Invoking asttable,  , Table, Table
address@hidden Invoking Table
+
+Table will convert FITS binary and ASCII tables into other such tables, or
+print them on the command-line, or save them in a plain text file. Output
+columns can also be determined by number or regular expression matching of
+column names. The executable name is @file{asttable} with the following
+general template
+
address@hidden
+$ asttable [OPTION...] InputFile
address@hidden example
+
address@hidden
+One line examples:
+
address@hidden
+## Get the table column information (name, data type, or units)
+$ asttable bintab.fits --information
+
+## Only print those columns which have a name starting with "MAG_"
+$ asttable bintab.fits --columns=^MAG_
+
+## Only print the 2nd column, and the third column multiplied by 5
+$ asttable bintab.fits | awk '@{print $2, address@hidden'
+
+## Only print those rows with a value in the 10th column above 100000
+$ asttable bintab.fits | awk '$10>10e5 @address@hidden'
+
+## Sort the output columns by the third column, save output
+$ asttable bintab.fits | sort -k3 > output.txt
+
+## Convert a plain text table to a binary FITS table
+$ asttable plaintext.txt --output=inbinary.fits
address@hidden example
+
+Table can accept plain text files, or binary and ASCII FITS table
+extensions in a FITS file. For the full list of options common to all
+Gnuastro utilities please see @ref{Common options}. Options can also be
+stored in directory, user or system-wide configuration files to avoid
+repeating on the command-line, see @ref{Configuration files}. Currently all
+plain text files are processed (and thus printed, or saved to a binary FITS
+table) as double floating point types. We are still working on this many
+many more features.
 
+Table does not follow Automatic output that is common in most Gnuastro, see
address@hidden output}. If no value is given to the @option{--output}
+option, the desired columns will be printed to the standard output (on the
+command-line). This feature makes it very useful to directly pipe the
+output as input to other programs as the examples above demonstrate. Note
+that the options below which relate to print formatting are only relevant
+when the output is in human readable format (on the command-line and plain
+text files), they are ignored when the output is a binary FITS table.
 
address@hidden @option
 
address@hidden -i
address@hidden --information
+Print the information for each column and abort. The information for each
+column will be printed as a row on the command-line. The column name (if
+present), units (if present) and datatype will printed. Note that the FITS
+standard does not require a name or units for columns, only the datatype is
+mandatory.
+
address@hidden -c
address@hidden --column
+(@option{=STR} or @option{=INT}) Specify the columns to output for this
+table. If an integer number is given, the column number will be used
+(counting from 1). Otherwise the value (a string) will be passed to an
+internal regular expression processor which will try to match all the
+columns in the table with the given regular expression. Currently only FITS
+tables might have column names (it is an optional feature in the FITS
+standard). There is currently no particular standard to name plain text
+columns.
+
+This option can be called any number of times with one run of Table. The
+order of the output columns will be determined by the input order. This
+option is also not mandatory. If not given, all the input table columns are
+output.
+
+Regular expressions are a very powerful tool in matching text and since
+FITS binary tables usually have a large number of columns, this feature can
+greatly simply the selection of the output columns.
+
address@hidden -I
address@hidden --ignorescase
+Ignore case while matching the column names with the value(s) of the
address@hidden option. The FITS standard suggests to treat the column
+names as case insensitive, however it is not a requirement.
+
address@hidden --feg
+(@option{=STR}) Format of printing floating point numbers in non-binary
+outputs. It can only accept one of the three following values (same as C's
address@hidden):
address@hidden
address@hidden
address@hidden: Print complete floating point value, this is good when the 
numbers
+aren't too small, for example @mymath{3.286}. But it will print all the
+zeros in @mymath{3.2\times10^{-15}}.
+
address@hidden
address@hidden: Only print in exponential format. This is good for very large or
+very small numbers, but can make reading the values of more ordinary
+numbers a little hard.
+
address@hidden
address@hidden: Let the system choose which representation is better for the
+number.
address@hidden itemize
+
address@hidden --sintwidth
+(@option{=INT}) The minimum width (number of characters) for printing
+columns of shorter integer datatypes. The shorter datatypes are considered
+to be signed and unsigned characters, short integers, integers.
+
address@hidden --lintwidth
+(@option{=INT}) The minimum width (number of characters) for printing
+columns of longer datatypes. The longer datatypes are considered to be long
+and longlong types.
+
address@hidden --floatwidth
+(@option{=INT}) The minimum width (number of characters) for printing
+columns of single precision floating point datatypes.
+
address@hidden --doublewidth
+(@option{=INT}) The minimum width (number of characters) for printing
+columns of double precision floating point datatypes.
+
address@hidden --strwidth
+(@option{=INT}) The minimum width (number of characters) for printing
+columns of strings (given as one column, the FITS standard allows ASCII
+strings as table elements).
+
address@hidden --floatprecision
+(@option{=INT}) The number of digits to print after the floating point for
+single precision floating point numbers.
+
address@hidden --doubleprecision
+(@option{=INT}) The number of digits to print after the floating point for
+double precision floating point numbers.
+
address@hidden --fitstabletype
+(@option{=STR}) The type of FITS table when a FITS file is specified as the
+output. This option can only have two values: @option{binary}, or
address@hidden However, currently ASCII FITS table outputs are not yet
+implemented due to lack of need. If you need it, please get in touch with
+so we implement it.
address@hidden table
 
 
 
@@ -5942,14 +6165,14 @@ is best to call this option so the image is not 
inverted.
 
 
 
address@hidden Image manipulation, Image analysis, Files, Top
address@hidden Image manipulation, Image analysis, Extensions and Tables, Top
 @chapter Image manipulation
 
-Images are one of the major formats of data that is used in
-astronomy. The functions in this chapter explain the GNU Astronomy
-Utilities which are provided for their manipulation. For example
-cropping out a part of a larger image or convolving the image with a
-given kernel or applying a transformation to it.
+Images are one of the major formats of data that is used in astronomy. The
+functions in this chapter explain the GNU Astronomy Utilities which are
+provided for their manipulation. For example cropping out a part of a
+larger image or convolving the image with a given kernel or applying a
+transformation to it.
 
 @menu
 * ImageCrop::                   Crop region(s) from FITS image(s).
diff --git a/genauthors b/genauthors
index 5371b6d..8bb3889 100755
--- a/genauthors
+++ b/genauthors
@@ -153,6 +153,8 @@ for util in src/* "lib/" "doc/"; do
     elif [ $util = src/mkprof      ]; then name=MakeProfile
     elif [ $util = src/noisechisel ]; then name=NoiseChisel
     elif [ $util = src/subtractsky ]; then name=SubtractSky
+    elif [ $util = src/table       ]; then name=Table
+    elif [ $util = src/TEMPLATE    ]; then name=TEMPLATE
     elif [ $util = lib/            ]; then name=Libraries
     elif [ $util = doc/            ]; then name=Documentation
     else echo; echo; echo "genauthors.sh: $util not recognized!"; exit 1;
diff --git a/lib/checkset.c b/lib/checkset.c
index 58de7cd..1ad53e3 100644
--- a/lib/checkset.c
+++ b/lib/checkset.c
@@ -125,8 +125,8 @@ gal_checkset_int_zero_or_one(char *optarg, int *var, char 
*lo, char so,
 
 
 void
-gal_checkset_int_4_or_8(char *optarg, int *var, char *lo, char so, char *spack,
-                        char *filename, size_t lineno)
+gal_checkset_int_4_or_8(char *optarg, int *var, char *lo, char so,
+                        char *spack, char *filename, size_t lineno)
 {
   long tmp;
   char *tailptr;
@@ -150,8 +150,8 @@ gal_checkset_int_4_or_8(char *optarg, int *var, char *lo, 
char so, char *spack,
 
 
 void
-gal_checkset_int_el_zero(char *optarg, int *var, char *lo, char so, char 
*spack,
-                         char *filename, size_t lineno)
+gal_checkset_int_el_zero(char *optarg, int *var, char *lo, char so,
+                         char *spack, char *filename, size_t lineno)
 {
   long tmp;
   char *tailptr;
@@ -174,8 +174,8 @@ gal_checkset_int_el_zero(char *optarg, int *var, char *lo, 
char so, char *spack,
 
 
 void
-gal_checkset_int_l_zero(char *optarg, int *var, char *lo, char so, char *spack,
-                        char *filename, size_t lineno)
+gal_checkset_int_l_zero(char *optarg, int *var, char *lo, char so,
+                        char *spack, char *filename, size_t lineno)
 {
   long tmp;
   char *tailptr;
@@ -247,8 +247,8 @@ gal_checkset_long_el_zero(char *optarg, long *var, char 
*lo, char so,
 
 
 void
-gal_checkset_any_long(char *optarg, long *var, char *lo, char so, char *spack,
-                      char *filename, size_t lineno)
+gal_checkset_any_long(char *optarg, long *var, char *lo, char so,
+                      char *spack, char *filename, size_t lineno)
 {
   char *tailptr;
   *var=strtol(optarg, &tailptr, 0);
@@ -339,8 +339,8 @@ gal_checkset_sizet_p_odd(char *optarg, size_t *var, char 
*lo, char so,
 
 
 void
-gal_checkset_float_l_0(char *optarg, float *var, char *lo, char so, char* 
spack,
-                       char *filename, size_t lineno)
+gal_checkset_float_l_0(char *optarg, float *var, char *lo, char so,
+                       char* spack, char *filename, size_t lineno)
 {
   float tmp;
   char *tailptr;
@@ -386,8 +386,8 @@ gal_checkset_float_l_0_s_1(char *optarg, float *var, char 
*lo, char so,
 
 
 void
-gal_checkset_any_float(char *optarg, float *var, char *lo, char so, char 
*spack,
-                       char *filename, size_t lineno)
+gal_checkset_any_float(char *optarg, float *var, char *lo, char so,
+                       char *spack, char *filename, size_t lineno)
 {
   char *tailptr;
   *var=strtof(optarg, &tailptr);
@@ -622,6 +622,28 @@ gal_checkset_check_file(char *filename)
 
 
 
+/* Similar to `gal_checkset_check_file', but will report the result instead
+   of doing it quietly. */
+int
+gal_checkset_check_file_report(char *filename)
+{
+  FILE *tmpfile;
+  errno=0;
+  tmpfile = fopen(filename, "r");
+  if(tmpfile)                        /* The file opened. */
+    {
+      if(fclose(tmpfile)==EOF)
+        error(EXIT_FAILURE, errno, "%s", filename);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+
+
+
 /* Check if a file exists. If so, remove it. */
 void
 gal_checkset_check_remove_file(char *filename, int dontdelete)
@@ -672,10 +694,10 @@ gal_checkset_dir_0_file_1(char *name, int dontdelete)
   struct stat nameinfo;
 
   if(name==NULL)
-    error(EXIT_FAILURE, 0, "a bug! The input to the gal_checkset_dir_0_file_1 "
-          "function in checkset.c should not be NULL. Please contact us at "
-          PACKAGE_BUGREPORT" so we can see what went wrong and fix it in "
-          "future updates");
+    error(EXIT_FAILURE, 0, "a bug! The input to the "
+          "gal_checkset_dir_0_file_1 function in checkset.c should not "
+          "be NULL. Please contact us at "PACKAGE_BUGREPORT" so we can "
+          "see what went wrong and fix it in future updates");
 
   errno=0;
   if(stat(name, &nameinfo)!=0)
diff --git a/lib/fits.c b/lib/fits.c
index 1168bd3..38ecb07 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -29,7 +29,6 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <assert.h>
 
 #include <gnuastro/fits.h>
 #include <gnuastro/checkset.h>
@@ -167,6 +166,12 @@ gal_fits_bitpix_to_dtype(int bitpix)
       return TFLOAT;
     case DOUBLE_IMG:
       return TDOUBLE;
+    case SBYTE_IMG:
+      return TSBYTE;
+    case USHORT_IMG:
+      return TUSHORT;
+    case ULONG_IMG:
+      return TULONG;
     default:
       error(EXIT_FAILURE, 0, "bitpix value of %d not recognized",
             bitpix);
@@ -178,64 +183,204 @@ gal_fits_bitpix_to_dtype(int bitpix)
 
 
 
+/* The values to the TFORM header keyword are single letter capital
+   letters, but that is useless in identifying the data type of the
+   column. So this function will do the conversion based on the CFITSIO
+   manual.*/
+int
+gal_fits_tform_to_dtype(char tform)
+{
+  switch(tform)
+    {
+    case 'X':
+      return TBIT;
+    case 'B':
+      return TBYTE;
+    case 'L':
+      return TLOGICAL;
+    case 'A':
+      return TSTRING;
+    case 'I':
+      return TSHORT;
+    case 'J':
+      return TLONG;
+    case 'K':
+      return TLONGLONG;
+    case 'E':
+      return TFLOAT;
+    case 'D':
+      return TDOUBLE;
+    case 'C':
+      return TCOMPLEX;
+    case 'M':
+      return TDBLCOMPLEX;
+    case 'S':
+      return TSBYTE;
+    case 'V':
+      return TUINT;
+    case 'U':
+      return TUSHORT;
+    default:
+      error(EXIT_FAILURE, 0, "'%c' is not a recognized CFITSIO value for "
+            "the TFORMn header keyword(s).", tform);
+    }
+
+  error(EXIT_FAILURE, 0, "A bug! Please contact us so we can fix this. "
+        "For some reason, control has reached to the end of the "
+        "gal_fits_tform_to_dtype function in fits.c.");
+  return -1;
+}
+
+
+
+
+
 void *
-gal_fits_bitpix_blank(int bitpix)
+gal_fits_datatype_blank(int datatype)
 {
+  /* Define the pointers, note that we are ordering them based on the
+     CFITSIO manual to be more easily comparable. */
   unsigned char *b;
+  char *c;
+  char **str;
   short *s;
   long *l;
   LONGLONG *L;
   float *f;
   double *d;
+  gsl_complex_float *cx;
+  gsl_complex *dcx;
+  int *i;
+  unsigned int *ui;
+  unsigned short *us;
+  unsigned long *ul;
 
   errno=0;
-  switch(bitpix)
+  switch(datatype)
     {
-    case BYTE_IMG:
-      b=malloc(sizeof(unsigned char));
+    case TBIT:
+      error(EXIT_FAILURE, 0, "Currently Gnuastro doesn't support TBIT "
+            "datatype, please get in touch with us to implement it.");
+
+    case TBYTE:
+      b=malloc(sizeof *b);
       if(b==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(unsigned char));
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TBYTE",
+              sizeof *b);
       *b=GAL_FITS_BYTE_BLANK;
       return b;
 
-    case SHORT_IMG:
-      s=malloc(sizeof(short));
+      /* CFITSIO says "int for keywords, char for table columns". Here we
+         are only assuming table columns. So in practice this also applies
+         to TSBYTE.*/
+    case TLOGICAL: case TSBYTE:
+      c=malloc(sizeof *c);
+      if(c==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TLOGICAL, or TSBYTE",
+              sizeof *c);
+      *c=GAL_FITS_LOGICAL_BLANK;
+      return c;
+
+    case TSTRING:
+      str=malloc(sizeof *str);
+      if(str==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TSTRING",
+              sizeof *s);
+      *str=GAL_FITS_STRING_BLANK;
+      return str;
+
+    case TSHORT:
+      s=malloc(sizeof *s);
       if(s==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(short));
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TSHORT",
+              sizeof *s);
       *s=GAL_FITS_SHORT_BLANK;
       return s;
 
-    case LONG_IMG:
-      l=malloc(sizeof(long));
+    case TLONG:
+      l=malloc(sizeof *l);
       if(l==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(long));
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TLONG",
+              sizeof *l);
       *l=GAL_FITS_LONG_BLANK;
       return l;
 
-    case LONGLONG_IMG:
-      L=malloc(sizeof(LONGLONG));
+    case TLONGLONG:
+      L=malloc(sizeof *L);
       if(L==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(LONGLONG));
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TLONGLONG",
+              sizeof *L);
       *L=GAL_FITS_LLONG_BLANK;
       return L;
 
-    case FLOAT_IMG:
-      f=malloc(sizeof(float));
+    case TFLOAT:
+      f=malloc(sizeof *f);
       if(f==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(float));
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TFLOAT",
+              sizeof *f);
       *f=GAL_FITS_FLOAT_BLANK;
       return f;
 
-    case DOUBLE_IMG:
-      d=malloc(sizeof(double));
+    case TDOUBLE:
+      d=malloc(sizeof *d);
       if(d==NULL)
-        error(EXIT_FAILURE, errno, "%lu bytes", sizeof(double));
-      *d=GAL_FITS_FLOAT_BLANK;
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TDOUBLE",
+              sizeof *d);
+      *d=GAL_FITS_DOUBLE_BLANK;
       return d;
 
+    case TCOMPLEX:
+      cx=malloc(sizeof *cx);
+      if(cx==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TCOMPLEX",
+              sizeof *cx);
+      GSL_SET_COMPLEX(cx,GAL_FITS_FLOAT_BLANK,GAL_FITS_FLOAT_BLANK);
+      return cx;
+
+    case TDBLCOMPLEX:
+      dcx=malloc(sizeof *dcx);
+      if(dcx==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TDBLCOMPLEX",
+              sizeof *dcx);
+      GSL_SET_COMPLEX(dcx,GAL_FITS_DOUBLE_BLANK,GAL_FITS_DOUBLE_BLANK);
+      return dcx;
+
+    case TINT:
+      i=malloc(sizeof *i);
+      if(i==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TINT",
+              sizeof *i);
+      *i=GAL_FITS_INT_BLANK;
+      return i;
+
+    case TUINT:
+      ui=malloc(sizeof *ui);
+      if(ui==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TUINT",
+              sizeof *ui);
+      *ui=GAL_FITS_UINT_BLANK;
+      return ui;
+
+    case TUSHORT:
+      us=malloc(sizeof *us);
+      if(us==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TUSHORT",
+              sizeof *us);
+      *ui=GAL_FITS_USHORT_BLANK;
+      return us;
+
+    case TULONG:
+      ul=malloc(sizeof *ul);
+      if(ul==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for blank TULONG",
+              sizeof *ul);
+      *ul=GAL_FITS_ULONG_BLANK;
+      return ul;
+
+
     default:
-      error(EXIT_FAILURE, 0, "bitpix value of %d not recognized",
-            bitpix);
+      error(EXIT_FAILURE, 0, "datatype value of %d not recognized",
+            datatype);
     }
 
   return NULL;
@@ -245,55 +390,108 @@ gal_fits_bitpix_blank(int bitpix)
 
 
 
-/* Allocate an array based on the value of bitpix. */
+/* Allocate an array based on the value of bitpix. Note that the argument
+   `size' is the number of elements, necessary in the array, the number of
+   bytes each element needs will be determined internaly by this function
+   using the datatype argument, so you don't have to worry about it. */
 void *
-gal_fits_bitpix_alloc(size_t size, int bitpix)
+gal_fits_datatype_alloc(size_t size, int datatype)
 {
   void *array;
 
   /* Allocate space for the array to keep the image. */
-  switch(bitpix)
+  switch(datatype)
     {
-    case BYTE_IMG:
-      size*=sizeof(unsigned char);
+    case TBIT:
+      error(EXIT_FAILURE, 0, "Currently Gnuastro doesn't support TBIT "
+            "datatype, please get in touch with us to implement it.");
+
+      /* The parenthesis after sizeof is not a function, it is actually a
+         type cast, so we have put a space between size of and the
+         parenthesis to highlight this. In C, `sizeof' is an operator, not
+         a function.*/
+    case TBYTE:
+      size *= sizeof (unsigned char);
       break;
 
-    case SHORT_IMG:
-      size*=sizeof(short);
+    case TLOGICAL: case TSBYTE:
+      size *= sizeof (char);
       break;
 
-    case LONG_IMG:
-      size*=sizeof(long);
+    case TSTRING:
+      size *= sizeof (char *);
       break;
 
-    case LONGLONG_IMG:
-      size*=sizeof(LONGLONG);
+    case TSHORT:
+      size *= sizeof (short);
       break;
 
-    case FLOAT_IMG:
-      if(sizeof(float)!=4)
+    case TLONG:
+      size *= sizeof (long);
+      break;
+
+    case TLONGLONG:
+      size *= sizeof (LONGLONG);
+      break;
+
+    case TFLOAT:
+      if( sizeof (float) != 4 )
         error(EXIT_FAILURE, 0,
               "`float` is not 32bits on this machine. The FITS standard "
               "Requires this size");
-      size*=sizeof(float);
+      size *= sizeof (float);
       break;
 
-    case DOUBLE_IMG:
-      if(sizeof(double)!=8)
+    case TDOUBLE:
+      if( sizeof (double) != 8 )
+        error(EXIT_FAILURE, 0,
+              "`double` is not 64bits on this machine. The FITS standard "
+              "requires this size");
+      size *= sizeof (double);
+      break;
+
+    case TCOMPLEX:
+      if( sizeof (float) != 4 )
+        error(EXIT_FAILURE, 0,
+              "`float` is not 32bits on this machine. The FITS standard "
+              "Requires this size");
+      size *= sizeof (gsl_complex_float);
+      break;
+
+    case TDBLCOMPLEX:
+      if( sizeof (double) != 8 )
         error(EXIT_FAILURE, 0,
               "`double` is not 64bits on this machine. The FITS standard "
               "requires this size");
-      size*=sizeof(double);
+      size *= sizeof (gsl_complex);
+      break;
+
+    case TINT:
+      size *= sizeof (int);
+      break;
+
+    case TUINT:
+      size *= sizeof (unsigned int);
+      break;
+
+    case TUSHORT:
+      size *= sizeof (unsigned short);
+      break;
+
+    case TULONG:
+      size *= sizeof (unsigned long);
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "bitpix value of %d not recognized", bitpix);
+      error(EXIT_FAILURE, 0, "datatype value of %d not recognized in "
+            "gal_fits_datatype_alloc", datatype);
     }
 
   errno=0;
   array=malloc(size);
   if(array==NULL)
-    error(EXIT_FAILURE, errno, "array of %lu bytes", size);
+    error(EXIT_FAILURE, errno,
+          "array of %lu bytes in gal_fits_datatype_alloc", size);
 
   return array;
 }
@@ -303,54 +501,155 @@ gal_fits_bitpix_alloc(size_t size, int bitpix)
 
 
 void
-blanktovalue(void *array, int bitpix, size_t size, void *value)
+gal_fits_blank_to_value(void *array, int datatype, size_t size, void *value)
 {
-  /* 'value' will only be read from one of these based on bitpix. Which the
-     caller assigned. If there is any problem, it is their responsability,
-     not this functions :-).*/
-  unsigned char *b, *bf, bv=*(uint8_t *) value;
-  short *s, *sf, sv=*(int16_t *) value;
-  long *l, *lf, lv=*(int32_t *) value;
-  LONGLONG *L, *Lf, Lv=*(int64_t *) value;
-  float   *f, *ff, fv=*(float   *) value;
-  double  *d, *df, dv=*(double  *) value;
-
-  switch(bitpix)
+  /* 'value' will only be read from one of these based on the
+     datatype. Which the caller assigned. If there is any problem, it is
+     their responsability, not this function's.*/
+  unsigned char *b, *bf,        bv = *(uint8_t *) value;
+  char *c, *cf,                 cv = *(char *) value;
+  char **str, **strf,        *strv = *(char **) value;
+  short *s, *sf,                sv = *(int16_t *) value;
+  long *l, *lf,                 lv = *(int32_t *) value;
+  LONGLONG *L, *Lf,             Lv = *(int64_t *) value;
+  float   *f, *ff,              fv = *(float *) value;
+  double  *d, *df,              dv = *(double *) value;
+  gsl_complex_float *cx, *cxf, cxv = *(gsl_complex_float *) value;
+  gsl_complex *dcx, *dcxf,    dcxv = *(gsl_complex *) value;
+  int *in, *inf,               inv = *(int *) value;
+  unsigned int *ui, *uif,      uiv = *(unsigned int *) value;
+  unsigned short *us, *usf,    usv = *(unsigned short *) value;
+  unsigned long *ul, *ulf,     ulv = *(unsigned long *) value;
+
+  switch(datatype)
     {
-    case BYTE_IMG:
+    case TBIT:
+      error(EXIT_FAILURE, 0, "Currently Gnuastro doesn't support TBIT "
+            "datatype, please get in touch with us to implement it.");
+
+    case TBYTE:
       bf=(b=array)+size;
-      do if(*b==GAL_FITS_BYTE_BLANK) *b=bv; while(++b<bf);
+      do if(*b==GAL_FITS_BYTE_BLANK) *b++=bv; while(b<bf);
       break;
 
-    case SHORT_IMG:
+
+    case TLOGICAL: case TSBYTE:
+      cf=(c=array)+size;
+      do if(*c==GAL_FITS_LOGICAL_BLANK) *c++=cv; while(c<cf);
+      break;
+
+
+    case TSTRING:
+      strf=(str=array)+size;
+      do if(*str==GAL_FITS_STRING_BLANK) *str++=strv; while(str<strf);
+      break;
+
+
+    case TSHORT:
       sf=(s=array)+size;
-      do if(*s==GAL_FITS_SHORT_BLANK) *s=sv; while(++s<sf);
+      do if(*s==GAL_FITS_SHORT_BLANK) *s++=sv; while(s<sf);
       break;
 
-    case LONG_IMG:
+
+    case TLONG:
       lf=(l=array)+size;
-      do if(*l==GAL_FITS_LONG_BLANK) *l=lv; while(++l<lf);
+      do if(*l==GAL_FITS_LONG_BLANK) *l++=lv; while(l<lf);
       break;
 
-    case LONGLONG_IMG:
+
+    case TLONGLONG:
       Lf=(L=array)+size;
-      do if(*L==GAL_FITS_LLONG_BLANK) *L=Lv; while(++L<Lf);
+      do if(*L==GAL_FITS_LLONG_BLANK) *L++=Lv; while(L<Lf);
       break;
 
-    case FLOAT_IMG:
+
+      /* Note that a NaN value is not equal to another NaN value, so we
+         can't use the easy check for cases were the blank value is
+         NaN. Also note that `isnan' is actually a macro, so it works for
+         both float and double types.*/
+    case TFLOAT:
       ff=(f=array)+size;
-      do if(*f==GAL_FITS_FLOAT_BLANK) *f=fv; while(++f<ff);
+      if(isnan(GAL_FITS_FLOAT_BLANK))
+        do if(isnan(*f)) *f++=fv; while(f<ff);
+      else
+        do if(*f==GAL_FITS_FLOAT_BLANK) *f++=fv; while(f<ff);
       break;
 
-    case DOUBLE_IMG:
+
+    case TDOUBLE:
       df=(d=array)+size;
-      do if(*d==GAL_FITS_FLOAT_BLANK) *d=dv; while(++d<df);
+      if(isnan(GAL_FITS_DOUBLE_BLANK))
+        do if(isnan(*d)) *d++=dv; while(d<df);
+      else
+        do if(*d==GAL_FITS_FLOAT_BLANK) *d++=dv; while(d<df);
+      break;
+
+
+    case TCOMPLEX:
+      cxf=(cx=array)+size;
+      if(isnan(GAL_FITS_FLOAT_BLANK))
+          do
+            if(isnan(GSL_COMPLEX_P_REAL(cx))
+               && isnan(GSL_COMPLEX_P_IMAG(cx)) )
+              GSL_SET_COMPLEX(cx, GSL_COMPLEX_P_REAL(&cxv),
+                              GSL_COMPLEX_P_IMAG(&cxv));
+          while(++cx<cxf);
+      else
+        do
+          if( GSL_COMPLEX_P_REAL(cx) == GAL_FITS_FLOAT_BLANK
+              && GSL_COMPLEX_P_IMAG(cx) == GAL_FITS_FLOAT_BLANK)
+            GSL_SET_COMPLEX(cx, GSL_COMPLEX_P_REAL(&cxv),
+                            GSL_COMPLEX_P_IMAG(&cxv));
+        while(++cx<cxf);
+      break;
+
+
+    case TDBLCOMPLEX:
+      dcxf=(dcx=array)+size;
+      if(isnan(GAL_FITS_DOUBLE_BLANK))
+          do
+            if(isnan(GSL_COMPLEX_P_REAL(dcx))
+               && isnan(GSL_COMPLEX_P_IMAG(dcx)) )
+              GSL_SET_COMPLEX(dcx, GSL_COMPLEX_P_REAL(&dcxv),
+                              GSL_COMPLEX_P_IMAG(&dcxv));
+          while(++dcx<dcxf);
+      else
+        do
+          if( GSL_COMPLEX_P_REAL(dcx) == GAL_FITS_FLOAT_BLANK
+              && GSL_COMPLEX_P_IMAG(dcx) == GAL_FITS_FLOAT_BLANK)
+            GSL_SET_COMPLEX(dcx, GSL_COMPLEX_P_REAL(&dcxv),
+                            GSL_COMPLEX_P_IMAG(&dcxv));
+        while(++dcx<dcxf);
+      break;
+
+
+    case TINT:
+      inf=(in=array)+size;
+      do if(*in==GAL_FITS_INT_BLANK) *in++=inv; while(in<inf);
+      break;
+
+
+    case TUINT:
+      uif=(ui=array)+size;
+      do if(*ui==GAL_FITS_UINT_BLANK) *ui++=uiv; while(ui<uif);
+      break;
+
+
+    case TUSHORT:
+      usf=(us=array)+size;
+      do if(*us==GAL_FITS_USHORT_BLANK) *us++=usv; while(us<usf);
+      break;
+
+
+    case TULONG:
+      ulf=(ul=array)+size;
+      do if(*ul==GAL_FITS_ULONG_BLANK) *ul++=ulv; while(ul<ulf);
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "a bug! Bitpix value of %d not recognized. "
+      error(EXIT_FAILURE, 0, "a bug! datatype value of %d not recognized. "
             "This should not happen here (blanktovalue in fitsarrayvv.c). "
-            "Please contact us at %s to see how this happened", bitpix,
+            "Please contact us at %s to see how this happened", datatype,
             PACKAGE_BUGREPORT);
     }
 }
@@ -364,15 +663,15 @@ gal_fits_change_type(void *in, int inbitpix, size_t size, 
int anyblank,
                      void **out, int outbitpix)
 {
   size_t i=0;
-  unsigned char *b, *bf, *ib=in, *iib=in;
-  short *s, *sf, *is=in, *iis=in;
-  long *l, *lf, *il=in, *iil=in;
-  LONGLONG *L, *Lf, *iL=in, *iiL=in;
-  float *f, *ff, *iif=in, *iiif=in;
-  double *d, *df, *id=in, *iid=in;
+  unsigned char *b, *bf,  *ib=in,  *iib=in;
+  short         *s, *sf,  *is=in,  *iis=in;
+  long          *l, *lf,  *il=in,  *iil=in;
+  LONGLONG      *L, *Lf,  *iL=in,  *iiL=in;
+  float         *f, *ff, *iif=in, *iiif=in;
+  double        *d, *df,  *id=in,  *iid=in;
 
   /* Allocate space for the output and start filling it. */
-  *out=gal_fits_bitpix_alloc(size, outbitpix);
+  *out=gal_fits_datatype_alloc(size, gal_fits_bitpix_to_dtype(outbitpix) );
   switch(outbitpix)
     {
     case BYTE_IMG:
@@ -733,7 +1032,7 @@ hdutypestring(int hdutype)
 /* Check the desired HDU in a FITS image and also if it has the
    desired type. */
 void
-gal_fits_read_hdu(char *filename, char *hdu, int desiredtype,
+gal_fits_read_hdu(char *filename, char *hdu, unsigned char img0_tab1,
                   fitsfile **outfptr)
 {
   size_t len;
@@ -758,11 +1057,24 @@ gal_fits_read_hdu(char *filename, char *hdu, int 
desiredtype,
   if (fits_get_hdu_type(fptr, &hdutype, &status) )
     gal_fits_io_error(status, NULL);
 
-  if(hdutype!=desiredtype)
-    error(EXIT_FAILURE, 0, "%s: HDU %s is %s, not %s",
-          filename, hdu, hdutypestring(hdutype),
-          hdutypestring(desiredtype));
 
+  /* Check if the type of the HDU is the expected type. We could have
+     written these as && conditions, but this is easier to read, it makes
+     no meaningful difference to the compiler. */
+  if(img0_tab1)
+    {
+      if(hdutype==IMAGE_HDU)
+        error(EXIT_FAILURE, 0, "%s: HDU %s is an image, not a table",
+              filename, hdu);
+    }
+  else
+    {
+      if(hdutype!=IMAGE_HDU)
+        error(EXIT_FAILURE, 0, "%s: HDU %s is %s, not an image",
+              filename, hdu, hdutypestring(hdutype));
+    }
+
+  /* Clean up. */
   free(ffname);
 }
 
@@ -1309,7 +1621,7 @@ gal_fits_read_wcs(char *filename, char *hdu, size_t 
hstartwcs,
   fitsfile *fptr;
 
   /* Check HDU for realistic conditions: */
-  gal_fits_read_hdu(filename, hdu, IMAGE_HDU, &fptr);
+  gal_fits_read_hdu(filename, hdu, 0, &fptr);
 
   /* Read the WCS information: */
   gal_fits_read_wcs_from_pointer(fptr, nwcs, wcs, hstartwcs, hendwcs);
@@ -1332,15 +1644,15 @@ gal_fits_read_wcs(char *filename, char *hdu, size_t 
hstartwcs,
    the macros in fitsarrayvv.h and depends on the type of the data.*/
 int
 gal_fits_hdu_to_array(char *filename, char *hdu, int *bitpix,
-                                void **array, size_t *s0, size_t *s1)
+                      void **array, size_t *s0, size_t *s1)
 {
   void *bitblank;
   fitsfile *fptr;
-  int status=0, anyblank=0;
   long naxes[2], fpixel[]={1,1};
+  int status=0, anyblank=0, datatype;
 
   /* Check HDU for realistic conditions: */
-  gal_fits_read_hdu(filename, hdu, IMAGE_HDU, &fptr);
+  gal_fits_read_hdu(filename, hdu, 0, &fptr);
 
   /* Get the bitpix and size of the image: */
   gal_fits_img_bitpix_size(fptr, bitpix, naxes);
@@ -1348,8 +1660,9 @@ gal_fits_hdu_to_array(char *filename, char *hdu, int 
*bitpix,
   *s1=naxes[0];
 
   /* Allocate space for the array. */
-  bitblank=gal_fits_bitpix_blank(*bitpix);
-  *array=gal_fits_bitpix_alloc(*s0 * *s1, *bitpix);
+  datatype=gal_fits_bitpix_to_dtype(*bitpix);
+  bitblank=gal_fits_datatype_blank(datatype);
+  *array=gal_fits_datatype_alloc(*s0 * *s1, datatype);
 
   /* Read the image into the allocated array: */
   fits_read_pix(fptr, gal_fits_bitpix_to_dtype(*bitpix), fpixel,
@@ -1416,7 +1729,7 @@ gal_fits_array_to_file(char *filename, char *hdu, int 
bitpix,
     if(bitpix==BYTE_IMG || bitpix==SHORT_IMG
        || bitpix==LONG_IMG || bitpix==LONGLONG_IMG)
       {
-        blank=gal_fits_bitpix_blank(bitpix);
+        blank=gal_fits_datatype_blank( gal_fits_bitpix_to_dtype(bitpix) );
         if(fits_write_key(fptr, datatype, "BLANK", blank,
                           "Pixels with no data.", &status) )
           gal_fits_io_error(status, "adding the BLANK keyword");
@@ -1516,6 +1829,85 @@ gal_fits_atof_correct_wcs(char *filename, char *hdu, int 
bitpix,
 
 
 /**************************************************************/
+/**********                 Table                  ************/
+/**************************************************************/
+/* Get the size of a table HDU. CFITSIO doesn't use size_t, also we want to
+   check status here.*/
+void
+gal_fits_table_size(fitsfile *fitsptr, size_t *nrows, size_t *ncols)
+{
+  long lnrows;
+  int incols, status=0;
+
+  /* Read the sizes and put them in. */
+  fits_get_num_rows(fitsptr, &lnrows, &status);
+  fits_get_num_cols(fitsptr, &incols, &status);
+  *ncols=incols;
+  *nrows=lnrows;
+
+  /* Report an error if any was issued. */
+  gal_fits_io_error(status, NULL);
+}
+
+
+
+
+
+int
+gal_fits_table_type(fitsfile *fptr)
+{
+  int status=0;
+  char value[FLEN_VALUE];
+
+  fits_read_key(fptr, TSTRING, "XTENSION", value, NULL, &status);
+
+  if(status==0)
+    {
+      if(!strcmp(value, "TABLE   "))
+        return ASCII_TBL;
+      else if(!strcmp(value, "BINTABLE"))
+        return BINARY_TBL;
+      else
+        error(EXIT_FAILURE, 0, "The `XTENSION' keyword of this FITS file "
+              "doesn't have a standard value (`%s')", value);
+    }
+  else
+    {
+      if(status==KEY_NO_EXIST)
+        error(EXIT_FAILURE, 0, "the `gal_fits_table_type' function was "
+              "called on a FITS extension which is not a table. As part "
+              "of a utility, this is bug, so please contact us at %s so "
+              "we can fix it.", PACKAGE_BUGREPORT);
+      else
+        gal_fits_io_error(status, NULL);
+    }
+
+  error(EXIT_FAILURE, 0, "A bug! Please contact us at %s so we can fix it. "
+        "for some reason, the control of `gal_fits_table_type' has reached "
+        "the end of the function! This must not happen", PACKAGE_BUGREPORT);
+  return -1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
 /**********          Check prepare file            ************/
 /**************************************************************/
 /* We have the name of the input file. But in most cases, the files
diff --git a/lib/gnuastro/checkset.h b/lib/gnuastro/checkset.h
index 532a64d..1de6428 100644
--- a/lib/gnuastro/checkset.h
+++ b/lib/gnuastro/checkset.h
@@ -205,6 +205,9 @@ gal_checkset_allocate_copy_set(char *arg, char **copy, int 
*set);
 void
 gal_checkset_check_file(char *filename);
 
+int
+gal_checkset_check_file_report(char *filename);
+
 void
 gal_checkset_check_remove_file(char *filename, int dontdelete);
 
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index a0456a4..4a81360 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -26,18 +26,36 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <math.h>
 #include <float.h>
 #include <stdint.h>
+#include <limits.h>
 
 #include <fitsio.h>
 #include <wcslib/wcs.h>
 #include <wcslib/wcshdr.h>
 #include <wcslib/wcsfix.h>
+#include <gsl/gsl_complex.h>
+
+
+
+
+
+/* Order is based on the CFITSIO manual. Note that for the unsigned types
+   or small types (like char), the maximum value is considered as a blank
+   value, since the minimum value of an unsigned type is zero and zero is
+   often meaningful in contexts were unsigned values are used. */
+#define GAL_FITS_STRING_BLANK     NULL
+#define GAL_FITS_BYTE_BLANK       UCHAR_MAX
+#define GAL_FITS_LOGICAL_BLANK    SCHAR_MAX
+#define GAL_FITS_SHORT_BLANK      INT16_MIN
+#define GAL_FITS_LONG_BLANK       INT32_MIN
+#define GAL_FITS_LLONG_BLANK      INT64_MIN
+#define GAL_FITS_FLOAT_BLANK      NAN
+#define GAL_FITS_DOUBLE_BLANK     NAN
+#define GAL_FITS_INT_BLANK        INT_MIN
+#define GAL_FITS_SBYTE_BLANK      SCHAR_MAX
+#define GAL_FITS_UINT_BLANK       UINT_MAX
+#define GAL_FITS_USHORT_BLANK     USHRT_MAX
+#define GAL_FITS_ULONG_BLANK      ULONG_MAX
 
-#define GAL_FITS_STRING_BLANK   NULL
-#define GAL_FITS_BYTE_BLANK     UCHAR_MAX /* 0 is often meaningful here! */
-#define GAL_FITS_SHORT_BLANK    INT16_MIN
-#define GAL_FITS_LONG_BLANK     INT32_MIN
-#define GAL_FITS_LLONG_BLANK    INT64_MIN
-#define GAL_FITS_FLOAT_BLANK    NAN
 
 
 
@@ -160,11 +178,14 @@ gal_fits_copyright_end(fitsfile *fptr,
  ******************        Read/Write        *****************
  *************************************************************/
 void *
-gal_fits_bitpix_blank(int bitpix);
+gal_fits_datatype_blank(int datatype);
 
 void
 gal_fits_convert_blank(void *array, int bitpix, size_t size, void *value);
 
+void
+gal_fits_blank_to_value(void *array, int datatype, size_t size, void *value);
+
 int
 gal_fits_bitpix_to_dtype(int bitpix);
 
@@ -172,11 +193,11 @@ void
 gal_fits_img_bitpix_size(fitsfile *fptr, int *bitpix, long *naxis);
 
 void
-gal_fits_read_hdu(char *filename, char *hdu, int desiredtype,
+gal_fits_read_hdu(char *filename, char *hdu, unsigned char img0_tab1,
                   fitsfile **outfptr);
 
 void *
-gal_fits_bitpix_alloc(size_t size, int bitpix);
+gal_fits_datatype_alloc(size_t size, int datatype);
 
 void
 gal_fits_change_type(void *in, int inbitpix, size_t size, int anyblank,
@@ -213,6 +234,22 @@ gal_fits_atof_correct_wcs(char *filename, char *hdu, int 
bitpix,
 
 
 /**************************************************************/
+/**********                  Table                 ************/
+/**************************************************************/
+int
+gal_fits_tform_to_dtype(char tform);
+
+void
+gal_fits_table_size(fitsfile *fitsptr, size_t *nrows, size_t *ncols);
+
+int
+gal_fits_table_type(fitsfile *fptr);
+
+
+
+
+
+/**************************************************************/
 /**********          Check prepare file            ************/
 /**************************************************************/
 void
diff --git a/lib/gnuastro/linkedlist.h b/lib/gnuastro/linkedlist.h
index 045a950..daac34c 100644
--- a/lib/gnuastro/linkedlist.h
+++ b/lib/gnuastro/linkedlist.h
@@ -170,14 +170,14 @@ void
 gal_linkedlist_pop_from_sll(struct gal_linkedlist_sll **list, size_t *value);
 
 size_t
-gal_linkedlist_num_in_xsll(struct gal_linkedlist_sll *list);
+gal_linkedlist_num_in_sll(struct gal_linkedlist_sll *list);
 
 void
 gal_linkedlist_print_sll(struct gal_linkedlist_sll *list);
 
 void
 gal_linkedlist_sll_to_array(struct gal_linkedlist_sll *list,
-                            size_t **f, size_t *num);
+                            size_t **f, size_t *num, int inverse);
 
 void
 gal_linkedlist_free_sll(struct gal_linkedlist_sll *list);
diff --git a/lib/gnuastro/txtarray.h b/lib/gnuastro/txtarray.h
index 277adb3..6c65281 100644
--- a/lib/gnuastro/txtarray.h
+++ b/lib/gnuastro/txtarray.h
@@ -50,6 +50,10 @@ gal_txtarray_txt_to_array(char *filename, double **array,
                           size_t *s0, size_t *s1);
 
 void
+gal_txtarray_printf_format(int numcols, char **fmt, int *int_cols,
+                           int *accu_cols, int *space, int *prec, char forg);
+
+void
 gal_txtarray_array_to_txt(double *array, size_t s0, size_t s1, char *comments,
                           int *int_cols, int *accu_cols, int *space, int *prec,
                           char forg, const char *filename);
diff --git a/lib/linkedlist.c b/lib/linkedlist.c
index 5d79777..f3eb1f1 100644
--- a/lib/linkedlist.c
+++ b/lib/linkedlist.c
@@ -447,9 +447,9 @@ gal_linkedlist_num_in_sll(struct gal_linkedlist_sll *list)
 
 void
 gal_linkedlist_sll_to_array(struct gal_linkedlist_sll *list,
-                            size_t **f, size_t *num)
+                            size_t **f, size_t *num, int inverse)
 {
-  size_t i=0, *tf;
+  size_t i, *tf;
   struct gal_linkedlist_sll *tmp;
 
   *num=gal_linkedlist_num_in_sll(list);
@@ -461,8 +461,13 @@ gal_linkedlist_sll_to_array(struct gal_linkedlist_sll 
*list,
           "with %lu elements", *num);
   tf=*f;
 
-  for(tmp=list;tmp!=NULL;tmp=tmp->next)
-    tf[i++]=tmp->v;
+  i = inverse ? *num-1: 0;
+  if(inverse)
+    for(tmp=list;tmp!=NULL;tmp=tmp->next)
+      tf[i--]=tmp->v;
+  else
+    for(tmp=list;tmp!=NULL;tmp=tmp->next)
+      tf[i++]=tmp->v;
 }
 
 
diff --git a/lib/txtarray.c b/lib/txtarray.c
index 638bd79..d5f11de 100644
--- a/lib/txtarray.c
+++ b/lib/txtarray.c
@@ -292,8 +292,8 @@ gal_txtarray_txt_to_array(char *filename, double **array,
    by writeasciitable and makes an array of formatting conditions that
    is suitable for printing.  */
 void
-doformatting(int numcols, char **fmt, int *int_cols, int *accu_cols,
-             int *space, int *prec, char forg)
+gal_txtarray_printf_format(int numcols, char **fmt, int *int_cols,
+                           int *accu_cols, int *space, int *prec, char forg)
 {
   int i,j, found=0;
 
@@ -403,7 +403,8 @@ gal_txtarray_array_to_txt(double *array, size_t s0, size_t 
s1,
           "column with %lu elements", s1);
 
   /* Prepare the formatting for each column */
-  doformatting(s1, fmt, int_cols, accu_cols, space, prec, forg);
+  gal_txtarray_printf_format(s1, fmt, int_cols, accu_cols,
+                             space, prec, forg);
 
   /* Open the output file: */
   errno=0;
diff --git a/src/imgcrop/crop.c b/src/imgcrop/crop.c
index 2d234fd..244f3cd 100644
--- a/src/imgcrop/crop.c
+++ b/src/imgcrop/crop.c
@@ -295,8 +295,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
   LONGLONG *Lb, *La=array;
   unsigned char *bb, *ba=array;
   double *db, *ipolygon, point[2], *da=array;
-  int outpolygon=crp->p->outpolygon, bitpix=crp->p->bitpix;
   size_t i, *ordinds, size=s0*s1, nvertices=crp->p->nvertices;
+  int outpolygon=crp->p->outpolygon, datatype=crp->p->datatype;
 
 
   /* First of all, allocate enough space to put a copy of the input
@@ -325,10 +325,10 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
 
   /* Go over all the pixels in the image and if they are within the
      polygon keep them if the user has asked for it.*/
-  switch(bitpix)
+  switch(datatype)
     {
-    case BYTE_IMG:
-      bb=gal_fits_bitpix_blank(bitpix);
+    case TBYTE:
+      bb=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -337,8 +337,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
         }
       free(bb);
       break;
-    case SHORT_IMG:
-      sb=gal_fits_bitpix_blank(bitpix);
+    case TSHORT:
+      sb=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -347,8 +347,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
         }
       free(sb);
       break;
-    case LONG_IMG:
-      lb=gal_fits_bitpix_blank(bitpix);
+    case TLONG:
+      lb=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -357,8 +357,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
         }
       free(lb);
       break;
-    case LONGLONG_IMG:
-      Lb=gal_fits_bitpix_blank(bitpix);
+    case TLONGLONG:
+      Lb=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -367,8 +367,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
         }
       free(Lb);
       break;
-    case FLOAT_IMG:
-      fb=gal_fits_bitpix_blank(bitpix);
+    case TFLOAT:
+      fb=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -377,8 +377,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
         }
       free(fb);
       break;
-    case DOUBLE_IMG:
-      db=gal_fits_bitpix_blank(bitpix);
+    case TDOUBLE:
+      db=gal_fits_datatype_blank(datatype);
       for(i=0;i<size;++i)
         {
           point[0]=i%s1+1; point[1]=i/s1+1;
@@ -390,8 +390,8 @@ polygonmask(struct cropparams *crp, void *array, long 
*fpixel_i,
     default:
       error(EXIT_FAILURE, 0, "a bug! Please contact us at %s, so we "
             "can fix the problem. For some reason, an unrecognized "
-            "bitpix value (%d) has been seen in polygonmask (crop.c)",
-            PACKAGE_BUGREPORT, bitpix);
+            "datatype value (%d) has been seen in polygonmask (crop.c)",
+            PACKAGE_BUGREPORT, datatype);
     }
 
   /* Clean up: */
@@ -713,7 +713,7 @@ onecrop(struct cropparams *crp)
       /* Read the desired part of the image, then write it into this
          array. */
       cropsize=(lpixel_i[0]-fpixel_i[0]+1)*(lpixel_i[1]-fpixel_i[1]+1);
-      array=gal_fits_bitpix_alloc(cropsize, bitpix);
+      array=gal_fits_datatype_alloc(cropsize, p->datatype);
       status=0;
       if(fits_read_subset(ifp, p->datatype, fpixel_i, lpixel_i, inc,
                           p->bitnul, array, &anynul, &status))
@@ -828,7 +828,7 @@ iscenterfilled(struct cropparams *crp)
 
   /* Allocate the array and read in the pixels. */
   size=checkcenter*checkcenter;
-  array=gal_fits_bitpix_alloc(size, bitpix);
+  array=gal_fits_datatype_alloc(size, gal_fits_bitpix_to_dtype(bitpix) );
   if( fits_read_subset(ofp, p->datatype, fpixel, lpixel, inc,
                        p->bitnul, array, &anynul, &status) )
     gal_fits_io_error(status, NULL);
diff --git a/src/imgcrop/imgcrop.c b/src/imgcrop/imgcrop.c
index 849ed75..2f17b09 100644
--- a/src/imgcrop/imgcrop.c
+++ b/src/imgcrop/imgcrop.c
@@ -90,7 +90,7 @@ imgmodecrop(void *inparam)
   /* The whole catalog is from one image, so you can get the
      information here:*/
   img=&p->imgs[crp->imgindex];
-  gal_fits_read_hdu(img->name, cp->hdu, IMAGE_HDU, &crp->infits);
+  gal_fits_read_hdu(img->name, cp->hdu, 0, &crp->infits);
 
   /* Go over all the outputs that are assigned to this thread: */
   for(i=0;crp->indexs[i]!=GAL_THREADS_NON_THRD_INDEX;++i)
@@ -181,7 +181,7 @@ wcsmodecrop(void *inparam)
         if(radecoverlap(crp))
           {
             gal_fits_read_hdu(p->imgs[crp->imgindex].name, p->cp.hdu,
-                                        IMAGE_HDU, &crp->infits);
+                              0, &crp->infits);
 
             if(log->name==NULL) cropname(crp);
 
diff --git a/src/imgcrop/ui.c b/src/imgcrop/ui.c
index 61cc70f..61813c0 100644
--- a/src/imgcrop/ui.c
+++ b/src/imgcrop/ui.c
@@ -608,7 +608,7 @@ preparearrays(struct imgcropparams *p)
       status=0;
       img=&p->imgs[i];
       gal_linkedlist_pop_from_stll(&p->up.gal_linkedlist_stll, &img->name);
-      gal_fits_read_hdu(img->name, p->cp.hdu, IMAGE_HDU, &tmpfits);
+      gal_fits_read_hdu(img->name, p->cp.hdu, 0, &tmpfits);
       gal_fits_img_bitpix_size(tmpfits, &p->bitpix, img->naxes);
       gal_fits_read_wcs_from_pointer(tmpfits, &img->nwcs, &img->wcs,
                                      p->hstartwcs, p->hendwcs);
@@ -635,7 +635,7 @@ preparearrays(struct imgcropparams *p)
         {
           firstbitpix=p->bitpix;
           p->datatype=gal_fits_bitpix_to_dtype(p->bitpix);
-          p->bitnul=gal_fits_bitpix_blank(p->bitpix);
+          p->bitnul=gal_fits_datatype_blank(p->datatype);
         }
       else if(firstbitpix!=p->bitpix)
         error(EXIT_FAILURE, 0, "%s: BITPIX=%d. Previous images had a "
diff --git a/src/mkcatalog/ui.c b/src/mkcatalog/ui.c
index 38fef36..0d898f4 100644
--- a/src/mkcatalog/ui.c
+++ b/src/mkcatalog/ui.c
@@ -1095,7 +1095,7 @@ preparearrays(struct mkcatalogparams *p)
 
   /* Prepare the columns and allocate the p->objcols and p->clumpcols
      arrays to keep the macros of what output they should keep. */
-  gal_linkedlist_sll_to_array(p->allcolsll, &p->allcols, &p->allncols);
+  gal_linkedlist_sll_to_array(p->allcolsll, &p->allcols, &p->allncols, 0);
   if(p->allncols==0)
     error(EXIT_FAILURE, 0, "no columns specified for output");
   errno=0; p->objcols=malloc(p->allncols*sizeof *p->objcols);
diff --git a/src/table/Makefile.am b/src/table/Makefile.am
new file mode 100644
index 0000000..4c66f48
--- /dev/null
+++ b/src/table/Makefile.am
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.inx
+##
+## Original author:
+##     Mohammad Akhlaghi <address@hidden>
+## Contributing author(s):
+## Copyright (C) 2016, 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/>.
+
+
+
+
+
+## Utility and its sources
+bin_PROGRAMS = asttable
+
+asttable_SOURCES = main.c main.h cite.h ui.c ui.h args.h       \
+table.c table.h
+
+asttable_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la             \
+-lgalconfigfiles -lgalfits -lgalcheckset -lgaltiming -lgallinkedlist    \
+-lgaltxtarray
+
+
+
+
+
+# To destribute the defaults file.
+# NOTE: the man page is created in doc/Makefile.am
+dist_sysconf_DATA = asttable.conf
diff --git a/src/table/args.h b/src/table/args.h
new file mode 100644
index 0000000..ac82696
--- /dev/null
+++ b/src/table/args.h
@@ -0,0 +1,371 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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/>.
+**********************************************************************/
+#ifndef ARGS_H
+#define ARGS_H
+
+#include <argp.h>
+
+#include <gnuastro/commonargs.h>
+#include <gnuastro/linkedlist.h>
+#include <gnuastro/fixedstringmacros.h>
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/**************        argp.h definitions       ***************/
+/**************************************************************/
+
+
+
+
+/* Definition parameters for the argp: */
+const char *argp_program_version=SPACK_STRING"\n"GAL_STRINGS_COPYRIGHT
+  "\n\nWritten by Mohammad Akhlaghi";
+const char *argp_program_bug_address=PACKAGE_BUGREPORT;
+static char args_doc[] = "ASTRdata";
+
+
+
+
+
+const char doc[] =
+  /* Before the list of options: */
+  GAL_STRINGS_TOP_HELP_INFO
+  SPACK_NAME" prints (in a human readable format) a FITS table or its "
+  "information. The output columns can either be selected by number, name "
+  "or using regular expressions. The format of their printing can also "
+  "be set (based on the type of data in the column).\n"
+  GAL_STRINGS_MORE_HELP_INFO
+  /* After the list of options: */
+  "\v"
+  PACKAGE_NAME" home page: "PACKAGE_URL;
+
+
+
+
+
+/* Available letters for short options:
+
+   a b d e f g j k l m n p r s u v w x y z
+   A B C E F G H J L M O Q R T U W X Y Z
+
+   Number keys used: 1008
+
+   Options with keys (second structure element) larger than 500 do not
+   have a short version.
+ */
+static struct argp_option options[] =
+  {
+    {
+      0, 0, 0, 0,
+      "Input:",
+      1
+    },
+    {
+      "column",
+      'c',
+      "STR",
+      0,
+      "Input column name, number or regular expression.",
+      1
+    },
+    {
+      "ignorecase",
+      'I',
+      0,
+      0,
+      "Ignore case when matching column names.",
+      1
+    },
+
+
+
+
+    {
+      0, 0, 0, 0,
+      "Output:",
+      2
+    },
+    {
+      "feg",
+      1001,
+      "STR",
+      0,
+      "`f': only decimals, `e': scientific, `g': either.",
+      2
+    },
+    {
+      "sintwidth",
+      1002,
+      "INT",
+      0,
+      "Shorter integer column(s) width (num charachers).",
+      2
+    },
+    {
+      "lintwidth",
+      1003,
+      "INT",
+      0,
+      "Longer integer column(s) width (num charachers).",
+      2
+    },
+    {
+      "floatwidth",
+      1004,
+      "INT",
+      0,
+      "`float' column(s) width (num charachers).",
+      2
+    },
+    {
+      "doublewidth",
+      1005,
+      "INT",
+      0,
+      "`double' column(s) width (num charachers).",
+      2
+    },
+    {
+      "strwidth",
+      1006,
+      "INT",
+      0,
+      "String column(s) width (num charachers).",
+      2
+    },
+    {
+      "floatprecision",
+      1007,
+      "INT",
+      0,
+      "`float' column(s) precision.",
+      2
+    },
+    {
+      "doubleprecision",
+      1008,
+      "INT",
+      0,
+      "`double' column(s) precision.",
+      2
+    },
+    {
+      "fitstabletype",
+      't',
+      "STR",
+      0,
+      "Only `ascii', or `binary' are acceptable.",
+      2
+    },
+
+
+
+
+    {
+      0, 0, 0, 0,
+      "Operating modes:",
+      -1
+    },
+    {
+      "information",
+      'i',
+      0,
+      0,
+      "Only print table and columns information.",
+      -1
+    },
+
+
+    {0}
+  };
+
+
+
+
+
+/* Parse a single option: */
+static error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+  /* Save the arguments structure: */
+  char *tstring;
+  struct tableparams *p = state->input;
+
+  /* Set the pointer to the common parameters for all programs
+     here: */
+  state->child_inputs[0]=&p->cp;
+
+  /* In case the user incorrectly uses the equal sign (for example
+     with a short format or with space in the long format, then `arg`
+     start with (if the short version was called) or be (if the long
+     version was called with a space) the equal sign. So, here we
+     check if the first character of arg is the equal sign, then the
+     user is warned and the program is stopped: */
+  if(arg && arg[0]=='=')
+    argp_error(state, "incorrect use of the equal sign (`=`). For short "
+               "options, `=` should not be used and for long options, "
+               "there should be no space between the option, equal sign "
+               "and value");
+
+  switch(key)
+    {
+
+
+    /* Input: */
+    case 'c':
+      gal_checkset_allocate_copy(arg, &tstring);
+      gal_linkedlist_add_to_stll(&p->up.columns, tstring);
+      break;
+
+    case 'I':
+      p->up.ignorecase=1;
+      p->up.ignorecaseset=1;
+      break;
+
+
+    /* Output: */
+    case 1001:
+      checksetfge(arg, &p->up.feg, NULL, 0);
+      p->up.fegset=1;
+      break;
+
+    case 1002:
+      gal_checkset_sizet_el_zero(arg, &p->up.sintwidth, "sintwidth", key,
+                                 SPACK, NULL, 0);
+      p->up.sintwidthset=1;
+      break;
+
+    case 1003:
+      gal_checkset_sizet_el_zero(arg, &p->up.lintwidth, "lintwidth", key,
+                                 SPACK, NULL, 0);
+      p->up.lintwidthset=1;
+      break;
+
+    case 1004:
+      gal_checkset_sizet_el_zero(arg, &p->up.floatwidth, "floatwidth", key,
+                                 SPACK, NULL, 0);
+      p->up.floatwidthset=1;
+      break;
+
+    case 1005:
+      gal_checkset_sizet_el_zero(arg, &p->up.doublewidth, "doublewidth", key,
+                                 SPACK, NULL, 0);
+      p->up.doublewidthset=1;
+      break;
+
+    case 1006:
+      gal_checkset_sizet_el_zero(arg, &p->up.strwidth, "strwidth", key,
+                                 SPACK, NULL, 0);
+      p->up.strwidthset=1;
+      break;
+
+    case 1007:
+      gal_checkset_sizet_el_zero(arg, &p->up.floatprecision,
+                                 "floatprecision", key, SPACK, NULL, 0);
+      p->up.floatprecisionset=1;
+      break;
+
+    case 1008:
+      gal_checkset_sizet_el_zero(arg, &p->up.doubleprecision,
+                                 "doubleprecision", key, SPACK, NULL, 0);
+      p->up.doubleprecisionset=1;
+      break;
+
+    case 't':
+      checksetfitstabletype(arg, &p->up.feg, NULL, 0);
+      p->up.fitstabletypeset=1;
+      break;
+
+
+    /* Operating modes: */
+    case 'i':
+      p->up.information=1;
+      p->up.informationset=1;
+      break;
+
+
+    /* Read the non-option arguments: */
+    case ARGP_KEY_ARG:
+
+      /* Table gets only one input argument. */
+      if(p->up.inputset)
+        argp_error(state, "only one input file should be given");
+
+      /* This is the first (and must be only) argument. */
+      p->up.inputset=1;
+
+      /* See what type of input this is, and save the value. */
+      if( gal_fits_name_is_fits(arg) )
+        p->up.fitsname=arg;
+      else
+        p->up.txtname=arg;
+
+      break;
+
+
+    /* The command line options and arguments are finished. */
+    case ARGP_KEY_END:
+      if(p->cp.setdirconf==0 && p->cp.setusrconf==0
+         && p->cp.printparams==0)
+        if(state->arg_num==0)
+          argp_error(state, "no argument given");
+      break;
+
+
+
+
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+
+
+
+
+/* Specify the children parsers: */
+struct argp_child children[]=
+  {
+    {&commonargp, 0, NULL, 0},
+    {0, 0, 0, 0}
+  };
+
+
+
+
+
+/* Basic structure defining the whole argument reading process. */
+static struct argp thisargp = {options, parse_opt, args_doc,
+                               doc, children, NULL, NULL};
+
+#endif
diff --git a/src/table/asttable.conf b/src/table/asttable.conf
new file mode 100644
index 0000000..7bb2a00
--- /dev/null
+++ b/src/table/asttable.conf
@@ -0,0 +1,32 @@
+# Default parameters (System) for Table.
+# Table is part of GNU Astronomy Utitlies.
+#
+# Use the long option name of each paramter followed by
+# a value. The name and value should be separated by
+# atleast one of the following charaacters:
+# space, `,`, `=` or `:`
+#
+# Run with `--help` option or read the manual for a full
+# explanation of what each option means.
+#
+# NOTE I:  All counting is from zero, not one.
+# NOTE II: Lines starting with `#` are ignored.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without any warranty.
+
+# Input:
+ hdu              0
+
+# Output:
+ feg              f
+ sintwidth        5
+ lintwidth       15
+ floatwidth      10
+ doublewidth     15
+ strwidth        20
+ floatprecision   3
+ doubleprecision  8
+ fitstabletype    binary
diff --git a/src/table/cite.h b/src/table/cite.h
new file mode 100644
index 0000000..de9ab4f
--- /dev/null
+++ b/src/table/cite.h
@@ -0,0 +1,38 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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/>.
+**********************************************************************/
+#ifndef CITE_H
+#define CITE_H
+
+#define TABLEBIBTEX ""
+
+#define PRINTCITEABORT {                                                \
+    printf("\nWe hope %s has been useful for your research.\n"          \
+           "Citations are vital for the continued work on %s.\n"        \
+           "Thank you for citing it in your research paper.\n"          \
+           "\nPlease cite as \"%s\":\n\n%s\n\n%s",                      \
+           SPACK_NAME, SPACK_NAME, SPACK_STRING,                        \
+           GAL_STRINGS_MAIN_BIBTEX, TABLEBIBTEX);                       \
+    exit(EXIT_SUCCESS);                                                 \
+  }
+
+#endif
diff --git a/src/table/main.c b/src/table/main.c
new file mode 100644
index 0000000..b2eb42a
--- /dev/null
+++ b/src/table/main.c
@@ -0,0 +1,56 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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 <stdlib.h>
+#include <progname.h>
+
+#include <gnuastro/timing.h>    /* Includes time.h and sys/time.h */
+
+#include "main.h"
+
+#include "ui.h"                 /* needs main.h.                  */
+#include "table.h"              /* needs main.h.                  */
+
+int
+main (int argc, char *argv[])
+{
+  struct tableparams p={{0}, {0}, 0};
+
+  /* Set the program name (needed by non-gnu operating systems): */
+  time(&p.rawtime);
+  set_program_name(argv[0]);
+
+  /* Read the input parameters. */
+  setparams(argc, argv, &p);
+
+  /* Run MakeProfiles */
+  table(&p);
+
+  /* Free all non-freed allocations. */
+  freeandreport(&p);
+
+  /* Return successfully.*/
+  return EXIT_SUCCESS;
+}
diff --git a/src/table/main.h b/src/table/main.h
new file mode 100644
index 0000000..50f5c80
--- /dev/null
+++ b/src/table/main.h
@@ -0,0 +1,126 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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/>.
+**********************************************************************/
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <gnuastro/fits.h>
+#include <gnuastro/commonparams.h>
+
+/* Progarm name macros: */
+#define SPACK           "asttable" /* Subpackage executable name. */
+#define SPACK_NAME      "Table"    /* Subpackage full name.       */
+#define SPACK_STRING    SPACK_NAME" ("PACKAGE_NAME") "PACKAGE_VERSION
+
+#define MAX_COL_FORMAT_LENGTH 20
+
+
+
+/* Structure to keep the information for each output column */
+struct outcolumn
+{
+  size_t inindex;               /* Row index (from 0) in input array.   */
+  int   datatype;               /* Type of data (from CFITSIO macros).  */
+  int     anynul;               /* If there is any blank characters.    */
+  void   *nulval;               /* The blank value for this column.     */
+  void     *data;               /* Array keeping the column data.       */
+  char fmt[MAX_COL_FORMAT_LENGTH];  /* format to use in printf.         */
+};
+
+
+
+
+
+/* User interface structure. */
+struct uiparams
+{
+  int             information;  /* ==1, only print FITS information.    */
+  char              *fitsname;  /* Name of input FITS file.             */
+  char               *txtname;  /* Name of input text file.             */
+  int              ignorecase;  /* Ignore case matching column names.   */
+
+  /* Input table parameters. */
+  size_t                ncols;  /* Number of columns in table.          */
+  int               *datatype;  /* Type of data in column.              */
+  char                **ttstr;  /* TFORM (another format for type).     */
+  char                **tname;  /* Column name (one word).              */
+  char                **tunit;  /* Unit of values in column.            */
+  double            *txtarray;  /* Array keeping text file values.      */
+  int         infitstabletype;  /* Input table is ASCII or binary.      */
+
+  /* Print parameters: */
+  int                     feg;  /* format of floating points.           */
+  size_t            sintwidth;  /* Full width for short integers.       */
+  size_t            lintwidth;  /* Full width for short integers.       */
+  size_t           floatwidth;  /* Full width for all floats.           */
+  size_t          doublewidth;  /* Full width for all doubles.          */
+  size_t             strwidth;  /* Full width for all floats.           */
+  size_t       floatprecision;  /* Number of decimals for floats.       */
+  size_t      doubleprecision;  /* Number of decimals for doubles.      */
+
+  /* If values are set: */
+  int                inputset;
+  int          informationset;
+  int           ignorecaseset;
+  int                  fegset;
+  int            sintwidthset;
+  int            lintwidthset;
+  int           floatwidthset;
+  int          doublewidthset;
+  int             strwidthset;
+  int       floatprecisionset;
+  int      doubleprecisionset;
+  int        fitstabletypeset;
+
+
+  struct gal_linkedlist_stll *columns;
+};
+
+
+
+
+
+/* Main program parameters structure */
+struct tableparams
+{
+  /* Other structures: */
+  struct uiparams          up;  /* User interface parameters.           */
+  struct gal_commonparams  cp;  /* Common parameters.                   */
+
+  /* Input: */
+  fitsfile           *fitsptr;  /* FITS pointer (input or output).      */
+
+  /* Output: */
+  size_t                nrows;  /* Number of rows in table.             */
+  size_t               nocols;  /* Number of output columns.            */
+  struct outcolumn     *ocols;  /* Array of output column informatio.   */
+  int            outputtofits;  /* ==1: output is a FITS file.          */
+  int             outputtotxt;  /* ==1: output is a text file.          */
+  int          outputtostdout;  /* ==1: output is the standard output.  */
+  int           fitstabletype;  /* ASCII, or binary table CFITSIO macro.*/
+
+  /* Internal: */
+  int                onlyview;
+  time_t              rawtime;  /* Starting time of the program.        */
+};
+
+#endif
diff --git a/src/table/table.c b/src/table/table.c
new file mode 100644
index 0000000..6b682b0
--- /dev/null
+++ b/src/table/table.c
@@ -0,0 +1,437 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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 <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gnuastro/fits.h>
+
+#include "main.h"
+
+
+
+/**************************************************************/
+/***************        Input table         *******************/
+/**************************************************************/
+/* Set the format string for each column: */
+void
+setformatstring(struct tableparams *p, size_t outcolid)
+{
+  struct uiparams *up=&p->up;
+  char width[10], accu[10], *type=NULL;
+  struct outcolumn *ocol=&p->ocols[outcolid];
+
+
+  switch(ocol->datatype)
+    {
+    case TBIT:
+      error(EXIT_FAILURE, 0, "Table doesn't print TBIT data type "
+            "currently, please contact us at %s so we can implement "
+            "it.", PACKAGE_BUGREPORT);
+
+    case TBYTE:
+      type="u";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TLOGICAL: case TSBYTE:
+      type="d";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TSTRING:
+      type="s";
+      if(up->sintwidth) sprintf(width, "%lu", up->sintwidth);
+      else width[0]='\0';
+      accu[0]='\0';
+      break;
+
+    case TSHORT:
+      type="d";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TLONG:
+      type="ld";
+      sprintf(width, "%lu", up->lintwidth);
+      accu[0]='\0';
+      break;
+
+    case TLONGLONG:
+      type="ld";
+      sprintf(width, "%lu", up->lintwidth);
+      accu[0]='\0';
+      break;
+
+    case TFLOAT:
+      type = up->feg=='f' ? "f" : ( up->feg=='e' ? "e" : "g");
+      sprintf(width, "%lu", up->floatwidth);
+      sprintf(accu, ".%lu", up->floatprecision);
+      break;
+
+    case TDOUBLE:
+      type = up->feg=='f' ? "f" : ( up->feg=='e' ? "e" : "g");
+      sprintf(width, "%lu", up->doublewidth);
+      sprintf(accu, ".%lu", up->doubleprecision);
+      break;
+
+    case TCOMPLEX:
+      error(EXIT_FAILURE, 0, "Table doesn't print TCOMPLEX data type "
+            "currently, please contact us at %s so we can implement "
+            "it.", PACKAGE_BUGREPORT);
+      break;
+
+    case TDBLCOMPLEX:
+      error(EXIT_FAILURE, 0, "Table doesn't print TDBLCOMPLEX data type "
+            "currently, please contact us at %s so we can implement "
+            "it.", PACKAGE_BUGREPORT);
+      break;
+
+    case TINT:
+      type="d";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TUINT:
+      type="u";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TUSHORT:
+      type="u";
+      sprintf(width, "%lu", up->sintwidth);
+      accu[0]='\0';
+      break;
+
+    case TULONG:
+      type="lu";
+      sprintf(width, "%lu", up->lintwidth);
+      accu[0]='\0';
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "datatype value of %d not recognized in "
+            "gal_fits_datatype_alloc", ocol->datatype);
+    }
+
+  /* Put the type, width and accu into the format string for this
+     column: */
+  sprintf(ocol->fmt, "%%-%s%s%s", width, accu, type);
+}
+
+
+
+
+
+/* Read all the input columns */
+void
+readinputcols(struct tableparams *p)
+{
+  double *colfromtxt;
+  struct outcolumn *col;
+  int datatype, status=0;
+  size_t i, j, nrows=p->nrows, incols=p->up.ncols;
+
+  /* Get the contents of each table column: */
+  for(i=0;i<p->nocols;++i)
+    {
+      /* Variables for simple reading */
+      col=&p->ocols[i];
+
+      datatype=col->datatype;
+
+      /* Allocate the blank value for this column. Note that we will also
+         need the blankvalue for a text file when outputing to a FITS. */
+      col->nulval=gal_fits_datatype_blank(datatype);
+
+      /* Read the input column. */
+      if(p->fitsptr)
+        {
+          /* Allocate space for the data in this column */
+          col->data=gal_fits_datatype_alloc(nrows, datatype);
+
+          /* Call CFITSIO to read the column information. */
+          fits_read_col(p->fitsptr, datatype, col->inindex+1, 1, 1,
+                        nrows, col->nulval, col->data, &col->anynul,
+                        &status);
+        }
+      else
+        {
+          /* This is a text file, read by Gnuastro's current txtarray
+             library. This library currently only reads a 2D table into a
+             2D array of type double. The important thing here is that the
+             array is row-contiguous. But here we want column contiguous
+             data. So we allocate an array to only put this column's values
+             in.*/
+          errno=0;
+          colfromtxt=col->data=malloc(nrows * sizeof *col->data);
+
+          if(col->data==NULL)
+            error(EXIT_FAILURE, errno, "%lu bytes for col->data",
+                  nrows * sizeof *col->data);
+          for(j=0;j<nrows;++j)
+            colfromtxt[j]=p->up.txtarray[ j * incols + col->inindex ];
+        }
+
+      /* Set the format string to print the column values if the output is
+         to be printed as text (either in a text file or to the standard
+         output. */
+      if(p->outputtotxt || p->outputtostdout)
+        setformatstring(p, i);
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Output table         *******************/
+/**************************************************************/
+void
+saveouttofits(struct tableparams *p)
+{
+  size_t i;
+  int status=0;
+  fitsfile *fptr;
+  struct uiparams *up=&p->up;
+  char **ttype, **tform, **tunit;
+  struct outcolumn *ocols=p->ocols;
+
+  /* Allocate the information arrays for CFITSIO. */
+  errno=0;
+  ttype=malloc(p->nocols*sizeof *ttype);
+  if(ttype==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for ttype",
+          p->nocols*sizeof *ttype);
+  errno=0;
+  tform=malloc(p->nocols*sizeof *tform);
+  if(tform==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for tform",
+          p->nocols*sizeof *tform);
+  errno=0;
+  tunit=malloc(p->nocols*sizeof *tunit);
+  if(tunit==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for tunit",
+          p->nocols*sizeof *tunit);
+
+  /* Fill in the information arrays: */
+  for(i=0;i<p->nocols;++i)
+    {
+      tform[i]=up->ttstr[ ocols[i].inindex ];
+      ttype[i]=up->tname[ ocols[i].inindex ];
+      tunit[i]=up->tunit[ ocols[i].inindex ];
+    }
+
+  /* Open the output FITS file. */
+  if(access(p->cp.output,F_OK) != -1 )
+    fits_open_file(&fptr, p->cp.output, READWRITE, &status);
+  else
+    fits_create_file(&fptr, p->cp.output, &status);
+
+  /* Create a new table extension */
+  fits_create_tbl(fptr, p->fitstabletype, p->nrows, p->nocols, ttype,
+                  tform, tunit, "Table", &status);
+
+  /* Write this column's data into the FITS file. */
+  for(i=0;i<p->nocols;++i)
+    fits_write_colnull(fptr, ocols[i].datatype, i+1, 1, 1, p->nrows,
+                       ocols[i].data, ocols[i].nulval, &status);
+
+  /* Include the ending comments and close the file. */
+  gal_fits_copyright_end(fptr, NULL, SPACK_STRING);
+  fits_close_file(fptr, &status);
+  gal_fits_io_error(status, NULL);
+
+  /* Clean up */
+  free(ttype);
+  free(tform);
+  free(tunit);
+}
+
+
+
+
+
+void
+printoutput(struct tableparams *p)
+{
+  FILE *fp;
+  size_t i, row;
+  struct outcolumn *ocols=p->ocols;
+
+  /* Determine the output stream and open the file for writing if its a
+     file. */
+  if(p->outputtotxt)
+    {
+      errno=0;
+      fp=fopen(p->cp.output, "w");
+      if(fp==NULL)
+        error(EXIT_FAILURE, errno, "%s", p->cp.output);
+    }
+  else
+    fp=stdout;
+
+  /* Print each column and each row: */
+  for(row=0;row<p->nrows;++row)
+    {
+      for(i=0;i<p->nocols;++i)
+        switch(ocols[i].datatype)
+          {
+          case TBIT:
+            error(EXIT_FAILURE, 0, "Table doesn't print TBIT data type "
+                  "currently, please contact us at %s so we can implement "
+                  "it.", PACKAGE_BUGREPORT);
+
+          case TBYTE:
+            fprintf(fp, ocols[i].fmt, ((unsigned char *)ocols[i].data)[row]);
+            break;
+
+          case TLOGICAL: case TSBYTE:
+            fprintf(fp, ocols[i].fmt, ((char *)ocols[i].data)[row]);
+            break;
+
+          case TSTRING:
+            fprintf(fp, ocols[i].fmt, ((char **)ocols[i].data)[row]);
+            break;
+
+          case TSHORT:
+            fprintf(fp, ocols[i].fmt, ((short *)ocols[i].data)[row]);
+            break;
+
+          case TLONG:
+            fprintf(fp, ocols[i].fmt, ((long *)ocols[i].data)[row]);
+            break;
+
+          case TLONGLONG:
+            fprintf(fp, ocols[i].fmt, ((LONGLONG *)ocols[i].data)[row]);
+            break;
+
+          case TFLOAT:
+            fprintf(fp, ocols[i].fmt, ((float *)ocols[i].data)[row]);
+            break;
+
+          case TDOUBLE:
+            fprintf(fp, ocols[i].fmt, ((double *)ocols[i].data)[row]);
+            break;
+
+          case TCOMPLEX:
+            error(EXIT_FAILURE, 0, "Table doesn't print TCOMPLEX data type "
+                  "currently, please contact us at %s so we can implement "
+                  "it.", PACKAGE_BUGREPORT);
+            break;
+
+          case TDBLCOMPLEX:
+            error(EXIT_FAILURE, 0, "Table doesn't print TDBLCOMPLEX data "
+                  "type currently, please contact us at %s so we can "
+                  "implement it.", PACKAGE_BUGREPORT);
+            break;
+
+          case TINT:
+            fprintf(fp, ocols[i].fmt, ((char *)ocols[i].data)[row]);
+            break;
+
+          case TUINT:
+            fprintf(fp, ocols[i].fmt, ((unsigned int *)ocols[i].data)[row]);
+            break;
+
+          case TUSHORT:
+            fprintf(fp, ocols[i].fmt, ((unsigned short *)ocols[i].data)[row]);
+            break;
+
+          case TULONG:
+            fprintf(fp, ocols[i].fmt, ((unsigned long *)ocols[i].data)[row]);
+            break;
+
+          default:
+            error(EXIT_FAILURE, 0, "datatype value of %d not recognized in "
+                  "printoutput", ocols[i].datatype);
+          }
+      fprintf(fp, "\n");
+    }
+
+  /* If we printed to a file, then close it. */
+  if(p->outputtotxt)
+    {
+      errno=0;
+      if( fclose(fp) == EOF )
+        error(EXIT_FAILURE, errno, "%s", p->cp.output);
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Top function         *******************/
+/**************************************************************/
+void
+table(struct tableparams *p)
+{
+  readinputcols(p);
+
+  if(p->outputtofits)
+    saveouttofits(p);
+  else
+    printoutput(p);
+}
diff --git a/src/table/table.h b/src/table/table.h
new file mode 100644
index 0000000..8c164ec
--- /dev/null
+++ b/src/table/table.h
@@ -0,0 +1,31 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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/>.
+**********************************************************************/
+#ifndef TABLE_H
+#define TABLE_H
+
+
+
+void
+table(struct tableparams *p);
+
+#endif
diff --git a/src/table/ui.c b/src/table/ui.c
new file mode 100644
index 0000000..d4825d1
--- /dev/null
+++ b/src/table/ui.c
@@ -0,0 +1,1041 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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 <math.h>
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fitsio.h>
+
+#include <nproc.h>               /* From Gnulib.                   */
+
+#include <gnuastro/fits.h>
+#include <gnuastro/timing.h>     /* Includes time.h and sys/time.h */
+#include <gnuastro/checkset.h>
+#include <gnuastro/txtarray.h>
+#include <gnuastro/commonargs.h>
+#include <gnuastro/configfiles.h>
+
+#include "main.h"
+
+#include "ui.h"                  /* Needs main.h                   */
+#include "args.h"                /* Needs main.h, includes argp.h. */
+
+
+/* Set the file names of the places where the default parameters are
+   put. */
+#define CONFIG_FILE SPACK CONF_POSTFIX
+#define SYSCONFIG_FILE SYSCONFIG_DIR "/" CONFIG_FILE
+#define USERCONFIG_FILEEND USERCONFIG_DIR CONFIG_FILE
+#define CURDIRCONFIG_FILE CURDIRCONFIG_DIR CONFIG_FILE
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/**************       Options and parameters    ***************/
+/**************************************************************/
+/* Check the value given for the fge option. */
+void
+checksetfge(char *optarg, int *fge, char *filename, size_t lineno)
+{
+  *fge=optarg[0];
+  if( *fge!='f' && *fge!='g' && *fge!='g' )
+    {
+      if(filename)
+        error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                      "the value of `fge' must only be one of the three "
+                      "`f', `g', or `e' characters. You have given `%s'",
+                      optarg);
+      else
+        error(EXIT_FAILURE, 0, "the value of `--fge' (`-f') must only be "
+              "one of the three `f', `g', or `e' characters. You have "
+              "given `%s'", optarg);
+    }
+}
+
+
+
+
+
+void
+checksetfitstabletype(char *optarg, int *fitstabletype, char *filename,
+                   size_t lineno)
+{
+  if( !strcmp(optarg, "ascii") )
+    *fitstabletype=ASCII_TBL;
+  else if( !strcmp(optarg, "binary") )
+    *fitstabletype=BINARY_TBL;
+  else
+    {
+      if(filename)
+        error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                      "The value to the `fitstabletype' must be either "
+                      "`ascii', or `binary'. You have given `%s'", optarg);
+      else
+        error(EXIT_FAILURE, 0, "The value to the `--fitstabletype' (`-t') "
+              "option must be either one of `ascii', or `binary'. You have, "
+              "given `%s'", optarg);
+    }
+}
+
+
+
+
+
+void
+readconfig(char *filename, struct tableparams *p)
+{
+  FILE *fp;
+  size_t lineno=0, len=200;
+  /*struct uiparams *up=&p->up;*/
+  struct gal_commonparams *cp=&p->cp;
+  char *line, *name, *value, *tstring;
+  char key='a';        /* Not used, just a place holder. */
+
+  /* When the file doesn't exist or can't be opened, it is ignored. It
+     might be intentional, so there is no error. If a parameter is
+     missing, it will be reported after all defaults are read. */
+  fp=fopen(filename, "r");
+  if (fp==NULL) return;
+
+
+  /* Allocate some space for `line` with `len` elements so it can
+     easily be freed later on. The value of `len` is arbitarary at
+     this point, during the run, getline will change it along with the
+     pointer to line. */
+  errno=0;
+  line=malloc(len*sizeof *line);
+  if(line==NULL)
+    error(EXIT_FAILURE, errno, "ui.c: %lu bytes in readdefaults",
+          len * sizeof *line);
+
+  /* Read the tokens in the file:  */
+  while(getline(&line, &len, fp) != -1)
+    {
+      /* Prepare the "name" and "value" strings, also set lineno. */
+      GAL_CONFIGFILES_START_READING_LINE;
+
+
+
+
+      /* Inputs: */
+      if(strcmp(name, "hdu")==0)
+        gal_checkset_allocate_copy_set(value, &cp->hdu, &cp->hduset);
+
+      else if(strcmp(name, "column")==0)
+        {
+          gal_checkset_allocate_copy(value, &tstring);
+          gal_linkedlist_add_to_stll(&p->up.columns, tstring);
+        }
+
+      else if(strcmp(name, "ignorecase")==0)
+        {
+          if(p->up.ignorecaseset) continue;
+          gal_checkset_int_zero_or_one(value, &p->up.ignorecase, "ignorecase",
+                                       key, SPACK, filename, lineno);
+          p->up.ignorecaseset=1;
+        }
+
+
+
+      /* Outputs */
+      else if(strcmp(name, "output")==0)
+        gal_checkset_allocate_copy_set(value, &cp->output, &cp->outputset);
+
+      else if (strcmp(name, "feg")==0)
+        {
+          if(p->up.fegset) continue;
+          checksetfge(value, &p->up.feg, filename, lineno);
+          p->up.fegset=1;
+        }
+
+      else if (strcmp(name, "sintwidth")==0)
+        {
+          if(p->up.sintwidthset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.sintwidth, name,
+                                       key, SPACK, filename, lineno);
+          p->up.sintwidthset=1;
+        }
+
+      else if (strcmp(name, "lintwidth")==0)
+        {
+          if(p->up.lintwidthset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.lintwidth, name,
+                                       key, SPACK, filename, lineno);
+          p->up.lintwidthset=1;
+        }
+
+      else if (strcmp(name, "floatwidth")==0)
+        {
+          if(p->up.floatwidthset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.floatwidth, name,
+                                       key, SPACK, filename, lineno);
+          p->up.floatwidthset=1;
+        }
+
+      else if (strcmp(name, "doublewidth")==0)
+        {
+          if(p->up.doublewidthset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.doublewidth, name,
+                                       key, SPACK, filename, lineno);
+          p->up.doublewidthset=1;
+        }
+
+      else if (strcmp(name, "strwidth")==0)
+        {
+          if(p->up.strwidthset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.strwidth, name,
+                                       key, SPACK, filename, lineno);
+          p->up.strwidthset=1;
+        }
+
+      else if (strcmp(name, "floatprecision")==0)
+        {
+          if(p->up.floatprecisionset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.floatprecision,
+                                       name, key, SPACK, filename, lineno);
+          p->up.floatprecisionset=1;
+        }
+
+      else if (strcmp(name, "doubleprecision")==0)
+        {
+          if(p->up.doubleprecisionset) continue;
+          gal_checkset_sizet_el_zero(value, &p->up.doubleprecision,
+                                       name, key, SPACK, filename, lineno);
+          p->up.doubleprecisionset=1;
+        }
+      else if (strcmp(name, "fitstabletype")==0)
+        {
+          if(p->up.fitstabletypeset) continue;
+          checksetfitstabletype(value, &p->fitstabletype, filename, lineno);
+          p->up.fitstabletypeset=1;
+        }
+
+
+
+      /* Operating modes: */
+      else if (strcmp(name, "information")==0)
+        {
+          if(p->up.informationset) continue;
+          gal_checkset_int_zero_or_one(value, &p->up.information, name,
+                                       key, SPACK, filename, lineno);
+          p->up.informationset=1;
+        }
+
+
+      /* Read options common to all programs */
+      GAL_CONFIGFILES_READ_COMMONOPTIONS_FROM_CONF
+
+
+      else
+        error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                      "`%s` not recognized.\n", name);
+    }
+
+  free(line);
+  fclose(fp);
+}
+
+
+
+
+
+void
+printvalues(FILE *fp, struct tableparams *p)
+{
+  struct uiparams *up=&p->up;
+  struct gal_linkedlist_stll *tmp;
+  struct gal_commonparams *cp=&p->cp;
+
+
+  /* Print all the options that are set. Separate each group with a
+     commented line explaining the options in that group. */
+  fprintf(fp, "\n# Input:\n");
+  if(cp->hduset)
+    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("hdu", cp->hdu);
+  if(up->columns)
+    for(tmp=up->columns;tmp!=NULL;tmp=tmp->next)
+      GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("column", tmp->v);
+  if(up->ignorecaseset)
+    fprintf(fp, CONF_SHOWFMT"%d\n", "ignorecase", up->ignorecase);
+
+
+  fprintf(fp, "\n# Output:\n");
+  if(up->fegset)
+    fprintf(fp, CONF_SHOWFMT"%c\n", "feg", up->feg);
+  if(up->sintwidthset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "sintwidth", up->sintwidth);
+  if(up->lintwidthset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "lintwidth", up->lintwidth);
+  if(up->floatwidthset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "floatwidth", up->floatwidth);
+  if(up->doublewidthset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "doublewidth", up->doublewidth);
+  if(up->strwidthset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "strwidth", up->strwidth);
+  if(up->floatprecisionset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "floatprecision", up->floatprecision);
+  if(up->doubleprecisionset)
+    fprintf(fp, CONF_SHOWFMT"%lu\n", "doubleprecision", up->doubleprecision);
+  if(up->fitstabletypeset)
+    fprintf(fp, CONF_SHOWFMT"%s\n", "fitstabletype",
+            p->fitstabletype==ASCII_TBL ? "ascii" : "binary");
+
+
+  /* For the operating mode, first put the macro to print the common
+     options, then the (possible options particular to this
+     program). */
+  fprintf(fp, "\n# Operating mode:\n");
+  if(up->informationset)
+    fprintf(fp, CONF_SHOWFMT"%d\n", "information", up->information);
+
+  GAL_CONFIGFILES_PRINT_COMMONOPTIONS;
+}
+
+
+
+
+
+
+/* Note that numthreads will be used automatically based on the
+   configure time. */
+void
+checkifset(struct tableparams *p)
+{
+  /*struct uiparams *up=&p->up;*/
+  struct uiparams *up=&p->up;
+  struct gal_commonparams *cp=&p->cp;
+
+  int intro=0;
+  if(cp->hduset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("hdu");
+
+  if(up->fegset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("feg");
+  if(up->sintwidthset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("sintwidth");
+  if(up->lintwidthset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("lintwidth");
+  if(up->floatwidthset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("floatwidth");
+  if(up->doublewidthset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("doublewidth");
+  if(up->strwidthset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("strwidth");
+  if(up->floatprecisionset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("floatprecision");
+  if(up->doubleprecisionset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("doubleprecision");
+  if(up->fitstabletypeset==0)
+    GAL_CONFIGFILES_REPORT_NOTSET("fitstabletype");
+
+  GAL_CONFIGFILES_END_OF_NOTSET_REPORT;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************     Read and Write column info    ***************/
+/**************************************************************/
+
+/*  */
+  /* Allocate the arrays to keep the column information. Initialize the
+     arrays with a NULL pointer to make sure that they are all found in the
+     end if they are necessary. The values that are directly read from the
+     input file are initialized to NULL, so they can be treated
+     appropriately if they do not exist in the input (for example if they
+     are mandatory, or if they need to be printed).*/
+void
+allocinputcolinfo(struct tableparams *p)
+{
+  char **c, **fc;
+  size_t ncols=p->up.ncols;
+  struct uiparams *up=&p->up;
+
+  /* up->datatype keeps the type of data in each column as CFITSIO type
+     macros. */
+  errno=0;
+  up->datatype=malloc(ncols * sizeof *up->datatype);
+  if(up->datatype==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for up->datatype",
+          ncols * sizeof *up->datatype);
+
+  /* up->ttstr keeps the actual string used to specify the datatype. */
+  errno=0;
+  up->ttstr=malloc(ncols * sizeof *up->ttstr);
+  if(up->ttstr==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for ttstr",
+          ncols * sizeof *up->ttstr);
+  fc=(c=up->ttstr)+ncols; do *c++=NULL; while(c<fc);
+
+  /* up->tname keeps the name of the column. */
+  errno=0;
+  up->tname=malloc(ncols * sizeof *up->tname);
+  if(up->tname==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for tname",
+          ncols * sizeof *up->tname);
+  fc=(c=up->tname)+ncols; do *c++=NULL; while(c<fc);
+
+  /* up->tunit keeps the input units of the column. */
+  errno=0;
+  up->tunit=malloc(ncols * sizeof *up->tunit);
+  if(up->tunit==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for tunit",
+          ncols * sizeof *up->tunit);
+  fc=(c=up->tunit)+ncols; do *c++=NULL; while(c<fc);
+}
+
+
+
+
+
+/* This function will read all the table information from a FITS table HDU
+   and store them in arrays for use later. It is mainly good for getting
+   all the information in a FITS table HDU. This function will only go
+   through the header keywords once and does not depend on the ordering of
+   the keywords, so it is much more efficient than having to ask for each
+   column's information separately.*/
+void
+allfitscolinfo(struct tableparams *p)
+{
+  char *tailptr;
+  int i, status=0;
+  struct uiparams *up=&p->up;
+  size_t index, ncols=p->up.ncols;
+  char keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE];
+
+
+  /* First allocate the arrays to keep the input column information. */
+  allocinputcolinfo(p);
+
+
+  /* Read all the keywords one by one and if they match, then put them in
+     the correct value. Note that we are starting from keyword 9 because
+     according to the FITS standard, the first 8 keys in a FITS table are
+     reserved. */
+  for(i=9; strcmp(keyname, "END"); ++i)
+    {
+      /* Read the next keyword. */
+      fits_read_keyn(p->fitsptr, i, keyname, value, NULL, &status);
+
+      /* Check the type of the keyword. */
+      if(strncmp(keyname, "TFORM", 5)==0)
+        {
+          /* Currently we can only read repeat==1 cases. When no number
+             exists before the defined capital letter, it defaults to 1,
+             but if a number exists (for example `5D'), then the repeat is
+             5 (there are actually five values here. Note that value[0] is
+             a single quote.*/
+          if(isdigit(value[1] && value[1]!='1'))
+             error(EXIT_FAILURE, 0, "The repeat value of column %d is "
+                   "%c, currently Table can only use columns with a repeat "
+                   "of 1.", i+1, value[1]);
+
+
+          /* The values to TFORM are only a single character, so start the
+             pointer to copy at 1 and put the string terminator at 3. */
+          value[2]='\0';
+          index=strtoul(&keyname[5], &tailptr, 10)-1;
+          if(index<ncols)
+            {
+              gal_checkset_allocate_copy(&value[1], &up->ttstr[index] );
+              up->datatype[index]=gal_fits_tform_to_dtype(value[1]);
+            }
+        }
+      else if(strncmp(keyname, "TTYPE", 5)==0)
+        {
+          /* All strings in CFITSIO start and finish with single quotation
+             marks, CFITSIO puts them in itsself, so if we don't remove
+             them here, we might have duplicates later, its easier to just
+             remove them to have a simple string that might be used else
+             where too (without the single quotes). */
+          value[strlen(value)-1]='\0';
+          index=strtoul(&keyname[5], &tailptr, 10)-1;
+          if(index<ncols)
+            gal_checkset_allocate_copy(&value[1], &up->tname[index] );
+        }
+      else if(strncmp(keyname, "TUNIT", 5)==0)
+        {
+          /* similar to tname, see above.*/
+          value[strlen(value)-1]='\0';
+          index=strtoul(&keyname[5], &tailptr, 10)-1;
+          if(index<ncols)
+            gal_checkset_allocate_copy(&value[1], &up->tunit[index] );
+        }
+    }
+
+
+  /* Check if the mandatory TFORMn values are set: */
+  for(i=0;i<ncols;++i)
+    if(!up->ttstr[i])
+      error(EXIT_FAILURE, 0, "TFORM%d could not be found in header", i+1);
+}
+
+
+
+
+
+/* Prepare column information from a text input file. Note that we are
+   currently using Gnuastro's very simple txtarray library, which was only
+   designed for a 2D array of floating point numbers. Later, we must update
+   it to be more aware of the types of input columns and also accept
+   non-number columns.*/
+void
+alltxtcolinfo(struct tableparams *p)
+{
+  size_t i;
+  size_t ncols=p->up.ncols;
+
+  /* Check if there were any strings in the array. If there were strings,
+     then warn the user that we currently can't deal with them. */
+  if(gal_checkset_check_file_report(GAL_TXTARRAY_LOG))
+    error(EXIT_FAILURE, 0, "The input text file `%s' contained "
+          "non-numerical values (probably strings). Please see `%s' for "
+          "a listing of all such terms. Currently Table cannot operate on "
+          "such files. We are working on correcting this issue.",
+          p->up.txtname, GAL_TXTARRAY_LOG);
+
+  /* Allocate the arrays to keep the input column information. */
+  allocinputcolinfo(p);
+
+  /* Set all the column types to double and leave the other fields
+     blank.*/
+  for(i=0;i<ncols;++i)
+    {
+      p->up.datatype[i]=TDOUBLE;
+      gal_checkset_allocate_copy("D", &p->up.ttstr[i] );
+      gal_checkset_allocate_copy("", &p->up.tname[i] );
+      gal_checkset_allocate_copy("", &p->up.tunit[i] );
+    }
+}
+
+
+
+
+/* Print the column information. */
+void
+printinfo(struct tableparams *p)
+{
+  size_t i;
+  char *typestring=NULL;
+  struct uiparams *up=&p->up;
+
+  printf("---------------------------------------------------------\n");
+  if(up->fitsname)
+    printf("%s (hdu: %s)\n", p->up.fitsname, p->cp.hdu);
+  else
+    printf("%s\n", p->up.txtname);
+  printf("%-5s%-25s%-15s%s\n", "No.", "Column name", "Units",
+         "Data type");
+  printf("---------------------------------------------------------\n");
+  for(i=0;i<up->ncols;++i)
+    {
+      switch(up->datatype[i])
+        {
+        case TBIT:
+          typestring="bit";
+          break;
+        case TBYTE:
+          typestring="byte";
+          break;
+        case TLOGICAL:
+          typestring="logicals";
+          break;
+        case TSTRING:
+          typestring="string";
+          break;
+        case TSHORT:
+          typestring="short";
+          break;
+        case TLONG:
+          typestring="long";
+          break;
+        case TLONGLONG:
+          typestring="longlong";
+          break;
+        case TFLOAT:
+          typestring="float";
+          break;
+        case TDOUBLE:
+          typestring="double";
+          break;
+        case TCOMPLEX:
+          typestring="complex";
+          break;
+        case TDBLCOMPLEX:
+          typestring="dblcomplex";
+          break;
+        case TSBYTE:
+          typestring="signed byte";
+          break;
+        case TUINT:
+          typestring="unsigned int";
+          break;
+        case TUSHORT:
+          typestring="unsigned short";
+          break;
+        default:
+          error(EXIT_FAILURE, 0, "%d (from TFORM%lu='%c') is not a "
+                "recognized CFITSIO datatype.", up->datatype[i],
+                i, up->ttstr[i][0]);
+        }
+      printf("%-5lu%-25s%-15s%s\n", i+1, up->tname[i] ? up->tname[i] : "---",
+             up->tunit[i] ? up->tunit[i] : "---", typestring);
+    }
+
+
+  /* Print the number of rows: */
+  printf("---------------------------------------------------------\n");
+  printf("Number of rows: %lu\n", p->nrows);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Sanity Check         *******************/
+/**************************************************************/
+void
+sanitycheck(struct tableparams *p)
+{
+  struct uiparams *up=&p->up;
+
+  /* If the desired FITS output type is ASCII, then inform the user that
+     this type is not yet supported. */
+  if(up->fitsname && p->fitstabletype==ASCII_TBL)
+    error(EXIT_FAILURE, 0, "output to ASCII type FITS tables is currently "
+          "not supported (due to lack of need!). If you need this feature, "
+          "please get in touch with us at %s so we can increase the "
+          "priority of this feature.", PACKAGE_BUGREPORT);
+
+  if(up->fitsname)
+    {
+      /* Set the FITS pointer and check the type of the fits file. */
+      gal_fits_read_hdu(p->up.fitsname, p->cp.hdu, 1, &p->fitsptr);
+      gal_fits_table_size(p->fitsptr, &p->nrows, &up->ncols);
+      up->infitstabletype=gal_fits_table_type(p->fitsptr);
+      allfitscolinfo(p);
+    }
+  else
+    {
+      /* Read the text file into an input array and make the basic column
+         information. */
+      gal_txtarray_txt_to_array(up->txtname, &up->txtarray, &p->nrows,
+                                &up->ncols);
+      alltxtcolinfo(p);
+    }
+
+  /* Print the column information and exit successfully if the
+     `--information' option is given. */
+  if(p->up.information)
+    {
+      printinfo(p);
+      freeandreport(p);
+      exit(EXIT_SUCCESS);
+    }
+
+  /* Check the status of the output file. If no output file is set, then
+     the output will be printed to standard output on the terminal.*/
+  p->outputtofits=p->outputtotxt=p->outputtostdout=0;
+  if(p->cp.outputset)
+    {
+      /* First check if the file exists and remove it if it does. */
+      gal_checkset_check_remove_file(p->cp.output, p->cp.dontdelete);
+
+      /* Now set the type of output. */
+      if(gal_fits_name_is_fits(p->cp.output))
+        p->outputtofits=1;
+      else
+        p->outputtotxt=1;
+    }
+  else
+    p->outputtostdout=1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Preparations         *******************/
+/**************************************************************/
+
+/* FUnction to print regular expression error. This is taken from the GNU C
+   library manual, with small modifications to fit out style, */
+void
+regexerrorexit(int errcode, regex_t *compiled, char *input)
+{
+  char *regexerrbuf;
+  size_t length = regerror (errcode, compiled, NULL, 0);
+
+  errno=0;
+  regexerrbuf=malloc(length);
+  if(regexerrbuf==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for regexerrbuf", length);
+  (void) regerror(errcode, compiled, regexerrbuf, length);
+
+  error(EXIT_FAILURE, 0, "Regular expression error: %s in value to "
+        "`--column' (`-c'): `%s'", regexerrbuf, input);
+}
+
+
+
+
+
+/* If values were given to the columns option, use them to make a list of
+   columns that must be output. Note that because regular expressions are
+   also allowed as values to the column option, we have no idea how many
+   columns must be printed at first, so we define a linked list to keep the
+   column numbers for later.*/
+void
+outputcolumns(struct tableparams *p)
+{
+  long tlong;
+  regex_t *regex;
+  int regreturn=0;
+  size_t i, inindex;
+  char *tailptr, *colstring;
+  struct uiparams *up=&p->up;
+  struct gal_linkedlist_sll *colsll=NULL;
+
+  /* Go through each given column string and take the appropriate step. */
+  while(p->up.columns)
+    {
+      /* Pop out the top node in the string linked list. */
+      gal_linkedlist_pop_from_stll(&p->up.columns, &colstring);
+
+
+      /* First, see if this given column is an integer or a name/regex. If
+         the string is an integer, then tailptr shoult point to the null
+         character. If it points to anything else, it shows that we are not
+         dealing with an integer (usable as a column number). So floating
+         point values are also not acceptable. */
+      tlong=strtol(colstring, &tailptr,0);
+      if(*tailptr=='\0')
+        {
+          /* Make sure we are not dealing with a negative number! */
+          if(tlong<0)
+            error(EXIT_FAILURE, 0, "the column numbers given to the "
+                  "`--column' (`-c') option must not be negative, you "
+                  "have given a value of `%ld'", tlong);
+
+          /* Check if the given value is not larger than the number of
+             columns in the input catalog. */
+          if(tlong>up->ncols)
+            error(EXIT_FAILURE, 0, "%s (hdu: %s) has %lu columns, but "
+                  "you have asked for column number %lu", p->up.fitsname,
+                  p->cp.hdu, up->ncols, tlong);
+
+          /* Everything seems to be fine, put this column number in the
+             output column numbers linked list. Note that internally, the
+             column numbers start from 0, not 1.*/
+          gal_linkedlist_add_to_sll(&colsll, tlong-1);
+        }
+      else
+        {
+          /* Allocate the regex_t structure: */
+          errno=0; regex=malloc(sizeof *regex);
+          if(regex==NULL)
+            error(EXIT_FAILURE, errno, "%lu bytes for regex", sizeof *regex);
+
+          /* Go through all the columns names and see if this matches
+             them. But first we have to "compile" the string into the
+             regular expression, see the "POSIX Regular Expression
+             Compilation" section of the GNU C Library.
+
+             About the case of the string: the FITS standard says: "It is
+             _strongly recommended_ that every field of the table be
+             assigned a unique, case insensitive name with this keyword..."
+             So the column names can be case-sensitive.
+
+             Here, we don't care about the details of a match, the only
+             important thing is a match, so we are using the REG_NOSUB
+             flag.*/
+          regreturn=0;
+          regreturn=regcomp(regex, colstring, ( p->up.ignorecase
+                                                ? REG_NOSUB + REG_ICASE
+                                                : REG_NOSUB ) );
+          if(regreturn)
+            regexerrorexit(regreturn, regex, colstring);
+
+
+          /* With the regex structure "compile"d you can go through all the
+             column names. Just note that column names are not mandatory in
+             the FITS standard, so some (or all) columns might not have
+             names, if so `p->tname[i]' will be NULL. */
+          for(i=0;i<up->ncols;++i)
+            if(up->tname[i] && regexec(regex, up->tname[i], 0, 0, 0)==0)
+              gal_linkedlist_add_to_sll(&colsll, i);
+
+          /* Free the regex_t structure: */
+          regfree(regex);
+        }
+
+      /* We don't need this user provided column string any more. */
+      free(colstring);
+    }
+
+  /* Based on the number of columns found above, allocate an array of
+     `outcolumn' structures to keep the information for each column. */
+  p->nocols=gal_linkedlist_num_in_sll(colsll);
+  errno=0;
+  p->ocols=malloc(p->nocols*sizeof *p->ocols);
+  if(p->ocols==NULL)
+    error(EXIT_FAILURE, errno, "%lu bytes for p->ocols",
+          p->nocols*sizeof *p->ocols);
+
+  /* Fill in the output column with the needed input table
+     information. Note that a simple linked list is first-in-last-out, so
+     we have to fill in the output columns in reverse order. Also, note
+     that we are popping from the linked list keeping the indexs of the
+     output columns and thus also freeing their allocated space. */
+  i=p->nocols-1;
+  while(colsll)
+    {
+      gal_linkedlist_pop_from_sll(&colsll, &inindex);
+      p->ocols[i].datatype=up->datatype[inindex];
+      p->ocols[i].inindex=inindex;
+      --i;
+    }
+}
+
+
+
+
+
+void
+preparearrays(struct tableparams *p)
+{
+  size_t i;
+  struct uiparams *up=&p->up;
+
+  /* Reverse the columns linked list here (before possibly printing).*/
+  gal_linkedlist_reverse_stll(&up->columns);
+
+  /* Set the columns that should be included in the output. If up->columns
+     is set, then use it, otherwise, set all the columns for printing. */
+  if(p->up.columns)
+    outputcolumns(p);
+  else
+    {
+      p->nocols=up->ncols;
+      errno=0;
+      p->ocols=malloc(p->nocols * sizeof *p->ocols);
+      if(p->ocols==NULL)
+        error(EXIT_FAILURE, errno, "%lu bytes for p->ocols",
+              p->nocols * sizeof *p->ocols);
+      for(i=0;i<p->nocols;++i)
+        {
+          p->ocols[i].datatype=up->datatype[i];
+          p->ocols[i].inindex=i;
+        }
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************         Set the parameters          *************/
+/**************************************************************/
+void
+setparams(int argc, char *argv[], struct tableparams *p)
+{
+  struct uiparams *up=&p->up;
+  struct gal_commonparams *cp=&p->cp;
+
+  /* Set the non-zero initial values, the structure was initialized to
+     have a zero value for all elements. */
+  cp->spack         = SPACK;
+  cp->verb          = 1;
+  cp->numthreads    = num_processors(NPROC_CURRENT);
+  cp->removedirinfo = 1;
+
+  /* Initialize this utility's pointers to NULL. */
+  p->ocols=NULL;
+  up->columns=NULL;
+  up->txtname=up->fitsname=NULL;
+  up->ttstr=up->tname=up->tunit=NULL;
+
+  /* Read the arguments. */
+  errno=0;
+  if(argp_parse(&thisargp, argc, argv, 0, 0, p))
+    error(EXIT_FAILURE, errno, "parsing arguments");
+
+  /* Add the user default values and save them if asked. */
+  GAL_CONFIGFILES_CHECK_SET_CONFIG;
+
+  /* Check if all the required parameters are set. */
+  checkifset(p);
+
+  /* Print the values for each parameter. */
+  if(cp->printparams)
+    GAL_CONFIGFILES_REPORT_PARAMETERS_SET;
+
+  /* Do a sanity check. */
+  sanitycheck(p);
+
+  /* Make the array of input images. */
+  preparearrays(p);
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************      Free allocated, report         *************/
+/**************************************************************/
+void
+freeandreport(struct tableparams *p)
+{
+  size_t i, j;
+  int status=0;
+  char **rowofstrings;
+  struct uiparams *up=&p->up;
+
+  /* Free the allocated arrays: */
+  free(p->cp.hdu);
+  free(up->datatype);
+  free(p->cp.output);
+
+  /* Free the input column information: */
+  for(i=0;i<up->ncols;++i)
+    {
+      if(up->ttstr) free(up->ttstr[i]);
+      if(up->tname) free(up->tname[i]);
+      if(up->tunit) free(up->tunit[i]);
+    }
+  free(up->ttstr);
+  free(up->tname);
+  free(up->tunit);
+
+  /* Free the output column information: */
+  for(i=0;i<p->nocols;++i)
+    {
+      free(p->ocols[i].nulval);
+      if(p->ocols[i].datatype==TSTRING)
+        {
+          rowofstrings=(char **)(p->ocols[i].data);
+          for(j=0;j<p->nrows;++j)
+            free(rowofstrings[j]);
+        }
+      else
+        free(p->ocols[i].data);
+    }
+  free(p->ocols);
+
+  /* Close the FITS file: */
+  if(p->up.fitsname && fits_close_file(p->fitsptr, &status))
+    gal_fits_io_error(status, NULL);
+}
diff --git a/src/table/ui.h b/src/table/ui.h
new file mode 100644
index 0000000..1b949be
--- /dev/null
+++ b/src/table/ui.h
@@ -0,0 +1,39 @@
+/*********************************************************************
+Table - View and manipulate a FITS table structures.
+Table is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2016, 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/>.
+**********************************************************************/
+#ifndef UI_H
+#define UI_H
+
+void
+checksetfge(char *optarg, int *fge, char *filename, size_t lineno);
+
+void
+checksetfitstabletype(char *optarg, int *fitstabletype, char *filename,
+                   size_t lineno);
+
+void
+setparams(int argc, char *argv[], struct tableparams *p);
+
+void
+freeandreport(struct tableparams *p);
+
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f9810ab..7008c4b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,12 +42,12 @@ mkprof/ellipticalmasks.sh mkprof/inputascanvas.sh 
header/write.sh           \
 header/print.sh header/update.sh header/delete.sh imgstat/basicstats.sh     \
 subtractsky/subtractsky.sh noisechisel/noisechisel.sh mkcatalog/simple.sh   \
 mkcatalog/aperturephot.sh arithmetic/snimage.sh arithmetic/onlynumbers.sh   \
-cosmiccal/simpletest.sh
+cosmiccal/simpletest.sh table/asciitobinary.sh table/binarytoascii.sh
 
 EXTRA_DIST = $(TESTS) during-dev.sh mkprof/mkprofcat1.txt                  \
 mkprof/ellipticalmasks.txt mkprof/inputascanvas.txt mkprof/mkprofcat2.txt  \
 mkprof/mkprofcat3.txt mkprof/mkprofcat4.txt mkprof/radeccat.txt            \
-imgcrop/cat.txt
+imgcrop/cat.txt table/asciitobinary.txt
 
 CLEANFILES = *.log *.txt *.jpg *.fits *.pdf *.eps
 
@@ -78,6 +78,8 @@ mkprof/mosaic2.sh: prepconf.sh.log
 mkprof/mosaic3.sh: prepconf.sh.log
 mkprof/mosaic4.sh: prepconf.sh.log
 mkprof/radeccat.sh: prepconf.sh.log
+table/asciitobinary.sh: prepconf.sh.log
+table/binarytoascii.sh: table/asciitobinary.sh.log
 imgcrop/imgcat.sh: mkprof/mosaic1.sh.log
 imgcrop/wcscat.sh: mkprof/mosaic1.sh.log mkprof/mosaic2.sh.log     \
                    mkprof/mosaic3.sh.log mkprof/mosaic4.sh.log
diff --git a/tests/during-dev.sh b/tests/during-dev.sh
index 84ba39e..61065b1 100755
--- a/tests/during-dev.sh
+++ b/tests/during-dev.sh
@@ -102,8 +102,9 @@ if [ x"$builddir" = x ]; then echo "builddir is not set."; 
exit 1; fi
 # absolute. This is done because we will be going into the output directory
 # for executing the utility and we need to know the absolute address of the
 # top build directory.
+srcdir=$(pwd)
 if [ ! "${builddir:0:1}" = "/" ]; then
-   builddir=$(pwd)"/$builddir"
+   builddir="$srcdir/$builddir"
 fi
 
 
@@ -120,12 +121,29 @@ if [ -f "$utility" ]; then rm "$utility"; fi
 # edit/rebuild the libraries too). If Make is successful, then change to
 # the output directory and run the utility with the given arguments and
 # options.
-curdir=$(pwd)
+#
+# Before actually running put a copy of the configuration file in the
+# output directory and also add the onlydirconf option so user or system
+# wide configuration files don't interfere.
 if make -C "$builddir"; then
 
     # Change to the output directory.
     cd "$outdir"
 
+    # Make the .gnuastro directory if it doesn't exist.
+    if [ ! -d .gnuastro ]; then
+       mkdir .gnuastro
+    fi
+
+    # Put a copy of this utility's configuration file there and add the
+    # onlydirconf option. We are first printing an empty line just in case
+    # the last line in the configuration file doesn't actualy end with a
+    # new line (in which case the appended string will be added to the end
+    # of the last line).
+    cp "$srcdir/src/$utilname/ast$utilname.conf" .gnuastro/
+    echo ""               >> .gnuastro/ast$utilname.conf
+    echo " onlydirconf 1" >> .gnuastro/ast$utilname.conf
+
     # Run the built utility with the given arguments and options.
     "$utility" $arguments $options
 fi
diff --git a/tests/prepconf.sh b/tests/prepconf.sh
index 437ec72..068c06b 100755
--- a/tests/prepconf.sh
+++ b/tests/prepconf.sh
@@ -76,7 +76,7 @@ EOF
 # by `make check'.
 for prog in arithmetic convertt convolve cosmiccal header imgcrop \
             imgstat imgwarp mkcatalog mknoise mkprof noisechisel  \
-            subtractsky
+            subtractsky table
 do
 
     # Copy the configuration file from the utility source and add the
diff --git a/tests/table/asciitobinary.sh b/tests/table/asciitobinary.sh
new file mode 100755
index 0000000..42ae47b
--- /dev/null
+++ b/tests/table/asciitobinary.sh
@@ -0,0 +1,51 @@
+# Convert an ASCII table to a binary table
+#
+# See the Tests subsection of the manual for a complete explanation
+# (in the Installing gnuastro section).
+#
+# Original author:
+#     Mohammad Akhlaghi <address@hidden>
+# Contributing author(s):
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without any warranty.
+
+
+
+
+
+# Preliminaries
+# =============
+#
+# Set the variabels (The executable is in the build tree). Do the
+# basic checks to see if the executable is made or if the defaults
+# file exists (basicchecks.sh is in the source tree).
+prog=table
+execname=../src/$prog/ast$prog
+table=$topsrc/tests/$prog/asciitobinary.txt
+
+
+
+
+
+# Skip?
+# =====
+#
+# If the dependencies of the test don't exist, then skip it. There are two
+# types of dependencies:
+#
+#   - The executable was not made (for example due to a configure option),
+#
+#   - The input data was not made (for example the test that created the
+#     data file failed).
+if [ ! -f $execname ] || [ ! -f $img ]; then exit 77; fi
+
+
+
+
+
+# Actual test script
+# ==================
+$execname $table --output=asciitobinary.fits
diff --git a/tests/table/asciitobinary.txt b/tests/table/asciitobinary.txt
new file mode 100644
index 0000000..c4533a0
--- /dev/null
+++ b/tests/table/asciitobinary.txt
@@ -0,0 +1,2 @@
+1 2.323 43.34
+2 3213  1232
diff --git a/tests/table/binarytoascii.sh b/tests/table/binarytoascii.sh
new file mode 100755
index 0000000..5eb8cc0
--- /dev/null
+++ b/tests/table/binarytoascii.sh
@@ -0,0 +1,51 @@
+# Convert an ASCII table to a binary table
+#
+# See the Tests subsection of the manual for a complete explanation
+# (in the Installing gnuastro section).
+#
+# Original author:
+#     Mohammad Akhlaghi <address@hidden>
+# Contributing author(s):
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without any warranty.
+
+
+
+
+
+# Preliminaries
+# =============
+#
+# Set the variabels (The executable is in the build tree). Do the
+# basic checks to see if the executable is made or if the defaults
+# file exists (basicchecks.sh is in the source tree).
+prog=table
+execname=../src/$prog/ast$prog
+table=asciitobinary.fits
+
+
+
+
+
+# Skip?
+# =====
+#
+# If the dependencies of the test don't exist, then skip it. There are two
+# types of dependencies:
+#
+#   - The executable was not made (for example due to a configure option),
+#
+#   - The input data was not made (for example the test that created the
+#     data file failed).
+if [ ! -f $execname ] || [ ! -f $img ]; then exit 77; fi
+
+
+
+
+
+# Actual test script
+# ==================
+$execname $table --output=binarytoascii.txt -h1



reply via email to

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