gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 685bf31: Arithmetic: new tofile- operator to w


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 685bf31: Arithmetic: new tofile- operator to write the top operand to a file
Date: Wed, 16 Jan 2019 14:03:09 -0500 (EST)

branch: master
commit 685bf3159e73995fc1871c265b80af9b6223e8b3
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    Arithmetic: new tofile- operator to write the top operand to a file
    
    Until now, as the Arithmetic command got more and more complicated, there
    it was relatively hard to check the progress and debug/understand
    undexpected outputs.
    
    With this commit, a new `tofile-AAA' operator is added to Arithmetic. It
    can be placed anywhere in between the operands and operators to check the
    top operand.
---
 NEWS                             |  6 ++++
 bin/arithmetic/arithmetic.c      | 47 ++++++++++++++++++++++++-----
 bin/arithmetic/main.h            |  6 ++--
 bin/arithmetic/operands.c        |  4 +--
 bin/arithmetic/ui.c              | 65 ++++++++++++++++++++++++++--------------
 doc/gnuastro.texi                | 19 ++++++++++++
 lib/checkset.c                   | 55 +++++++++++++++++++++++++++++++++-
 lib/fits.c                       |  4 +++
 lib/gnuastro-internal/checkset.h |  3 ++
 lib/jpeg.c                       | 10 ++++++-
 10 files changed, 182 insertions(+), 37 deletions(-)

diff --git a/NEWS b/NEWS
index 5389881..d2f3afe 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,12 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
      debugging (finding which configuration file is responsible for a given
      option's value).
 
+  Arithmetic:
+   - The new `tofile-' operator will save the top operand into a file
+     without changing the state of the operand stack (popping the top
+     element). This can greatly help in debugging/understanding an
+     Arithmetic command, especially as it gets longer, or more complicated.
+
   Fits:
    - Add "title" to group FITS keywords with `--write=/,"title name". This
      "title" is composed of two keyword records: a blank one, followed by
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index 595c28e..d3b5dc9 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -804,6 +804,32 @@ arithmetic_collapse(struct arithmeticparams *p, char 
*token, int operator)
 
 
 
+void
+arithmetic_tofile(struct arithmeticparams *p, char *token)
+{
+  /* Pop the top dataset. */
+  gal_data_t *popped = operands_pop(p, token);
+  char *filename=&token[ OPERATOR_PREFIX_LENGTH_TOFILE ];
+
+  /* Save it to a file. */
+  popped->wcs=p->refdata.wcs;
+  if(popped->ndim==1 && p->onedasimage==0)
+    gal_table_write(popped, NULL, p->cp.tableformat, filename,
+                    "ARITHMETIC", 0);
+  else
+    gal_fits_img_write(popped, filename, NULL, PROGRAM_NAME);
+  if(!p->cp.quiet)
+    printf(" - Write: %s\n", filename);
+
+  /* Reset the WCS to NULL and put it back on the stack. */
+  popped->wcs=NULL;
+  operands_add(p, NULL, popped);
+}
+
+
+
+
+
 
 
 
@@ -848,17 +874,22 @@ reversepolish(struct arithmeticparams *p)
   /* Go over each input token and do the work. */
   for(token=p->tokens;token!=NULL;token=token->next)
     {
-      /* If we have a name or number, then add it to the operands linked
-         list. Otherwise, pull out two members and do the specified
-         operation on them. */
-      if( gal_array_name_recognized(token->v)
+      /* The `tofile-' operator's string can end in a `.fits', similar to a
+         FITS file input file. So, it needs to be checked before checking
+         for a filename. If we have a name or number, then add it to the
+         operands linked list. Otherwise, pull out two members and do the
+         specified operation on them. */
+      if( !strncmp(OPERATOR_PREFIX_TOFILE, token->v,
+                   OPERATOR_PREFIX_LENGTH_TOFILE) )
+        arithmetic_tofile(p, token->v);
+      else if( !strncmp(token->v, OPERATOR_PREFIX_SET,
+                        OPERATOR_PREFIX_LENGTH_SET) )
+        operands_set_name(p, token->v);
+      else if( gal_array_name_recognized(token->v)
           || operands_is_name(p, token->v) )
         operands_add(p, token->v, NULL);
       else if( (d1=gal_data_copy_string_to_number(token->v)) )
         operands_add(p, NULL, d1);
-      else if( !strncmp(token->v, SET_OPERATOR_PREFIX,
-                        SET_OPERATOR_PREFIX_LENGTH) )
-        operands_set_name(p, token->v);
       else
         {
 
@@ -1186,7 +1217,7 @@ reversepolish(struct arithmeticparams *p)
       else
         gal_fits_img_write(d1, p->cp.output, NULL, PROGRAM_NAME);
       if(!p->cp.quiet)
-        printf(" - Output written to %s\n", p->cp.output);
+        printf(" - Write (final): %s\n", p->cp.output);
     }
 
 
diff --git a/bin/arithmetic/main.h b/bin/arithmetic/main.h
index 2d30c32..73110d8 100644
--- a/bin/arithmetic/main.h
+++ b/bin/arithmetic/main.h
@@ -40,8 +40,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Constants: */
 #define NEG_DASH_REPLACE 11 /* Vertical tab (ASCII=11) for negative dash */
-#define SET_OPERATOR_PREFIX        "set-"
-#define SET_OPERATOR_PREFIX_LENGTH strlen(SET_OPERATOR_PREFIX)
+#define OPERATOR_PREFIX_SET               "set-"
+#define OPERATOR_PREFIX_TOFILE            "tofile-"
+#define OPERATOR_PREFIX_LENGTH_SET        strlen(OPERATOR_PREFIX_SET)
+#define OPERATOR_PREFIX_LENGTH_TOFILE     strlen(OPERATOR_PREFIX_TOFILE)
 
 
 
diff --git a/bin/arithmetic/operands.c b/bin/arithmetic/operands.c
index 580b8b7..dc4f31c 100644
--- a/bin/arithmetic/operands.c
+++ b/bin/arithmetic/operands.c
@@ -141,7 +141,7 @@ void
 operands_set_name(struct arithmeticparams *p, char *token)
 {
   gal_data_t *tmp, *tofree;
-  char *varname=&token[ SET_OPERATOR_PREFIX_LENGTH ];
+  char *varname=&token[ OPERATOR_PREFIX_LENGTH_SET ];
 
   /* If a dataset with this name already exists, it will be removed/deleted
      so we can use the name for the newly designated dataset. */
@@ -387,7 +387,7 @@ operands_pop(struct arithmeticparams *p, char *operator)
         }
 
       /* Report the read image if desired: */
-      if(!p->cp.quiet) printf(" - %s (hdu %s) is read.\n", filename, hdu);
+      if(!p->cp.quiet) printf(" - Read: %s (hdu %s).\n", filename, hdu);
 
       /* Free the HDU string: */
       if(hdu) free(hdu);
diff --git a/bin/arithmetic/ui.c b/bin/arithmetic/ui.c
index bf41d96..8b84588 100644
--- a/bin/arithmetic/ui.c
+++ b/bin/arithmetic/ui.c
@@ -230,9 +230,10 @@ parse_opt(int key, char *arg, struct argp_state *state)
 static void
 ui_check_options_and_arguments(struct arithmeticparams *p)
 {
+  char *filename;
   int output_checked=0;
-  size_t nummultiext=0, numhdus=0;
   gal_list_str_t *token, *hdu;
+  size_t nummultiext=0, numhdus=0;
 
   /* First, make sure that any tokens are actually given. */
   if(p->tokens==NULL)
@@ -256,33 +257,51 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
      list. */
   for(token=p->tokens; token!=NULL; token=token->next)
     {
-      /* This token is a file, count how many mult-extension files we haev
-         and use the first to set the output filename (if it has not been
-         set). */
-      if( gal_array_name_recognized(token->v) )
-        {
-          /* Increment the counter for FITS files. */
-          if( gal_array_name_recognized_multiext(token->v) )
-            ++nummultiext;
+      /* Strings given to the `tofile' operator are also considered as
+         outputs and we should delete them before starting the parse. */
+      if( strncmp(OPERATOR_PREFIX_TOFILE, token->v,
+                  OPERATOR_PREFIX_LENGTH_TOFILE) )
 
