gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 232e059 041/125: First draft of FITS table wri


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 232e059 041/125: First draft of FITS table writing function complete
Date: Sun, 23 Apr 2017 22:36:33 -0400 (EDT)

branch: master
commit 232e0599ac2d4fc540d774375abcb80290234fa4
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    First draft of FITS table writing function complete
    
    A first draft of the FITS table writing function (`gal_fits_table_write')
    has been written and apparently works nicely for binary FITS tables. I had
    these two problems with ASCII tables:
    
     - In writing, when there is a blank value in a string column, I get a
       FITSIO error 314: "Null value string for ASCII table column is not
       defined (FTPCLU)".
    
     - When I remove the blank value from the information of the string column
       (the problem above), the ASCII FITS file is created. But when I try to
       convert it back into a text file, I get a CFITSIO 409: "Cannot read
       number from ASCII table\nColumn field = nan." error on trying to read
       NaN values from a floating point column.
    
    Another major change was that the blank value for strings is no longer the
    NULL pointer, but an actual string. The same way that a blank float or
    integer value is also an actual piece of memory of the same type. This was
    necessary to make things consistent. All the functions that deal with blank
    strings now account for this change (using `strcmp').
    
    During this process the following changes were also made:
    
     - The default HDU number for the Table program was changed to `1' from `0'
       because this is how CFITSIO writes table extensions apparently.
    
     - `gal_data_alloc_number' won't print a string since its not a "number"
       type.
    
     - `gal_data_alloc_number' was cleaned to be more easily readable and
       hopefully less buggy.
    
     - The new function `gal_data_string_fixed_alloc_size' was defined to make
       all the strings allocated to a similar size and also set all the
       remaining space blank in case it is important (for FITS binary table
       writing for example).
    
     - `gal_data_blank_as_string' is a new function to write the blank value as
       a string, especially for FITS headers.
    
     - The incrementations in `gal_data_blank_to_value' were corrected.
    
     - An empty `gal_data_string_to_array_elem' was created to be a more
       generic version of the current `gal_table_read_blank' (for writing a
       given string number into the appropriate type and putting into the array
       of that data structure).
    
     - The old `gal_fits_tform_to_type' was replaced to
       `gal_fits_type_to_bin_tform' which is actually needed, we won't need the
       opposite because `TFORMn' are differently formatted between ASCII and
       binary FITS tables, and contain more information than simply the type,
       they also keep the width.
    
     - If `program_name' is not defined in `gal_fits_write_keys_version', the
       generic `PACKAGE_NAME' (GNU Astronomy Utilities) is used.
    
     - `gal_fits_table_info' now also stores the width value from `TFORMn'
       (which is necessary for string types.
    
     - In reading the FITS table information, the index is checked first before
       removing trailing space in the value and preparing it.
    
     - When we want to read a string column, it was necessary to have allocated
       space ready. So `gal_fits_table_read' now allocates the necessary space
       before reading in the array.
    
     - The old `GAL_TXT_STRING_BLANK' was removed because as described above,
       the blank string value is not a NULL pointer any more.
    
     - Some of the functions from `txt.h' that were also necessary in `fits.h'
       were moved to the more general `table.h'. For example
       `gal_table_col_print_info' and `gal_table_read_blank'.
---
 bin/table/asttable.conf |   2 +-
 lib/data.c              | 231 +++++++++++++++++++++-----
 lib/fits.c              | 429 ++++++++++++++++++++++++++++++++++++++++--------
 lib/gnuastro/data.h     |   8 +-
 lib/gnuastro/fits.h     |   4 +-
 lib/gnuastro/table.h    |  15 +-
 lib/gnuastro/txt.h      |   1 -
 lib/table.c             | 260 ++++++++++++++++++++++++++++-
 lib/txt.c               | 307 +++++-----------------------------
 9 files changed, 869 insertions(+), 388 deletions(-)

diff --git a/bin/table/asttable.conf b/bin/table/asttable.conf
index 16d4116..4ce41c6 100644
--- a/bin/table/asttable.conf
+++ b/bin/table/asttable.conf
@@ -18,7 +18,7 @@
 # without any warranty.
 
 # Input:
- hdu              0
+ hdu              1
  searchin         name
  ignorecase       0
 
diff --git a/lib/data.c b/lib/data.c
index f03185b..a4a512e 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -239,10 +239,6 @@ gal_data_alloc_number(int type, void *number)
       *(char *)allocated=*(char *)number;
       break;
 
-    case GAL_DATA_TYPE_STRING:
-      *(unsigned char **)allocated=*(unsigned char **)number;
-      break;
-
     case GAL_DATA_TYPE_USHORT:
       *(unsigned short *)allocated=*(unsigned short *)number;
       break;
@@ -412,6 +408,7 @@ gal_data_initialize(gal_data_t *data, void *array, int type,
      `dsize[0]=1', A 1D array also has `ndim=1', but `dsize[0]>1'. */
   if(ndim)
     {
+      /* Allocate dsize. */
       errno=0;
       data->dsize=malloc(ndim*sizeof *data->dsize);
       if(data->dsize==NULL)
@@ -433,33 +430,31 @@ gal_data_initialize(gal_data_t *data, void *array, int 
type,
              elements. */
           data->size *= ( data->dsize[i] = dsize[i] );
         }
-    }
-  else
-    {
-      data->size=0;
-      data->dsize=NULL;
-    }
-
 
-  /* Set the array pointer. If an non-NULL array pointer was given, then
-     use it. If `array==NULL', then check if `ndim==0'. If it is, then you
-     can also set `data->array=array' (==NULL). Otherwise, mmap or allocate
-     (and possibly) clean the space. */
-  if(array || ndim==0)
-    data->array=array;
-  else
-    {
-      if( gal_data_sizeof(type)*data->size  > minmapsize )
-        gal_data_mmap(data, clear);
+      /* Set the array pointer. If an non-NULL array pointer was given,
+         then use it. */
+      if(array)
+        data->array=array;
       else
         {
-          /* Allocate the space for the array. */
-          if(clear)
-            data->array = gal_data_calloc_array(data->type, data->size);
+          if( gal_data_sizeof(type)*data->size  > minmapsize )
+            gal_data_mmap(data, clear);
           else
-            data->array = gal_data_malloc_array(data->type, data->size);
+            {
+              /* Allocate the space for the array. */
+              if(clear)
+                data->array = gal_data_calloc_array(data->type, data->size);
+              else
+                data->array = gal_data_malloc_array(data->type, data->size);
+            }
         }
     }
+  else
+    {
+      data->size=0;
+      data->array=NULL;
+      data->dsize=NULL;
+    }
 }
 
 
@@ -535,6 +530,60 @@ gal_data_calloc_dataarray(size_t size)
 
 
 
+/* In some contexts, it is necessary for all the strings to have the same
+   allocated space (when the `strlen' is different). This function will
+   allocate new copies for all elements to have the same length as the
+   maximum length and set all trailing elements to `\0' for those that are
+   shorter than the length. The return value is the allocated space. If the
+   dataset is not a string, the returned value will be -1 (largest number
+   of `size_t'). */
+size_t
+gal_data_string_fixed_alloc_size(gal_data_t *data)
+{
+  size_t i, j, maxlen=0;
+  char *tmp, **strarr=data->array;
+
+  /* Return 0 if the dataset is not a string. */
+  if(data->type!=GAL_DATA_TYPE_STRING)
+    return -1;
+
+  /* Get the maximum length. */
+  for(i=0;i<data->size;++i)
+    maxlen = strlen(strarr[i])>maxlen ? strlen(strarr[i]) : maxlen;
+
+  /* For all elements, check the length and if they aren't equal to maxlen,
+     then allocate a maxlen sized array and put the values in. */
+  for(i=0;i<data->size;++i)
+    {
+      /* Allocate (and clear) the space for the new string. We want it to
+         be cleared, so when the strings are smaller, the rest of the space
+         is filled with '\0' (ASCII for 0) values.*/
+      errno=0;
+      tmp=calloc(maxlen+1, sizeof *strarr[i]);
+      if(tmp==NULL)
+        error(EXIT_FAILURE, 0, "%zu bytes for tmp in "
+              "`gal_data_fixed_alloc_size_for_string'",
+              maxlen+1*sizeof *strarr[i]);
+
+      /* Put the old array into the newly allocated space. `tmp' was
+         cleared (all values set to `\0', so we don't need to set the final
+         one explicity after the copy.*/
+      for(j=0;strarr[i][j]!='\0';++j)
+        tmp[j]=strarr[i][j];
+
+      /* Free the old array and put in the new one. */
+      free(strarr[i]);
+      strarr[i]=tmp;
+    }
+
+  /* Return the allocated space. */
+  return maxlen+1;
+}
+
+
+
+
+
 /* Free the allocated contents of a data structure and possibly also the
    data structure its self. When `only_contents' is zero, the actual data
    structure will also be freed, see bellow.
@@ -750,9 +799,9 @@ void *
 gal_data_alloc_blank(int type)
 {
   /* Define the pointers. */
+  char            *str;
   unsigned char     uc = GAL_DATA_BLANK_UCHAR;
   char               c = GAL_DATA_BLANK_CHAR;
-  char            *str = GAL_DATA_BLANK_STRING;
   unsigned short    us = GAL_DATA_BLANK_USHORT;
   short              s = GAL_DATA_BLANK_SHORT;
   unsigned int      ui = GAL_DATA_BLANK_UINT;
@@ -768,6 +817,10 @@ gal_data_alloc_blank(int type)
   /* Put the blank value into it. */
   switch(type)
     {
+    case GAL_DATA_TYPE_STRING:
+      gal_checkset_allocate_copy(GAL_DATA_BLANK_STRING, &str);
+      return str;
+
     case GAL_DATA_TYPE_BIT:
       error(EXIT_FAILURE, 0, "Currently Gnuastro doesn't support blank "
             "values for `GAL_DATA_TYPE_BIT', please get in touch with "
@@ -782,9 +835,6 @@ gal_data_alloc_blank(int type)
     case GAL_DATA_TYPE_CHAR: case GAL_DATA_TYPE_LOGICAL:
       return gal_data_alloc_number(type, &c);
 
-    case GAL_DATA_TYPE_STRING:
-      return gal_data_alloc_number(type, &str);
-
     case GAL_DATA_TYPE_USHORT:
       return gal_data_alloc_number(type, &us);
 
@@ -832,6 +882,78 @@ gal_data_alloc_blank(int type)
 
 
 
+/* Print the blank value as a string. */
+char *
+gal_data_blank_as_string(int type)
+{
+  char *blank;
+  switch(type)
+    {
+    case GAL_DATA_TYPE_STRING:
+      error(EXIT_FAILURE, 0, "`gal_data_blank_as_string' can't print the "
+            "blank value for a string, it is for other types.");
+      break;
+
+    case GAL_DATA_TYPE_BIT:
+      error(EXIT_FAILURE, 0, "bit types are not implemented in "
+            "`gal_data_blank_as_string' yet.");
+      break;
+
+    case GAL_DATA_TYPE_UCHAR:
+      asprintf(&blank, "%u", (unsigned char)GAL_DATA_BLANK_UCHAR);
+      break;
+
+    case GAL_DATA_TYPE_CHAR:
+      asprintf(&blank, "%d", (char)GAL_DATA_BLANK_CHAR);
+      break;
+
+    case GAL_DATA_TYPE_USHORT:
+      asprintf(&blank, "%u", (unsigned short)GAL_DATA_BLANK_USHORT);
+      break;
+
+    case GAL_DATA_TYPE_SHORT:
+      asprintf(&blank, "%d", (short)GAL_DATA_BLANK_SHORT);
+      break;
+
+    case GAL_DATA_TYPE_UINT:
+      asprintf(&blank, "%u", (unsigned int)GAL_DATA_BLANK_UINT);
+      break;
+
+    case GAL_DATA_TYPE_INT:
+      asprintf(&blank, "%d", (int)GAL_DATA_BLANK_INT);
+      break;
+
+    case GAL_DATA_TYPE_ULONG:
+      asprintf(&blank, "%lu", (unsigned long)GAL_DATA_BLANK_ULONG);
+      break;
+
+    case GAL_DATA_TYPE_LONG:
+      asprintf(&blank, "%ld", (long)GAL_DATA_BLANK_LONG);
+      break;
+
+    case GAL_DATA_TYPE_LONGLONG:
+      asprintf(&blank, "%lld", (LONGLONG)GAL_DATA_BLANK_LONGLONG);
+      break;
+
+    case GAL_DATA_TYPE_FLOAT:
+      asprintf(&blank, "%f", GAL_DATA_BLANK_FLOAT);
+      break;
+
+    case GAL_DATA_TYPE_DOUBLE:
+      asprintf(&blank, "%f", GAL_DATA_BLANK_DOUBLE);
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "type code %d not recognized in "
+            "`gal_data_blank_as_string'", type);
+    }
+  return blank;
+}
+
+
+
+
+
 /* Any non-zero pixel in the `mask' array is set as blank in the `in'
    array. */
 void
@@ -1014,52 +1136,55 @@ gal_data_blank_to_value(gal_data_t *data, void *value)
             "datatype, please get in touch with us to implement it.");
 
     case GAL_DATA_TYPE_UCHAR:
-      do if(*uc==GAL_DATA_BLANK_UCHAR) *uc++=ucv; while(uc<ucf);
+      do if(*uc==GAL_DATA_BLANK_UCHAR) *uc=ucv; while(++uc<ucf);
       break;
 
 
     case GAL_DATA_TYPE_CHAR: case GAL_DATA_TYPE_LOGICAL:
-      do if(*c==GAL_DATA_BLANK_CHAR) *c++=cv; while(c<cf);
+      do if(*c==GAL_DATA_BLANK_CHAR) *c=cv; while(++c<cf);
       break;
 
 
     case GAL_DATA_TYPE_STRING:
-      do if(*str==GAL_DATA_BLANK_STRING) *str++=strv; while(str<strf);
+      do
+        if(!strcmp(*str, GAL_DATA_BLANK_STRING))
+          gal_checkset_allocate_copy(strv, str);
+      while(++str<strf);
       break;
 
 
     case GAL_DATA_TYPE_USHORT:
-      do if(*us==GAL_DATA_BLANK_USHORT) *us++=usv; while(us<usf);
+      do if(*us==GAL_DATA_BLANK_USHORT) *us=usv; while(++us<usf);
       break;
 
 
     case GAL_DATA_TYPE_SHORT:
-      do if(*s==GAL_DATA_BLANK_SHORT) *s++=sv; while(s<sf);
+      do if(*s==GAL_DATA_BLANK_SHORT) *s=sv; while(++s<sf);
       break;
 
 
     case GAL_DATA_TYPE_UINT:
-      do if(*ui==GAL_DATA_BLANK_UINT) *ui++=uiv; while(ui<uif);
+      do if(*ui==GAL_DATA_BLANK_UINT) *ui=uiv; while(++ui<uif);
       break;
 
 
     case GAL_DATA_TYPE_INT:
-      do if(*in==GAL_DATA_BLANK_INT) *in++=inv; while(in<inf);
+      do if(*in==GAL_DATA_BLANK_INT) *in=inv; while(++in<inf);
       break;
 
 
     case GAL_DATA_TYPE_ULONG:
-      do if(*ul==GAL_DATA_BLANK_ULONG) *ul++=ulv; while(ul<ulf);
+      do if(*ul==GAL_DATA_BLANK_ULONG) *ul=ulv; while(++ul<ulf);
       break;
 
 
     case GAL_DATA_TYPE_LONG:
-      do if(*l==GAL_DATA_BLANK_LONG) *l++=lv; while(l<lf);
+      do if(*l==GAL_DATA_BLANK_LONG) *l=lv; while(++l<lf);
       break;
 
 
     case GAL_DATA_TYPE_LONGLONG:
-      do if(*L==GAL_DATA_BLANK_LONGLONG) *L++=Lv; while(L<Lf);
+      do if(*L==GAL_DATA_BLANK_LONGLONG) *L=Lv; while(++L<Lf);
       break;
 
 
@@ -1168,7 +1293,7 @@ gal_data_has_blank(gal_data_t *data)
 
 
     case GAL_DATA_TYPE_STRING:
-      do if(*str++==GAL_DATA_BLANK_STRING) return 1; while(str<strf);
+      do if(!strcmp(*str++,GAL_DATA_BLANK_STRING)) return 1; while(str<strf);
       break;
 
 
@@ -1328,7 +1453,7 @@ gal_data_flag_blank(gal_data_t *data)
 
 
     case GAL_DATA_TYPE_STRING:
-      do *o++ = *str==GAL_DATA_BLANK_STRING; while(++str<strf);
+      do *o++ = !strcmp(*str,GAL_DATA_BLANK_STRING); while(++str<strf);
       break;
 
 
@@ -1443,6 +1568,7 @@ gal_data_flag_blank(gal_data_t *data)
 
 
 
+
 /*************************************************************
  **************       Types and copying        ***************
  *************************************************************/
@@ -1739,6 +1865,29 @@ gal_data_string_to_number(char *string)
 
 
 
+/* You have a string and you want to put it into an element of the array of
+   a data structure. The array doesn't necessarily have to be allocated
+   (!=NULL). If it wasn't allocated, it will be allocated here based on the
+   `size' element in the data structure. If size is 0, a one element array
+   will be allocated. If the data structure isn't already allocated or its
+   type is negative, this function will act like
+   `gal_data_string_to_number' where the type will be set based on the
+   value of the number. If the string is blank, this function won't do
+   anything.
+
+   Use `gal_table_read_blank' as a basis for this function, then remove
+   that function. */
+void
+gal_data_string_to_array_elem(gal_data_t **outdata, size_t index,
+                              char *string)
+{
+}
+
+
+
+
+
+
 
 
 
diff --git a/lib/fits.c b/lib/fits.c
index 1f270d0..c061ab3 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -258,52 +258,50 @@ gal_fits_type_to_bitpix(int type)
 
 
 
-/* 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.
-
-   Note that the characters are the same for ASCII or binary tables.*/
-int
-gal_fits_tform_to_type(char tform)
+/* The values to the TFORM header keywords of FITS binary tables 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.*/
+char
+gal_fits_type_to_bin_tform(int type)
 {
-  switch(tform)
+  switch(type)
     {
-    case 'X':
-      return GAL_DATA_TYPE_BIT;
-    case 'B':
-      return GAL_DATA_TYPE_UCHAR;
-    case 'S': case 'L':
-      return GAL_DATA_TYPE_CHAR;
-    case 'A':
-      return GAL_DATA_TYPE_STRING;
-    case 'V':
-      return GAL_DATA_TYPE_UINT;
-    case 'U':
-      return GAL_DATA_TYPE_USHORT;
-    case 'I':
-      return GAL_DATA_TYPE_SHORT;
-    case 'J':
-      return GAL_DATA_TYPE_LONG;
-    case 'K':
-      return GAL_DATA_TYPE_LONGLONG;
-    case 'E':
-      return GAL_DATA_TYPE_FLOAT;
-    case 'D':
-      return GAL_DATA_TYPE_DOUBLE;
-    case 'C':
-      return GAL_DATA_TYPE_COMPLEX;
-    case 'M':
-      return GAL_DATA_TYPE_DCOMPLEX;
+    case GAL_DATA_TYPE_BIT:
+      return 'X';
+    case GAL_DATA_TYPE_UCHAR:
+      return 'B';
+    case GAL_DATA_TYPE_CHAR: case GAL_DATA_TYPE_LOGICAL:
+      return 'S';
+    case GAL_DATA_TYPE_STRING:
+      return 'A';
+    case GAL_DATA_TYPE_UINT:
+      return 'V';
+    case GAL_DATA_TYPE_USHORT:
+      return 'U';
+    case GAL_DATA_TYPE_SHORT:
+      return 'I';
+    case GAL_DATA_TYPE_LONG:
+      return 'J';
+    case GAL_DATA_TYPE_LONGLONG:
+      return 'K';
+    case GAL_DATA_TYPE_FLOAT:
+      return 'E';
+    case GAL_DATA_TYPE_DOUBLE:
+      return 'D';
+    case GAL_DATA_TYPE_COMPLEX:
+      return 'C';
+    case GAL_DATA_TYPE_DCOMPLEX:
+      return 'M';
     default:
-      error(EXIT_FAILURE, 0, "'%c' is not a recognized CFITSIO value for "
-            "the TFORMn header keyword(s).", tform);
+      error(EXIT_FAILURE, 0, "type code %d not recognized in "
+            "`gal_fits_type_to_bin_tform'", type);
     }
 
   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_datatype function in fits.c.");
-  return -1;
+        "`gal_fits_type_to_bin_tform'");
+  return '\0';
 }
 
 
@@ -990,7 +988,7 @@ gal_fits_update_keys(fitsfile *fptr, struct gal_fits_key_ll 
**keylist)
 
 void
 gal_fits_write_keys_version(fitsfile *fptr, struct gal_fits_key_ll *headers,
-                            char *spack_string)
+                            char *program_name)
 {
   size_t i;
   int status=0;
@@ -1019,7 +1017,8 @@ gal_fits_write_keys_version(fitsfile *fptr, struct 
gal_fits_key_ll *headers,
   if(headers)
     {
       fits_write_record(fptr, blankrec, &status);
-      sprintf(titlerec, "%s%s", startblank, spack_string);
+      sprintf(titlerec, "%s%s", startblank,
+              program_name ? program_name : PACKAGE_NAME);
       for(i=strlen(titlerec);i<79;++i) titlerec[i]=' ';
       fits_write_record(fptr, titlerec, &status);
       gal_fits_update_keys(fptr, &headers);
@@ -1593,7 +1592,7 @@ gal_fits_table_type(fitsfile *fptr)
 
   if(status==0)
     {
-      if(!strcmp(value, "TABLE   "))
+      if(!strcmp(value, "TABLE"))
         return GAL_TABLE_TYPE_AFITS;
       else if(!strcmp(value, "BINTABLE"))
         return GAL_TABLE_TYPE_BFITS;
@@ -1734,19 +1733,18 @@ gal_fits_table_info(char *filename, char *hdu, size_t 
*numcols,
 {
   long repeat;
   int tfields;        /* The maximum number of fields in FITS is 999 */
+  char *tailptr;
   fitsfile *fptr;
   size_t i, index;
   gal_data_t *allcols;
   int status=0, datatype;
-  char *tailptr, keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE];
-
+  char keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE];
 
   /* Open the FITS file and get the basic information. */
   fptr=gal_fits_read_hdu(filename, hdu, 1);
   *tabletype=gal_fits_table_type(fptr);
   gal_fits_table_size(fptr, numrows, numcols);
 
-
   /* Read the total number of fields, then allocate space for the data
      structure array and store the information within it. */
   fits_read_key(fptr, TINT, "TFIELDS", &tfields, NULL, &status);
@@ -1772,25 +1770,41 @@ gal_fits_table_info(char *filename, char *hdu, size_t 
*numcols,
          value[0] is a single quote.*/
       if(strncmp(keyname, "TFORM", 5)==0)
         {
-          /* Remove the ending trailing space and quotation sign. */
-          remove_trailing_space(value);
-          if(*tabletype==GAL_TABLE_TYPE_AFITS)
-            fits_ascii_tform(&value[1], &datatype, NULL, NULL, &status);
-          else
-            fits_binary_tform(&value[1], &datatype, &repeat, NULL, &status);
-
-          /* Small sanity check. */
-          if(repeat>1)
-             error(EXIT_FAILURE, 0, "The repeat value of %s is %ld, "
-                   "currently we can only use columns with a repeat "
-                   "of 1. Please get in touch with us at %s to add this "
-                   "feature", keyname, repeat, PACKAGE_BUGREPORT);
-
-          /* See which column this information was for and add it. In the
-             meantime, also do a sanity check. */
+          /* See which column this information was for and add it, if the
+             index is larger than the number of columns, then ignore
+             the . The FITS standard says there should be no extra TFORM
+             keywords beyond the number of columns, but we don't want to be
+             that strict here.*/
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)     /* Counting from zero was corrected above. */
-            allcols[index].type=gal_fits_datatype_to_type(datatype);
+            {
+              /* Remove the ending trailing space and quotation sign. */
+              remove_trailing_space(value);
+              if(*tabletype==GAL_TABLE_TYPE_AFITS)
+                fits_ascii_tform(&value[1], &datatype, NULL, NULL, &status);
+              else
+                fits_binary_tform(&value[1], &datatype, &repeat, NULL,
+                                  &status);
+
+              /* Write the type into the data structure. */
+              allcols[index].type=gal_fits_datatype_to_type(datatype);
+
+              /* If we are dealing with a string type, we need to know the
+                 number of bytes in both cases. */
+              if( allcols[index].type==GAL_DATA_TYPE_STRING )
+                {
+                  if(*tabletype==GAL_TABLE_TYPE_AFITS)
+                    {
+                      repeat=strtol(&value[2], &tailptr, 0);
+                      if(tailptr=='\0')
+                        error(EXIT_FAILURE, 0, "%s (hdu: %s): the value to "
+                              "keyword `%s' (`%s') is not in `Aw' format "
+                              "(for strings) as required by the FITS "
+                              "standard", filename, hdu, keyname, &value[1]);
+                    }
+                  allcols[index].disp_width=repeat;
+                }
+            }
         }
 
       /* COLUMN NAME. All strings in CFITSIO start and finish with single
@@ -1800,16 +1814,17 @@ gal_fits_table_info(char *filename, char *hdu, size_t 
*numcols,
          where too (without the single quotes).*/
       else if(strncmp(keyname, "TTYPE", 5)==0)
         {
-          remove_trailing_space(value);
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            gal_checkset_allocate_copy(&value[1], &allcols[index].name);
+            {
+              remove_trailing_space(value);
+              gal_checkset_allocate_copy(&value[1], &allcols[index].name);
+            }
         }
 
       /* COLUMN UNITS. */
       else if(strncmp(keyname, "TUNIT", 5)==0)
         {
-          /* similar to tname, see above.*/
           remove_trailing_space(value);
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
@@ -1819,22 +1834,46 @@ gal_fits_table_info(char *filename, char *hdu, size_t 
*numcols,
       /* COLUMN COMMENTS */
       else if(strncmp(keyname, "TCOMM", 5)==0)
         {
-          /* similar to tname, see above.*/
-          remove_trailing_space(value);
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            gal_checkset_allocate_copy(&value[1], &allcols[index].comment);
+            {
+              remove_trailing_space(value);
+              gal_checkset_allocate_copy(&value[1], &allcols[index].comment);
+            }
+        }
+
+      /* COLUMN BLANK VALUE. Note that to interpret the blank value the
+         type of the column must already have been defined for this column
+         in previous keywords. Otherwise, there will be a warning and it
+         won't be used. */
+      else if(strncmp(keyname, "TNULL", 5)==0)
+        {
+          index = strtoul(&keyname[5], &tailptr, 10) - 1;
+          if(index<tfields )
+            {
+              if(allcols[index].type<0)
+                fprintf(stderr, "%s (hdu: %s): %s is located before "
+                        "TFORM%zu, so the proper type to read/store the "
+                        "blank value cannot be deduced", filename, hdu,
+                        keyname, index+1);
+              else
+                {
+                  remove_trailing_space(value);
+                  gal_table_read_blank(&allcols[index], value);
+                }
+            }
         }
 
       /* COLUMN DISPLAY FORMAT */
       else if(strncmp(keyname, "TDISP", 5)==0)
         {
-          /* similar to tname, see above.*/
-          remove_trailing_space(value);
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            set_display_format(&value[1], &allcols[index], filename, hdu,
-                               keyname);
+            {
+              remove_trailing_space(value);
+              set_display_format(&value[1], &allcols[index], filename, hdu,
+                                 keyname);
+            }
         }
     }
 
@@ -1857,8 +1896,10 @@ gal_fits_table_read(char *filename, char *hdu, size_t 
numrows,
                     gal_data_t *allcols, struct gal_linkedlist_sll *indexll,
                     int minmapsize)
 {
+  size_t i=0;
   long dsize;
   void *blank;
+  char **strarr;
   fitsfile *fptr;
   int status=0, anynul;
   gal_data_t *out=NULL;
@@ -1877,11 +1918,28 @@ gal_fits_table_read(char *filename, char *hdu, size_t 
numrows,
                          minmapsize, allcols[ind->v].name,
                          allcols[ind->v].unit, allcols[ind->v].comment);
 
+      /* For a string column, we need an allocated array for each element,
+         even in binary values. This value should be stored in the
+         disp_width element of the data structure, which is done
+         automatically in `gal_fits_table_info'. */
+      if(out->type==GAL_DATA_TYPE_STRING)
+        for(i=0;i<numrows;++i)
+          {
+            strarr=out->array;
+            errno=0;
+            strarr[i]=calloc(allcols[ind->v].disp_width, sizeof *strarr[i]);
+            if(strarr[i]==NULL)
+              error(EXIT_FAILURE, errno, "%zu bytes for strarr[%zu] in "
+                    "`gal_fits_table_read'",
+                    allcols[ind->v].disp_width * sizeof *strarr[i], i);
+          }
+
       /* Allocate a blank value for the given type and read/store the
          column using CFITSIO. Afterwards, free the blank value. */
       blank=gal_data_alloc_blank(out->type);
       fits_read_col(fptr, gal_fits_type_to_datatype(out->type), ind->v+1,
                     1, 1, out->size, blank, out->array, &anynul, &status);
+      gal_fits_io_error(status, NULL);
       free(blank);
     }
 
@@ -1895,11 +1953,236 @@ gal_fits_table_read(char *filename, char *hdu, size_t 
numrows,
 
 
 
+static void
+fits_table_prepare_arrays(gal_data_t *cols, size_t numcols, int tabletype,
+                          char ***outtform, char ***outttype,
+                          char ***outtunit)
+{
+  size_t i=0;
+  gal_data_t *col;
+  char fmt[2], lng[3];
+  size_t width, precision;
+  char *blank, **tform, **ttype, **tunit;
+
+
+  /* Allocate the arrays to keep the `tform' values */
+  errno=0;
+  tform=*outtform=malloc(numcols*sizeof *tform);
+  if(tform==NULL)
+    error(EXIT_FAILURE, 0, "%zu bytes for tform in "
+          "`fits_table_prepare_arrays'", numcols*sizeof *tform);
+  errno=0;
+  ttype=*outttype=malloc(numcols*sizeof *ttype);
+  if(ttype==NULL)
+    error(EXIT_FAILURE, 0, "%zu bytes for ttype in "
+          "`fits_table_prepare_arrays'", numcols*sizeof *ttype);
+  errno=0;
+  tunit=*outtunit=malloc(numcols*sizeof *tunit);
+  if(tunit==NULL)
+    error(EXIT_FAILURE, 0, "%zu bytes for tunit in "
+          "`fits_table_prepare_arrays'", numcols*sizeof *tunit);
+
+
+  /* Go over each column and fill in these arrays. */
+  for(col=cols; col!=NULL; col=col->next)
+    {
+      /* Set the `ttype' and `tunit' values: */
+      ttype[i]=col->name;
+      tunit[i]=col->unit;
+
+
+      /* Set the blank value. */
+      if( gal_data_has_blank(col) && ( col->type!=GAL_DATA_TYPE_FLOAT
+                                       || col->type!=GAL_DATA_TYPE_DOUBLE ))
+        {
+          /* Set the blank value. */
+          if(col->type==GAL_DATA_TYPE_STRING)
+            gal_checkset_allocate_copy(GAL_DATA_BLANK_STRING, &blank);
+          else
+            blank=gal_data_blank_as_string(col->type);
+        }
+      else
+        blank=NULL;
+
+
+      /* FITS's TFORM depends on the type of FITS table, so work
+         differently. */
+      switch(tabletype)
+        {
+        /* FITS ASCII table. */
+        case GAL_TABLE_TYPE_AFITS:
+
+            /* Fill the printing format. */
+            gal_table_col_print_info(col, GAL_TABLE_TYPE_AFITS, &width,
+                                     &precision, fmt, lng);
+
+            /* Adjust the width. */
+            if(blank)
+              width = ( strlen(blank)>width ? strlen(blank) : width );
+
+            /* Print the value to be used as TFORMn:  */
+            switch(col->type)
+              {
+              case GAL_DATA_TYPE_STRING:
+              case GAL_DATA_TYPE_UCHAR:
+              case GAL_DATA_TYPE_CHAR:
+              case GAL_DATA_TYPE_USHORT:
+              case GAL_DATA_TYPE_SHORT:
+              case GAL_DATA_TYPE_UINT:
+              case GAL_DATA_TYPE_INT:
+              case GAL_DATA_TYPE_ULONG:
+              case GAL_DATA_TYPE_LONG:
+              case GAL_DATA_TYPE_LONGLONG:
+                asprintf(&tform[i], "%c%zu", fmt[0], width);
+                break;
+
+              case GAL_DATA_TYPE_FLOAT:
+              case GAL_DATA_TYPE_DOUBLE:
+                asprintf(&tform[i], "%c%zu.%zu", fmt[0], width, precision);
+                break;
+
+              default:
+                error(EXIT_FAILURE, 0, "col->type code %d not recognized "
+                      "fits_table_prepare_arrays", col->type);
+              }
+          break;
+
+
+        /* FITS binary table. */
+        case GAL_TABLE_TYPE_BFITS:
+
+          /* If this is a string column, set all the strings to same size,
+             then write the value of tform depending on the type. */
+          width=gal_data_string_fixed_alloc_size(col);
+          fmt[0]=gal_fits_type_to_bin_tform(col->type);
+          if( col->type==GAL_DATA_TYPE_STRING )
+            asprintf(&tform[i], "%zu%c", width, fmt[0]);
+          else
+            asprintf(&tform[i], "%c", fmt[0]);
+          break;
+
+        default:
+          error(EXIT_FAILURE, 0, "tabletype code %d not recognized in "
+                "`fits_table_prepare_arrays'", tabletype);
+        }
+
+
+      /* Clean up and increment the column index. */
+      if(blank) free(blank);
+      ++i;
+    }
+}
+
+
+
+
+
 /* Write the given columns (a linked list of `gal_data_t') into a FITS
    table.*/
 void
 gal_fits_table_write(gal_data_t *cols, char *comments, int tabletype,
                      char *filename, int dontdelete)
 {
-  error(EXIT_FAILURE, 0, "writing FITS tables is not implemented yet!");
+  void *blank;
+  fitsfile *fptr;
+  gal_data_t *col;
+  size_t i, numrows=-1;
+  char *keyname, *bcomment;
+  char **ttype, **tform, **tunit;
+  int tbltype, numcols=0, status=0;
+
+
+  /* Make sure all the input columns have the same number of elements */
+  for(col=cols; col!=NULL; col=col->next)
+    {
+      if(numrows==-1) numrows=col->size;
+      else if(col->size!=numrows)
+        error(EXIT_FAILURE, 0, "The number of records/rows in the input "
+              "columns to `gal_fits_table_write' are not equal");
+      ++numcols;
+    }
+
+
+  /* Remove the output if it already exists. */
+  gal_checkset_check_remove_file(filename, dontdelete);
+
+
+  /* Create the FITS file */
+  fits_create_file(&fptr, filename, &status);
+  gal_fits_io_error(status, NULL);
+
+
+  /* prepare necessary arrays and if integer type columns have blank
+     values, write the TNULLn keywords into the FITS file. */
+  fits_table_prepare_arrays(cols, numcols, tabletype,
+                            &tform, &ttype, &tunit);
+
+
+  /* Make the FITS file pointer. Note that tabletype was checked in
+     `fits_table_prepare_arrays'. */
+  tbltype = tabletype==GAL_TABLE_TYPE_AFITS ? ASCII_TBL : BINARY_TBL;
+  fits_create_tbl(fptr, tbltype, numrows, numcols, ttype, tform, tunit,
+                  "table", &status);
+  gal_fits_io_error(status, NULL);
+
+  /* Write the columns into the file and also write the blank values into
+     the header when necessary. */
+  i=0;
+  for(col=cols; col!=NULL; col=col->next)
+    {
+      /* Write the blank value into the header if we are not dealing with a
+         floating point. */
+      if( gal_data_has_blank(col)
+          && ( col->type!=GAL_DATA_TYPE_FLOAT
+               && col->type!=GAL_DATA_TYPE_DOUBLE ) )
+        {
+          /* Write the keyword. */
+          asprintf(&keyname, "TNULL%zu", i+1);
+          blank=gal_data_alloc_blank(col->type);
+          fits_write_key(fptr, gal_fits_type_to_datatype(col->type),
+                         keyname, blank, "blank value for this column",
+                         &status);
+          gal_fits_io_error(status, NULL);
+          free(keyname);
+        }
+      else
+        blank=NULL;
+
+      /* Write the comments if there is any. */
+      if(col->comment)
+        {
+          asprintf(&keyname, "TCOMM%zu", i+1);
+          asprintf(&bcomment, "comment for field %zu", i+1);
+          fits_write_key(fptr, TSTRING, keyname, col->comment,
+                         bcomment, &status);
+          gal_fits_io_error(status, NULL);
+          free(keyname);
+          free(bcomment);
+        }
+
+      /* Write the full column into the table. */
+      fits_write_colnull(fptr, gal_fits_type_to_datatype(col->type),
+                         i+1, 1, 1, col->size, col->array, blank, &status);
+      gal_fits_io_error(status, NULL);
+
+      /* Clean up and Increment the column counter. */
+      if(blank) free(blank);
+      ++i;
+    }
+
+
+  /* Write all the headers and the version information. */
+  gal_fits_write_keys_version(fptr, NULL, NULL);
+
+
+  /* Clean up and close the FITS file. Note that each element in the
+     `ttype' and `tunit' arrays just points to the respective string in the
+     column data structure, the space for each element of the array wasn't
+     allocated.*/
+  for(i=0;i<numcols;++i) if(tform[i]) free(tform[i]);
+  free(tform);
+  free(ttype);
+  free(tunit);
+  fits_close_file(fptr, &status);
+  gal_fits_io_error(status, NULL);
 }
diff --git a/lib/gnuastro/data.h b/lib/gnuastro/data.h
index 1156b49..76bc83e 100644
--- a/lib/gnuastro/data.h
+++ b/lib/gnuastro/data.h
@@ -89,7 +89,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 #define GAL_DATA_BLANK_UCHAR      UCHAR_MAX
 #define GAL_DATA_BLANK_CHAR       SCHAR_MAX
 #define GAL_DATA_BLANK_LOGICAL    SCHAR_MAX
-#define GAL_DATA_BLANK_STRING     NULL
+#define GAL_DATA_BLANK_STRING     "n/a"
 #define GAL_DATA_BLANK_USHORT     USHRT_MAX
 #define GAL_DATA_BLANK_SHORT      INT16_MIN
 #define GAL_DATA_BLANK_UINT       UINT_MAX
@@ -235,6 +235,9 @@ gal_data_alloc(void *array, int type, size_t ndim, long 
*dsize,
 gal_data_t *
 gal_data_calloc_dataarray(size_t size);
 
+size_t
+gal_data_string_fixed_alloc_size(gal_data_t *data);
+
 void
 gal_data_free(gal_data_t *data, int only_contents);
 
@@ -273,6 +276,9 @@ gal_data_free_ll(gal_data_t *list);
 void *
 gal_data_alloc_blank(int type);
 
+char *
+gal_data_blank_as_string(int type);
+
 void
 gal_data_apply_mask(gal_data_t *in, gal_data_t *mask);
 
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index 89fc8f0..1bc3f93 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -122,8 +122,8 @@ gal_fits_bitpix_to_type(int bitpix);
 int
 gal_fits_type_to_bitpix(int type);
 
-int
-gal_fits_tform_to_type(char tform);
+char
+gal_fits_type_to_bin_tform(int type);
 
 int
 gal_fits_type_to_datatype(int type);
diff --git a/lib/gnuastro/table.h b/lib/gnuastro/table.h
index f374d83..075bc5d 100644
--- a/lib/gnuastro/table.h
+++ b/lib/gnuastro/table.h
@@ -60,7 +60,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 #define GAL_TABLE_DEF_INT_WIDTH       6
 #define GAL_TABLE_DEF_LINT_WIDTH      10
 #define GAL_TABLE_DEF_FLT_WIDTH       10
-#define GAL_TABLE_DEF_DBL_WIDTH       15
+#define GAL_TABLE_DEF_DBL_WIDTH       18
 
 #define GAL_TABLE_DEF_INT_PRECISION   0
 #define GAL_TABLE_DEF_FLT_PRECISION   6
@@ -68,8 +68,6 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
-
-
 /* Types of table storage for input or output. */
 enum gal_table_types
 {
@@ -123,9 +121,16 @@ gal_table_string_to_type(char *string);
 int
 gal_table_string_to_searchin(char *string);
 
+void
+gal_table_col_print_info(gal_data_t *col, int tabletype, size_t *width,
+                         size_t *precision, char *fmt, char *lng);
+
+void
+gal_table_read_blank(gal_data_t *col, char *blank);
+
 gal_data_t *
-gal_table_info(char *filename, char *hdu, size_t *numcols, size_t *numrows,
-                 int *tabletype);
+gal_table_info(char *filename, char *hdu, size_t *numcols,
+               size_t *numrows, int *tabletype);
 
 gal_data_t *
 gal_table_read(char *filename, char *hdu, struct gal_linkedlist_stll *cols,
diff --git a/lib/gnuastro/txt.h b/lib/gnuastro/txt.h
index e387201..217d836 100644
--- a/lib/gnuastro/txt.h
+++ b/lib/gnuastro/txt.h
@@ -61,7 +61,6 @@ __BEGIN_C_DECLS  /* From C++ preparations */
  acts as a place holder for the blank string columns ONLY in ASCII
  tables.*/
 #define GAL_TXT_DELIMITERS     " ,\t\f\v"
-#define GAL_TXT_STRING_BLANK   "blank"
 #define GAL_TXT_MAX_FMT_LENGTH 20
 
 
diff --git a/lib/table.c b/lib/table.c
index 23e4343..b8bbb56 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -32,7 +32,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/txt.h>
 #include <gnuastro/table.h>
 
-
+#include <checkset.h>
 
 
 
@@ -120,6 +120,264 @@ gal_table_string_to_searchin(char *string)
 
 
 /************************************************************************/
+/***************          Printing information            ***************/
+/************************************************************************/
+/* Fill in the basic information necessary to print a column. This
+   information can be used for printing a plain text file or for FITS ASCII
+   tables. The `fmt' and `lng' should point to pre-allocated arrays. The
+   best way is: `char fmt[2], lng[3];' in the same function calling this.*/
+void
+gal_table_col_print_info(gal_data_t *col, int tabletype, size_t *width,
+                         size_t *precision, char *fmt, char *lng)
+{
+  size_t j;
+  int maxstrlen;
+  char **strarr;
+
+  /* First do a sanity check, so we can safly stop checking in the steps
+     below. */
+  switch(tabletype)
+    {
+    case GAL_TABLE_TYPE_TXT:
+    case GAL_TABLE_TYPE_AFITS:
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "tabletype code %d not recognized in "
+            "`set_col_print_info'", tabletype);
+    }
+
+
+
+  /* Set the formats and widths based on the type of the column. Initialize
+     the characters and blank pointer. The long prefix is not necessary for
+     most types, so just initialize it once up here.*/
+  fmt[0]=fmt[1]=lng[0]=lng[1]=lng[2]='\0';
+  switch(col->type)
+    {
+    case GAL_DATA_TYPE_BIT:
+      error(EXIT_FAILURE, 0, "printing of bit types is currently "
+            "not supported");
+      break;
+
+
+
+
+    case GAL_DATA_TYPE_STRING:
+
+      /* Set the basic information. */
+      fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 's' : 'A';
+
+      /* Go through all the strings in the column and find the maximum
+         length to use as printing. If the user asked for a larger width
+         (through the data structure's disp_width element), then set
+         that. */
+      maxstrlen=0;
+      strarr=col->array;
+      for(j=0;j<col->size;++j)
+        maxstrlen = ( strlen(strarr[j]) > maxstrlen
+                      ? strlen(strarr[j]) : maxstrlen );
+      *width = col->disp_width>maxstrlen ? col->disp_width : maxstrlen;
+      break;
+
+
+
+
+    case GAL_DATA_TYPE_UCHAR:
+    case GAL_DATA_TYPE_USHORT:
+    case GAL_DATA_TYPE_UINT:
+    case GAL_DATA_TYPE_ULONG:
+
+      /* For the FITS ASCII table, there is only one format for all
+         integers.  */
+      if(tabletype==GAL_TABLE_TYPE_AFITS)
+        fmt[0]='I';
+      else
+        switch(col->disp_fmt)
+          {
+          case GAL_TABLE_DISPLAY_FMT_UDECIMAL: fmt[0]='u'; break;
+          case GAL_TABLE_DISPLAY_FMT_OCTAL:    fmt[0]='o'; break;
+          case GAL_TABLE_DISPLAY_FMT_HEX:      fmt[0]='X'; break;
+          default:                             fmt[0]='u';
+          }
+
+      /* If we have a long type, then make changes. */
+      if(col->type==GAL_DATA_TYPE_ULONG)
+        {
+          lng[0]='l';
+          *width=( col->disp_width<=0 ? GAL_TABLE_DEF_LINT_WIDTH
+                   : col->disp_width );
+        }
+      else *width=( col->disp_width<=0 ? GAL_TABLE_DEF_INT_WIDTH
+                    : col->disp_width );
+      *precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
+                  : col->disp_precision );
+      break;
+
+
+
+
+    case GAL_DATA_TYPE_CHAR:
+    case GAL_DATA_TYPE_LOGICAL:
+    case GAL_DATA_TYPE_SHORT:
+    case GAL_DATA_TYPE_INT:
+      fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'd' : 'I';
+      *width = ( col->disp_width<=0 ? GAL_TABLE_DEF_INT_WIDTH
+                 : col->disp_width );
+      *precision = ( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
+                     : col->disp_precision );
+      break;
+
+
+
+
+    case GAL_DATA_TYPE_LONG:
+    case GAL_DATA_TYPE_LONGLONG:
+      lng[0] = 'l';
+      fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'd' : 'I';
+      lng[1] = col->type==GAL_DATA_TYPE_LONGLONG ? 'l' : '\0';
+      *width=( col->disp_width<=0 ? GAL_TABLE_DEF_LINT_WIDTH
+              : col->disp_width );
+      *precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
+                  : col->disp_precision );
+      break;
+
+
+
+    /* We need a default value (because in most cases, it won't be set. */
+    case GAL_DATA_TYPE_FLOAT:
+    case GAL_DATA_TYPE_DOUBLE:
+      switch(col->disp_fmt)
+        {
+        case GAL_TABLE_DISPLAY_FMT_FLOAT:
+          fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'f' : 'F'; break;
+        case GAL_TABLE_DISPLAY_FMT_EXP:
+          fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'e' : 'E'; break;
+        case GAL_TABLE_DISPLAY_FMT_GENERAL:
+          fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'g' : 'E'; break;
+        default:
+          fmt[0] = tabletype==GAL_TABLE_TYPE_TXT ? 'f' : 'F'; break;
+        }
+      *width = ( col->disp_width<=0
+                 ? ( col->type==GAL_DATA_TYPE_FLOAT
+                     ? GAL_TABLE_DEF_FLT_WIDTH
+                     : GAL_TABLE_DEF_DBL_WIDTH )
+                 : col->disp_width );
+      *precision = ( col->disp_precision<=0 ? GAL_TABLE_DEF_FLT_PRECISION
+                     : col->disp_precision );
+      break;
+
+
+
+    default:
+      error(EXIT_FAILURE, 0, "type code %d not recognized in "
+            "`gal_table_col_print_info'", col->type);
+    }
+}
+
+
+
+
+
+/* Use the input `blank' string and the input column to put the blank value
+   in the column's array. This function should later be generalized into a
+   function to read a string into a given data type (see
+   `gal_data_string_to_array_elem'). It is only here temporarily. */
+void
+gal_table_read_blank(gal_data_t *col, char *blank)
+{
+  double d;
+  long long L;
+  char *tailptr;
+
+  /* If there is nothing to use as blank, then don't continue, note that
+     the column data structure was initialized to mean that there is no
+     blank value. */
+  if(blank==NULL) return;
+
+  /* Allocate space to keep the blank value and initialize the dimension
+     and size parameters appropriately. */
+  errno=0;
+  col->dsize=malloc(sizeof *col->dsize);
+  if(col->dsize==NULL)
+    error(EXIT_FAILURE, 0, "%zu bytes for `col->dsize' in `txt_read_blank' ",
+          sizeof *col->dsize);
+  col->dsize[0]=col->ndim=col->size=1;
+  col->array=gal_data_malloc_array(col->type, col->size);
+
+  /* Put the blank value in depending on the type.*/
+  switch(col->type)
+    {
+
+    /* String. */
+    case GAL_DATA_TYPE_STRING:
+      gal_checkset_allocate_copy(blank, col->array);
+      break;
+
+    /* Floating point: Read it as a double or long, then put it in the
+       array. When the conversion can't be done (the string isn't a number
+       for example), then just assume no blank value was given. */
+    case GAL_DATA_TYPE_FLOAT:
+    case GAL_DATA_TYPE_DOUBLE:
+      d=strtod(blank, &tailptr);
+      if(*tailptr!='\0') { free(col->array); col->array=NULL;}
+      else
+        {
+          if(col->type==GAL_DATA_TYPE_FLOAT) *(float *) col->array=d;
+          else                              *(double *) col->array=d;
+        }
+      break;
+
+    /* Integers. */
+    default:
+      L=strtoll(blank, &tailptr, 0);
+      if(*tailptr!='\0') { free(col->array); col->array=NULL; }
+      else
+        switch(col->type)
+          {
+          case GAL_DATA_TYPE_UCHAR:   *(unsigned char *) col->array=L; break;
+          case GAL_DATA_TYPE_CHAR:             *(char *) col->array=L; break;
+          case GAL_DATA_TYPE_USHORT: *(unsigned short *) col->array=L; break;
+          case GAL_DATA_TYPE_SHORT:           *(short *) col->array=L; break;
+          case GAL_DATA_TYPE_UINT:     *(unsigned int *) col->array=L; break;
+          case GAL_DATA_TYPE_INT:               *(int *) col->array=L; break;
+          case GAL_DATA_TYPE_ULONG:   *(unsigned long *) col->array=L; break;
+          case GAL_DATA_TYPE_LONG:             *(long *) col->array=L; break;
+          case GAL_DATA_TYPE_LONGLONG:     *(LONGLONG *) col->array=L; break;
+          default:
+            error(EXIT_FAILURE, 0, "type code %d not recognized in "
+                  "`txt_str_to_blank'", col->type);
+          }
+    }
+
+  /* If the blank value couldn't be read, then set the initialized
+     lengths to zero again. */
+  if(col->array==NULL)
+    {
+      col->ndim=col->size=0;
+      free(col->dsize);
+      col->dsize=NULL;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/************************************************************************/
 /***************         Information about a table        ***************/
 /************************************************************************/
 /* Store the information of each column in a table (either as a text file
diff --git a/lib/txt.c b/lib/txt.c
index c29dfbf..7c31544 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -114,77 +114,6 @@ txt_trim_space(char *str)
 
 
 
-/* Use the input `blank' string and the input column to put the blank value
-   in the column's array. If no blank string is given, then free the
-   column's array. */
-static void
-txt_read_blank(gal_data_t *col, char *blank)
-{
-  double d;
-  long long L;
-  char *tailptr;
-
-  /* If there is nothing to use as blank, then free the array. */
-  if(blank==NULL) return;
-
-  /* Allocate space to keep the blank value. */
-  col->ndim=col->size=1;
-  col->array=gal_data_malloc_array(col->type, col->size);
-
-  /* Set the dsize variable. */
-  errno=0;
-  col->dsize=malloc(sizeof *col->dsize);
-  if(col->dsize==NULL)
-    error(EXIT_FAILURE, 0, "%zu bytes for `col->dsize' in `txt_read_blank' ",
-          sizeof *col->dsize);
-
-  /* String type. Copy the string.*/
-  if(col->type==GAL_DATA_TYPE_STRING)
-    gal_checkset_allocate_copy(blank, col->array);
-
-  /* Floating point: Read it as a double or long, then put it in the
-     array. When the conversion can't be done (the string isn't a number
-     for example), then just assume no blank value was given. */
-  else if(col->type==GAL_DATA_TYPE_FLOAT || col->type==GAL_DATA_TYPE_DOUBLE)
-    {
-      d=strtod(blank, &tailptr);
-      if(*tailptr!='\0') free(col->array);
-      else
-        {
-          if(col->type==GAL_DATA_TYPE_FLOAT) *(float *) col->array=d;
-          else                              *(double *) col->array=d;
-        }
-    }
-
-  /* Integers. */
-  else
-    {
-      L=strtoll(blank, &tailptr, 0);
-      if(*tailptr!='\0') free(col->array);
-      else
-        switch(col->type)
-          {
-          case GAL_DATA_TYPE_UCHAR:   *(unsigned char *) col->array=L; break;
-          case GAL_DATA_TYPE_CHAR:             *(char *) col->array=L; break;
-          case GAL_DATA_TYPE_USHORT: *(unsigned short *) col->array=L; break;
-          case GAL_DATA_TYPE_SHORT:           *(short *) col->array=L; break;
-          case GAL_DATA_TYPE_UINT:     *(unsigned int *) col->array=L; break;
-          case GAL_DATA_TYPE_INT:               *(int *) col->array=L; break;
-          case GAL_DATA_TYPE_ULONG:   *(unsigned long *) col->array=L; break;
-          case GAL_DATA_TYPE_LONG:             *(long *) col->array=L; break;
-          case GAL_DATA_TYPE_LONGLONG:     *(LONGLONG *) col->array=L; break;
-          default:
-            error(EXIT_FAILURE, 0, "type code %d not recognized in "
-                  "`txt_str_to_blank'", col->type);
-          }
-    }
-}
-
-
-
-
-
-
 /* Each column information comment should have a format like this:
 
       # Column N: NAME [UNITS, TYPE, BLANK] COMMENT
@@ -313,7 +242,7 @@ txt_info_from_comment(char *line, gal_data_t **colsll)
 
       /* Write the blank value into the array. Note that this is not the
          final column, we are just collecting information now. */
-      txt_read_blank(*colsll, txt_trim_space(blank));
+      gal_table_read_blank(*colsll, txt_trim_space(blank));
     }
 }
 
@@ -608,7 +537,8 @@ txt_fill_columns(char *line, char **tokens, size_t 
maxcolnum,
               && !strcmp( *strb, str[lineind] ) )
             {
               free(str[lineind]);
-              str[lineind]=GAL_DATA_BLANK_STRING;
+              gal_checkset_allocate_copy(GAL_DATA_BLANK_STRING,
+                                         &str[lineind]);
             }
           break;
 
@@ -821,12 +751,11 @@ static char **
 make_fmts_for_printf(gal_data_t *cols, size_t numcols, int leftadjust,
                      size_t *len)
 {
+  size_t i=0;
   char **fmts;
-  size_t i=0, j;
   gal_data_t *col;
-  char *fmt=NULL, *lng, **strarr;
-  char bfmt[GAL_TXT_MAX_FMT_LENGTH];
-  int width=0, precision=0, maxstrlen;
+  char fmt[2], lng[3];
+  size_t width, precision;
 
 
   /* Allocate space for the output. */
@@ -844,10 +773,6 @@ make_fmts_for_printf(gal_data_t *cols, size_t numcols, int 
leftadjust,
   /* Go over all the columns and make their formats. */
   for(col=cols;col!=NULL;col=col->next)
     {
-      /* Initialize */
-      lng="";
-
-
       /* First allocate the necessary space to keep the string. */
       errno=0;
       fmts[ i*FMTS_COLS   ] = malloc(GAL_TXT_MAX_FMT_LENGTH*sizeof **fmts);
@@ -859,183 +784,31 @@ make_fmts_for_printf(gal_data_t *cols, size_t numcols, 
int leftadjust,
               i*FMTS_COLS, i*FMTS_COLS+1);
 
 
-      /* Write the proper format. */
-      switch(col->type)
+      /* If we have a blank value, get the blank value as a string and
+         adjust the width */
+      if(gal_data_has_blank(col)==0)
+        fmts[i*FMTS_COLS+2]=NULL;
+      else
         {
-
-
-
-        case GAL_DATA_TYPE_BIT:
-          error(EXIT_FAILURE, 0, "printing of bit types is currently "
-                "not supported");
-          break;
-
-
-
-        case GAL_DATA_TYPE_STRING:
-          /* Set the basic information. */
-          fmt="s";
-
-          /* If `disp_width' was not set (is negative), go through all the
-             strings in the column and find the maximum length to use as
-             printing width when no value was given. */
-          if(col->disp_width<=0)
-            {
-              maxstrlen=-1;
-              strarr=col->array;
-              for(j=0;j<col->size;++j)
-                if(strarr[j])
-                  maxstrlen = ( strlen(strarr[j]) > maxstrlen
-                                ? strlen(strarr[j])
-                                : maxstrlen );
-              width = maxstrlen==-1 ? GAL_TABLE_DEF_STR_WIDTH : maxstrlen ;
-            }
+          /* Set the blank value. */
+          if(col->type==GAL_DATA_TYPE_STRING)
+            gal_checkset_allocate_copy(GAL_DATA_BLANK_STRING,
+                                       &fmts[i*FMTS_COLS+2]);
           else
-            width = col->disp_width;
-          break;
-
-
-
-        case GAL_DATA_TYPE_UCHAR:
-        case GAL_DATA_TYPE_USHORT:
-        case GAL_DATA_TYPE_UINT:
-        case GAL_DATA_TYPE_ULONG:
-
-          /* Set the final printing format. */
-          switch(col->disp_fmt)
-            {
-            case GAL_TABLE_DISPLAY_FMT_UDECIMAL: fmt="u"; break;
-            case GAL_TABLE_DISPLAY_FMT_OCTAL:    fmt="o"; break;
-            case GAL_TABLE_DISPLAY_FMT_HEX:      fmt="X"; break;
-            default:                             fmt="u";
-            }
-
-          /* If we have a long type, then make changes. */
-          if(col->type==GAL_DATA_TYPE_ULONG)
-            {
-              lng="l";
-              width=( col->disp_width<=0 ? GAL_TABLE_DEF_LINT_WIDTH
-                      : col->disp_width );
-            }
-          else width=( col->disp_width<=0 ? GAL_TABLE_DEF_INT_WIDTH
-                       : col->disp_width );
-          precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
-                      : col->disp_precision );
-          break;
-
-
-
-        case GAL_DATA_TYPE_CHAR:
-        case GAL_DATA_TYPE_LOGICAL:
-        case GAL_DATA_TYPE_SHORT:
-        case GAL_DATA_TYPE_INT:
-          fmt="d";
-          width=( col->disp_width<=0 ? GAL_TABLE_DEF_INT_WIDTH
-                  : col->disp_width );
-          precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
-                      : col->disp_precision );
-          break;
-
-
-
-        case GAL_DATA_TYPE_LONG:
-        case GAL_DATA_TYPE_LONGLONG:
-          fmt="d";
-          lng = col->type==GAL_DATA_TYPE_LONG ? "l" : "ll";
-          width=( col->disp_width<=0 ? GAL_TABLE_DEF_LINT_WIDTH
-                  : col->disp_width );
-          precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_INT_PRECISION
-                      : col->disp_precision );
-          break;
-
-
-
-        case GAL_DATA_TYPE_FLOAT:
-        case GAL_DATA_TYPE_DOUBLE:
-          switch(col->disp_fmt)
-            {
-            case GAL_TABLE_DISPLAY_FMT_FLOAT:    fmt="f"; break;
-            case GAL_TABLE_DISPLAY_FMT_EXP:      fmt="e"; break;
-            case GAL_TABLE_DISPLAY_FMT_GENERAL:  fmt="g"; break;
-            default:                             fmt="f";
-            }
-          width=( col->disp_width<=0
-                  ? ( col->type==GAL_DATA_TYPE_FLOAT
-                      ? GAL_TABLE_DEF_FLT_WIDTH
-                      : GAL_TABLE_DEF_DBL_WIDTH )
-                  : col->disp_width );
-          precision=( col->disp_precision<=0 ? GAL_TABLE_DEF_FLT_PRECISION
-                      : col->disp_precision );
-          break;
-
-
-
-        default:
-          error(EXIT_FAILURE, 0, "type code %d not recognized for output "
-                "column %zu (counting from 1)", col->type, i+1);
+            fmts[i*FMTS_COLS+2]=gal_data_blank_as_string(col->type);
         }
 
 
-      /* Print the blank value if there is any blank values in this
-         column. */
-      if(gal_data_has_blank(col))
-        {
-          sprintf(bfmt, "%%%s%s", lng, fmt);
-          switch(col->type)
-            {
-            case GAL_DATA_TYPE_STRING:
-              gal_checkset_allocate_copy(GAL_TXT_STRING_BLANK,
-                                         &fmts[i*FMTS_COLS+2]);
-              break;
-            case GAL_DATA_TYPE_UCHAR:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (unsigned char)GAL_DATA_BLANK_UCHAR);
-              break;
-            case GAL_DATA_TYPE_CHAR:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (char)GAL_DATA_BLANK_CHAR);
-              break;
-            case GAL_DATA_TYPE_USHORT:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (unsigned short)GAL_DATA_BLANK_USHORT);
-              break;
-            case GAL_DATA_TYPE_SHORT:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (short)GAL_DATA_BLANK_SHORT);
-              break;
-            case GAL_DATA_TYPE_UINT:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (unsigned int)GAL_DATA_BLANK_UINT);
-              break;
-            case GAL_DATA_TYPE_INT:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (int)GAL_DATA_BLANK_INT);
-              break;
-            case GAL_DATA_TYPE_ULONG:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (unsigned long)GAL_DATA_BLANK_ULONG);
-              break;
-            case GAL_DATA_TYPE_LONG:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (long)GAL_DATA_BLANK_LONG);
-              break;
-            case GAL_DATA_TYPE_LONGLONG:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt,
-                       (LONGLONG)GAL_DATA_BLANK_LONGLONG);
-              break;
-            case GAL_DATA_TYPE_FLOAT:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt, GAL_DATA_BLANK_FLOAT);
-              break;
-            case GAL_DATA_TYPE_DOUBLE:
-              asprintf(&fmts[i*FMTS_COLS+2], bfmt, GAL_DATA_BLANK_DOUBLE);
-              break;
-            }
+      /* Fill in the printing paramters. */
+      gal_table_col_print_info(col, GAL_TABLE_TYPE_TXT, &width,
+                               &precision, fmt, lng);
 
