1 /*********************************************************************
2 Function to parse options and configuration file values.
3 
4 Original author:
5      Mohammad Akhlaghi <mohammad@akhlaghi.org>
6 Contributing author(s):
7 Copyright (C) 2017-2021, Free Software Foundation, Inc.
8 
9 Gnuastro is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation, either version 3 of the License, or (at your
12 option) any later version.
13 
14 Gnuastro is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
21 **********************************************************************/
22 #include <config.h>
23 
24 #include <time.h>
25 #include <argp.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <gnuastro/wcs.h>
32 #include <gnuastro/git.h>
33 #include <gnuastro/txt.h>
34 #include <gnuastro/ds9.h>
35 #include <gnuastro/fits.h>
36 #include <gnuastro/list.h>
37 #include <gnuastro/data.h>
38 #include <gnuastro/table.h>
39 #include <gnuastro/blank.h>
40 #include <gnuastro/threads.h>
41 #include <gnuastro/pointer.h>
42 #include <gnuastro/arithmetic.h>
43 #include <gnuastro/interpolate.h>
44 
45 #include <gnuastro-internal/timing.h>
46 #include <gnuastro-internal/options.h>
47 #include <gnuastro-internal/checkset.h>
48 #include <gnuastro-internal/tableintern.h>
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 /**********************************************************************/
70 /************             Option utilities              ***************/
71 /**********************************************************************/
72 int
gal_options_is_last(struct argp_option * option)73 gal_options_is_last(struct argp_option *option)
74 {
75   return ( option->key==0 && option->name==NULL
76            && option->doc==NULL && option->group==0 );
77 }
78 
79 
80 
81 
82 int
gal_options_is_category_title(struct argp_option * option)83 gal_options_is_category_title(struct argp_option *option)
84 {
85   return ( option->key==0 && option->name==NULL );
86 }
87 
88 
89 
90 
91 
92 void
gal_options_add_to_not_given(struct gal_options_common_params * cp,struct argp_option * option)93 gal_options_add_to_not_given(struct gal_options_common_params *cp,
94                              struct argp_option *option)
95 {
96   gal_list_str_add(&cp->novalue_doc, (char *)option->doc, 0);
97   gal_list_str_add(&cp->novalue_name, (char *)option->name, 0);
98 }
99 
100 
101 
102 
103 
104 void
gal_options_abort_if_mandatory_missing(struct gal_options_common_params * cp)105 gal_options_abort_if_mandatory_missing(struct gal_options_common_params *cp)
106 {
107   int namewidth=0;
108   gal_list_str_t *tmp;
109   char info[5000], *name, *doc;
110 
111   /* If there is no mandatory options, then just return. */
112   if(cp->novalue_name==NULL)
113     return;
114 
115   /* Get the maximum width of the given names: */
116   for(tmp=cp->novalue_name; tmp!=NULL; tmp=tmp->next)
117     if( strlen(tmp->v) > namewidth ) namewidth=strlen(tmp->v);
118 
119   /* Print the introductory information. */
120   sprintf(info, "to continue, the following options need a value ");
121   sprintf(info+strlen(info), "(parenthesis after option name contain its "
122           "description):\n\n");
123 
124   /* Print the list of options along with their description. */
125   while(cp->novalue_name!=NULL)
126     {
127       doc  = gal_list_str_pop(&cp->novalue_doc);
128       name = gal_list_str_pop(&cp->novalue_name);
129       sprintf(info+strlen(info), "  %-*s (%s\b)\n", namewidth+4, name, doc);
130     }
131   sprintf(info+strlen(info), "\n");
132 
133   /* Print suggestions, way to solve it. */
134   sprintf(info+strlen(info), "Use the command-line or a configuration file "
135           "to set value(s).\n\nFor a complete description of command-line "
136           "options and configuration files, please see the \"Options\" and "
137           "\"Configuration files\" section of the Gnuastro book "
138           "respectively. You can read them on the command-line by running "
139           "the following commands (type 'SPACE' to flip through pages, type "
140           "'Q' to return to the command-line):\n\n"
141           "  info gnuastro Options\n"
142           "  info gnuastro \"Configuration files\"\n");
143 
144   error(EXIT_FAILURE, 0, "%s", info);
145 }
146 
147 
148 
149 
150 
151 static char *
options_get_home()152 options_get_home()
153 {
154   char *home;
155   home=getenv("HOME");
156   if(home==NULL)
157     error(EXIT_FAILURE, 0, "HOME environment variable not defined");
158   return home;
159 }
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 /**********************************************************************/
181 /************     Parser functions for common options   ***************/
182 /**********************************************************************/
183 void *
gal_options_check_version(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)184 gal_options_check_version(struct argp_option *option, char *arg,
185                           char *filename, size_t lineno, void *junk)
186 {
187   char *str;
188 
189   /* First see if we are reading or writing. */
190   if(lineno==-1)
191     {
192       /* 'PACKAGE_VERSION' is a static/literal string, but the pointer
193          returned by this function will be freed, so we must allocate space
194          for it.
195 
196          We didn't allocate and give this option a value when we read it
197          because it is redundant and much more likely for the option to
198          just be present (for a check in a reproduction pipeline for
199          example) than for it to be printed. So we don't want to waste
200          resources in allocating a redundant value. */
201       gal_checkset_allocate_copy(PACKAGE_VERSION, &str);
202       return str;
203     }
204 
205   /* Check if the given value is different from this version. */
206   else
207     {
208       if(arg==NULL)
209         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
210               "the problem. The value to 'arg' is NULL", __func__,
211               PACKAGE_BUGREPORT);
212       else if( strcmp(arg, PACKAGE_VERSION) )
213         {
214           /* Print an error message and abort.  */
215           error_at_line(EXIT_FAILURE, 0, filename, lineno, "version "
216                         "mis-match: you are running GNU Astronomy Utilities "
217                         "(Gnuastro) version '%s'. However, the 'onlyversion' "
218                         "option is set to version '%s'.\n\n"
219                         "This was probably done for reproducibility. "
220                         "Therefore, manually removing, or changing, the "
221                         "option value might produce errors or unexpected "
222                         "results. It is thus strongly advised to build "
223                         "Gnuastro %s and re-run this command/script.\n\n"
224                         "You can download previously released tar-balls "
225                         "from the following URLs respectively:\n\n"
226                         "    Stable (version format: X.Y):      "
227                         "http://ftpmirror.gnu.org/gnuastro\n"
228                         "    Alpha  (version format: X.Y.A-B):  "
229                         "http://alpha.gnu.org/gnu/gnuastro\n\n"
230                         "Alternatively, you can clone Gnuastro, checkout the "
231                         "respective commit (from the version number), then "
232                         "bootstrap and build it. Please run the following "
233                         "command for more information:\n\n"
234                         "    $ info gnuastro \"Version controlled source\"\n",
235                         PACKAGE_VERSION, arg, arg);
236 
237           /* Just to avoid compiler warnings for unused variables. The program
238              will never reach this point! */
239           arg=filename=NULL; lineno=0; option=NULL; junk=NULL;
240         }
241     }
242   return NULL;
243 }
244 
245 
246 
247 
248 
249 void *
gal_options_print_citation(struct argp_option * option,char * arg,char * filename,size_t lineno,void * pa)250 gal_options_print_citation(struct argp_option *option, char *arg,
251                            char *filename, size_t lineno, void *pa)
252 {
253   struct gal_options_common_params *cp=(struct gal_options_common_params *)pa;
254   char *gnuastro_acknowledgement;
255   char *gnuastro_bibtex=
256     "First paper introducing Gnuastro\n"
257     "--------------------------------\n"
258     "  @ARTICLE{gnuastro,\n"
259     "     author = {{Akhlaghi}, M. and {Ichikawa}, T.},\n"
260     "      title = \"{Noise-based Detection and Segmentation of Nebulous "
261     "Objects}\",\n"
262     "    journal = {ApJS},\n"
263     "  archivePrefix = \"arXiv\",\n"
264     "     eprint = {1505.01664},\n"
265     "   primaryClass = \"astro-ph.IM\",\n"
266     "   keywords = {galaxies: irregular, galaxies: photometry,\n"
267     "               galaxies: structure, methods: data analysis,\n"
268     "               techniques: image processing, techniques: photometric},\n"
269     "       year = 2015,\n"
270     "      month = sep,\n"
271     "     volume = 220,\n"
272     "        eid = {1},\n"
273     "      pages = {1},\n"
274     "        doi = {10.1088/0067-0049/220/1/1},\n"
275     "     adsurl = {https://ui.adsabs.harvard.edu/abs/2015ApJS..220....1A},\n"
276     "    adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n"
277     "  }";
278 
279 
280   /* Print the statements. */
281   printf("\nThank you for using %s (%s) %s.\n\n", cp->program_name,
282          PACKAGE_NAME, PACKAGE_VERSION);
283   printf("Citations and acknowledgement are vital for the continued "
284          "work on Gnuastro.\n\n"
285          "Please cite the following record(s) and add the acknowledgement "
286          "statement below in your work to support us. Please note that "
287          "different Gnuastro programs may have different corresponding "
288          "papers. Hence, please check all the programs you used. Don't "
289          "forget to also include the version as shown above for "
290          "reproducibility.\n\n"
291          "%s\n\n", gnuastro_bibtex);
292 
293 
294   /* Only print the citation for the program if one exists. */
295   if(cp->program_bibtex[0]!='\0') printf("%s\n\n", cp->program_bibtex);
296 
297 
298   /* Notice for acknowledgements. */
299   if( asprintf(&gnuastro_acknowledgement,
300                "Acknowledgement\n"
301                "---------------\n"
302                "This work was partly done using GNU Astronomy Utilities "
303                "(Gnuastro, ascl.net/1801.009) version %s. Work on Gnuastro "
304                "has been funded by the Japanese Ministry of Education, "
305                "Culture, Sports, Science, and Technology (MEXT) scholarship "
306                "and its Grant-in-Aid for Scientific Research (21244012, "
307                "24253003), the European Research Council (ERC) advanced "
308                "grant 339659-MUSICOS, European Union’s Horizon 2020 research "
309                "and innovation programme under Marie Sklodowska-Curie grant "
310                "agreement No 721463 to the SUNDIAL ITN, and from the Spanish "
311                "Ministry of Economy and Competitiveness (MINECO) under grant "
312                "number AYA2016-76219-P. ", PACKAGE_VERSION)<0 )
313     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
314   printf("%s\n", gnuastro_acknowledgement);
315   free(gnuastro_acknowledgement);
316 
317 
318   /* Print a thank you message. */
319   printf("                                               ,\n"
320          "                                              {|'--.\n"
321          "                                             {{\\    \\\n"
322          "      Many thanks from all                   |/`'--./=.\n"
323          "      Gnuastro developers!                   `\\.---' `\\\\\n"
324          "                                                  |\\  ||\n"
325          "                                                  | |//\n"
326          "                                                   \\//_/|\n"
327          "                                                   //\\__/\n"
328          "                                                  //\n"
329          "                   (http://www.chris.com/ascii/) |/\n");
330 
331 
332 
333   /* Exit the program. */
334   exit(EXIT_SUCCESS);
335 
336   /* Just to avoid compiler warnings for unused variables. The program
337      will never reach this point! */
338   arg=filename=NULL; lineno=0; option=NULL;
339 }
340 
341 
342 
343 
344 
345 void *
gal_options_check_config(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)346 gal_options_check_config(struct argp_option *option, char *arg,
347                           char *filename, size_t lineno, void *junk)
348 {
349   char *str;
350 
351   /* First see if we are reading or writing. */
352   if(lineno==-1)
353     {
354       gal_checkset_allocate_copy("1", &str);
355       return str;
356     }
357 
358   /* Check if the given value is different from this version. */
359   else
360     {
361       /* If its already set then ignore it. */
362       if(option->set) return NULL;
363 
364       /* Activate the option and let the user know its activated. */
365       (*(uint8_t *)(option->value)) = 1;
366       printf("-----------------\n"
367              "Parsing of options AFTER '--checkconfig'.\n\n"
368              "IMPORTANT: Any option that was parsed before encountering "
369              "'--checkconfig', on the command-line or in a configuration "
370              "file, is not shown here. It is thus recommended to use this "
371              "option before calling any other option.\n"
372              "-----------------\n");
373 
374       /* Print where this option was first seen: if 'checkconfig' is called
375          within a configuration file, 'filename!=NULL' and has an argument
376          (=="1"). But on the command-line, it has no argument or
377          filename. */
378       if(filename)
379         printf("%s:\n", filename);
380       else
381         {
382           if(arg)
383             error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
384                   "fix the problem. 'filename==NULL', but 'arg!=NULL'",
385                   __func__, PACKAGE_BUGREPORT);
386           else
387             printf("Command-line:\n");
388         }
389 
390       /* Just to avoid compiler warnings for unused variables. The program
391          will never reach this point! */
392       arg=filename=NULL; lineno=0; option=NULL; junk=NULL;
393     }
394   return NULL;
395 }
396 
397 
398 
399 
400 
401 void *
gal_options_read_type(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)402 gal_options_read_type(struct argp_option *option, char *arg,
403                       char *filename, size_t lineno, void *junk)
404 {
405   char *str;
406   if(lineno==-1)
407     {
408       /* Note that 'gal_data_type_as_string' returns a static string. But
409          the output must be an allocated string so we can free it. */
410       gal_checkset_allocate_copy(
411            gal_type_name( *(uint8_t *)(option->value), 1), &str);
412       return str;
413     }
414   else
415     {
416       /* If the option is already set, just return. */
417       if(option->set) return NULL;
418 
419       /* Read the value. */
420       if ( (*(uint8_t *)(option->value) = gal_type_from_name(arg) )
421            == GAL_TYPE_INVALID )
422         error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
423                       "'%s' option) couldn't be recognized as a known "
424                       "type.\n\nFor the full list of known types, please "
425                       "run the following command (press SPACE key to go "
426                       "down, and 'q' to return to the command-line):\n\n"
427                       "    $ info gnuastro \"Numeric data types\"\n",
428                       arg, option->name);
429 
430       /* For no un-used variable warning. This function doesn't need the
431          pointer.*/
432       return junk=NULL;
433     }
434 }
435 
436 
437 
438 
439 
440 void *
gal_options_read_searchin(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)441 gal_options_read_searchin(struct argp_option *option, char *arg,
442                           char *filename, size_t lineno, void *junk)
443 {
444   char *str;
445   if(lineno==-1)
446     {
447       /* Note that 'gal_data_type_as_string' returns a static string. But
448          the output must be an allocated string so we can free it. */
449       gal_checkset_allocate_copy(
450         gal_tableintern_searchin_as_string( *(uint8_t *)(option->value)),
451         &str);
452       return str;
453     }
454   else
455     {
456       /* If the option is already set, just return. */
457       if(option->set) return NULL;
458 
459       /* Read the value. */
460       if((*(uint8_t *)(option->value)=gal_tableintern_string_to_searchin(arg))
461          == GAL_TABLE_SEARCH_INVALID )
462         error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
463                       "'%s' option) couldn't be recognized as a known table "
464                       "search-in field ('name', 'unit', or 'comment').\n\n"
465                       "For more explanation, please run the following "
466                       "command (press SPACE key to go down, and 'q' to "
467                       "return to the command-line):\n\n"
468                       "    $ info gnuastro \"Selecting table columns\"\n",
469                       arg, option->name);
470 
471       /* For no un-used variable warning. This function doesn't need the
472          pointer.*/
473       return junk=NULL;
474     }
475 }
476 
477 
478 
479 
480 
481 void *
gal_options_read_wcslinearmatrix(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)482 gal_options_read_wcslinearmatrix(struct argp_option *option, char *arg,
483                                  char *filename, size_t lineno, void *junk)
484 {
485   char *str=NULL;
486   uint8_t value=GAL_WCS_LINEAR_MATRIX_INVALID;
487   if(lineno==-1)
488     {
489       /* The output must be an allocated string (will be 'free'd later). */
490       value=*(uint8_t *)(option->value);
491       switch(value)
492         {
493         case GAL_WCS_LINEAR_MATRIX_PC: gal_checkset_allocate_copy("pc", &str);
494           break;
495         case GAL_WCS_LINEAR_MATRIX_CD: gal_checkset_allocate_copy("cd", &str);
496           break;
497         default:
498           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' "
499                 "to fix the problem. %u is not a recognized WCS rotation "
500                 "matrix code", __func__, PACKAGE_BUGREPORT, value);
501         }
502       return str;
503     }
504   else
505     {
506       /* If the option is already set, just return. */
507       if(option->set) return NULL;
508 
509       /* Read the value. */
510       if(      !strcmp(arg, "pc") ) value = GAL_WCS_LINEAR_MATRIX_PC;
511       else if( !strcmp(arg, "cd") ) value = GAL_WCS_LINEAR_MATRIX_CD;
512       else
513         error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value "
514                       "to '%s' option) couldn't be recognized as a known "
515                       "WCS rotation matrix. Acceptable values are 'pc' "
516                       "or 'cd'", arg, option->name);
517       *(uint8_t *)(option->value)=value;
518 
519       /* For no un-used variable warning. This function doesn't need the
520          pointer.*/
521       return junk=NULL;
522     }
523 }
524 
525 
526 
527 
528 
529 void *
gal_options_read_tableformat(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)530 gal_options_read_tableformat(struct argp_option *option, char *arg,
531                              char *filename, size_t lineno, void *junk)
532 {
533   char *str;
534   if(lineno==-1)
535     {
536       /* Note that 'gal_data_type_as_string' returns a static string. But
537          the output must be an allocated string so we can free it. */
538       gal_checkset_allocate_copy(
539         gal_tableintern_format_as_string( *(uint8_t *)(option->value)), &str);
540       return str;
541     }
542   else
543     {
544       /* If the option is already set, then you don't have to do anything. */
545       if(option->set) return NULL;
546 
547       /* Read the value. */
548       if( (*(uint8_t *)(option->value)=gal_tableintern_string_to_format(arg) )
549           ==GAL_TABLE_FORMAT_INVALID )
550         error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
551                       "'%s' option) couldn't be recognized as a known table "
552                       "format field ('txt', 'fits-ascii', or "
553                       "'fits-binary').\n\n", arg, option->name);
554 
555       /* For no un-used variable warning. This function doesn't need the
556          pointer.*/
557       return junk=NULL;
558     }
559 }
560 
561 
562 
563 
564 
565 void *
gal_options_read_interpmetric(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)566 gal_options_read_interpmetric(struct argp_option *option, char *arg,
567                               char *filename, size_t lineno, void *junk)
568 {
569   char *str=NULL;
570   if(lineno==-1)
571     {
572       switch(*(uint8_t *)(option->value))
573         {
574         case GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL:
575           gal_checkset_allocate_copy("radial", &str);
576           break;
577         case GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN:
578           gal_checkset_allocate_copy("manhattan", &str);
579           break;
580         default:
581           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
582                 "fix the problem. The code %u is not recognized as a "
583                 "nearest-neighbor interpolation metric", __func__,
584                 PACKAGE_BUGREPORT, *(uint8_t *)(option->value));
585         }
586       return str;
587     }
588   else
589     {
590       /* If the option is already set, just return. */
591       if(option->set) return NULL;
592 
593       /* Set the value. */
594       if(       !strcmp(arg, "radial") )
595         *(uint8_t *)(option->value) = GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL;
596       else if ( !strcmp(arg, "manhattan") )
597         *(uint8_t *)(option->value) = GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN;
598       else
599         error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
600                       "'%s' option) isn't valid. Currently only 'radial' "
601                       "and 'manhattan' metrics are recognized for nearest "
602                       "neighbor interpolation", arg, option->name);
603 
604       /* For no un-used variable warning. This function doesn't need the
605          pointer. */
606       return junk=NULL;
607     }
608 }
609 
610 
611 
612 
613 
614 /* The input to this function is a string of any number of numbers
615    separated by a comma (',') and possibly containing fractions, for
616    example: '1,2/3, 4.95'. The output 'gal_data_t' contains the array of
617    given values in 'double' type. You can read the number from its 'size'
618    element. */
619 gal_data_t *
gal_options_parse_list_of_numbers(char * string,char * filename,size_t lineno)620 gal_options_parse_list_of_numbers(char *string, char *filename, size_t lineno)
621 {
622   size_t i, num=0;
623   gal_data_t *out;
624   char *c=string, *tailptr;
625   gal_list_f64_t *list=NULL, *tdll;
626   double numerator=NAN, denominator=NAN, tmp;
627 
628   /* The nature of the arrays/numbers read here is very small, so since
629      'p->cp.minmapsize' might not have been read yet, we will set it to -1
630      (largest size_t number), so the values are kept in memory. */
631   int quietmmap=1;
632   size_t minmapsize=-1;
633 
634   /* If we have an empty string, just return NULL. */
635   if(string==NULL || *string=='\0') return NULL;
636 
637   /* Go through the input character by character. */
638   while(string && *c!='\0')
639     {
640       switch(*c)
641         {
642 
643         /* Ignore space or tab. */
644         case ' ':
645         case '\t':
646           ++c;
647           break;
648 
649         /* Comma marks the transition to the next number. */
650         case ',':
651         case ':':
652           if(isnan(numerator))
653             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a number "
654                           "must be given before ','. You have given: '%s'",
655                           string);
656           gal_list_f64_add(&list, isnan(denominator)
657                            ? numerator : numerator/denominator);
658           numerator=denominator=NAN;
659           ++num;
660           ++c;
661           break;
662 
663         /* Divide two numbers. */
664         case '/':
665           if( isnan(numerator) || !isnan(denominator) )
666             error_at_line(EXIT_FAILURE, 0, filename, lineno, "'/' must "
667                           "only be between two numbers and used for "
668                           "division. But you have given '%s'", string);
669           ++c;
670           break;
671 
672         /* Extra dot is an error (cases like 2.5.5). Valid '.'s will be
673            read by 'strtod'. */
674         case '.':
675           error_at_line(EXIT_FAILURE, 0, filename, lineno, "extra '.' in "
676                         "'%s'", string);
677           break;
678 
679         /* Read the number. */
680         default:
681 
682           /* Parse the string. */
683           tmp=strtod(c, &tailptr);
684           if(tailptr==c)
685             error_at_line(EXIT_FAILURE, 0, filename, lineno, "the first "
686                           "part of '%s' couldn't be read as a number. This "
687                           "was part of '%s'", c, string);
688 
689           /* See if the number should be put in the numerator or
690              denominator. */
691           if(isnan(numerator)) numerator=tmp;
692           else
693             {
694               if(isnan(denominator)) denominator=tmp;
695               else error_at_line(EXIT_FAILURE, 0, filename, lineno, "more "
696                                  "than two numbers in each element.");
697             }
698 
699           /* Set 'c' to tailptr. */
700           c=tailptr;
701         }
702     }
703 
704 
705   /* If the last number wasn't finished by a ',', add the read value to the
706      list */
707   if( !isnan(numerator) )
708     {
709       ++num;
710       gal_list_f64_add(&list, isnan(denominator)
711                        ? numerator : numerator/denominator);
712     }
713 
714 
715   /* Allocate the output data structure and fill it up. */
716   if(num)
717     {
718       i=num;
719       out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &num, NULL, 0,
720                          minmapsize, quietmmap, NULL, NULL, NULL);
721       for(tdll=list;tdll!=NULL;tdll=tdll->next)
722         ((double *)(out->array))[--i]=tdll->v;
723     }
724   else
725     {
726       /* It is not possible to allocate a dataset with a size of 0 along
727          any dimension (in C it's possible, but conceptually it isn't). So,
728          we'll allocate space for one element, then free it. */
729       i=1;
730       out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &i, NULL, 0,
731                          minmapsize, quietmmap, NULL, NULL, NULL);
732       out->size=out->dsize[0]=0;
733       free(out->array);
734       out->array=NULL;
735     }
736 
737 
738   /* Clean up and return. */
739   gal_list_f64_free(list);
740   return out;
741 }
742 
743 
744 
745 
746 
747 gal_data_t *
gal_options_parse_list_of_strings(char * string,char * filename,size_t lineno)748 gal_options_parse_list_of_strings(char *string, char *filename, size_t lineno)
749 {
750   size_t num;
751   gal_data_t *out;
752   gal_list_str_t *list=NULL, *tll;
753   char *cp, *token, **strarr, delimiters[]=",:";
754 
755   /* The nature of the arrays/numbers read here is very small, so since
756      'p->cp.minmapsize' might not have been read yet, we will set it to -1
757      (largest size_t number), so the values are kept in memory. */
758   int quietmmap=1;
759   size_t minmapsize=-1;
760 
761   /* If we have an empty string, just return NULL. */
762   if(string==NULL || *string=='\0') return NULL;
763 
764   /* Make a copy of the input string, and save the tokens */
765   gal_checkset_allocate_copy(string, &cp);
766   token=strtok(cp, delimiters);
767   gal_list_str_add(&list, token, 1);
768   while(token!=NULL)
769     {
770       token=strtok(NULL, delimiters);
771       if(token!=NULL)
772         gal_list_str_add(&list, token, 1);
773     }
774 
775 
776   /* Allocate the output dataset (array containing all the given
777      strings). */
778   num=gal_list_str_number(list);
779   out=gal_data_alloc(NULL, GAL_TYPE_STRING, 1, &num, NULL, 0,
780                      minmapsize, quietmmap, NULL, NULL, NULL);
781 
782   /* Fill the output dataset. */
783   strarr=out->array;
784   for(tll=list;tll!=NULL;tll=tll->next)
785     strarr[--num]=tll->v;
786 
787   /* Clean up and return. Note that we don't want to free the values in the
788      list, the elements in 'out->array' point to them and will later use
789      them.*/
790   free(cp);
791   gal_list_str_free(list, 0);
792   return out;
793 }
794 
795 
796 
797 
798 
799 /* The input to this function is a string of any number of strings
800    separated by a comma (',') for example: 'a,abc,abcd'. The output
801    'gal_data_t' contains the array of given strings. You can read the
802    number of inputs from its 'size' element. */
803 gal_data_t *
gal_options_parse_csv_strings_raw(char * string,char * filename,size_t lineno)804 gal_options_parse_csv_strings_raw(char *string, char *filename, size_t lineno)
805 {
806   size_t i, num;
807   gal_data_t *out;
808   char *c=string, *str=NULL;
809   gal_list_str_t *list=NULL, *tstrll=NULL;
810 
811 
812   /* The nature of the arrays/numbers read here is very small, so since
813      'p->cp.minmapsize' might not have been read yet, we will set it to -1
814      (largest size_t number), so the values are kept in memory. */
815   int quietmmap=1;
816   size_t minmapsize=-1;
817 
818 
819   /* Go through the input character by character. */
820   while(string && *c!='\0')
821     {
822       switch(*c)
823         {
824         /* Comma marks the transition to the next string. */
825         case ',':
826           if(str==NULL)
827             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a string "
828                           "must exist before the first ','. You have "
829                           "given: '%s'", string);
830           *c='\0';
831           gal_list_str_add(&list, str, 1);
832           str=NULL;  /* Mark that the next character is the start */
833           break;
834 
835         /* If the character isn't a coma, it is either in the middle of a
836            string at the start of it. If 'str==NULL', then it is at the
837            start. */
838         default: if(str==NULL) str=c;
839         }
840 
841       /* Increment C. */
842       ++c;
843     }
844 
845 
846   /* If the last element wasn't a comma, the last string hasn't been added
847      to the list yet. */
848   if(str) gal_list_str_add(&list, str, 1);
849 
850 
851   /* Allocate the output data structure and fill it up. */
852   if(list)
853     {
854       i=num=gal_list_str_number(list);
855       out=gal_data_alloc(NULL, GAL_TYPE_STRING, 1, &num, NULL, 0,
856                          minmapsize, quietmmap, NULL, NULL, NULL);
857       for(tstrll=list;tstrll!=NULL;tstrll=tstrll->next)
858         ((char **)(out->array))[--i]=tstrll->v;
859     }
860   else
861     {
862       /* It is not possible to allocate a dataset with a size of 0 along
863          any dimension (in C it's possible, but conceptually it isn't). So,
864          we'll allocate space for one element, then free it. */
865       i=1;
866       out=gal_data_alloc(NULL, GAL_TYPE_STRING, 1, &i, NULL, 0,
867                          minmapsize, quietmmap, NULL, NULL, NULL);
868       out->size=out->dsize[0]=0;
869       free(out->array);
870       out->array=NULL;
871     }
872 
873 
874   /* Clean up and return. Note that we don't want to free the space of
875      each string becuse it has been passed  */
876   gal_list_str_free(list, 0);
877   return out;
878 }
879 
880 
881 
882 
883 
884 /* 'arg' is the value given to an option. It contains multiple strings
885    separated by a comma (','). This function will parse 'arg' and make a
886    'gal_data_t' array of strings from it. The output 'gal_data_t' will be
887    put in 'option->value'. */
888 void *
gal_options_parse_csv_strings(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)889 gal_options_parse_csv_strings(struct argp_option *option, char *arg,
890                               char *filename, size_t lineno, void *junk)
891 {
892   size_t nc;
893   char *c, **strarr;
894   int i, has_space=0;
895   gal_data_t *values;
896   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
897 
898   /* We want to print the stored values. */
899   if(lineno==-1)
900     {
901       /* Set the pointer to the values dataset. */
902       values = *(gal_data_t **)(option->value);
903 
904       /* See if there are any space characters in the final string. */
905       strarr=values->array;
906       for(i=0;i<values->size;++i)
907         if(has_space==0)
908         {
909           for(c=strarr[i];*c!='\0';++c)
910             if(*c==' ' || *c=='\t')
911               {
912                 has_space=1;
913                 break;
914               }
915         }
916 
917       /* If there is a space, the string must start wth quotation marks. */
918       nc = has_space ? 1 : 0;
919       if(has_space) {sstr[0]='"'; sstr[1]='\0';}
920 
921 
922       /* Write each string into the output string */
923       for(i=0;i<values->size;++i)
924         {
925           if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
926             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
927                   "can address the problem. The number of necessary "
928                   "characters in the statically allocated string has become "
929                   "too close to %d", __func__, PACKAGE_BUGREPORT,
930                   GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
931           nc += sprintf(sstr+nc, "%s,", strarr[i]);
932         }
933 
934       /* If there was a space, we need a quotation mark at the end of the
935          string. */
936       if(has_space) { sstr[nc-1]='"'; sstr[nc]='\0'; }
937       else            sstr[nc-1]='\0';
938 
939       /* Copy the string into a dynamically allocated space, because it
940          will be freed later.*/
941       gal_checkset_allocate_copy(sstr, &str);
942       return str;
943     }
944 
945   /* We want to read the user's string. */
946   else
947     {
948       /* If the option is already set, just return. */
949       if(option->set) return NULL;
950 
951       /* Make sure an argument is actually given. */
952       if(*arg=='\0')
953         error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
954                       "given to '--%s'", option->name);
955 
956       /* Read the values. */
957       values=gal_options_parse_csv_strings_raw(arg, filename, lineno);
958 
959       /* Put the values into the option. */
960       *(gal_data_t **)(option->value) = values;
961       return NULL;
962     }
963 }
964 
965 
966 
967 
968 
969 /* Some options can be called multiple times on the command-line, but
970    within the program, all the values must be merged into one. For example
971    '--column=1 --column=2,3 --column=4,5,6'. In this example, the output
972    'gal_list_str_t' will have 3 nodes, but we actually want a list that has
973    6 nodes. */
974 void
gal_options_merge_list_of_csv(gal_list_str_t ** list)975 gal_options_merge_list_of_csv(gal_list_str_t **list)
976 {
977   size_t i;
978   gal_data_t *strs;
979   char *c, **strarr;
980   gal_list_str_t *tmp, *in=*list, *out=NULL;
981 
982   /* Go over each input and add it to the list. */
983   for(tmp=in; tmp!=NULL; tmp=tmp->next)
984     {
985       /* Remove any possibly commented new-line where we have a backslash
986          followed by a new-line character (replace the two characters with
987          two single space characters). This can happen with options have
988          have longer scripts and the user is forced to break the line with
989          a '\' followed by newline. */
990       for(c=tmp->v;*c!='\0';++c)
991         if(*c=='\\' && *(c+1)=='\n') { *c=' '; *(++c)=' '; }
992 
993       /* Read the different comma-separated strings into an array (within a
994          'gal_data_t'). */
995       strs=gal_options_parse_csv_strings_raw(tmp->v, NULL, 0);
996       strarr=strs->array;
997 
998       /* Go through all the items and add the pointers to the output
999          list. We won't re-allocate the string, we'll just set it to NULL
1000          in the array. */
1001       for(i=0;i<strs->size;++i)
1002         {
1003           gal_list_str_add(&out, strarr[i], 0);
1004           strarr[i]=NULL;
1005         }
1006 
1007       /* Clean up. */
1008       gal_data_free(strs);
1009     }
1010 
1011   /* Free the input list and reverse the output list to be in the same
1012      input order. */
1013   gal_list_str_free(in, 1);
1014   gal_list_str_reverse(&out);
1015 
1016   /* Reset the input pointer. */
1017   *list=out;
1018 }
1019 
1020 
1021 
1022 
1023 
1024 /* Parse the given string into a series of size values (integers, stored as
1025    an array of size_t). The output array will be stored in the 'value'
1026    element of the option. The last element of the array is
1027    'GAL_BLANK_SIZE_T' to allow finding the number of elements within it
1028    later (similar to a string which terminates with a '\0' element). */
1029 void *
gal_options_parse_sizes_reverse(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1030 gal_options_parse_sizes_reverse(struct argp_option *option, char *arg,
1031                                 char *filename, size_t lineno, void *junk)
1032 {
1033   int i;
1034   double *v;
1035   gal_data_t *values;
1036   size_t nc, num, *array;
1037   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
1038 
1039   /* We want to print the stored values. */
1040   if(lineno==-1)
1041     {
1042       /* Find the number of elements within the array. */
1043       array = *(size_t **)(option->value);
1044       for(i=0; array[i]!=-1; ++i);
1045       num=i;
1046 
1047       /* Write all the dimensions into the static string. */
1048       nc=0;
1049       for(i=num-1;i>=0;--i)
1050         {
1051           if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
1052             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
1053                   "can address the problem. The number of necessary "
1054                   "characters in the statically allocated string has become "
1055                   "too close to %d", __func__, PACKAGE_BUGREPORT,
1056                   GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
1057           nc += sprintf(sstr+nc, "%zu,", array[i]);
1058         }
1059       sstr[nc-1]='\0';
1060 
1061       /* Copy the string into a dynamically allocated space, because it
1062          will be freed later.*/
1063       gal_checkset_allocate_copy(sstr, &str);
1064       return str;
1065     }
1066 
1067   /* We want to read the user's string. */
1068   else
1069     {
1070       /* If the option is already set, just return. */
1071       if(option->set) return NULL;
1072 
1073       /* Make sure an argument is actually given. */
1074       if(*arg=='\0')
1075         error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
1076                       "given to '--%s'", option->name);
1077 
1078       /* Read the values. */
1079       values=gal_options_parse_list_of_numbers(arg, filename, lineno);
1080 
1081       /* Check if the values are an integer. */
1082       v=values->array;
1083       for(i=0;i<values->size;++i)
1084         {
1085           if(v[i]<0)
1086             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a given "
1087                           "value in '%s' (%g) is not 0 or positive. The "
1088                           "values to the '--%s' option must be positive",
1089                           arg, v[i], option->name);
1090 
1091           if(ceil(v[i]) != v[i])
1092             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a given "
1093                           "value in '%s' (%g) is not an integer. The "
1094                           "values to the '--%s' option must be integers",
1095                           arg, v[i], option->name);
1096         }
1097 
1098       /* Write the values into an allocated size_t array and finish it with
1099          a '-1' so the total number can be found later.*/
1100       num=values->size;
1101       array=gal_pointer_allocate(GAL_TYPE_SIZE_T, num+1, 0, __func__,
1102                                  "array");
1103       for(i=0;i<num;++i) array[num-1-i]=v[i];
1104       array[num] = GAL_BLANK_SIZE_T;
1105 
1106       /* Put the array of size_t into the option, clean up and return.*/
1107       *(size_t **)(option->value) = array;
1108       gal_data_free(values);
1109       return NULL;
1110     }
1111 }
1112 
1113 
1114 
1115 
1116 
1117 /* Parse options with values of a list of numbers. */
1118 void *
gal_options_parse_csv_float64(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1119 gal_options_parse_csv_float64(struct argp_option *option, char *arg,
1120                               char *filename, size_t lineno, void *junk)
1121 {
1122   size_t i, nc;
1123   double *darray;
1124   gal_data_t *values;
1125   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
1126 
1127   /* We want to print the stored values. */
1128   if(lineno==-1)
1129     {
1130       /* Set the pointer to the values dataset. */
1131       values = *(gal_data_t **)(option->value);
1132       darray=values->array;
1133 
1134       /* Write each string into the output string */
1135       nc=0;
1136       for(i=0;i<values->size;++i)
1137         {
1138           if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
1139             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
1140                   "can address the problem. The number of necessary "
1141                   "characters in the statically allocated string has become "
1142                   "too close to %d", __func__, PACKAGE_BUGREPORT,
1143                   GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
1144           nc += sprintf(sstr+nc, "%g,", darray[i]);
1145         }
1146       sstr[nc-1]='\0';
1147 
1148       /* Copy the string into a dynamically allocated space, because it
1149          will be freed later.*/
1150       gal_checkset_allocate_copy(sstr, &str);
1151       return str;
1152     }
1153 
1154   /* We want to read the user's string. */
1155   else
1156     {
1157       /* If the option is already set, just return. */
1158       if(option->set) return NULL;
1159 
1160       /* Make sure an argument is actually given. */
1161       if(*arg=='\0')
1162         error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
1163                       "given to '--%s'", option->name);
1164 
1165       /* Read the values. */
1166       values=gal_options_parse_list_of_numbers(arg, filename, lineno);
1167 
1168       /* Put the values into the option. */
1169       *(gal_data_t **)(option->value) = values;
1170 
1171       /* The return value is only for printing mode, so we can return
1172          NULL after reading is complete. */
1173       return NULL;
1174     }
1175 }
1176 
1177 
1178 
1179 
1180 
1181 /* Two numbers must be provided as an argument. This function will read
1182    them as the sigma-clipping multiple and parameter and store the two in a
1183    2-element array. 'option->value' must point to an already allocated
1184    2-element array of double type. */
1185 void *
gal_options_read_sigma_clip(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1186 gal_options_read_sigma_clip(struct argp_option *option, char *arg,
1187                             char *filename, size_t lineno, void *junk)
1188 {
1189   char *str;
1190   gal_data_t *in;
1191   double *sigmaclip=option->value;
1192 
1193   /* Caller wants to print the option values. */
1194   if(lineno==-1)
1195     {
1196       if( asprintf(&str, "%g,%g", sigmaclip[0], sigmaclip[1])<0 )
1197         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
1198       return str;
1199     }
1200 
1201   /* Caller wants to read the values into memory, so parse the inputs. */
1202   in=gal_options_parse_list_of_numbers(arg, filename, lineno);
1203 
1204   /* Check if there was only two numbers. */
1205   if(in->size!=2)
1206     error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--%s' "
1207                   "option takes two values (separated by a comma) for "
1208                   "defining the sigma-clip. However, %zu numbers were "
1209                   "read in the string '%s' (value to this option).\n\n"
1210                   "The first number is the multiple of sigma, and the "
1211                   "second is either the tolerance (if its is less than "
1212                   "1.0), or a specific number of times to clip (if it "
1213                   "is equal or larger than 1.0).", option->name, in->size,
1214                   arg);
1215 
1216   /* Copy the sigma clip parameters into the space the caller has given (as
1217      the 'value' element of 'option'). */
1218   memcpy(option->value, in->array, 2*sizeof *sigmaclip);
1219 
1220   /* Multiple of sigma must be positive. */
1221   if( sigmaclip[0] <= 0 )
1222     error_at_line(EXIT_FAILURE, 0, filename, lineno, "the first value to "
1223                   "the '--%s' option (multiple of sigma), must be "
1224                   "greater than zero. From the string '%s' (value to "
1225                   "this option), you have given a value of %g for the "
1226                   "first value", option->name, arg, sigmaclip[0]);
1227 
1228   /* Second value must also be positive. */
1229   if( sigmaclip[1] <= 0 )
1230     error_at_line(EXIT_FAILURE, 0, filename, lineno, "the second value "
1231                   "to the '--%s' option (tolerance to stop clipping or "
1232                   "number of clips), must be greater than zero. From "
1233                   "the string '%s' (value to this option), you have "
1234                   "given a value of %g for the second value",
1235                   option->name, arg, sigmaclip[1]);
1236 
1237   /* if the second value is larger or equal to 1.0, it must be an
1238      integer. */
1239   if( sigmaclip[1] >= 1.0f && ceil(sigmaclip[1]) != sigmaclip[1])
1240     error_at_line(EXIT_FAILURE, 0, filename, lineno, "when the second "
1241                   "value to the '--%s' option is >=1, it is interpretted "
1242                   "as an absolute number of clips. So it must be an "
1243                   "integer. However, your second value is a floating "
1244                   "point number: %g (parsed from '%s')", option->name,
1245                   sigmaclip[1], arg);
1246 
1247   /* Clean up and return. */
1248   gal_data_free(in);
1249   return NULL;
1250 }
1251 
1252 
1253 
1254 
1255 
1256 /* Parse name and (string/float64) values:  name,value1,value2,value3,...
1257 
1258    The output is a 'gal_data_t', where the 'name' is the given name and the
1259    values are in its array (of 'char *' or 'float64' type). */
1260 static void *
gal_options_parse_name_and_values(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk,int str0_f641)1261 gal_options_parse_name_and_values(struct argp_option *option, char *arg,
1262                                   char *filename, size_t lineno, void *junk,
1263                                   int str0_f641)
1264 {
1265   size_t i, nc;
1266   double *darray=NULL;
1267   gal_data_t *tmp, *existing, *dataset;
1268   char *c, *name, *values, **strarr=NULL;
1269   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
1270 
1271   /* We want to print the stored values. */
1272   if(lineno==-1)
1273     {
1274       /* Set the value pointer to 'existing'. */
1275       existing=*(gal_data_t **)(option->value);
1276       if(str0_f641) darray = existing->array;
1277       else          strarr = existing->array;
1278 
1279       /* First write the name. */
1280       nc=0;
1281       nc += sprintf(sstr+nc, "%s,", existing->name);
1282 
1283       /* Write the values into a string. */
1284       for(i=0;i<existing->size;++i)
1285         {
1286           if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
1287             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
1288                   "can address the problem. The number of necessary "
1289                   "characters in the statically allocated string has become "
1290                   "too close to %d", __func__, PACKAGE_BUGREPORT,
1291                   GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
1292           if(str0_f641) nc += sprintf(sstr+nc, "%g,", darray[i]);
1293           else          nc += sprintf(sstr+nc, "%s,", strarr[i]);
1294         }
1295 
1296       /* Finish the string. */
1297       sstr[nc-1]='\0';
1298 
1299       /* Copy the string into a dynamically allocated space, because it
1300          will be freed later.*/
1301       gal_checkset_allocate_copy(sstr, &str);
1302       return str;
1303     }
1304   else
1305     {
1306       /* Make sure an argument is actually given. */
1307       if(*arg=='\0')
1308         error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
1309                       "given to '--%s'", option->name);
1310 
1311       /* Parse until the comma or the end of the string.*/
1312       c=arg; while(*c!='\0' && *c!=',') ++c;
1313       values = (*c=='\0') ? NULL : c+1;
1314 
1315       /* Name of the dataset (note that 'c' is already pointing the end of
1316          the 'name' and 'values' points to the next character). So we can
1317          safely set 'c' to '\0' to have the 'name'. */
1318       *c='\0';
1319       gal_checkset_allocate_copy(arg, &name);
1320 
1321       /* Read the values. */
1322       dataset=( str0_f641
1323                 ? gal_options_parse_list_of_numbers(values, filename, lineno)
1324                 : gal_options_parse_list_of_strings(values, filename, lineno));
1325 
1326       /* If there actually was a string of numbers, add the dataset to the
1327          rest. */
1328       if(dataset)
1329         {
1330           dataset->name=name;
1331 
1332           /* Add the given dataset to the end of an existing dataset. */
1333           existing = *(gal_data_t **)(option->value);
1334           if(existing)
1335             {
1336               for(tmp=existing;tmp!=NULL;tmp=tmp->next)
1337                 if(tmp->next==NULL) { tmp->next=dataset; break; }
1338             }
1339           else
1340             *(gal_data_t **)(option->value) = dataset;
1341 
1342           /* For a check.
1343              printf("arg: %s\n", arg);
1344              darray=dataset->array;
1345              for(i=0;i<dataset->size;++i) printf("%f\n", darray[i]);
1346              exit(0);
1347           */
1348         }
1349       else
1350         error(EXIT_FAILURE, 0, "'--%s' requires a series of %s "
1351               "(separated by ',' or ':') following its first argument, "
1352               "please run with '--help' for more information",
1353               option->name, str0_f641?"numbers":"strings");
1354 
1355       /* Our job is done, return NULL. */
1356       return NULL;
1357     }
1358 }
1359 
1360 
1361 
1362 
1363 
1364 void *
gal_options_parse_name_and_strings(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1365 gal_options_parse_name_and_strings(struct argp_option *option, char *arg,
1366                                    char *filename, size_t lineno, void *junk)
1367 {
1368   return gal_options_parse_name_and_values(option, arg, filename, lineno,
1369                                            junk, 0);
1370 }
1371 
1372 
1373 
1374 
1375 
1376 void *
gal_options_parse_name_and_float64s(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1377 gal_options_parse_name_and_float64s(struct argp_option *option, char *arg,
1378                                     char *filename, size_t lineno, void *junk)
1379 {
1380   return gal_options_parse_name_and_values(option, arg, filename, lineno,
1381                                            junk, 1);
1382 }
1383 
1384 
1385 
1386 
1387 
1388 /* Parse strings like this: 'num1,num2:num3,n4:num5,num6' and return it as
1389    a data container array: all elements are simply placed after each other
1390    in the array. */
1391 gal_data_t *
gal_options_parse_colon_sep_csv_raw(char * instring,char * filename,size_t lineno)1392 gal_options_parse_colon_sep_csv_raw(char *instring, char *filename,
1393                                     size_t lineno)
1394 {
1395   char *tailptr;
1396   gal_data_t *out;
1397   char *pt=instring;
1398   size_t dim=0, size;
1399   double read, *array;
1400   gal_list_f64_t *vertices=NULL;
1401 
1402   /* Parse the string. */
1403   while(*pt!='\0')
1404     {
1405       switch(*pt)
1406         {
1407         case ',':
1408           ++dim;
1409           if(dim==2)
1410             error_at_line(EXIT_FAILURE, 0, filename, lineno,
1411                           "Extra ',' in '%s'", instring);
1412           ++pt;
1413           break;
1414         case ':':
1415           if(dim==0)
1416             error_at_line(EXIT_FAILURE, 0, filename, lineno,
1417                           "not enough coordinates for at least "
1418                           "one polygon vertex (in %s)", instring);
1419           dim=0;
1420           ++pt;
1421           break;
1422         default:
1423           break;
1424         }
1425 
1426       /* strtod will skip white spaces if they are before a number,
1427          but not when they are before a : or ,. So we need to remove
1428          all white spaces. White spaces are usually put beside each
1429          other, so if one is encountered, go along the string until
1430          the white space characters finish.  */
1431       if(isspace(*pt))
1432         ++pt;
1433       else
1434         {
1435           /* Read the number: */
1436           read=strtod(pt, &tailptr);
1437 
1438           /* Check if there actually was a number.
1439           printf("\n\n------\n%zu: %f (%s)\n", dim, read, tailptr);
1440           */
1441 
1442           /* Make sure if a number was read at all? */
1443           if(tailptr==pt) /* No number was read! */
1444             error_at_line(EXIT_FAILURE, 0, filename, lineno,
1445                           "%s could not be parsed as a floating point "
1446                           "number", tailptr);
1447 
1448           /* Check if there are no extra characters in the number, for
1449              example we don't have a case like '1.00132.17', or
1450              1.01i:2.0. Such errors are not uncommon when typing large
1451              numbers, and if ignored, they can lead to unpredictable
1452              results, so its best to abort and inform the user. */
1453           if( *tailptr!='\0'
1454               && !isspace(*tailptr)
1455               && strchr(":,", *tailptr)==NULL )
1456             error_at_line(EXIT_FAILURE, 0, filename, lineno,
1457                           "'%s' is an invalid floating point number "
1458                           "sequence in the value to the '--polygon' "
1459                           "option, error detected at '%s'", pt, tailptr);
1460 
1461           /* Add the read coordinate to the list of coordinates. */
1462           gal_list_f64_add(&vertices, read);
1463 
1464           /* The job here is done, start from tailptr */
1465           pt=tailptr;
1466         }
1467     }
1468 
1469   /* Convert the list to an array, put it in a data structure, clean up and
1470      return. */
1471   array=gal_list_f64_to_array(vertices, 1, &size);
1472   out=gal_data_alloc(array, GAL_TYPE_FLOAT64, 1, &size, NULL, 0, -1, 1,
1473                      NULL, NULL, NULL);
1474   gal_list_f64_free(vertices);
1475   return out;
1476 }
1477 
1478 
1479 
1480 
1481 
1482 /* Parse strings that are given to a function in this format
1483    'num1,num2:num3,n4:num5,num6' */
1484 void *
gal_options_parse_colon_sep_csv(struct argp_option * option,char * arg,char * filename,size_t lineno,void * junk)1485 gal_options_parse_colon_sep_csv(struct argp_option *option, char *arg,
1486                                 char *filename, size_t lineno, void *junk)
1487 {
1488   double *darray;
1489   size_t i, nc, size;
1490   gal_data_t *tmp, *dataset, *existing;
1491   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
1492 
1493   /* We want to print the stored values. */
1494   if(lineno==-1)
1495     {
1496       /* Set the value pointer to 'existing'. */
1497       existing=*(gal_data_t **)(option->value);
1498       darray=existing->array;
1499 
1500       /* Start printing the values. */
1501       nc=0;
1502       size=existing->size;
1503       for(i=0;i<size;i+=2)
1504         {
1505           /* Make sure we aren't passing the allocated space. */
1506           if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
1507             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
1508                   "can address the problem. The number of necessary "
1509                   "characters in the statically allocated string has become "
1510                   "too close to %d", __func__, PACKAGE_BUGREPORT,
1511                   GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
1512 
1513           /* Print the two values in the expected format. */
1514           nc += sprintf(sstr+nc, "%.6f,%.6f%s", darray[i], darray[i+1],
1515                         (i==(size-2) ? "" : ":") );
1516         }
1517 
1518       /* Finish the string. */
1519       sstr[nc-1]='\0';
1520 
1521       /* Copy the string into a dynamically allocated space, because it
1522          will be freed later.*/
1523       gal_checkset_allocate_copy(sstr, &str);
1524       return str;
1525     }
1526   else
1527     {
1528       /* Make sure an argument is actually given. */
1529       if(*arg=='\0')
1530         error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
1531                       "given to '--%s'", option->name);
1532 
1533       /* Check if the argument is a string (it contains a ':' or a ',') or
1534          a filename. Then parse the desired format and put it in this
1535          option's pointer. */
1536       if( strchr(arg, ',')==NULL && strchr(arg, ':')==NULL )
1537         dataset=gal_ds9_reg_read_polygon(arg);
1538       else
1539         dataset=gal_options_parse_colon_sep_csv_raw(arg, filename, lineno);
1540 
1541       /* Add the given dataset to the end of an existing dataset. */
1542       existing = *(gal_data_t **)(option->value);
1543       if(existing)
1544         {
1545           for(tmp=existing;tmp!=NULL;tmp=tmp->next)
1546             if(tmp->next==NULL) { tmp->next=dataset; break; }
1547         }
1548       else
1549         *(gal_data_t **)(option->value) = dataset;
1550 
1551       /* In this scenario, there is no NULL value. */
1552       return NULL;
1553     }
1554 }
1555 
1556 
1557 
1558 
1559 
1560 
1561 
1562 
1563 
1564 
1565 
1566 
1567 
1568 
1569 
1570 
1571 /**********************************************************************/
1572 /************              Option actions               ***************/
1573 /**********************************************************************/
1574 /* The option value has been read and put into the 'value' field of the
1575    'argp_option' structure. This function will use the 'range' field to
1576    define a check and abort with an error if the value is not in the given
1577    range. It also takes the 'arg' so it can be used for good error message
1578    (showing the value that could not be read). */
1579 static void
options_sanity_check(struct argp_option * option,char * arg,char * filename,size_t lineno)1580 options_sanity_check(struct argp_option *option, char *arg,
1581                      char *filename, size_t lineno)
1582 {
1583   size_t dsize=1;
1584   char *message=NULL;
1585   int mcflag=GAL_ARITHMETIC_FLAGS_BASIC;
1586   int operator1=GAL_ARITHMETIC_OP_INVALID;
1587   int operator2=GAL_ARITHMETIC_OP_INVALID;
1588   int multicheckop=GAL_ARITHMETIC_OP_INVALID;
1589   gal_data_t *value, *ref1=NULL, *ref2=NULL, *check1, *check2;
1590 
1591   /* Currently, this function is only for numeric types, so if the value is
1592      string type, or its 'range' field is 'GAL_OPTIONS_RANGE_ANY', then
1593      just return without any checks. */
1594   if( option->type==GAL_TYPE_STRING
1595       || option->type==GAL_TYPE_STRLL
1596       || option->range==GAL_OPTIONS_RANGE_ANY )
1597     return;
1598 
1599   /* Put the option value into a data structure. */
1600   value=gal_data_alloc(option->value, option->type, 1, &dsize, NULL,
1601                        0, -1, 1, NULL, NULL, NULL);
1602 
1603   /* Set the operator(s) and operands: */
1604   switch(option->range)
1605     {
1606 
1607     case GAL_OPTIONS_RANGE_GT_0:
1608       message="greater than zero";
1609       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1610                           0, -1, 1, NULL, NULL, NULL);
1611       *(unsigned char *)(ref1->array)=0;
1612       operator1=GAL_ARITHMETIC_OP_GT;
1613       ref2=NULL;
1614       break;
1615 
1616 
1617     case GAL_OPTIONS_RANGE_GE_0:
1618       message="greater or equal to zero";
1619       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1620                           0, -1, 1, NULL, NULL, NULL);
1621       *(unsigned char *)(ref1->array)=0;
1622       operator1=GAL_ARITHMETIC_OP_GE;
1623       ref2=NULL;
1624       break;
1625 
1626 
1627     case GAL_OPTIONS_RANGE_0_OR_1:
1628       message="either 0 or 1";
1629       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1630                           0, -1, 1, NULL, NULL, NULL);
1631       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1632                           0, -1, 1, NULL, NULL, NULL);
1633       *(unsigned char *)(ref1->array)=0;
1634       *(unsigned char *)(ref2->array)=1;
1635 
1636       operator1=GAL_ARITHMETIC_OP_EQ;
1637       operator2=GAL_ARITHMETIC_OP_EQ;
1638       multicheckop=GAL_ARITHMETIC_OP_OR;
1639       break;
1640 
1641 
1642     case GAL_OPTIONS_RANGE_GE_0_LE_1:
1643       message="between zero and one (inclusive)";
1644       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1645                           0, -1, 1, NULL, NULL, NULL);
1646       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1647                           0, -1, 1, NULL, NULL, NULL);
1648       *(unsigned char *)(ref1->array)=0;
1649       *(unsigned char *)(ref2->array)=1;
1650 
1651       operator1=GAL_ARITHMETIC_OP_GE;
1652       operator2=GAL_ARITHMETIC_OP_LE;
1653       multicheckop=GAL_ARITHMETIC_OP_AND;
1654       break;
1655 
1656 
1657     case GAL_OPTIONS_RANGE_GE_0_LT_1:
1658       message="between zero (inclusive) and one (exclusive)";
1659       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1660                           0, -1, 1, NULL, NULL, NULL);
1661       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1662                           0, -1, 1, NULL, NULL, NULL);
1663       *(unsigned char *)(ref1->array)=0;
1664       *(unsigned char *)(ref2->array)=1;
1665 
1666       operator1=GAL_ARITHMETIC_OP_GE;
1667       operator2=GAL_ARITHMETIC_OP_LT;
1668       multicheckop=GAL_ARITHMETIC_OP_AND;
1669       break;
1670 
1671 
1672     case GAL_OPTIONS_RANGE_GT_0_LT_1:
1673       message="between zero and one (not inclusive)";
1674       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1675                           0, -1, 1, NULL, NULL, NULL);
1676       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1677                           0, -1, 1, NULL, NULL, NULL);
1678       *(unsigned char *)(ref1->array)=0;
1679       *(unsigned char *)(ref2->array)=1;
1680 
1681       operator1=GAL_ARITHMETIC_OP_GT;
1682       operator2=GAL_ARITHMETIC_OP_LT;
1683       multicheckop=GAL_ARITHMETIC_OP_AND;
1684       break;
1685 
1686 
1687     case GAL_OPTIONS_RANGE_GT_0_ODD:
1688       message="greater than zero and odd";
1689       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1690                           0, -1, 1, NULL, NULL, NULL);
1691       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1692                           0, -1, 1, NULL, NULL, NULL);
1693       *(unsigned char *)(ref1->array)=0;
1694       *(unsigned char *)(ref2->array)=2;
1695 
1696       operator1=GAL_ARITHMETIC_OP_GT;
1697       operator2=GAL_ARITHMETIC_OP_MODULO;
1698       multicheckop=GAL_ARITHMETIC_OP_AND;
1699       break;
1700 
1701     case GAL_OPTIONS_RANGE_0_OR_ODD:
1702       message="greater than, or equal to, zero and odd";
1703       ref1=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1704                           0, -1, 1, NULL, NULL, NULL);
1705       ref2=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &dsize, NULL,
1706                           0, -1, 1, NULL, NULL, NULL);
1707       *(unsigned char *)(ref1->array)=0;
1708       *(unsigned char *)(ref2->array)=2;
1709 
1710       operator1=GAL_ARITHMETIC_OP_EQ;
1711       operator2=GAL_ARITHMETIC_OP_MODULO;
1712       multicheckop=GAL_ARITHMETIC_OP_OR;
1713       break;
1714 
1715     default:
1716       error(EXIT_FAILURE, 0, "%s: range code %d not recognized",
1717             __func__, option->range);
1718     }
1719 
1720 
1721   /* Use the arithmetic library to check for the condition. We don't want
1722      to free the value or change its value, so when dealing with the value
1723      directly, we won't use the 'GAL_ARITHMETIC_FREE', or
1724      'GAL_ARITHMETIC_INPLACE' flags. But we will do this when there are
1725      multiple checks so from the two check data structures, we only have
1726      one remaining. */
1727   check1=gal_arithmetic(operator1, 1, GAL_ARITHMETIC_FLAG_NUMOK,
1728                         value, ref1);
1729   if(ref2)
1730     {
1731       check2=gal_arithmetic(operator2, 1, GAL_ARITHMETIC_FLAG_NUMOK,
1732                             value, ref2);
1733       check1=gal_arithmetic(multicheckop, 1, mcflag, check1, check2);
1734     }
1735 
1736 
1737   /* If the final check is not successful, then print an error. */
1738   if( *(unsigned char *)(check1->array)==0 )
1739     error_at_line(EXIT_FAILURE, 0, filename, lineno,
1740                   "value to option '%s' must be %s, but the given value "
1741                   "is '%s'. Recall that '%s' is '%s'", option->name,
1742                   message, arg, option->name, option->doc);
1743 
1744 
1745   /* Clean up and finish. Note that we used the actual value pointer in the
1746      data structure, so first we need to set it to NULL, so 'gal_data_free'
1747      doesn't free it, we need it for later (for example to print the option
1748      values). */
1749   value->array=NULL;
1750   gal_data_free(ref1);
1751   gal_data_free(ref2);
1752   gal_data_free(value);
1753   gal_data_free(check1);
1754 }
1755 
1756 
1757 
1758 
1759 
1760 static void
gal_options_read_check(struct argp_option * option,char * arg,char * filename,size_t lineno,struct gal_options_common_params * cp)1761 gal_options_read_check(struct argp_option *option, char *arg, char *filename,
1762                        size_t lineno, struct gal_options_common_params *cp)
1763 {
1764   void *topass;
1765 
1766   /* If a function is defined, leave everything to the function. */
1767   if(option->func)
1768     {
1769       /* For the functions that are defined here (for all programs) and
1770          need the last pointer, we must pass the 'cp' pointer. For the
1771          rest, we must pass the 'cp->program_struct'. */
1772       switch(option->key)
1773         {
1774         case GAL_OPTIONS_KEY_CITE:
1775         case GAL_OPTIONS_KEY_CONFIG:
1776           topass=cp;
1777           break;
1778         default:
1779           topass=cp->program_struct;
1780         }
1781 
1782       /* Call the function to parse the value, flag the option as set and
1783          return (except for the '--config' option, which must always be
1784          unset). */
1785       option->func(option, arg, filename, lineno, topass);
1786       if(option->key!=GAL_OPTIONS_KEY_CONFIG) option->set=GAL_OPTIONS_SET;
1787 
1788       /* The '--config' option is printed for '--checkconfig' by its
1789          function ('gal_options_call_parse_config_file'), so must be
1790          ignored here. */
1791       if(cp->checkconfig && option->key!=GAL_OPTIONS_KEY_CONFIG)
1792         printf("  %-25s%s\n", option->name, arg?arg:"ACTIVATED");
1793       return;
1794     }
1795 
1796 
1797   /* Check if an argument is actually given (only options given on the
1798      command-line can have a NULL arg value). */
1799   if(arg)
1800     {
1801       if(option->type==GAL_TYPE_STRLL)
1802         gal_list_str_add(option->value, arg, 1);
1803       else
1804         {
1805           /* If the option is already set, ignore the given value. */
1806           if(option->set==GAL_OPTIONS_SET)
1807             {
1808               if(cp->checkconfig)
1809                 printf("  %-25s--ALREADY-SET--\n", option->name);
1810               return;
1811             }
1812 
1813           /* Read the string argument into the value. */
1814           if( gal_type_from_string(&option->value, arg, option->type) )
1815             /* Fortunately 'error_at_line' will behave like 'error' when the
1816                filename is NULL (the option was read from a command-line). */
1817             error_at_line(EXIT_FAILURE, 0, filename, lineno,
1818                           "'%s' (value to option '--%s') couldn't be read "
1819                           "into the proper numerical type. Common causes "
1820                           "for this error are:\n"
1821                           "  - It contains non-numerical characters.\n"
1822                           "  - It is negative, but the expected value is "
1823                           "positive.\n"
1824                           "  - It is floating point, but the expected value "
1825                           "is an integer.\n"
1826                           "  - The previous option required a value, but you "
1827                           "forgot to give it one, so the next option's "
1828                           "name(+value, if there are no spaces between them) "
1829                           "is read as the value of the previous option.", arg,
1830                           option->name);
1831 
1832           /* Do a sanity check on the value. */
1833           options_sanity_check(option, arg, filename, lineno);
1834         }
1835     }
1836   else
1837     {
1838       /* If the option is already set, ignore the given value. */
1839       if(option->set==GAL_OPTIONS_SET)
1840         {
1841           if(cp->checkconfig)
1842             printf("  %-25s--ALREADY-SET--\n", option->name);
1843           return;
1844         }
1845 
1846       /* Make sure the option has the type set for options with no
1847          argument. So, give it a value of 1. */
1848       if(option->type==GAL_OPTIONS_NO_ARG_TYPE)
1849         *(uint8_t *)(option->value)=1;
1850       else
1851         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
1852               "correct it. Options with no arguments, must have "
1853               "type '%s'. However, the '%s' option has type %s",
1854               __func__, PACKAGE_BUGREPORT,
1855               gal_type_name(GAL_OPTIONS_NO_ARG_TYPE, 1),
1856               option->name, gal_type_name(option->type, 1));
1857     }
1858 
1859 
1860   /* If the user wanted to check the value. */
1861   if(cp->checkconfig)
1862     printf("  %-25s%s\n", option->name,
1863            (arg && option->type!=GAL_OPTIONS_NO_ARG_TYPE)?arg:"ACTIVATED");
1864 
1865 
1866   /* Flip the 'set' flag to 'GAL_OPTIONS_SET'. */
1867   option->set=GAL_OPTIONS_SET;
1868 }
1869 
1870 
1871 
1872 
1873 
1874 
1875 
1876 
1877 
1878 
1879 
1880 
1881 
1882 
1883 
1884 
1885 
1886 
1887 
1888 /**********************************************************************/
1889 /************            Command-line options           ***************/
1890 /**********************************************************************/
1891 /* Set the value given to the command-line, where we have the integer 'key'
1892    of the option, not its long name as a string. */
1893 error_t
gal_options_set_from_key(int key,char * arg,struct argp_option * options,struct gal_options_common_params * cp)1894 gal_options_set_from_key(int key, char *arg, struct argp_option *options,
1895                          struct gal_options_common_params *cp)
1896 {
1897   size_t i;
1898 
1899   /* Go through all the options and find the one that should keep this
1900      value, then put its value into the appropriate key. Note that the
1901      options array finishs with an all zero element, so we don't need to
1902      know the number before hand.*/
1903   for(i=0;1;++i)
1904     {
1905       /* Check if the key corresponds to this option. */
1906       if( options[i].key==key )
1907         {
1908           /* When options are read from keys (by this function), they are
1909              read from the command-line. On the commandline, the last
1910              invokation of the option is important. Especially in contexts
1911              like scripts, this is important because you can change a given
1912              command-line option (that is not a linked list) by calling it
1913              a second time, instead of going back and changing the first
1914              value.
1915 
1916              As a result, only when searching for options on the
1917              command-line, a second value to the same option will replace
1918              the first one. This will not happen in configuration files. */
1919           if(options[i].set && gal_type_is_list(options[i].type)==0)
1920             options[i].set=GAL_OPTIONS_NOT_SET;
1921 
1922           /* Parse the value. */
1923           gal_options_read_check(&options[i], arg, NULL, 0, cp);
1924 
1925           /* We have found and set the value given to this option, so just
1926              return success (an error_t of 0 means success). */
1927           return 0;
1928         }
1929       else
1930         {
1931           /* The last option has all its values set to zero. */
1932           if(gal_options_is_last(&options[i]))
1933             return ARGP_ERR_UNKNOWN;
1934         }
1935     }
1936 }
1937 
1938 
1939 
1940 
1941 
1942 error_t
gal_options_common_argp_parse(int key,char * arg,struct argp_state * state)1943 gal_options_common_argp_parse(int key, char *arg, struct argp_state *state)
1944 {
1945   struct gal_options_common_params *cp=state->input;
1946 
1947   /* In case the user incorrectly uses the equal sign (for example
1948      with a short format or with space in the long format, then 'arg'
1949      start with (if the short version was called) or be (if the long
1950      version was called with a space) the equal sign. So, here we
1951      check if the first character of arg is the equal sign, then the
1952      user is warned and the program is stopped: */
1953   if(arg && arg[0]=='=')
1954     argp_error(state, "incorrect use of the equal sign ('='). For short "
1955                "options, '=' should not be used and for long options, "
1956                "there should be no space between the option, equal sign "
1957                "and value");
1958 
1959   /* Read the options. */
1960   return gal_options_set_from_key(key, arg, cp->coptions, cp);
1961 }
1962 
1963 
1964 
1965 
1966 
1967 
1968 char *
gal_options_stdin_error(long stdintimeout,int precedence,char * name)1969 gal_options_stdin_error(long stdintimeout, int precedence, char *name)
1970 {
1971   char *out;
1972 
1973   if( asprintf(&out, "no %s!\n\n"
1974                "The %s can be read from a file (specified as an argument), "
1975                "or the standard input.%s Standard input can come from a "
1976                "pipe (output of another program) or typed on the "
1977                "command-line before %ld micro-seconds (configurable with "
1978                "the '--stdintimeout' option).", name, name,
1979                ( precedence
1980                  ? " If both are provided, a file takes precedence."
1981                  : "" ), stdintimeout )<0 )
1982     error(EXIT_FAILURE, 0, "%s: 'asprintf' allocation error", __func__);
1983 
1984   return out;
1985 }
1986 
1987 
1988 
1989 
1990 
1991 /* Make the notice that is printed before program terminates, when no input
1992    is given and Standard input is also available. */
1993 gal_list_str_t *
gal_options_check_stdin(char * inputname,long stdintimeout,char * name)1994 gal_options_check_stdin(char *inputname, long stdintimeout, char *name)
1995 {
1996   gal_list_str_t *lines=inputname ? NULL : gal_txt_stdin_read(stdintimeout);
1997 
1998   /* See if atleast one of the two inputs is given. */
1999   if(inputname==NULL && lines==NULL)
2000     error( EXIT_FAILURE, 0, "%s", gal_options_stdin_error(stdintimeout,
2001                                                           1, name));
2002 
2003   /* Return the output. */
2004   return lines;
2005 }
2006 
2007 
2008 
2009 
2010 
2011 
2012 
2013 
2014 
2015 
2016 
2017 
2018 
2019 
2020 
2021 
2022 
2023 
2024 
2025 
2026 /**********************************************************************/
2027 /************            Configuration files            ***************/
2028 /**********************************************************************/
2029 
2030 /* Read the option and the argument from the line and return.*/
2031 static void
options_read_name_arg(char * line,char * filename,size_t lineno,char ** name,char ** arg)2032 options_read_name_arg(char *line, char *filename, size_t lineno,
2033                       char **name, char **arg)
2034 {
2035   int notyetfinished=1, inword=0, inquote=0;
2036 
2037   /* Initialize name and value: */
2038   *arg=NULL;
2039   *name=NULL;
2040 
2041   /* Go through the characters and set the values: */
2042   do
2043     switch(*line)
2044       {
2045       case ' ': case '\t': case '\v': case '\n': case '\r':
2046         if(inword) /* Only considered in a word, not in a quote*/
2047           {
2048             inword=0;
2049             *line='\0';
2050             if(*arg && inquote==0)
2051               notyetfinished=0;
2052           }
2053         break;
2054       case '#':
2055         notyetfinished=0;
2056         break;
2057       case '"':
2058         if(inword)
2059           error_at_line(EXIT_FAILURE, 0, filename, lineno,
2060                         "Quotes have to be surrounded by whitespace "
2061                         "characters (space, tab, new line, etc).");
2062         if(inquote)
2063           {
2064             *line='\0';
2065             inquote=0;
2066             notyetfinished=0;
2067           }
2068         else
2069           {
2070             if(*name==NULL)
2071               error_at_line(EXIT_FAILURE, 0, filename, lineno,
2072                             "option name should not start with "
2073                             "double quotes (\").");
2074             inquote=1;
2075             *arg=line+1;
2076           }
2077         break;
2078       default:
2079         if(inword==0 && inquote==0)
2080           {
2081             if(*name==NULL)
2082               *name=line;
2083             else  /* *name is set, now assign *arg. */
2084               *arg=line;
2085             inword=1;
2086           }
2087         break;
2088       }
2089   while(*(++line)!='\0' && notyetfinished);
2090 
2091   /* In the last line of the file, there is no new line to be
2092      converted to a '\0' character! So if value has been assigned, we
2093      are not in a quote and the line has finished, it means the given
2094      value has also finished. */
2095   if(*line=='\0' && *arg && inquote==0)
2096     notyetfinished=0;
2097 
2098   /* This was a blank line: */
2099   if(*name==NULL && *arg==NULL)
2100     return;
2101 
2102   /* Name or value were set but not yet finished. */
2103   if(notyetfinished)
2104     error_at_line(EXIT_FAILURE, 0, filename, lineno,
2105                   "line finished before option name and value could "
2106                   "be read.");
2107 }
2108 
2109 
2110 
2111 
2112 
2113 static int
options_set_from_name(char * name,char * arg,struct argp_option * options,struct gal_options_common_params * cp,char * filename,size_t lineno)2114 options_set_from_name(char *name, char *arg,  struct argp_option *options,
2115                       struct gal_options_common_params *cp, char *filename,
2116                       size_t lineno)
2117 {
2118   size_t i;
2119 
2120   /* Go through all the options and find the one that should keep this
2121      value, then put its value into the appropriate key. Note that the
2122      options array finishs with an all zero element, so we don't need to
2123      know the number before hand.*/
2124   for(i=0;1;++i)
2125     {
2126       /* Check if the key corresponds to this option. */
2127       if( options[i].name && !strcmp(options[i].name, name) )
2128         {
2129           /* Ignore this option and its value. This can happen in several
2130              situations:
2131 
2132                - Not all common options are used by all programs. When a
2133                  program doesn't use an option, it will be given an
2134                  'OPTION_HIDDEN' flag. There is no point in reading the
2135                  values of such options.
2136 
2137                - When the option already has a value AND it ISN'T a linked
2138                  list. */
2139           if( options[i].flags==OPTION_HIDDEN
2140               || ( options[i].set
2141                    && !gal_type_is_list(options[i].type ) ) )
2142             {
2143               if(cp->checkconfig)
2144                 printf("  %-25s%s\n", name, ( options[i].flags==OPTION_HIDDEN
2145                                               ? "--IGNORED--"
2146                                               : "--ALREADY-SET--" ) );
2147               return 0;
2148             }
2149 
2150           /* Read the value into the option and do a sanity check. */
2151           gal_options_read_check(&options[i], arg, filename, lineno, cp);
2152 
2153           /* We have found and set the value given to this option, so just
2154              return success (an error_t of 0 means success). */
2155           return 0;
2156         }
2157       else
2158         {
2159           /* The last option has all its values set to zero. If we get to
2160              this point then the given name was not recognized and this
2161              function will return a 1. */
2162           if(gal_options_is_last(&options[i]))
2163             return 1;
2164         }
2165     }
2166 }
2167 
2168 
2169 
2170 
2171 
2172 /* If the last config option has a value which is 1, then in some previous
2173    configuration file the user has asked to stop parsing configuration
2174    files. In that case, don't read this configuration file. */
2175 static int
options_lastconfig_has_been_called(struct argp_option * coptions)2176 options_lastconfig_has_been_called(struct argp_option *coptions)
2177 {
2178   size_t i;
2179 
2180   for(i=0; !gal_options_is_last(&coptions[i]); ++i)
2181     if( coptions[i].key == GAL_OPTIONS_KEY_LASTCONFIG
2182         && coptions[i].set
2183         && *((unsigned char *)(coptions[i].value)) )
2184       return 1;
2185   return 0;
2186 }
2187 
2188 
2189 
2190 
2191 
2192 static void
options_parse_file(char * filename,struct gal_options_common_params * cp,int warning)2193 options_parse_file(char *filename,  struct gal_options_common_params *cp,
2194                    int warning)
2195 {
2196   FILE *fp;
2197   char *line, *name, *arg;
2198   size_t linelen=10, lineno=0;
2199 
2200 
2201   /* If 'lastconfig' was called prior to this file, then just return and
2202      ignore this configuration file. */
2203   if( options_lastconfig_has_been_called(cp->coptions) )
2204     return;
2205 
2206 
2207   /* Open the file. If the file doesn't exist or can't be opened, then just
2208      ignore the configuration file and return. */
2209   errno=0;
2210   fp=fopen(filename, "r");
2211   if(fp==NULL)
2212     {
2213       /* Print a warning  */
2214       if(warning && cp->quiet==0)
2215         error(EXIT_SUCCESS, errno, "%s", filename);
2216       return;
2217     }
2218 
2219 
2220   /* If necessary, print the configuration file name. */
2221   if(cp->checkconfig)
2222     printf("%s:\n", filename);
2223 
2224 
2225   /* Allocate the space necessary to keep a copy of each line as we parse
2226      it. Note that 'getline' is going to later 'realloc' this space to fit
2227      the line length. */
2228   errno=0;
2229   line=malloc(linelen*sizeof *line);
2230   if(line==NULL)
2231     error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'line'",
2232           __func__, linelen*sizeof *line);
2233 
2234 
2235   /* Read the parameters line by line. */
2236   while( getline(&line, &linelen, fp) != -1 )
2237     {
2238       ++lineno;
2239       if( gal_txt_line_stat(line) == GAL_TXT_LINESTAT_DATAROW )
2240         {
2241           /* Get the option name and argument/value. */
2242           options_read_name_arg(line, filename, lineno, &name, &arg);
2243 
2244           /* First look into this program's options, if the option isn't
2245              found there, 'options_set_from_name' will return 1. So the
2246              condition will succeed and we will start looking into the
2247              common options, if it isn't found there either, then report an
2248              error.*/
2249           if( options_set_from_name(name, arg, cp->poptions, cp,
2250                                     filename, lineno) )
2251             if( options_set_from_name(name, arg, cp->coptions, cp,
2252                                       filename, lineno) )
2253               error_at_line(EXIT_FAILURE, 0, filename, lineno,
2254                             "unrecognized option '%s', for the full list of "
2255                             "options, please run with '--help'", name);
2256         }
2257     }
2258 
2259 
2260   /* Close the file. */
2261   errno=0;
2262   if(fclose(fp))
2263     error(EXIT_FAILURE, errno, "%s: couldn't close after reading as "
2264           "a configuration file in %s", filename, __func__);
2265 
2266   /* Clean up and return. */
2267   free(line);
2268 }
2269 
2270 
2271 
2272 
2273 /* This function will be used when the '--config' option is called. */
2274 void *
gal_options_call_parse_config_file(struct argp_option * option,char * arg,char * filename,size_t lineno,void * c)2275 gal_options_call_parse_config_file(struct argp_option *option, char *arg,
2276                                    char *filename, size_t lineno, void *c)
2277 {
2278   struct gal_options_common_params *cp=(struct gal_options_common_params *)c;
2279 
2280   /* The '--config' option is a special function when it comes to
2281      '--checkconfig': we'll have to write its value before interpretting
2282      it. */
2283   if(cp->checkconfig)
2284     {
2285       printf("  %-25s%s\n", option->name, arg);
2286       printf("............................\n");
2287     }
2288 
2289   /* Call the confguration file parser. */
2290   options_parse_file(arg, cp, 1);
2291 
2292   /* Ending boundary of this file's options. */
2293   if(cp->checkconfig)
2294       printf("............................\n");
2295 
2296   /* Just to avoid compiler warnings, then return, note that all pointers
2297      are just copies. */
2298   option=NULL; filename=NULL; lineno=0;
2299   return NULL;
2300 }
2301 
2302 
2303 
2304 
2305 
2306 /* Read the configuration files and put the values of the options not given
2307    into it. The directories containing the configuration files are fixed
2308    for all the programs.
2309 
2310     - 'SYSCONFIG_DIR' is passed onto the library functions at compile time
2311       from the command-line. You can search for it in the outputs of
2312       'make'. The main reason is that we want the the user still has the
2313       chance to change the installation directory after 'configure'.
2314 
2315     - 'USERCONFIG_DIR' is defined in 'config.h'.
2316 
2317     - 'CURDIRCONFIG_DIR' is defined in 'config.h'. */
2318 static void
gal_options_parse_config_files(struct gal_options_common_params * cp)2319 gal_options_parse_config_files(struct gal_options_common_params *cp)
2320 {
2321   char *home;
2322   char *filename;
2323 
2324   /* A small sanity check because in multiple places, we have assumed the
2325      on/off options have a type of 'unsigned char'. */
2326   if(GAL_OPTIONS_NO_ARG_TYPE != GAL_TYPE_UINT8)
2327     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can fix "
2328           "the problem. 'GAL_OPTIONS_NO_ARG_TYPE' must be the "
2329           "'uint8' type", __func__, PACKAGE_BUGREPORT);
2330 
2331   /* The program's current directory configuration file. */
2332   if( asprintf(&filename, ".%s/%s.conf", PACKAGE, cp->program_exec)<0 )
2333     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2334   options_parse_file(filename, cp, 0);
2335   free(filename);
2336 
2337   /* Common options configuration file. */
2338   if( asprintf(&filename, ".%s/%s.conf", PACKAGE, PACKAGE)<0 )
2339     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2340   options_parse_file(filename, cp, 0);
2341   free(filename);
2342 
2343   /* Read the home environment variable. */
2344   home=options_get_home();
2345 
2346   /* The program's user-wide configuration file. */
2347   if( asprintf(&filename, "%s/%s/%s.conf", home, USERCONFIG_DIR,
2348                cp->program_exec)<0 )
2349     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2350   options_parse_file(filename, cp, 0);
2351   free(filename);
2352 
2353   /* Common options user-wide configuration file. */
2354   if( asprintf(&filename, "%s/%s/%s.conf", home, USERCONFIG_DIR, PACKAGE)<0 )
2355     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2356   options_parse_file(filename, cp, 0);
2357   free(filename);
2358 
2359   /* The program's system-wide configuration file. */
2360   if( asprintf(&filename, "%s/%s.conf", SYSCONFIG_DIR, cp->program_exec)<0 )
2361     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2362   options_parse_file(filename, cp, 0);
2363   free(filename);
2364 
2365   /* Common options system-wide configuration file. */
2366   if( asprintf(&filename, "%s/%s.conf", SYSCONFIG_DIR, PACKAGE)<0 )
2367     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2368   options_parse_file(filename, cp, 0);
2369   free(filename);
2370 }
2371 
2372 
2373 
2374 
2375 
2376 static void
options_reverse_lists_check_mandatory(struct gal_options_common_params * cp,struct argp_option * options)2377 options_reverse_lists_check_mandatory(struct gal_options_common_params *cp,
2378                                       struct argp_option *options)
2379 {
2380   size_t i;
2381 
2382   for(i=0; !gal_options_is_last(&options[i]); ++i)
2383     {
2384       if(options[i].set)
2385         switch(options[i].type)
2386           {
2387           case GAL_TYPE_STRLL:
2388             gal_list_str_reverse( (gal_list_str_t **)(options[i].value) );
2389             break;
2390           }
2391       else if(options[i].mandatory==GAL_OPTIONS_MANDATORY)
2392         gal_options_add_to_not_given(cp, &options[i]);
2393     }
2394 }
2395 
2396 
2397 
2398 
2399 
2400 void
gal_options_read_low_level_checks(struct gal_options_common_params * cp)2401 gal_options_read_low_level_checks(struct gal_options_common_params *cp)
2402 {
2403   size_t suggested_mmap=10000000;
2404 
2405   /* If 'numthreads' is 0, use the number of threads available to the
2406      system. */
2407   if(cp->numthreads==0)
2408     cp->numthreads=gal_threads_number();
2409 
2410   /* If 'minmapsize==0' and quiet isn't given, print a warning. */
2411   if(cp->minmapsize==0)
2412     {
2413       fprintf(stderr, "\n\n"
2414               "========= WARNING =========\n"
2415               "Minimum size to map an allocated space outside of RAM is "
2416               "not set, or set to zero. This can greatly slow down the "
2417               "processing of a program or cause strange crashes (recall "
2418               "that the number of files that can be memory-mapped is "
2419               "limited).\n\n"
2420 
2421               "On modern systems (with RAM larger than a giga-byte), it "
2422               "should be fine to set it to %zu (10 million bytes or 10Mb) "
2423               "with the command below. In this manner, only arrays that "
2424               "are larger than this will be memory-mapped and smaller "
2425               "arrays (which are much more numerous) will be allocated and "
2426               "freed in the RAM.\n\n"
2427 
2428               "     --minmapsize=%zu\n\n"
2429 
2430               "[This warning can be disabled with the '--quiet' (or '-q') "
2431               "option.]\n"
2432               "===========================\n\n", suggested_mmap,
2433               suggested_mmap);
2434     }
2435 
2436   /* If the user wanted to check the parsing of configuration files, then
2437      the program must stop here. */
2438   if(cp->checkconfig) exit(0);
2439 }
2440 
2441 
2442 
2443 
2444 
2445 /* Read all configuration files and set common options */
2446 void
gal_options_read_config_set(struct gal_options_common_params * cp)2447 gal_options_read_config_set(struct gal_options_common_params *cp)
2448 {
2449   /* Parse all the configuration files. */
2450   gal_options_parse_config_files(cp);
2451 
2452   /* Reverse the order of all linked list type options so the popping order
2453      is the same as the user's input order. We need to do this here because
2454      when printing those options, their order matters.*/
2455   options_reverse_lists_check_mandatory(cp, cp->poptions);
2456   options_reverse_lists_check_mandatory(cp, cp->coptions);
2457 
2458   /* Abort if any of the mandatory options are not set. */
2459   gal_options_abort_if_mandatory_missing(cp);
2460 
2461   /* Low-level/basic checks before passing control back to program. */
2462   gal_options_read_low_level_checks(cp);
2463 }
2464 
2465 
2466 
2467 
2468 
2469 
2470 
2471 
2472 
2473 
2474 
2475 
2476 
2477 
2478 
2479 
2480 
2481 
2482 
2483 
2484 /**********************************************************************/
2485 /************              Printing/Writing             ***************/
2486 /**********************************************************************/
2487 /* We don't want to print the values of configuration specific options and
2488    the output option. The output value is assumed to be specific to each
2489    input, and the configuration options are for reading the configuration,
2490    not writing it. */
2491 static int
option_is_printable(struct argp_option * option)2492 option_is_printable(struct argp_option *option)
2493 {
2494   /* Use non-key fields:
2495 
2496        - If option is hidden (not relevant to this program).
2497 
2498        - Options with an INVALID type are not to be printed (they are
2499          probably processed to a higher level value with functions). */
2500   if( (option->flags & OPTION_HIDDEN)
2501       || option->type==GAL_TYPE_INVALID )
2502     return 0;
2503 
2504   /* Then check if it is a pre-program option. */
2505   switch(option->key)
2506     {
2507     case GAL_OPTIONS_KEY_OUTPUT:
2508     case GAL_OPTIONS_KEY_CITE:
2509     case GAL_OPTIONS_KEY_PRINTPARAMS:
2510     case GAL_OPTIONS_KEY_CONFIG:
2511     case GAL_OPTIONS_KEY_SETDIRCONF:
2512     case GAL_OPTIONS_KEY_SETUSRCONF:
2513     case GAL_OPTIONS_KEY_LASTCONFIG:
2514       return 0;
2515     }
2516 
2517   /* Everything is fine, print the option. */
2518   return 1;
2519 }
2520 
2521 
2522 
2523 
2524 
2525 /* For a given type, print the value in 'ptr' in a space of 'width'
2526    elements. If 'width==0', then return the width necessary to print the
2527    value. */
2528 static int
options_print_any_type(struct argp_option * option,void * ptr,int type,int width,FILE * fp,struct gal_options_common_params * cp)2529 options_print_any_type(struct argp_option *option, void *ptr, int type,
2530                        int width, FILE *fp,
2531                        struct gal_options_common_params *cp)
2532 {
2533   char *str;
2534 
2535   /* Write the value into a string. */
2536   str = ( option->func
2537           ? option->func(option, NULL, NULL, (size_t)(-1), cp->program_struct)
2538           : gal_type_to_string(ptr, type, 1) );
2539 
2540   /* If only the width was desired, don't actually print the string, just
2541      return its length. Otherwise, print it. */
2542   if(width)
2543     fprintf(fp, "%-*s ", width, str);
2544   else
2545     width=strlen(str);
2546 
2547   /* Free the allocated space and return. */
2548   free(str);
2549   return width;
2550 }
2551 
2552 
2553 
2554 
2555 
2556 /* An option structure is given, return its name and value print
2557    lengths. */
2558 static void
options_correct_max_lengths(struct argp_option * option,int * max_nlen,int * max_vlen,struct gal_options_common_params * cp)2559 options_correct_max_lengths(struct argp_option *option, int *max_nlen,
2560                             int *max_vlen,
2561                             struct gal_options_common_params *cp)
2562 {
2563   int vlen;
2564   gal_list_str_t *tmp;
2565 
2566   /* Invalid types are set for functions that don't save the raw user
2567      input, but do higher-level analysis on them for storing. */
2568   if(option->type==GAL_TYPE_INVALID) return;
2569 
2570   /* Get the length of the value and save its length length if its
2571      larger than the widest value. */
2572   if(gal_type_is_list(option->type))
2573     {
2574       /* A small sanity check. */
2575       if(option->type!=GAL_TYPE_STRLL)
2576         error(EXIT_FAILURE, 0, "%s: currently only string linked lists "
2577               "are acceptable for printing", __func__);
2578 
2579       /* Check each node, one by one. */
2580       for(tmp=*(gal_list_str_t **)(option->value);
2581           tmp!=NULL; tmp=tmp->next)
2582         {
2583           /* Get the length of this node: */
2584           vlen=options_print_any_type(option, &tmp->v, GAL_TYPE_STRING,
2585                                       0, NULL, cp);
2586 
2587           /* If its larger than the maximum length, then put it in. */
2588           if( vlen > *max_vlen )
2589             *max_vlen=vlen;
2590         }
2591     }
2592   else
2593     {
2594       vlen=options_print_any_type(option, option->value, option->type,
2595                                   0, NULL, cp);
2596       if( vlen > *max_vlen )
2597         *max_vlen=vlen;
2598     }
2599 
2600   /* If the name of this option is larger than all existing, set its
2601      length as the largest name length. */
2602   if( strlen(option->name) > *max_nlen )
2603     *max_nlen = strlen(option->name);
2604 }
2605 
2606 
2607 
2608 
2609 
2610 /* To print the options nicely, we need the maximum lengths of the options
2611    and their values. */
2612 static void
options_set_lengths(struct argp_option * poptions,struct argp_option * coptions,int * namelen,int * valuelen,struct gal_options_common_params * cp)2613 options_set_lengths(struct argp_option *poptions,
2614                     struct argp_option *coptions,
2615                     int *namelen, int *valuelen,
2616                     struct gal_options_common_params *cp)
2617 {
2618   int i, max_nlen=0, max_vlen=0;
2619 
2620   /* For program specific options. */
2621   for(i=0; !gal_options_is_last(&poptions[i]); ++i)
2622     if(poptions[i].name && poptions[i].set)
2623       options_correct_max_lengths(&poptions[i], &max_nlen, &max_vlen, cp);
2624 
2625   /* For common options. Note that the options that will not be printed are
2626      in this category, so we also need to check them. The detailed steps
2627      are the same as before. */
2628   for(i=0; !gal_options_is_last(&coptions[i]); ++i)
2629     if( coptions[i].name && coptions[i].set
2630         && option_is_printable(&coptions[i]) )
2631       options_correct_max_lengths(&coptions[i], &max_nlen, &max_vlen, cp);
2632 
2633   /* Save the final values in the output pointers. */
2634   *namelen  = max_nlen;
2635   *valuelen = ( max_vlen < GAL_OPTIONS_MAX_VALUE_LEN
2636                 ? max_vlen
2637                 : GAL_OPTIONS_MAX_VALUE_LEN );
2638 }
2639 
2640 
2641 
2642 
2643 
2644 /* The '#' before the 'doc' string are not required by the configuration
2645    file parser when the documentation string fits in a line. However, when
2646    the 'doc' string is longer than 80 characters, it will be cut between
2647    multiple lines and without the '#', the start of the line will be read
2648    as an option. */
2649 static void
options_print_doc(FILE * fp,const char * doc,int nvwidth)2650 options_print_doc(FILE *fp, const char *doc, int nvwidth)
2651 {
2652   size_t len=strlen(doc);
2653 
2654   /* The '+3' is because of the three extra spaces in this line: one before
2655      the variable name, one after it and one after the value. */
2656   int i, prewidth=nvwidth+3, width=77-prewidth, cwidth;
2657 
2658   /* We only want the formatting when writing to stdout. */
2659   if(len<width)
2660     fprintf(fp, "# %s\n", doc);
2661   else
2662     {
2663       /* If the break is in the middle of a word, then pull set it before
2664          the word starts.*/
2665       cwidth=width; while( doc[cwidth]!=' ' ) --cwidth;
2666       fprintf(fp, "# %.*s\n", cwidth, doc);
2667       i=cwidth;
2668 
2669       /* Go over the rest of the line */
2670       while(i<len)
2671         {
2672           /* Remove any possible space before the first word. */
2673           while( doc[i]==' ' ) ++i;
2674 
2675           /* Check if the line break won't fall in the middle of a word. */
2676           cwidth=width;
2677           if( i+cwidth<len) while( doc[i+cwidth]!=' ' ) --cwidth;
2678           fprintf(fp, "%*s# %.*s\n", prewidth, "", cwidth, &doc[i]);
2679           i+=cwidth;
2680         }
2681     }
2682 }
2683 
2684 
2685 
2686 
2687 
2688 static void
options_print_all_in_group(struct argp_option * options,int groupint,int namelen,int valuelen,FILE * fp,struct gal_options_common_params * cp)2689 options_print_all_in_group(struct argp_option *options, int groupint,
2690                            int namelen, int valuelen, FILE *fp,
2691                            struct gal_options_common_params *cp)
2692 {
2693   size_t i;
2694   gal_list_str_t *tmp;
2695   int namewidth=namelen+1, valuewidth=valuelen+1;
2696 
2697   /* Go over all the options. */
2698   for(i=0; !gal_options_is_last(&options[i]); ++i)
2699     if( options[i].group == groupint           /* Is in this group.        */
2700         && options[i].set                      /* Has been given a value.  */
2701         && option_is_printable(&options[i]) )  /* Is relevant for printing.*/
2702       {
2703         /* Linked lists */
2704         if(gal_type_is_list(options[i].type))
2705           for(tmp=*(gal_list_str_t **)(options[i].value);
2706               tmp!=NULL; tmp=tmp->next)
2707             {
2708               fprintf(fp, " %-*s ", namewidth, options[i].name);
2709               options_print_any_type(&options[i], &tmp->v,
2710                                      GAL_TYPE_STRING, valuewidth,
2711                                      fp, cp);
2712               options_print_doc(fp, options[i].doc, namewidth+valuewidth);
2713             }
2714 
2715         /* Normal types. */
2716         else
2717           {
2718             fprintf(fp, " %-*s ", namewidth, options[i].name);
2719             options_print_any_type(&options[i], options[i].value,
2720                                    options[i].type, valuewidth, fp, cp);
2721             options_print_doc(fp, options[i].doc, namewidth+valuewidth);
2722           }
2723       }
2724 }
2725 
2726 
2727 
2728 
2729 
2730 static void
options_print_all(struct gal_options_common_params * cp,char * dirname,const char * optionname)2731 options_print_all(struct gal_options_common_params *cp, char *dirname,
2732                   const char *optionname)
2733 {
2734   size_t i;
2735   FILE *fp;
2736   int errnum;
2737   time_t rawtime;
2738   char *topicstr, *filename;
2739   gal_list_i32_t *group=NULL;
2740   gal_list_str_t *topic=NULL;
2741   int groupint, namelen, valuelen;
2742   struct argp_option *coptions=cp->coptions, *poptions=cp->poptions;
2743 
2744   /* If the configurations are to be written to a file, then do the
2745      preparations. */
2746   if(dirname)
2747     {
2748       /* Make the host directory if it doesn't already exist. */
2749       if( (errnum=gal_checkset_mkdir(dirname)) )
2750         error(EXIT_FAILURE, errnum, "making %s for configuration files",
2751               dirname);
2752 
2753       /* Prepare the full filename: */
2754       if( asprintf(&filename, "%s/%s.conf", dirname, cp->program_exec)<0 )
2755         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2756 
2757       /* Remove the file if it already exists. */
2758       gal_checkset_writable_remove(filename, 0, 0);
2759 
2760       /* Open the file for writing */
2761       errno=0;
2762       fp=fopen(filename, "w");
2763       if(fp==NULL)
2764         error(EXIT_FAILURE, errno, "%s: couldn't open to write "
2765               "configuration file in %s", dirname, __func__);
2766 
2767       /* Print the basic information as comments in the file first. */
2768       time(&rawtime);
2769       fprintf(fp,
2770               "# %s (%s) %s.\n"
2771               "# Written at %s#\n"
2772               "#  - Empty lines are ignored.\n"
2773               "#  - Lines starting with '#' are ignored.\n"
2774               "#  - The long option name is followed by a value.\n"
2775               "#  - The name and value should be separated by atleast\n"
2776               "#    one white space character (for example space or tab).\n"
2777               "#  - If the value has space, enclose the whole value in\n"
2778               "#    double quotation (\") signs.\n"
2779               "#  - After the value, the rest of the line is ignored.\n"
2780               "#\n# Run 'info %s' for a more elaborate description of each "
2781               "option.\n",
2782               cp->program_name, PACKAGE_NAME, PACKAGE_VERSION,
2783               ctime(&rawtime), cp->program_exec);
2784     }
2785   else fp=stdout;
2786 
2787   /* Parse all the options with a title, note that the 'Input', 'Output'
2788      and 'Operating mode' options are defined in the common options, while
2789      the (possible) other groups are in the program specific options. We
2790      will only be dealing with the 'topics' linked list in this function
2791      and the strings in 'poption' are statically allocated, so its fine to
2792      not waste CPU cycles allocating and freeing.*/
2793   for(i=0; !gal_options_is_last(&coptions[i]); ++i)
2794     if(coptions[i].name==NULL && coptions[i].key==0 && coptions[i].doc)
2795       {
2796         /* The '(char *)' is because '.doc' is a constant and this helps
2797            remove the compiler warning. */
2798         gal_list_i32_add(&group, coptions[i].group);
2799         gal_list_str_add(&topic, (char *)coptions[i].doc, 0);
2800       }
2801   for(i=0; !gal_options_is_last(&poptions[i]); ++i)
2802     if(poptions[i].name==NULL && poptions[i].key==0 && poptions[i].doc)
2803       {
2804         gal_list_i32_add(&group, poptions[i].group);
2805         gal_list_str_add(&topic, (char *)poptions[i].doc, 0);
2806       }
2807 
2808   /* Reverse the linked lists to get the same input order. */
2809   gal_list_str_reverse(&topic);
2810   gal_list_i32_reverse(&group);
2811 
2812   /* Get the maximum width of names and values. */
2813   options_set_lengths(poptions, coptions, &namelen, &valuelen, cp);
2814 
2815   /* Go over each topic and print every option that is in this group. */
2816   while(topic)
2817     {
2818       /* Pop the nodes from the linked list. */
2819       groupint = gal_list_i32_pop(&group);
2820       topicstr = gal_list_str_pop(&topic);
2821 
2822       /* First print the topic, */
2823       fprintf(fp, "\n# %s\n", topicstr);
2824       /*
2825       fprintf(fp, "# ");
2826       i=0; while(i++<strlen(topicstr)) fprintf(fp, "%c", '-');
2827       fprintf(fp, "\n");
2828       */
2829       /* Then, print all the options that are in this group. */
2830       options_print_all_in_group(coptions, groupint, namelen, valuelen,
2831                                  fp, cp);
2832       options_print_all_in_group(poptions, groupint, namelen, valuelen,
2833                                  fp, cp);
2834     }
2835 
2836   /* Let the user know. */
2837   if(dirname)
2838     {
2839       printf("\nNew/updated configuration file:\n\n  %s\n\n"
2840              "You may inspect it with 'cat %s'.\n"
2841              "You may use your favorite text editor to modify it later.\n"
2842              "Or, run %s again with new values for the options and '--%s'.\n",
2843              filename, filename, cp->program_name, optionname);
2844       free(filename);
2845     }
2846 
2847   /* Exit the program successfully */
2848   exit(EXIT_SUCCESS);
2849 }
2850 
2851 
2852 
2853 
2854 
2855 #define OPTIONS_UINT8VAL *(uint8_t *)(cp->coptions[i].value)
2856 void
gal_options_print_state(struct gal_options_common_params * cp)2857 gal_options_print_state(struct gal_options_common_params *cp)
2858 {
2859   size_t i;
2860   unsigned char sum=0;
2861   char *home, *dirname;
2862 
2863 
2864   /* A sanity check is necessary first. We want to make sure that the user
2865      hasn't called more than one of these options. */
2866   for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
2867     if(cp->coptions[i].set)
2868       switch(cp->coptions[i].key)
2869         {
2870         case GAL_OPTIONS_KEY_PRINTPARAMS:
2871         case GAL_OPTIONS_KEY_SETDIRCONF:
2872         case GAL_OPTIONS_KEY_SETUSRCONF:
2873 
2874           /* Note that these options can have a value of 1 (enabled) or 0
2875              (explicitly disabled). Therefore the printing should only be
2876              done if they have a value of 1. This is why we have defined
2877              the 'OPTIONS_UINT8VAL' macro above. */
2878           sum += OPTIONS_UINT8VAL;
2879         }
2880 
2881 
2882   /* See if the values should be printed and if so, where. */
2883   switch(sum)
2884     {
2885     /* No printing option has been called, so just return. */
2886     case 0:  return;
2887 
2888     /* (Only) one of the printing options has been called, so we'll need to
2889        print the values in the proper place. */
2890     case 1:
2891       for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
2892         if(cp->coptions[i].set && OPTIONS_UINT8VAL)
2893           switch(cp->coptions[i].key)
2894             {
2895             case GAL_OPTIONS_KEY_PRINTPARAMS:
2896               options_print_all(cp, NULL, NULL);
2897               break;
2898 
2899             case GAL_OPTIONS_KEY_SETDIRCONF:
2900               if( asprintf(&dirname, ".%s", PACKAGE)<0 )
2901                 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2902               options_print_all(cp, dirname, cp->coptions[i].name);
2903               free(dirname);
2904               break;
2905 
2906             case GAL_OPTIONS_KEY_SETUSRCONF:
2907               home=options_get_home();
2908               if( asprintf(&dirname, "%s/%s", home, USERCONFIG_DIR)<0 )
2909                 error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
2910               options_print_all(cp, dirname, cp->coptions[i].name);
2911               free(dirname);
2912               break;
2913             }
2914       break;
2915 
2916     /* More than one of the printing options has been called. */
2917     default:
2918       error(EXIT_FAILURE, 0, "only one of the 'printparams', 'setdirconf' "
2919             "and 'setusrconf' options can be called in each run");
2920     }
2921 }
2922 
2923 
2924 
2925 
2926 
2927 /* Main working function for common and program specific options. */
2928 static void
options_as_fits_keywords_write(gal_fits_list_key_t ** keys,struct argp_option * options,struct gal_options_common_params * cp)2929 options_as_fits_keywords_write(gal_fits_list_key_t **keys,
2930                                struct argp_option *options,
2931                                struct gal_options_common_params *cp)
2932 {
2933   size_t i;
2934   void *vptr;
2935   int vptrfree;
2936   uint8_t vtype;
2937   char *name, *doc;
2938   gal_list_str_t *tmp;
2939 
2940   for(i=0; !gal_options_is_last(&options[i]); ++i)
2941     if( options[i].set && option_is_printable(&options[i]) )
2942       {
2943         /* Linked lists (multiple calls to an option). */
2944         if(gal_type_is_list(options[i].type))
2945           for(tmp=*(gal_list_str_t **)(options[i].value);
2946               tmp!=NULL; tmp=tmp->next)
2947             {
2948               /* 'name' and 'doc' have a 'const' qualifier. */
2949               gal_checkset_allocate_copy(options[i].name, &name);
2950               gal_checkset_allocate_copy(options[i].doc,  &doc);
2951               gal_fits_key_list_add(keys, GAL_TYPE_STRING, name, 1,
2952                                     tmp->v, 0, doc, 1, NULL, 0);
2953             }
2954         /* Normal types. */
2955         else
2956           {
2957             /* If the option is associated with a special function for
2958                reading and writing, we'll need to write the value as a
2959                string. */
2960             vptrfree=0;
2961             if(options[i].func)
2962               {
2963                 vptrfree=1;
2964                 vtype=GAL_TYPE_STRING;
2965                 vptr=options[i].func(&options[i], NULL, NULL,
2966                                      (size_t)(-1), cp->program_struct);
2967               }
2968             else
2969               {
2970                 vtype=options[i].type;
2971                 vptr = ( vtype==GAL_TYPE_STRING
2972                          ? *((char **)(options[i].value))
2973                          : options[i].value );
2974               }
2975 
2976             /* Write the keyword. Note that 'name' and 'doc' have a 'const'
2977                qualifier. */
2978             gal_checkset_allocate_copy(options[i].name, &name);
2979             if(vtype==GAL_TYPE_STRING && strlen(vptr)>FLEN_KEYWORD)
2980               gal_fits_key_write_filename(name, vptr, keys, 1,
2981                                           cp->quiet);
2982             else
2983               {
2984                 gal_checkset_allocate_copy(options[i].doc,  &doc);
2985                 gal_fits_key_list_add(keys, vtype, name, 1, vptr,
2986                                       vptrfree, doc, 1, NULL, 0);
2987               }
2988           }
2989       }
2990 }
2991 
2992 
2993 
2994 
2995 /* Write all options as FITS keywords. */
2996 void
gal_options_as_fits_keywords(struct gal_options_common_params * cp)2997 gal_options_as_fits_keywords(struct gal_options_common_params *cp)
2998 {
2999   /* Write all the command and program-specific options. */
3000   options_as_fits_keywords_write(&cp->okeys, cp->coptions, cp);
3001   options_as_fits_keywords_write(&cp->okeys, cp->poptions, cp);
3002 
3003   /* Reverse the list (its a first-in-first-out list). */
3004   gal_fits_key_list_reverse(&cp->okeys);
3005 }
3006