-          /* If the output filename isn't set yet, then set it. */
-          if(output_checked==0)
+        {
+          /* This token is a file, count how many mult-extension files we
+             haev and use the first to set the output filename (if it has
+             not been set). */
+          if( gal_array_name_recognized(token->v) )
             {
-              if(p->cp.output)
-                gal_checkset_writable_remove(p->cp.output, 0,
-                                             p->cp.dontdelete);
-              else
-                p->cp.output=gal_checkset_automatic_output(&p->cp, token->v,
-                                                           "_arith.fits");
-              output_checked=1;
+              /* Increment the counter for FITS files (if they are
+                 input). Recall that the `tofile' operator can also have
+                 `.fits' suffixes (they are the names of the output
+                 files). */
+              if( gal_array_name_recognized_multiext(token->v)  )
+                ++nummultiext;
+
+              /* If the output filename isn't set yet, then set it. */
+              if(output_checked==0)
+                {
+                  if(p->cp.output)
+                    gal_checkset_writable_remove(p->cp.output, 0,
+                                                 p->cp.dontdelete);
+                  else
+                    p->cp.output=gal_checkset_automatic_output(&p->cp,
+                                                               token->v,
+                                                               "_arith.fits");
+                  output_checked=1;
+                }
             }
+
+          /* This token is a number. Check if a negative dash was present that
+             has been temporarily replaced with `NEG_DASH_REPLACE' before
+             option parsing. */
+          else if(token->v[0]==NEG_DASH_REPLACE && isdigit(token->v[1]) )
+            token->v[0]='-';
         }
 