-          /* Adjust the width based on the blank value. */
-          width = ( strlen(fmts[i*FMTS_COLS+2]) > width
-                    ? strlen(fmts[i*FMTS_COLS+2]) : width );
-        }
-      else fmts[i*FMTS_COLS+2]=NULL;
+
+      /* Adjust the width if a blank string was defined. */
+      if(fmts[i*FMTS_COLS+2])
+        width = ( strlen(fmts[i*FMTS_COLS+2])>width
+                  ? strlen(fmts[i*FMTS_COLS+2])
+                  : width );
 
 
       /* Print the result into the allocated string and add its length to
@@ -1043,18 +816,18 @@ make_fmts_for_printf(gal_data_t *cols, size_t numcols, 
int leftadjust,
          end of `fmts[i*2]' is to ensure that the columns don't merge, even
          if the printed string is larger than the expected width. */
       if(precision<=0)
-        *len += 1 + sprintf(fmts[i*FMTS_COLS], "%%%s%d.%d%s%s ",
+        *len += 1 + sprintf(fmts[i*FMTS_COLS], "%%%s%zu.%zu%s%s ",
                             leftadjust ? "-" : "", width, precision,
                             lng, fmt);
       else
-        *len += 1 + sprintf(fmts[i*FMTS_COLS], "%%%s%d%s%s ",
+        *len += 1 + sprintf(fmts[i*FMTS_COLS], "%%%s%zu%s%s ",
                             leftadjust ? "-" : "", width, lng, fmt);
 
 
       /* Set the string for the Gnuastro type. For strings, we also need to
          write the maximum number of characters.*/
       if(col->type==GAL_DATA_TYPE_STRING)
-        sprintf(fmts[i*FMTS_COLS+1], "%s%d",
+        sprintf(fmts[i*FMTS_COLS+1], "%s%zu",
                 gal_data_type_as_string(col->type, 0), width);
       else
         strcpy(fmts[i*FMTS_COLS+1], gal_data_type_as_string(col->type, 0));
@@ -1173,10 +946,12 @@ gal_txt_table_write(gal_data_t *cols, char *comment, 
char *filename,
               fprintf(fp, fmts[j*FMTS_COLS],
                       ((unsigned char *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_CHAR:
             case GAL_DATA_TYPE_LOGICAL:
               fprintf(fp, fmts[j*FMTS_COLS], ((char *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_USHORT:
               fprintf(fp, fmts[j*FMTS_COLS],
                       ((unsigned short *)col->array)[i]);
@@ -1184,31 +959,28 @@ gal_txt_table_write(gal_data_t *cols, char *comment, 
char *filename,
             case GAL_DATA_TYPE_SHORT:
               fprintf(fp, fmts[j*FMTS_COLS], ((short *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_UINT:
               fprintf(fp, fmts[j*FMTS_COLS], ((unsigned int *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_INT:
               fprintf(fp, fmts[j*FMTS_COLS], ((int *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_ULONG:
               fprintf(fp, fmts[j*FMTS_COLS],
                       ((unsigned long *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_LONG:
               fprintf(fp, fmts[j*FMTS_COLS], ((long *)col->array)[i]);
               break;
+
             case GAL_DATA_TYPE_LONGLONG:
               fprintf(fp, fmts[j*FMTS_COLS], ((LONGLONG *)col->array)[i]);
               break;
 
-            /* Special consideration (string, float, double). */
-            case GAL_DATA_TYPE_STRING:
-              if(((char **)col->array)[i])
-                fprintf(fp, fmts[j*FMTS_COLS], ((char **)col->array)[i]);
-              else
-                fprintf(fp, "%-*s ", col->disp_width, GAL_TXT_STRING_BLANK);
-              break;
-
             case GAL_DATA_TYPE_FLOAT:
               fprintf(fp, fmts[j*FMTS_COLS], ((float *)col->array)[i]);
               break;
@@ -1216,6 +988,15 @@ gal_txt_table_write(gal_data_t *cols, char *comment, char 
*filename,
             case GAL_DATA_TYPE_DOUBLE:
               fprintf(fp, fmts[j*FMTS_COLS], ((double *)col->array)[i]);
               break;
+
+            /* Special consideration for strings. */
+            case GAL_DATA_TYPE_STRING:
+              if( !strcmp( ((char **)col->array)[i], GAL_DATA_BLANK_STRING ) )
+                fprintf(fp, "%-*s ", col->disp_width, GAL_DATA_BLANK_STRING);
+              else
+                fprintf(fp, fmts[j*FMTS_COLS], ((char **)col->array)[i]);
+              break;
+
             default:
               error(EXIT_FAILURE, 0, "type code %d not recognized for "
                     "col->type in `gal_txt_write'", col->type);



reply via email to

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