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