1 /*********************************************************************
2 Query - Retreive data from a remote data server.
3 Query is part of GNU Astronomy Utilities (Gnuastro) package.
4
5 Original author:
6 Mohammad akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2020-2021, Free Software Foundation, Inc.
9
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24
25 #include <argp.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <gnuastro/fits.h>
32 #include <gnuastro/pointer.h>
33
34 #include <gnuastro-internal/timing.h>
35 #include <gnuastro-internal/options.h>
36 #include <gnuastro-internal/checkset.h>
37 #include <gnuastro-internal/fixedstringmacros.h>
38
39 #include "main.h"
40
41 #include "ui.h"
42 #include "query.h"
43 #include "authors-cite.h"
44
45
46
47
48
49 /**************************************************************/
50 /********* Argp necessary global entities ************/
51 /**************************************************************/
52 /* Definition parameters for the Argp: */
53 const char *
54 argp_program_version = PROGRAM_STRING "\n"
55 GAL_STRINGS_COPYRIGHT
56 "\n\nWritten/developed by "PROGRAM_AUTHORS;
57
58 const char *
59 argp_program_bug_address = PACKAGE_BUGREPORT;
60
61 static char
62 args_doc[] = "DATABASE";
63
64 const char
65 doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" is just a place holder "
66 "used as a minimal set of files and functions necessary for a program in "
67 "Gnuastro. It can be used for learning or as a template to build new "
68 "programs.\n"
69 GAL_STRINGS_MORE_HELP_INFO
70 /* After the list of options: */
71 "\v"
72 PACKAGE_NAME" home page: "PACKAGE_URL;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 /**************************************************************/
94 /********* Initialize & Parse command-line **************/
95 /**************************************************************/
96 static void
ui_initialize_options(struct queryparams * p,struct argp_option * program_options,struct argp_option * gal_commonopts_options)97 ui_initialize_options(struct queryparams *p,
98 struct argp_option *program_options,
99 struct argp_option *gal_commonopts_options)
100 {
101 size_t i;
102 struct gal_options_common_params *cp=&p->cp;
103
104
105 /* Set the necessary common parameters structure. */
106 cp->program_struct = p;
107 cp->poptions = program_options;
108 cp->program_name = PROGRAM_NAME;
109 cp->program_exec = PROGRAM_EXEC;
110 cp->program_bibtex = PROGRAM_BIBTEX;
111 cp->program_authors = PROGRAM_AUTHORS;
112 cp->coptions = gal_commonopts_options;
113
114 /* Program-specific initializations. */
115 p->head = GAL_BLANK_SIZE_T;
116
117 /* Modify common options. */
118 for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
119 {
120 /* Select individually. */
121 switch(cp->coptions[i].key)
122 {
123 case GAL_OPTIONS_KEY_LOG:
124 case GAL_OPTIONS_KEY_TYPE:
125 case GAL_OPTIONS_KEY_SEARCHIN:
126 case GAL_OPTIONS_KEY_QUIETMMAP:
127 case GAL_OPTIONS_KEY_IGNORECASE:
128 case GAL_OPTIONS_KEY_NUMTHREADS:
129 case GAL_OPTIONS_KEY_MINMAPSIZE:
130 case GAL_OPTIONS_KEY_STDINTIMEOUT:
131 cp->coptions[i].flags=OPTION_HIDDEN;
132 break;
133 }
134
135 /* Select by group. */
136 switch(cp->coptions[i].group)
137 {
138 case GAL_OPTIONS_GROUP_TESSELLATION:
139 cp->coptions[i].doc=NULL; /* Necessary to remove title. */
140 cp->coptions[i].flags=OPTION_HIDDEN;
141 break;
142 }
143 }
144 }
145
146
147
148
149
150 /* Fixed string */
151 #define UI_NODATABASE "Please use the '--database' ('-d') option to " \
152 "specify your desired database, see manual ('info gnuastro " \
153 "astquery' command) for the current databases, here is the list " \
154 "of acceptable values (with their web-based search URLs):\n\n" \
155 " astron https://vo.astron.nl/\n" \
156 " gaia https://gea.esac.esa.int/archive\n" \
157 " ned https://ned.ipac.caltech.edu/tap/sync\n" \
158 " vizier http://vizier.u-strasbg.fr/viz-bin/VizieR\n"
159
160
161
162
163 /* Parse a single option: */
164 error_t
parse_opt(int key,char * arg,struct argp_state * state)165 parse_opt(int key, char *arg, struct argp_state *state)
166 {
167 struct queryparams *p = state->input;
168
169 /* Pass 'gal_options_common_params' into the child parser. */
170 state->child_inputs[0] = &p->cp;
171
172 /* In case the user incorrectly uses the equal sign (for example
173 with a short format or with space in the long format, then 'arg'
174 start with (if the short version was called) or be (if the long
175 version was called with a space) the equal sign. So, here we
176 check if the first character of arg is the equal sign, then the
177 user is warned and the program is stopped: */
178 if(arg && arg[0]=='=')
179 argp_error(state, "incorrect use of the equal sign ('='). For short "
180 "options, '=' should not be used and for long options, "
181 "there should be no space between the option, equal sign "
182 "and value");
183
184 /* Set the key to this option. */
185 switch(key)
186 {
187 /* Read the non-option tokens (arguments): */
188 case ARGP_KEY_ARG:
189 /* The user may give a shell variable that is empty! In that case
190 'arg' will be an empty string! We don't want to account for such
191 cases (and give a clear error that no input has been given). */
192 if(arg[0]!='\0') p->databasestr=arg;
193 break;
194
195 /* This is an option, set its value. */
196 default:
197 return gal_options_set_from_key(key, arg, p->cp.poptions, &p->cp);
198 }
199
200 return 0;
201 }
202
203
204
205
206
207 char *
ui_strlist_to_str(gal_list_str_t * input)208 ui_strlist_to_str(gal_list_str_t *input)
209 {
210 char *out=NULL;
211 gal_list_str_t *node;
212 size_t n, nn, nnodes=0, alllen=0;
213
214 /* First calculate the full length of all nodes. */
215 for(node=input; node!=NULL; node=node->next)
216 {
217 /* We'll add two extra for each. One for the ',' that must come in
218 between it and the next one. One just for a buffer, incase we
219 haven't accounted for something. */
220 alllen += strlen(node->v) + 2;
221 ++nnodes;
222 }
223
224 /* Allocate the output string. */
225 out=gal_pointer_allocate(GAL_TYPE_STRING, alllen, 1, "out", __func__);
226
227 /* Write all the strings into the allocated space. */
228 n=nn=0;
229 for(node=input; node!=NULL; node=node->next)
230 {
231 if(nn++==nnodes-1)
232 sprintf(out+n, "%s", node->v);
233 else
234 n += sprintf(out+n, "%s,", node->v);
235 }
236
237 /* Return the merged string. */
238 return out;
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 /**************************************************************/
261 /*************** Sanity Check *******************/
262 /**************************************************************/
263 /* Read and check ONLY the options. When arguments are involved, do the
264 check in 'ui_check_options_and_arguments'. */
265 static void
ui_read_check_only_options(struct queryparams * p)266 ui_read_check_only_options(struct queryparams *p)
267 {
268 size_t i;
269 double *darray;
270 gal_data_t *tmp;
271 int keepinputdir;
272 char *suffix, *rdsuffix, *basename;
273
274 /* See if database has been specified. */
275 if(p->databasestr==NULL)
276 error(EXIT_FAILURE, 0, "no input database! " UI_NODATABASE);
277
278 /* Convert the given string into a code. */
279 if( !strcmp(p->databasestr, "astron") ) p->database=QUERY_DATABASE_ASTRON;
280 else if( !strcmp(p->databasestr, "gaia") ) p->database=QUERY_DATABASE_GAIA;
281 else if( !strcmp(p->databasestr, "ned") ) p->database=QUERY_DATABASE_NED;
282 else if( !strcmp(p->databasestr, "vizier") ) p->database=QUERY_DATABASE_VIZIER;
283 else
284 error(EXIT_FAILURE, 0, "'%s' is not a recognized database.\n\n"
285 "For the full list of recognized databases, please see the "
286 "documentation (with the command 'info astquery')", p->databasestr);
287
288 /* If '--limitinfo' is given, but the string is empty (possibly due to a
289 shell variable that wasn't set), remove it. */
290 if(p->limitinfo && p->limitinfo[0]=='\0')
291 {
292 free(p->limitinfo);
293 p->limitinfo=NULL;
294 for(i=0; !gal_options_is_last(&p->cp.poptions[i]); ++i)
295 if( p->cp.poptions[i].key == UI_KEY_LIMITINFO )
296 p->cp.poptions[i].set=GAL_OPTIONS_NOT_SET;
297 }
298
299 /* If '--ccol' is given, first merge all possible calls to it, confirm
300 that there are only two values and put them into the 'ra_name' and
301 'dec_name' variables. */
302 if(p->ccol)
303 {
304 gal_options_merge_list_of_csv(&p->ccol);
305 if(gal_list_str_number(p->ccol)!=2)
306 error(EXIT_FAILURE, 0, "2 values should be given to '--ccol', "
307 "but you have given %zu values (possibly in multiple calls "
308 "to '--ccols'). Recall that '--ccol' is the coordinate "
309 "column (usually RA and Dec). You can either put them in "
310 "one call (for example '--ccol=ra,dec') or in two (for "
311 "example '--ccol=ra --ccol=dec')",
312 gal_list_str_number(p->ccol));
313 p->ra_name=p->ccol->v;
314 p->dec_name=p->ccol->next->v;
315 }
316
317 /* If '--noblank' or '--sort' are given (possibly multiple times, each
318 with multiple column names) break it up into individual names. */
319 if(p->sort) gal_options_merge_list_of_csv(&p->sort);
320 if(p->noblank) gal_options_merge_list_of_csv(&p->noblank);
321
322 /* Make sure that '--query' and '--center' are not called together. */
323 if(p->query && (p->center || p->overlapwith) )
324 error(EXIT_FAILURE, 0, "the '--query' option cannot be called together "
325 "together with '--center' or '--overlapwith'");
326
327 /* Overlapwith cannot be called with the manual query. */
328 if( p->overlapwith && (p->center || p->width || p->radius) )
329 error(EXIT_FAILURE, 0, "the '--overlapwith' option cannot be called "
330 "with the manual region specifiers ('--center', '--width' or "
331 "'--radius')");
332
333 /* The radius and width cannot be called together. */
334 if(p->radius && p->width)
335 error(EXIT_FAILURE, 0, "the '--radius' and '--width' options cannot be "
336 "called together");
337
338 /* If radius is given, it should be one value and positive. */
339 if(p->radius)
340 {
341 if(p->radius->size>1)
342 error(EXIT_FAILURE, 0, "only one value can be given to '--radius' "
343 "('-r') option");
344
345 if( ((double *)(p->radius->array))[0]<0 )
346 error(EXIT_FAILURE, 0, "the '--radius' option value cannot be negative");
347 }
348
349 /* Make sure the range values are reasonable. */
350 i=0;
351 if(p->range)
352 for(tmp=p->range; tmp!=NULL; tmp=tmp->next)
353 {
354 /* Basic preparations. */
355 ++i;
356 darray=tmp->array;
357
358 /* Make sure only two values are given. */
359 if(tmp->size!=2)
360 error(EXIT_FAILURE, 0, "two values (separated by ',' or ':') "
361 "should be given to '--range'. But %zu values were given "
362 "to the %zu%s call of this option (recall that the first "
363 "value should be the column name in the given dataset)",
364 tmp->size, i,
365 i==1 ? "st" : i==2 ? "nd" : i==3 ? "rd" : "th");
366
367 /* Make sure the first value is large than the second. */
368 if(darray[0]>darray[1])
369 error(EXIT_FAILURE, 0, "the first value of '--range' "
370 "should be smaller than, or equal to, the second, "
371 "but %g>%g", darray[0], darray[1]);
372
373 /* None of the values should be 'nan'. */
374 if( isnan(darray[0]) || isnan(darray[1]) )
375 error(EXIT_FAILURE, 0, "values to '--range' cannot be NaN");
376
377 /* ADQL doesn't recognize 'inf', so if the user gives '-inf' or
378 'inf', change it to the smallest/largest possible floating point
379 number. */
380 if( isinf(darray[0]) == -1 ) darray[0] = -FLT_MAX;
381 if( isinf(darray[1]) == 1 ) darray[1] = FLT_MAX;
382 }
383
384 /* Make sure the widths are reasonable. */
385 if(p->width && p->center)
386 {
387 /* Width should have the same number of elements as the center
388 coordinates */
389 if( p->width->size > 1 && p->width->size != p->center->size )
390 error(EXIT_FAILURE, 0, "'--width' should either have a single "
391 "value (used for all dimensions), or one value for each "
392 "dimension. However, you have provided %zu coordinate "
393 "values, and %zu width values", p->center->size,
394 p->width->size);
395
396 /* All values must be positive. */
397 for(i=0;i<p->width->size;++i)
398 if( ((double *)(p->width->array))[i]<0 )
399 error(EXIT_FAILURE, 0, "the '--width' option value(s) cannot "
400 "be negative");
401 }
402
403 /* Make sure that the output name is in a writable location and that it
404 doesn't exist. If it exists, and the user hasn't called
405 '--dontdelete', then delete the existing file. */
406 gal_checkset_writable_remove(p->cp.output, p->cp.keep,
407 p->cp.dontdelete);
408
409 /* Set the suffix of the default download names for NED (since extinction
410 is given only in VOTable, with an '.xml' suffix). */
411 if( p->database==QUERY_DATABASE_NED
412 && p->datasetstr
413 && !strcmp(p->datasetstr, "extinction") )
414 {
415 suffix=".xml";
416 rdsuffix="-raw-download.xml";
417 }
418 else
419 {
420 suffix=".fits";
421 rdsuffix="-raw-download.fits";
422 }
423
424 /* Currently Gnuastro doesn't read or write XML files (VOTable). So if
425 the downloaded file is an XML file but the user hasn't given an XML
426 suffix, abort and inform the user. */
427 if(p->cp.output)
428 {
429 if( !strcmp(suffix,".xml")
430 && strcmp(&p->cp.output[strlen(p->cp.output)-4], ".xml") )
431 error(EXIT_FAILURE, 0, "this dataset's output is a VOTable (with "
432 "an '.xml' suffix). However, Gnuastro doesn't yet support "
433 "VOTable, so it won't do any checks and corrections on "
434 "the downloaded file. Please give an output name with an "
435 "'.xml' suffix to continue");
436 }
437
438 /* Set the name for the downloaded and final output name. These are due
439 to an internal low-level processing that will be done on the raw
440 downloaded file. */
441 else
442 {
443 basename=gal_checkset_malloc_cat(p->databasestr, suffix);
444 p->cp.output=gal_checkset_make_unique_suffix(basename, suffix);
445 free(basename);
446 }
447
448 /* Currently we don't interally process VOTable (in '.xml' suffix) files,
449 so to keep the next steps un-affected, we'll set Query to not delete
450 the raw download and copy the name of the output into the raw
451 download. */
452 if( !strcmp(suffix, ".xml") )
453 {
454 p->keeprawdownload=1;
455 gal_checkset_allocate_copy(p->cp.output, &p->downloadname);
456 }
457 else
458 {
459 /* Make sure the output name doesn't exist (and report an error if
460 '--dontdelete' is called. Just note that for the automatic output,
461 we are basing that on the output, not the input. So we are
462 temporarily activating 'keepinputdir'. */
463 keepinputdir=p->cp.keepinputdir;
464 p->cp.keepinputdir=1;
465 gal_checkset_writable_remove(p->cp.output, 0, p->cp.dontdelete);
466 p->downloadname=gal_checkset_automatic_output(&p->cp, p->cp.output,
467 rdsuffix);
468 p->cp.keepinputdir=keepinputdir;
469 }
470 }
471
472
473
474
475
476 static void
ui_check_options_and_arguments(struct queryparams * p)477 ui_check_options_and_arguments(struct queryparams *p)
478 {
479
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501 /**************************************************************/
502 /*************** Preparations *******************/
503 /**************************************************************/
504 static void
ui_preparations(struct queryparams * p)505 ui_preparations(struct queryparams *p)
506 {
507
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528 /**************************************************************/
529 /************ Set the parameters *************/
530 /**************************************************************/
531 void
ui_read_check_inputs_setup(int argc,char * argv[],struct queryparams * p)532 ui_read_check_inputs_setup(int argc, char *argv[], struct queryparams *p)
533 {
534 struct gal_options_common_params *cp=&p->cp;
535
536
537 /* Just to avoid warning on no minmapsize. It is irrelevant here. */
538 p->cp.minmapsize=-1;
539
540
541 /* Include the parameters necessary for argp from this program ('args.h')
542 and for the common options to all Gnuastro ('commonopts.h'). We want
543 to directly put the pointers to the fields in 'p' and 'cp', so we are
544 simply including the header here to not have to use long macros in
545 those headers which make them hard to read and modify. This also helps
546 in having a clean environment: everything in those headers is only
547 available within the scope of this function. */
548 #include <gnuastro-internal/commonopts.h>
549 #include "args.h"
550
551
552 /* Initialize the options and necessary information. */
553 ui_initialize_options(p, program_options, gal_commonopts_options);
554
555
556 /* Read the command-line options and arguments. */
557 errno=0;
558 if(argp_parse(&thisargp, argc, argv, 0, 0, p))
559 error(EXIT_FAILURE, errno, "parsing arguments");
560
561
562 /* Read the configuration files and set the common values. */
563 gal_options_read_config_set(&p->cp);
564
565
566 /* Read the options into the program's structure, and check them and
567 their relations prior to printing. */
568 ui_read_check_only_options(p);
569
570
571 /* Print the option values if asked. Note that this needs to be done
572 after the option checks so un-sane values are not printed in the
573 output state. */
574 gal_options_print_state(&p->cp);
575
576
577 /* Prepare all the options as FITS keywords to write in output later. */
578 gal_options_as_fits_keywords(&p->cp);
579
580
581 /* Check that the options and arguments fit well with each other. Note
582 that arguments don't go in a configuration file. So this test should
583 be done after (possibly) printing the option values. */
584 ui_check_options_and_arguments(p);
585
586
587 /* Read/allocate all the necessary starting arrays. */
588 ui_preparations(p);
589 }
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610 /**************************************************************/
611 /************ Free allocated, report *************/
612 /**************************************************************/
613 void
ui_free_report(struct queryparams * p,struct timeval * t1)614 ui_free_report(struct queryparams *p, struct timeval *t1)
615 {
616 /* Free the allocated arrays: */
617 free(p->cp.hdu);
618 free(p->cp.output);
619 free(p->downloadname);
620 }
621