"Fossies" - the Fresh Open Source Software Archive

Member "gnuastro-0.8/lib/checkset.c" (4 Nov 2018, 16527 Bytes) of package /linux/privat/gnuastro-0.8.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "checkset.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.7_vs_0.8.

    1 /*********************************************************************
    2 Functions to check and set command line argument values and files.
    3 This is part of GNU Astronomy Utilities (Gnuastro) package.
    4 
    5 Original author:
    6      Mohammad Akhlaghi <mohammad@akhlaghi.org>
    7 Contributing author(s):
    8 Copyright (C) 2015-2018, Free Software Foundation, Inc.
    9 
   10 Gnuastro is free software: you can redistribute it and/or modify it
   11 under the terms of the GNU General Public License as published by the
   12 Free Software Foundation, either version 3 of the License, or (at your
   13 option) any later version.
   14 
   15 Gnuastro is distributed in the hope that it will be useful, but
   16 WITHOUT ANY WARRANTY; without even the implied warranty of
   17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   18 General Public License for more details.
   19 
   20 You should have received a copy of the GNU General Public License
   21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
   22 **********************************************************************/
   23 #include <config.h>
   24 
   25 #include <stdio.h>
   26 #include <errno.h>
   27 #include <error.h>
   28 #include <stdlib.h>
   29 #include <string.h>
   30 #include <unistd.h>
   31 #include <sys/stat.h>
   32 
   33 #include <gnuastro/data.h>
   34 
   35 #include <gnuastro-internal/timing.h>
   36 #include <gnuastro-internal/checkset.h>
   37 
   38 
   39 
   40 
   41 
   42 
   43 
   44 
   45 
   46 /**************************************************************/
   47 /**********               Environment              ************/
   48 /**************************************************************/
   49 /* The GSL random number generator (RNG) reads values from the
   50    environment. This function is designed to make the job easier for any
   51    program using GSL's RNG. If the user doesn't want to set the */
   52 gsl_rng *
   53 gal_checkset_gsl_rng(uint8_t envseed_bool, const char **name,
   54                      unsigned long int *seed)
   55 {
   56   gsl_rng *rng;
   57 
   58   /* Let GSL read the environment and convert the type name (as string) to
   59      `gsl_rng_type'. After this function, `gsl_rng_default' contains the
   60      generator's type and `gsl_rng_default_seed' contains the (possibly)
   61      given seed.*/
   62   gsl_rng_env_setup();
   63 
   64   /* Allocate the random number generator based on the requested type and
   65      save its name. If no `GSL_RNG_TYPE' is set, then use a fixed
   66      generator.*/
   67   rng=gsl_rng_alloc(secure_getenv("GSL_RNG_TYPE")
   68                     ? gsl_rng_default
   69                     : gsl_rng_ranlxs1);
   70   *name = gsl_rng_name(rng);
   71 
   72   /* Initialize the random number generator, depending on the
   73      `envseed_bool' argument. */
   74   *seed = ( envseed_bool
   75             ? gsl_rng_default_seed
   76             : gal_timing_time_based_rng_seed() );
   77   gsl_rng_set(rng, *seed);
   78 
   79   /* Return the GSL RNG structure. */
   80   return rng;
   81 }
   82 
   83 
   84 
   85 
   86 
   87 
   88 
   89 
   90 
   91 
   92 
   93 
   94 
   95 
   96 
   97 
   98 
   99 
  100 
  101 
  102 /**************************************************************/
  103 /**********          My String functions:          ************/
  104 /**************************************************************/
  105 int
  106 gal_checkset_string_has_space(char *in)
  107 {
  108   do
  109     switch(*in)
  110       {
  111       case ' ': case '\t': case '\v':
  112         return 1;
  113       }
  114   while(*(++in)!='\0');
  115   return 0;
  116 }
  117 
  118 
  119 
  120 
  121 
  122 char *
  123 gal_checkset_malloc_cat(char *inname, char *toappend)
  124 {
  125   char *out;
  126   size_t inl, apl;
  127 
  128   inl=strlen(inname);
  129   apl=strlen(toappend);
  130 
  131   errno=0;
  132   out=malloc(inl+apl+1);
  133   if(out==NULL)
  134     error(EXIT_FAILURE, errno, "%s: allocating %zu bytes", __func__,
  135           inl+apl+1);
  136 
  137   strcpy(out, inname);
  138   strcat(out, toappend);
  139   return out;
  140 }
  141 
  142 
  143 
  144 
  145 /* Copy the input string to the output (and also allocate the
  146    output. */
  147 void
  148 gal_checkset_allocate_copy(const char *arg, char **copy)
  149 {
  150   if(arg)
  151     {
  152       errno=0;
  153       *copy=malloc(strlen(arg)+1);
  154       if(*copy==NULL)
  155         error(EXIT_FAILURE, errno, "%s: %zu bytes to copy %s", __func__,
  156               strlen(arg)+1, arg);
  157       strcpy(*copy, arg);
  158     }
  159   else
  160     *copy=NULL;
  161 }
  162 
  163 
  164 
  165 
  166 /* This function is mainly for reading in the arguments (from the
  167    command line or configuration files) that need to be copied. The
  168    set argument is for making sure that it has not already been set
  169    before, see the main.h files of any program. */
  170 void
  171 gal_checkset_allocate_copy_set(char *arg, char **copy, int *set)
  172 {
  173   /* Incase *set==1, then you shouldn't do anything, just return. */
  174   if(*set) return;
  175 
  176   /* The variable was not copied, copy it: */
  177   errno=0;
  178   *copy=malloc(strlen(arg)+1);
  179   if(*copy==NULL)
  180     error(EXIT_FAILURE, errno, "%s: %zu bytes to copy %s", __func__,
  181           strlen(arg)+1, arg);
  182   strcpy(*copy, arg);
  183   *set=1;
  184 }
  185 
  186 
  187 
  188 
  189 
  190 /* The dataset may be alone in a file (for example a table in a text file)
  191    or it may an extension of a FITS file. In error messages in particular,
  192    we need to differentiate between the two. This function will check the
  193    filename and if it is FITS, it will return a string with the filename
  194    and HDU in parenthesis. If it isn't a FITS file, it will only return the
  195    filename. Note that the output needs to be freed, although when used in
  196    an error message, you can leave it to the system to free the
  197    space. There is no problem. */
  198 char *
  199 gal_checkset_dataset_name(char *filename, char *hdu)
  200 {
  201   char *out;
  202   if( gal_fits_name_is_fits(filename) )
  203     {
  204       if( asprintf(&out, "%s (hdu %s)", filename, hdu)<0 )
  205         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
  206     }
  207   else
  208     gal_checkset_allocate_copy(filename, &out);
  209   return out;
  210 }
  211 
  212 
  213 
  214 
  215 
  216 
  217 
  218 
  219 
  220 
  221 
  222 
  223 
  224 
  225 
  226 
  227 
  228 
  229 
  230 /**************************************************************/
  231 /********** Set file names and check if they exist ************/
  232 /**************************************************************/
  233 /* Given a filename, this function will separate its directory name
  234    part. */
  235 char *
  236 gal_checkset_dir_part(char *filename)
  237 {
  238   char *out;
  239   size_t i, l=strlen(filename);
  240 
  241   /* Find the first slash from the end. */
  242   for(i=l;i!=0;--i)
  243     if(filename[i]=='/')
  244       break;
  245 
  246   /* If there was no slash, then the current directory should be
  247      given: */
  248   if(i==0 && filename[0]!='/')
  249     gal_checkset_allocate_copy("./", &out);
  250   else
  251     {
  252       gal_checkset_allocate_copy(filename, &out);
  253       out[i+1]='\0';
  254     }
  255 
  256   return out;
  257 }
  258 
  259 
  260 
  261 
  262 
  263 /* Given a file name, keep the non-directory part. Note that if there
  264    is no forward slash in the input name, the full input name is
  265    considered to be the notdir output.*/
  266 char *
  267 gal_checkset_not_dir_part(char *filename)
  268 {
  269   size_t i, l;
  270   char *out, *tmp=filename;
  271 
  272   /* Find the first `/' to identify the directory */
  273   l=strlen(filename);
  274   for(i=l;i!=0;--i)
  275     if(filename[i]=='/')
  276       { tmp=&filename[i+1]; break; }
  277 
  278   /* Get the length of the notdir name: */
  279   l=strlen(tmp);
  280   errno=0;
  281   out=malloc((l+1)*sizeof *out);
  282   if(out==NULL)
  283     error(EXIT_FAILURE, errno, "%s: %zu bytes for notdir", __func__,
  284           (l+1)*sizeof *out);
  285 
  286   strcpy(out, tmp);
  287   return out;
  288 }
  289 
  290 
  291 
  292 
  293 
  294 /* Check if a file exists and report if it doesn't: */
  295 void
  296 gal_checkset_check_file(char *filename)
  297 {
  298   FILE *tmpfile;
  299   errno=0;
  300   tmpfile = fopen(filename, "r");
  301   if(tmpfile)                        /* The file opened. */
  302     {
  303       if(fclose(tmpfile)==EOF)
  304         error(EXIT_FAILURE, errno, "%s", filename);
  305     }
  306   else
  307     error(EXIT_FAILURE, errno, "%s", filename);
  308 }
  309 
  310 
  311 
  312 
  313 
  314 /* Similar to `gal_checkset_check_file', but will report the result instead
  315    of doing it quietly. */
  316 int
  317 gal_checkset_check_file_return(char *filename)
  318 {
  319   FILE *tmpfile;
  320   errno=0;
  321   tmpfile = fopen(filename, "r");
  322   if(tmpfile)                        /* The file opened. */
  323     {
  324       if(fclose(tmpfile)==EOF)
  325         error(EXIT_FAILURE, errno, "%s", filename);
  326       return 1;
  327     }
  328   else
  329     return 0;
  330 }
  331 
  332 
  333 
  334 
  335 
  336 /* Check if a file exists and can be opened. If the `keep' value is
  337    non-zero, then the file will remain untouched, otherwise, it will be
  338    deleted (since most programs need to make a clean output). When the file
  339    is to be deleted and `dontdelete' has a non-zero value, then the file
  340    won't be deleted, but the program will abort with an error, informing
  341    the user that the output can't be made. */
  342 void
  343 gal_checkset_writable_remove(char *filename, int keep, int dontdelete)
  344 {
  345   char *dir;
  346   FILE *tmpfile;
  347 
  348   /* If the filename is `NULL' everything is ok (it doesn't exist)! In some
  349      cases, a NULL filename is interpretted to mean standard output. */
  350   if(filename==NULL)
  351     return;
  352 
  353   /* We want to make sure that we can open and write to this file. But
  354      the user might have asked to not delete the file, so the
  355      contents should not be changed. Therefore we have to open it with
  356      `r+`. */
  357   errno=0;
  358   tmpfile=fopen(filename, "r+");
  359   if (tmpfile)                        /* The file opened. */
  360     {
  361       /* Close the file. */
  362       errno=0;
  363       if(fclose(tmpfile))
  364         error(EXIT_FAILURE, errno, "%s", filename);
  365 
  366       /* See if the file should be deleted. */
  367       if(keep==0)
  368         {
  369           /* Make sure it is ok to delete the file. */
  370           if(dontdelete)
  371             error(EXIT_FAILURE, 0, "%s already exists and you have "
  372                   "asked to not remove it with the `--dontdelete` "
  373                   "(`-D`) option", filename);
  374 
  375           /* Delete the file: */
  376           errno=0;
  377           if(unlink(filename))
  378             error(EXIT_FAILURE, errno, "%s", filename);
  379         }
  380     }
  381 
  382   /* If the file doesn't exist, we just need to make sure if we have write
  383      permissions to its host directory. */
  384   else
  385     {
  386       /* Separate the directory part of the filename. */
  387       dir=gal_checkset_dir_part(filename);
  388 
  389       /* Make sure this directory is writable by this user. */
  390       errno=0;
  391       if( access(dir, W_OK) )
  392         error(EXIT_FAILURE, errno, "cannot create any file(s) in the "
  393               "directory `%s'", dir);
  394 
  395       /* Clean up. */
  396       free(dir);
  397     }
  398 }
  399 
  400 
  401 
  402 
  403 
  404 /* Check output file name: If a file exists or can exist and can be
  405    written to, this function will return 1. If not (for example it is
  406    a directory) it will return 0. Finally, if it exists but cannot be
  407    deleted, report an error and abort. */
  408 int
  409 gal_checkset_dir_0_file_1(char *name, int dontdelete)
  410 {
  411   FILE *tmpfile;
  412   struct stat nameinfo;
  413 
  414   if(name==NULL)
  415     error(EXIT_FAILURE, 0, "%s: a bug! The input should not be NULL. "
  416           "Please contact us at %s so we can see what went wrong and "
  417           "fix it in future updates", __func__, PACKAGE_BUGREPORT);
  418 
  419   errno=0;
  420   if(stat(name, &nameinfo)!=0)
  421     {
  422       if(errno==ENOENT)        /* ENOENT: No such file or directory. */
  423         {/* Make the file temporarily and see if everything is ok. */
  424           errno=0;
  425           tmpfile=fopen(name, "w");
  426           if (tmpfile)
  427             {
  428               fprintf(tmpfile, "Only to test write access.");
  429               errno=0;
  430               if(fclose(tmpfile))
  431                 error(EXIT_FAILURE, errno, "%s", name);
  432               errno=0;
  433               if(unlink(name))
  434                 error(EXIT_FAILURE, errno, "%s", name);
  435             }
  436           else
  437             error(EXIT_FAILURE, errno, "%s", name);
  438           return 1;                    /* It is a file name, GOOD */
  439         }
  440       else                             /* Some strange condition, ABORT */
  441         error(EXIT_FAILURE, errno, "%s", name);
  442     }
  443 
  444   if(S_ISDIR(nameinfo.st_mode))        /* It is a directory, BAD */
  445     return 0;
  446   else if (S_ISREG(nameinfo.st_mode))  /* It is a file, GOOD. */
  447     {
  448       gal_checkset_writable_remove(name, 0, dontdelete);
  449       return 1;
  450     }
  451   else                                 /* Not a file or a dir, ABORT */
  452     error(EXIT_FAILURE, 0, "%s not a file or a directory", name);
  453 
  454   error(EXIT_FAILURE, 0, "%s: a bug! The process should not reach the end "
  455         "of the function! Please contact us at %s so we can see what went "
  456         "wrong and fix it in future updates", __func__, PACKAGE_BUGREPORT);
  457   return 0;
  458 }
  459 
  460 
  461 
  462 
  463 
  464 /* Allocate space and write the output name (outname) based on a given
  465    input name (inname). The suffix of the input name (if present) will
  466    be removed and the given suffix will be put in the end. */
  467 char *
  468 gal_checkset_automatic_output(struct gal_options_common_params *cp,
  469                               char *inname, char *suffix)
  470 {
  471   char *out;
  472   size_t i, l, offset=0;
  473 
  474   /* Merge the contents of the input name and suffix name (while also
  475      allocating the necessary space).*/
  476   out=gal_checkset_malloc_cat(inname, suffix);
  477 
  478   /* If there is actually a suffix, replace it with the (possibly) existing
  479      suffix. */
  480   if(suffix)
  481     {
  482       /* Start from the end of the input array*/
  483       l=strlen(inname);
  484       for(i=l-1;i!=0;--i)
  485         {
  486           /* We don't want to touch anything before a `/' (directory
  487              names). We are only concerned with file names here. */
  488           if(out[i]=='/')
  489             {
  490               /* When `/' is the last input character, then the input is
  491                  clearly not a filename, but a directory name. In this
  492                  case, adding a suffix is meaningless (a suffix belongs to
  493                  a filename for Gnuastro's tools). So close the string
  494                  after the `/' and leave the loop. However, if the `/'
  495                  isn't the last input name charector, there is probably a
  496                  filename (without a "." suffix), so break from the
  497                  loop. No further action is required, since we initially
  498                  allocated the necessary space and concatenated the input
  499                  and suffix arrays. */
  500               if(i==l-1)
  501                 out[i+1]='\0';
  502               break;
  503             }
  504 
  505           /* The input file names can be compressed names (for example
  506              `.fits.gz'). Currently the only compressed formats
  507              (decompressed within CFITSIO) are listed in
  508              `gal_fits_name_is_fits' and `gal_fits_suffix_is_fits'.*/
  509           else if(out[i]=='.' && !( ( out[i+1]=='g' && out[i+2]=='z' )
  510                                     || (out[i+1]=='f' && out[i+2]=='z' )
  511                                     || out[i+1]=='Z' ) )
  512             {
  513               out[i]='\0';
  514               strcat(out, suffix);
  515               break;
  516             }
  517         }
  518     }
  519 
  520   /* If we don't want the input directory information, remove them
  521      here. */
  522   if(!cp->keepinputdir)
  523     {
  524       l=strlen(out);
  525       for(i=l;i!=0;--i)         /* Find the last forward slash.      */
  526         if(out[i]=='/')
  527           {offset=i+1; break;}
  528       if(offset)
  529         for(i=offset;i<=l;++i)  /* <= because we want to shift the   */
  530           out[i-offset]=out[i]; /* '\0' character in the string too. */
  531     }
  532 
  533   /* Remove the created filename if it already exits. */
  534   gal_checkset_writable_remove(out, cp->keep, cp->dontdelete);
  535 
  536   /* Return the resulting filename. */
  537   return out;
  538 }
  539 
  540 
  541 
  542 
  543 
  544 /* Check if dirname is actually a real directory and that we can
  545    actually write inside of it. To insure all conditions an actual
  546    file will be made */
  547 void
  548 gal_checkset_check_dir_write_add_slash(char **dirname)
  549 {
  550   int file_d;
  551   char *tmpname, *indir=*dirname/*, buf[]="A test"*/;
  552 
  553   /* Set the template for the temporary file: */
  554   if(indir[strlen(indir)-1]=='/')
  555     tmpname=gal_checkset_malloc_cat(indir, "gnuastroXXXXXX");
  556   else
  557     tmpname=gal_checkset_malloc_cat(indir, "/gnuastroXXXXXX");
  558 
  559   /* Make a temporary file name and try openning it. */
  560   errno=0;
  561   file_d=mkstemp(tmpname);
  562   if(file_d==-1)
  563     error(EXIT_FAILURE, errno, "cannot write output in the directory "
  564           "%s", indir);
  565   /*
  566   errno=0;
  567   printf("\n\n%s\n\n", tmpname);
  568   if( write(file_d, buf, strlen(buf)) == -1 )
  569     error(EXIT_FAILURE, errno, "%s: writing to this temporary file to "
  570           "check the given `%s` directory", tmpname, indir);
  571   */
  572   errno=0;
  573   if( close(file_d) == -1 )
  574     error(EXIT_FAILURE, errno, "%s: Closing this temporary file to check "
  575           "the given `%s` directory", tmpname, indir);
  576 
  577   /* Delete the temporary file: */
  578   errno=0;
  579   if(unlink(tmpname)==-1)
  580     error(EXIT_FAILURE, errno, "%s: removing this temporary file made "
  581           "to check the given `%s directory`", tmpname, indir);
  582 
  583   /* Remove the extra characters that were added for the random name. */
  584   tmpname[strlen(tmpname)-14]='\0';
  585 
  586   free(*dirname);
  587   *dirname=tmpname;
  588 }
  589 
  590 
  591 
  592 
  593 
  594 /* If the given directory exists, then nothing is done, if it doesn't, it
  595    will be created. */
  596 void
  597 gal_checkset_mkdir(char *dirname)
  598 {
  599   struct stat st={0};
  600   if( stat(dirname, &st) == -1 )
  601     {
  602       errno=0;
  603       if( mkdir(dirname, 0700) == -1 )
  604         error(EXIT_FAILURE, errno, "making %s", dirname);
  605     }
  606 }