-      /* This token is a number. Check if a negative dash was present that
-         has been temporarily replaced with `NEG_DASH_REPLACE' before
-         option parsing. */
-      else if(token->v[0]==NEG_DASH_REPLACE && isdigit(token->v[1]) )
-        token->v[0]='-';
+      /* We are on the `tofile' operator. */
+      else
+        {
+          filename=&token->v[ OPERATOR_PREFIX_LENGTH_TOFILE ];
+          gal_checkset_writable_remove(filename, 0, p->cp.dontdelete);
+        }
     }
 
   /* Count the number of HDU values (if globalhdu isn't given) and check if
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 5af6ba7..371ef36 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -12732,6 +12732,25 @@ But with this operator you can simply give 
@file{image.fits} the name
 $ astarithmetic image.fits set-i   i i 5 gt nan where
 @end example
 
address@hidden tofile-AAA
+Write the top oprand on the operands stack into a file called @code{AAA}
+(can be any FITS file name) without changing the operands stack (no operand
+will be operand).
+
+Any file that is given to this operator is deleted before Arithmetic
+actually starts working on the input datasets. The deletion can be
+deactivated with the @option{--dontdelete} option (as in all Gnuastro
+programs, see @ref{Input output options}). If the same FITS file is given
+to this operator multiple times, it will contain multiple extensions (in
+the same order that it was called.
+
+For example the operator @command{tofile-check.fits} will write the top
+operand to @file{check.fits}. Since it doesn't modify the operands stack,
+this operator is very convenient when you want to debug, or understanding,
+a string of operators and operands given to Arithmetic: simply put
address@hidden anywhere in the process to see what is happening
+behind the scenes without modifying the overall process.
+
 @end table
 
 @cartouche
diff --git a/lib/checkset.c b/lib/checkset.c
index 324e4c5..0cc2098 100644
--- a/lib/checkset.c
+++ b/lib/checkset.c
@@ -292,7 +292,7 @@ gal_checkset_not_dir_part(char *filename)
 
 
 
-/* Check if a file exists and report if it doesn't: */
+/* Check if a file exists and report if it doesn't. */
 void
 gal_checkset_check_file(char *filename)
 {
@@ -334,6 +334,59 @@ gal_checkset_check_file_return(char *filename)
 
 
 
+/* If a file doesn't exist and its directory is writable, return
+   1. Otherwise, return 0. */
+int
+gal_checkset_writable_notexist(char *filename)
+{
+  int out=1;
+  char *dir;
+  FILE *tmpfile;
+
+  /* If the filename is `NULL' everything is ok (it doesn't exist)! In some
+     cases, a NULL filename is interpretted to mean standard output. */
+  if(filename==NULL) return 1;
+
+  /* We want to make sure that we can open and write to this file. But
+     the user might have asked to not delete the file, so the
+     contents should not be changed. Therefore we have to open it with
+     `r+`. */
+  errno=0;
+  tmpfile=fopen(filename, "r+");
+  if (tmpfile)                        /* The file opened. */
+    {
+      /* Close the file. */
+      errno=0;
+      if(fclose(tmpfile))
+        error(EXIT_FAILURE, errno, "%s", filename);
+
+      /* The file exists, return 0. */
+      out=0;
+    }
+
+  /* If the file doesn't exist, we just need to make sure if we have write
+     permissions to its host directory. */
+  else
+    {
+      /* Separate the directory part of the filename. */
+      dir=gal_checkset_dir_part(filename);
+
+      /* See if this directory is writable by this user. */
+      errno=0;
+      if( access(dir, W_OK) ) out=0;
+
+      /* Clean up. */
+      free(dir);
+    }
+
+  /* Return the final value. */
+  return out;
+}
+
+
+
+
+
 /* Check if a file exists and can be opened. If the `keep' value is
    non-zero, then the file will remain untouched, otherwise, it will be
    deleted (since most programs need to make a clean output). When the file
diff --git a/lib/fits.c b/lib/fits.c
index 671b828..994a619 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -1833,6 +1833,10 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
   int nkeyrec, hasblank, status=0, datatype=0;
   gal_data_t *i64data, *towrite, *block=gal_tile_block(input);
 
+  /* Small sanity check. */
+  if( gal_fits_name_is_fits(filename)==0 )
+    error(EXIT_FAILURE, 0, "%s: not a FITS suffix", filename);
+
   /* If the input is a tile (isn't a contiguous region of memory), then
      copy it into a contiguous region. */
   towrite = input==block ? input : gal_data_copy(input);
diff --git a/lib/gnuastro-internal/checkset.h b/lib/gnuastro-internal/checkset.h
index 3d5a16e..35e1085 100644
--- a/lib/gnuastro-internal/checkset.h
+++ b/lib/gnuastro-internal/checkset.h
@@ -104,6 +104,9 @@ gal_checkset_check_file(char *filename);
 int
 gal_checkset_check_file_return(char *filename);
 
+int
+gal_checkset_writable_notexist(char *filename);
+
 void    /* keep==0 && dontdelete==0: file will be deleted if exists.*/
 gal_checkset_writable_remove(char *filename, int keep, int dontdelete);
 
diff --git a/lib/jpeg.c b/lib/jpeg.c
index c7c5151..34dc71b 100644
--- a/lib/jpeg.c
+++ b/lib/jpeg.c
@@ -35,7 +35,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/list.h>
 #include <gnuastro/jpeg.h>
 
-
+#include <gnuastro-internal/checkset.h>
 
 
 
@@ -404,6 +404,14 @@ gal_jpeg_write(gal_data_t *in, char *filename, uint8_t 
quality,
     error(EXIT_FAILURE, 0, "%s: input has a `%s' type, but JPEG images can "
           "only have a `uint8' type", __func__, gal_type_name(in->type, 1));
 
+  /* Make sure the file doesn't exist and that we have write
+     permission. Note that the JPEG standard doesn't have multple
+     extensions.*/
+  if( gal_checkset_writable_notexist(filename)==0 )
+    error(EXIT_FAILURE, 0, "%s: already exists or its directory doesn't "
+          "write permssion. Note that the JPEG standard only allows one "
+          "image per file", filename);
+
   /* Make sure the JSAMPLE is 8bits, then allocate the necessary space
      based on the number of channels. */
   if(sizeof *jsr!=1)



reply via email to

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