1# Common funcitons of Bash auto-completion in Gnuastro. For more details on 2# completion, see the "autocomplete feature" section under the "Developing" 3# chapter of Gnuastro's manual and the comments below. 4# 5# This script contains generic functions that can be used by all the 6# programs. Each program also has its own '*-complete.bash' file that will 7# use the generic functions here and define all the internal variables that 8# these functions take as input/output. During the installation, all those 9# '*-complete.bash' files will be appended to this (and installed as one 10# file to be loaded into the user's '.bashrc'). 11# 12# Because the combined file is loaded into the user's Bash environment 13# every time, we don't want to complicate the user's environment with 14# global environment variables. As a result, all the functions used in the 15# Bash auto-completion should be 'local' (to that particular function and 16# the functions it calls). 17# 18# To debug/test this script, you can take these steps after building 19# Gnuastro in the program's 'bin/progname/astprogname-complete.bash' 20# file. Building Gnuastro is necessary because one of the files 21# 22# 1. Uncomment the two 'source' lines in the program's completion 23# script. 24# 2. Correct the un-commented locations. Note that the second is an 25# automatically built file (it is not under version control or in the 26# source directory). So in case you have defined a separate build 27# directory, give that directory. If you are building within your 28# source (which is generally a bad idea!), it will be the same 29# directory. 30# 3. Give a value to the 'gnuastro_prefix' variable within 31# '_gnuastro_autocomplete_astprogname'. This is the location that 32# Gnuastro is installed in your OS (usually '/usr/local/bin'). 33# 4. Activate the program's script with this command in the terminal you 34# are testing: 'source bin/progname/astprogname-complete.bash' (here 35# assuming you are in the top Gnuastro source directory, you can run 36# it from anywhere, just correct the locatation). 37# 38# Original author: 39# Pedram Ashofteh Ardakani <pedramardakani@pm.me> 40# Contributing author(s): 41# Mohammad Akhlaghi <mohammad@akhlaghi.org> 42# Copyright (C) 2021 Free Software Foundation, Inc. 43# 44# Gnuastro is free software: you can redistribute it and/or modify it under 45# the terms of the GNU General Public License as published by the Free 46# Software Foundation, either version 3 of the License, or (at your option) 47# any later version. 48# 49# Gnuastro is distributed in the hope that it will be useful, but WITHOUT 50# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 51# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 52# more details. 53# 54# You should have received a copy of the GNU General Public License along 55# with Gnuastro. If not, see <http://www.gnu.org/licenses/>. 56 57 58 59 60 61####################################################################### 62############ Options and general operating mode ############ 63####################################################################### 64 65# Basic initialization. 66_gnuastro_autocomplete_initialize(){ 67 68 # Initialize the completion response with null 69 COMPREPLY=(); 70 71 # Variable "current", is the current word being completed. "$2" is the 72 # default value for the current word in completion scripts. But we are 73 # using the longer form: "${COMP_WORDS[COMP_CWORD]}" for clarity. 74 current="${COMP_WORDS[COMP_CWORD]}" 75 if [ "$current" = "=" ]; then 76 77 # The equal sign '=' raises complexities when filling suggestions 78 # for long options. Things will work out fine when they are simply 79 # ignored. 80 current="" 81 82 fi 83 84 # Variable "prev", is one word before the one being completed. By 85 # default, this is set as "$3" in completion scripts. But we are using 86 # the longer form: "${COMP_WORDS[COMP_CWORD-1]}" to avoid confusions 87 # with the arguments of our internal functions. Note that Bash will 88 # return the '=' sign as a separate component of the line, so in that 89 # case, we want the second-last word. 90 prev="${COMP_WORDS[COMP_CWORD-1]}" 91 if [ "$prev" = "=" ]; then 92 93 # While a user is writing a long option's argument, the previous 94 # word will be the equal sign '='. This is not helpful at all. But 95 # looking at a word just before '=' helps us understand which long 96 # option is being called upon. 97 prev="${COMP_WORDS[COMP_CWORD-2]}" 98 99 fi 100} 101 102 103 104 105 106# List all the options of the given program (an '=' is kept for the options 107# that need a value). In the output of '--help', option lines have these 108# properties: 109# 110# - The full line starts with two empty spaces. 111# - The first non-white character is a '-'. 112# - It contains a long option formated like '--XXXX'. 113# 114# But some options have a short version (which we ignore in 115# autocompletion), so if the first word ends with a ',' the option name we 116# want to show is the second word. 117_gnuastro_autocomplete_option_list(){ 118 options_all=$("${COMP_WORDS[0]}" --help \ 119 | awk '/^ / && $1 ~ /^-/ && /--+[a-zA-Z0-9]*/ { \ 120 if($1 ~ /,$/) name=$2; \ 121 else name=$1; \ 122 print name}' \ 123 | sed -e's|=.*|=|'); 124} 125 126 127 128 129 130# Return successfully if the previous token (specified as first argument) 131# is an option that requires a value and store the option name if it is. 132# 133# INPUT: 134# 1) [as argument] String to search for. 135# *) [as variable] 'options_all' (in case the list of options is 136# already read) 137_gnuastro_autocomplete_string_is_valued_option(){ 138 139 # For easy reading. 140 local string=$1 141 142 # If the first character of the string isn't a '-', then its not an 143 # option and there is no need to do any futher checks (and slow down 144 # the output) so we can safely return failure. 145 if [ ${string:0:1} != "-" ]; then return 1; fi 146 147 # List of option names (with an '=' after those that need a value. 148 if [ x"$options_all" = x ]; then 149 _gnuastro_autocomplete_option_list 150 fi 151 152 # Go over the option list and see if they match the '$1'. 153 for option in $options_all; do 154 if [[ $option = $string=* ]]; then return 0; fi 155 done 156 157 # If control reaches here, then return failure (1). 158 return 1 159} 160 161 162 163 164 165# See if the current word is an argument or option value. 166_gnuastro_autocomplete_mode_arg_optval(){ 167 168 # If the previous token is the first token, then this is an 169 # argument, no need for any further checks. 170 if [ $prev = "${COMP_WORDS[0]}" ]; then 171 argument=$current 172 173 # If the previous token is an option that needs a value, then this is 174 # an option value, this function will set 'option_name' and 175 # 'option_name_complete' if necessary. 176 elif _gnuastro_autocomplete_string_is_valued_option $prev; then 177 option_name=$prev 178 option_value=$current 179 option_name_complete=1 180 181 # The previous token wasn't an option that required a value, so this is 182 # an argument. 183 else 184 argument=$current 185 fi 186} 187 188 189 190 191 192# See if this is an argument, option name or option value. This function 193# will fill 'argument', 'option_name' and 'option_value' (they should be 194# initialized to an empty string before it). 195# 196# option_value=FULL: We are busy completing an option value. 197# option_name=FULL: Can mean different meanings. 198# { 199# option_name_complete==0: We are still filling the option name. 200# option_name_complete==1: We are starting the option values. 201# } 202# argument=FULL: We are busy filling an argument. 203# argument=EMPTY: We haven't started writing an argument yet. 204_gnuastro_autocomplete_mode(){ 205 206 # Local variable necessary only in this function. 207 local namevalue="" 208 209 # If the current word is empty, it may be an argument, or value of an 210 # option (in case a value-required option is given before). 211 if [ x$current = x ]; then 212 _gnuastro_autocomplete_mode_arg_optval 213 214 # The current word isn't empty. 215 else 216 217 # If the current word starts with a '-', it is an option name or 218 # 'name=value' pair. 219 if [ ${current:0:1} = "-" ]; then 220 221 # If there is an equal sign, then we should separate the option 222 # name from the value and keep 223 if [[ $current = *=* ]]; then 224 225 # By setting the "internal field separator" (IFS) to '=' 226 # and using 'read', we can separate the strings before and 227 # after the equal sign. 228 IFS="=" read -ra namevalue <<< $current 229 option_name=${namevalue[0]} 230 option_name_complete=1 231 232 # If the value isn't written yet, (for example '--hdu='), 233 # then the second string will just be an '='. But no value 234 # is given yet, so 'option_value' should be empty. 235 option_value=${namevalue[1]} 236 if [ x$option_value = x"\=" ]; then option_value=""; fi 237 else 238 option_name=$current 239 option_name_complete=0 240 fi 241 242 # The current word didn't start with a '-', so it may be an 243 # argument or option value. 244 else 245 # Bash may separate the '=' in 'name=value' tokens. In this 246 # scenario, when the user only gives 'name=' and presses TAB, 247 # then 'current' will be '='. In this case, we should just set 248 # it to empty. 249 if [ $current = "=" ]; then current=""; fi 250 251 # Check to see if its an argument or option value. 252 _gnuastro_autocomplete_mode_arg_optval 253 fi 254 255 fi 256} 257 258 259 260 261# Given a certain option (as first argument), find the value that the user 262# has given for it. 263# 264# OUTPUT: 265# read_option_value 266_gnuastro_autocomplete_read_option_value(){ 267 268 # Inputs: 269 local read_option_name=$1 270 271 # Initialize the output (defined as 'local' before this). 272 read_option_value="" 273 274 # Parse through the given command-line and find the value. 275 local option_found=0 276 for word in ${COMP_WORDS[*]}; do 277 278 # Ignore the program name (first word), current (last) word and any 279 # '=' signs. 280 if [ x$word = x${COMP_WORDS[0]} ] \ 281 || [ x$word = x$current ] \ 282 || [ x$word = x"=" ]; then 283 local just_a_place_holder=1 284 else 285 # If the 'option_found' flag is set, this is the answer, set it 286 # and return (this has to be *before* the place that we set 287 # 'option_found'). 288 if [ $option_found = 1 ]; then 289 read_option_value="$word"; 290 return 0 291 fi 292 293 # If this word is the desired option, set the 'option_found' 294 # flag so the next word is taken as the value. 295 if [ x$word = x$read_option_name ]; then option_found=1; fi 296 fi 297 done 298} 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319####################################################################### 320############ Files ############ 321####################################################################### 322 323# Check if the given file is a FITS file (that can actually be 324# opened). Note that FITS files have many possible extensions (see the 325# 'gal_fits_name_is_fits' function in 'lib/fits.c'). 326_gnuastro_autocomplete_is_fits(){ 327 if "$gnuastro_prefix"/astfits "$1" &> /dev/null; then return 0; 328 else return 1; 329 fi 330} 331 332 333 334 335 336# Return successfully if argument (a FITS file) has a image HDU. 337_gnuastro_autocomplete_fits_has_image(){ 338 if _gnuastro_autocomplete_is_fits "$1"; then 339 if [ $("$gnuastro_prefix"/astfits "$1" --hasimagehdu) = 1 ]; then 340 return 0 341 fi 342 fi 343 return 1 344} 345 346 347 348 349 350# Return successfully if argument (a FITS file) has a table HDU. 351_gnuastro_autocomplete_fits_has_table(){ 352 if _gnuastro_autocomplete_is_fits "$1"; then 353 if [ $("$gnuastro_prefix"/astfits "$1" --hastablehdu) = 1 ]; then 354 return 0 355 fi 356 fi 357 return 1 358} 359 360 361 362 363 364# Return successfully if first argument is plain-text (not binary). 365_gnuastro_autocomplete_is_plaintext(){ 366 if file "$1" | grep 'executable\|binary' &> /dev/null; then return 1; 367 else return 0; 368 fi 369} 370 371 372 373 374 375# Return successfully (with 0) if the given non-FITS file is a table. 376_gnuastro_autocomplete_is_plaintext_table(){ 377 378 # Only do the check if the file exists. 379 if [ -f "$1" ]; then 380 381 # If the file is not plain-text, it will contain an 'executable' or 382 # 'binary' in the output of the 'file' command. 383 if _gnuastro_autocomplete_is_plaintext "$1"; then 384 385 # The file is plain-text. Extract the first non-commented or 386 # empty line and feed it to 'asttable' to see if it can be 387 # interpretted properly. We don't want to bother with the other 388 # lines, because we don't want to waste computational power 389 # here. 390 if awk '!/^#/ && NF>0 {print; exit 0}' "$1" \ 391 | "$gnuastro_prefix"/asttable &> /dev/null; then 392 return 0 393 else 394 return 1 395 fi 396 397 # The file was binary 398 else return 1 399 fi 400 401 # The file didn't exist. 402 else return 1 403 fi 404} 405 406 407 408 409 410# Return successfully if the first argument is a table. 411_gnuastro_autocomplete_is_table(){ 412 if _gnuastro_autocomplete_fits_has_table "$1"; then return 0 413 elif _gnuastro_autocomplete_is_plaintext_table "$1"; then return 0 414 else return 1 415 fi 416} 417 418 419 420 421 422# If a certain file (image, table, or certain other files) is already given 423# in the previous tokens, return successfully (with zero), and will put its 424# name in 'given_file'. Otherwise, return a failure (1) and 'given_file' 425# will be untouched. 426_gnuastro_autocomplete_first_in_arguments(){ 427 428 # Inputs 429 local mode=$1 430 431 # Local variables (that are only for this function). 432 local word="" 433 local previous="" 434 435 # Initialize outputs 436 given_file="" 437 438 # Go over all the words/tokens given until now. 439 for word in ${COMP_WORDS[*]}; do 440 441 # Ignore the program name (first word), current (last) word, any 442 # directories or '=', or any word that starts with a '-' (which is 443 # an option). 444 if [ x$word = x${COMP_WORDS[0]} ] \ 445 || [ x$word = x$current ] \ 446 || [ ${word:0:1} = "-" ] \ 447 || [ x$word = x"=" ] \ 448 || [ -d $word ]; then 449 local just_a_place_holder=1 450 else 451 # If the previous word is a valued option, then it shouldn't be 452 # checked. 453 if _gnuastro_autocomplete_string_is_valued_option $previous; then 454 local just_a_place_holder=1 455 456 # Previous word was not a valued option, do the operation based 457 # on the mode. 458 else 459 case "$mode" in 460 fits) 461 if _gnuastro_autocomplete_is_fits "$word"; then 462 given_file="$word" 463 fi 464 ;; 465 image) 466 if _gnuastro_autocomplete_fits_has_image "$word"; then 467 given_file="$word" 468 return 0; 469 fi 470 ;; 471 table) 472 if _gnuastro_autocomplete_is_table "$word"; then 473 given_file="$word" 474 return 0; 475 fi 476 ;; 477 source_c) 478 if $(echo "$word" | grep "\.c$" &> /dev/null) \ 479 && [ -f "$word" ]; then 480 given_file="$word" 481 return 0; 482 fi 483 ;; 484 esac 485 fi 486 fi 487 488 # If this word isn't an '=', put it in 'previous' and go onto the 489 # next word. 490 if [ $word != "=" ]; then 491 previous=$word 492 fi 493 done 494 495 # If control reached here, then there weren't any tables on the 496 # command-line until now. 497 return 1; 498} 499 500 501 502 503 504# Find the requested file from the existing tokens on the command-line. 505# 506# INPUT ARGUMENTS: 507# 1) Mode of file ('table', 'image', 'fits'). 508# 2) Name of option containing file names. 509# 510# WRITTEN VARIABLES (should be defined before this function). 511# given_file: file name of given table. 512_gnuastro_autocomplete_given_file(){ 513 514 # Set inputs (for each readability). 515 local mode="$1" 516 local name="$2" 517 local read_option_value="" 518 519 # If 'name' is emtpy, we should look in the arguments, otherwise, we 520 # should look into the options. 521 if [ x"$name" = x ]; then 522 if _gnuastro_autocomplete_first_in_arguments $mode; then 523 # given_file is written by the function as a side-effect. 524 local just_a_place_holder=1 525 fi 526 527 # We are looking for a certain option. 528 else 529 # Read the given option's value. 530 _gnuastro_autocomplete_read_option_value "$name" 531 532 # If we are in image-mode, and the found file has an image, then 533 # put the name in 'given_file' (final output). Same for tables. 534 case $mode in 535 fits) 536 if _gnuastro_autocomplete_is_fits "$read_option_value"; then 537 given_file="$read_option_value" 538 fi 539 ;; 540 image) 541 if _gnuastro_autocomplete_fits_has_image \ 542 "$read_option_value"; then 543 given_file="$read_option_value" 544 fi 545 ;; 546 table) 547 if _gnuastro_autocomplete_is_table \ 548 "$read_option_value"; then 549 given_file="$read_option_value" 550 fi 551 ;; 552 esac 553 fi 554 555} 556 557 558 559 560 561# Find the requested filename and HDU within the already entered 562# command-line. This option needs three arguments: 563# 564# INPUT ARGUMENTS 565# 1) Mode of file ('table' or 'image'). 566# 2) The filename option (if empty string, 1st argument). 567# 3) The HDU option (only necessary if the file is FITS). 568# 569# WRITTEN VARIABLES (should be defined before this function). 570# given_file: file name of given table/image. 571# given_hdu: HDU of given table/table. 572_gnuastro_autocomplete_given_file_and_hdu(){ 573 574 # Set inputs (for each readability). 575 local mode=$1 576 local name=$2 577 local hduoption=$3 578 579 # Internal variables. 580 local read_option_value="" 581 582 # Initialize the outputs (defined before this). 583 given_hdu="" 584 given_file="" 585 586 # First, confirm the table file name. If requested table is in an 587 # argument, 'name' will be empty. 588 _gnuastro_autocomplete_given_file $mode $name 589 590 # If a file name existed and it is a FITS file, find the HDU given in 591 # the option. 592 if [ x"$given_file" != x ] \ 593 && _gnuastro_autocomplete_is_fits "$given_file"; then 594 _gnuastro_autocomplete_read_option_value $hduoption 595 given_hdu="$read_option_value" 596 fi 597} 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618####################################################################### 619############ Completion replies ############ 620####################################################################### 621# Given a set of strings, select the one that matches the first argument 622_gnuastro_autocomplete_compreply_from_string(){ 623 624 # Internal variables (for easy reading). 625 local string="$1" 626 local match="$2" 627 628 # When there isn't any match string, just add everything. 629 if [ x"$match" = x ]; then 630 for v in $string; do COMPREPLY+=("$v"); done 631 632 # When there is a match, limit it. We aren't using 'grep' because it 633 # can confuse a possible '--XXX', with its own options on some systems 634 # (and placing a '--' before the search string may not be portable). 635 else 636 for v in $(echo $string \ 637 | awk '{for(i=1;i<=NF;++i) \ 638 if($i ~ /^'$match'/) print $i}'); do 639 COMPREPLY+=("$v"); 640 done 641 fi 642} 643 644 645 646 647# Some options take multiple values, separated by a comma (for example 648# '--option=A,B,C'). Once the possible solutions have been found by the 649# caller function, this function will decide how to print the suggestions: 650# add a comma or not. 651_gnuastro_autocomplete_compreply_comma_when_matched(){ 652 653 # Input arguments: 654 local replies="$1" 655 local tomatch="$2" 656 local fullmatch="$3" 657 local continuematch="$4" 658 659 # Add the completion replies. 660 if [ x"$continuematch" = x ]; then 661 for c in $replies; do COMPREPLY+=("$c"); done 662 else 663 # If there is only one match, include the previously specified 664 # replies in the final filled value and append an ',' to let the 665 # user specify more values to the option. 666 if [ $(echo $replies | wc -w) = 1 ]; then 667 668 # In the continue-mode we don't want the final value to be 669 # appended with a space. 670 compopt -o nospace 671 672 # When 'fullmatch' and 'tomatch' are the same, this was the 673 # first requested reply, so we can safely just print it with a 674 # comma. 675 if [ x"$fullmatch" = x"$tomatch" ]; then 676 COMPREPLY+=("$replies,") 677 678 # This was not the first reply, so we need to add the old ones 679 # as a prefix. But we first need to remove any possible start 680 # of the current reply. 681 else 682 local oldreps=$(echo "$fullmatch" | sed -e's|'$tomatch'$||') 683 COMPREPLY+=("$oldreps$replies,") 684 fi 685 686 # There was more than one matching reply, so continue suggesting 687 # with only the column names. 688 else 689 local oldreps=$(echo "$fullmatch" | sed -e's|'$tomatch'$||') 690 for c in $replies; do COMPREPLY+=("$oldreps$c"); done 691 fi 692 fi 693} 694 695 696 697 698 699# Add completion replies for the values to '--searchin'. 700_gnuastro_autocomplete_compreply_searchin(){ 701 _gnuastro_autocomplete_compreply_from_string \ 702 "name unit comment" "$1" 703} 704 705 706 707 708 709# Add completion replies for the values to '--searchin'. 710_gnuastro_autocomplete_compreply_tableformat(){ 711 _gnuastro_autocomplete_compreply_from_string \ 712 "fits-ascii fits-binary txt" "$1" 713} 714 715 716 717 718 719# Add completion replies for the values to '--numthreads'. 720_gnuastro_autocomplete_compreply_numthreads(){ 721 if nproc &> /dev/null; then 722 local numthreads="$(seq $(nproc))" 723 _gnuastro_autocomplete_compreply_from_string \ 724 "$numthreads" "$1" 725 fi 726} 727 728 729 730 731 732# Values to the common '--interpmetric' option. 733_gnuastro_autocomplete_compreply_interpmetric(){ 734 _gnuastro_autocomplete_compreply_from_string \ 735 "radial manhattan" "$1" 736} 737 738 739 740 741 742# Values to the common '--type' option. 743_gnuastro_autocomplete_compreply_numbertype(){ 744 _gnuastro_autocomplete_compreply_from_string \ 745 "uint8 int8 uint16 int16 uint32 int32 uint64 int64 float32 float64" \ 746 "$1" 747} 748 749 750 751 752 753# Values to the common '--wcslinearmatrix' option. 754_gnuastro_autocomplete_compreply_wcslinearmatrix(){ 755 _gnuastro_autocomplete_compreply_from_string \ 756 "cd pc" "$1" 757} 758 759 760 761 762 763# Add matching options to the completion replies. 764_gnuastro_autocomplete_compreply_options_all(){ 765 766 # Variable only necessary here. 767 local options_match="" 768 769 # Get the list of option names (with an '=' after those that need a 770 # value (if 'options_all' isn't already set) 771 if [ x"$options_all" = x ]; then 772 _gnuastro_autocomplete_option_list 773 fi 774 775 # Limit the options to those that start with the already given portion. 776 if [ x$1 = x ]; then 777 options_match="$options_all" 778 else 779 # We aren't using 'grep' because it can confuse the '--XXX' with 780 # its own options on some systems (and placing a '--' before the 781 # search string may not be portable). 782 options_match=$(echo "$options_all" | awk '/^'$1'/') 783 fi 784 785 # Add the list of options. 786 for f in $options_match; do COMPREPLY+=("$f"); done 787 788 # Disable the extra space on the command-line after the match, only for 789 # this run (only relevant when we have found the match). 790 compopt -o nospace 791} 792 793 794 795 796 797# Add a file into the completion replies. The main point is to remove the 798# fixed directory name prefix of the file (that is appended by 'ls'). 799# 800# It takes two arguments: 801# 802# 1. Base string (that was fed into 'ls' to find the full string). 803# This string CAN BE EMPTY. 804# 2. Full string. 805# 806# If the first is a full directory, then it will remove it from the full 807# string before saving string (which is standard in autocomplete (the user 808# has already given it and it is just annoying!). 809_gnuastro_autocomplete_compreply_file(){ 810 811 # For some reason, when there are multiple matches in a sub-directory, 812 # removing the directory removes the whole thing that the user has 813 # already typed! So the part below (which was the main purpose of this 814 # function is currently commented). Until that bug is fixed, we'll just 815 # return the full file name. Here is how you can reproduce the problem 816 # (with the MAIN FUNCTION below uncommented and the WORK AROUND 817 # commented): 818 # 819 # $ ls 820 # image.fits 821 # $ mkdir subdir 822 # $ mv image.fits subdir/ab-123.fits 823 # $ cp subdir/ab-123.fits subdir/ab-456.fits 824 # $ astcrop subdir/ab-[TAB] 825 826 # MAIN FUNCTION 827 #if [ x$1 != x ] && [ -d $1 ]; then COMPREPLY+=("${2#$1}") 828 #else COMPREPLY+=("$2") 829 #fi 830 831 # WORK AROUND 832 COMPREPLY+=("$2") 833} 834 835 836 837 838 839# Add all the HDUs that contain a table/image in the first argument (a FITS 840# file) into the completion replies. 841# 842# INPUT ARGUMENTS 843# 1) Mode of file ('table', 'image', or 'all'). 844# 2) Name of file. 845# 3) Existing argument. 846_gnuastro_autocomplete_compreply_hdus(){ 847 848 # Local variables (for easy reading) 849 local mode="$1" 850 local given_file="$2" 851 local matchstr="$3" 852 853 if _gnuastro_autocomplete_is_fits "$given_file"; then 854 855 # Get list of the file's HDUs. 856 hdus=$("$gnuastro_prefix"/astfits "$given_file" \ 857 --list"$mode"hdus) 858 859 # Add the matching ones into COMPREPLY. 860 _gnuastro_autocomplete_compreply_from_string \ 861 "$hdus" "$matchstr" 862 fi 863} 864 865 866 867 868 869# Add all the keywords in current HDU to the replies. 870# 871# INPUT ARGUMENTS: 872# 1) FITS file name. 873# 2) HDU within the FITS file. 874# 3) Existing argument (to match). 875# 4) If a comma should be appended in case of match. 876_gnuastro_autocomplete_compreply_keys(){ 877 878 # Input arguments. 879 local given_file="$1" 880 local given_hdu="$2" 881 local fullmatch="$3" 882 local continuematch="$4" 883 884 # Local variables. 885 local keys="" 886 local tomatch="" 887 888 # Keywords can sometimes be given in series (for example 889 # '--keyvalue=A,B,C'). In this case, the caller will give a non-empty 890 # value to the fourth argument ('continuematch'). We therefore need to 891 # will take the last component of the comma-separated list for the 892 # matching of the next element. 893 if [ x"$continuematch" = x ]; then 894 tomatch="$fullmatch" 895 else 896 tomatch=$(echo $fullmatch | awk 'BEGIN{FS=","} {print $NF}') 897 fi 898 899 # Get list of keywords. 900 keys=$("$gnuastro_prefix"/astfits "$given_file" \ 901 --hdu="$given_hdu" \ 902 --printkeynames \ 903 | grep ^$tomatch) 904 905 _gnuastro_autocomplete_compreply_comma_when_matched \ 906 "$keys" "$tomatch" "$fullmatch" "$continuematch" 907} 908 909 910 911 912 913# Fill the replies with certain files. 914_gnuastro_autocomplete_compreply_directories(){ 915 916 # For easy reading. 917 local arg=$1 918 919 # Get the list of directories (all ending with '/', with '-d' we are 920 # telling 'ls' to not go into sub-directories). 921 local directs=($(ls -d "$arg"*/ 2> /dev/null)) 922 923 # If a directory exists at all. 924 if [ x"${directs[0]}" != x ]; then 925 926 # If there is only one match. 927 if [ x"${directs[1]}" = x ]; then 928 929 # If there are sub-directories, then keep the ending '/' and 930 # don't add space (the user may want to add sub-directories). 931 if $(ls -d "${directs[0]}"/*/ &> /dev/null); then 932 COMPREPLY+=("$d") 933 compopt -o nospace 934 935 # There are no sub-directories. In this case, remove the ending 936 # '/' and let Bash add a space after the matched name. 937 else 938 COMPREPLY+=($(echo "${directs[0]}" | sed -e's|/$||')) 939 fi 940 941 # More than one match: go over all the matches and add them. we 942 # should avoid printing a space (so the sub-directories continue in 943 # the same token). Also RECALL THAT '$d' ALREADY ENDS WITH A '/' 944 # HERE. 945 else 946 for d in ${directs[*]}; do 947 COMPREPLY+=("$d") 948 compopt -o nospace 949 done 950 fi 951 fi 952} 953 954 955 956 957 958# Fill the replies with all image formats (note that the 'image' of 959# '_gnuastro_autocomplete_compreply_files_certain' is only FITS files) 960_gnuastro_autocomplete_compreply_images_all(){ 961 962 # Local variables to be filled by functions. 963 local arg="$1" 964 local ls_in="" 965 local files="" 966 local suffixes_jpeg="" 967 local suffixes_tiff="" 968 969 # Get the FITS images. 970 _gnuastro_autocomplete_compreply_files_certain image "$arg" 971 972 # If the given name doesn't have a suffix, then search for desired 973 # suffixes. 974 if [ x"$arg" = x"$(echo $arg | cut -d. -f1)" ]; then 975 976 # Since the other formats are checked by suffix and there are many 977 # suffixes, its easier to just call 'ls' once. 978 _gnuastro_autocomplete_compreply_suffixes_jpeg 979 _gnuastro_autocomplete_compreply_suffixes_tiff 980 for s in $suffixes_jpeg $suffixes_tiff; do 981 ls_in="$ls_in "$arg"*$s" 982 done 983 984 # The given argument already contains a suffix. So you can safely 985 # ignore the matched suffixes. 986 else 987 ls_in=$arg"*" 988 fi 989 990 # Find the matching files and add them to the replies. 991 files=($(ls -d $ls_in 2> /dev/null)) 992 for f in ${files[*]}; do 993 if [ -d "$f" ]; then 994 COMPREPLY+=("$f/") 995 compopt -o nospace 996 else 997 _gnuastro_autocomplete_compreply_file "$arg" "$f" 998 fi 999 done 1000} 1001 1002 1003 1004 1005# Fill the replies with certain files. 1006_gnuastro_autocomplete_compreply_files_certain(){ 1007 1008 # For easy reading. 1009 local arg=$2 1010 local mode=$1 1011 1012 # Get list of matching files (with '-d' we are telling 'ls' to not go 1013 # into sub-directories). 1014 local files=($(ls -d "$arg"* 2> /dev/null)) 1015 1016 # Parse the list of files and add it when it is a directory or it can 1017 # be read as a table. 1018 for f in ${files[*]}; do 1019 if [ -d "$f" ]; then 1020 COMPREPLY+=("$f/") 1021 compopt -o nospace 1022 else 1023 case "$mode" in 1024 fits) 1025 if _gnuastro_autocomplete_is_fits "$f"; then 1026 _gnuastro_autocomplete_compreply_file "$arg" "$f" 1027 fi 1028 ;; 1029 image) 1030 if _gnuastro_autocomplete_fits_has_image "$f"; then 1031 _gnuastro_autocomplete_compreply_file "$arg" "$f" 1032 fi 1033 ;; 1034 table) 1035 if _gnuastro_autocomplete_is_table "$f"; then 1036 _gnuastro_autocomplete_compreply_file "$arg" "$f" 1037 fi 1038 ;; 1039 source_c) 1040 if $(echo $f | grep "\.c$" &> /dev/null); then 1041 _gnuastro_autocomplete_compreply_file "$arg" "$f" 1042 fi 1043 ;; 1044 source_la) 1045 if $(echo $f | grep "\.la$" &> /dev/null); then 1046 _gnuastro_autocomplete_compreply_file "$arg" "$f" 1047 fi 1048 ;; 1049 esac 1050 fi 1051 done 1052} 1053 1054 1055 1056 1057 1058# Add all table columns in given file (possibly with HDU) that start with 1059# the given string into the completion replies. 1060_gnuastro_autocomplete_compreply_table_columns(){ 1061 1062 # Inputs 1063 local table_file="$1" 1064 local table_hdu="$2" 1065 local fullmatch="$3" 1066 local continuematch="$4" 1067 1068 # Internal 1069 local tomatch="" 1070 local columns="" 1071 local hdu_option="" 1072 1073 # If no file has been given, don't set anything, just return. 1074 if [ x"$table_file" = x ]; then return 0; fi 1075 1076 # If a HDU is given, then add it to the options. 1077 if [ x"$table_hdu" != x ]; then hdu_option="--hdu=$table_hdu"; fi 1078 1079 # Columns can usually be given in series (for example 1080 # '--column=A,B,C'). In this case, the caller will give a non-empty 1081 # value to the fourth argument ('continuematch'). We therefore need to 1082 # will take the last component of the comma-separated list for the 1083 # matching of the next element. 1084 if [ x"$continuematch" = x ]; then 1085 tomatch="$fullmatch" 1086 else 1087 tomatch=$(echo $fullmatch | awk 'BEGIN{FS=","} {print $NF}') 1088 fi 1089 1090 # Get the list of columns from the output of '--information': the 1091 # column names are the second column of the lines that start with a 1092 # number. If there is no column name, print the column number. 1093 # 1094 # We are forcing 'awk' to read after the second line of 'asttable' 1095 # output, because the second line contains the filename. The filename 1096 # might start with numbers. If so, there will be an unwanted '(hdu:' 1097 # printed in the results. Here, 'awk' will print the second column in 1098 # lines that start with a number. 1099 columns=$("$gnuastro_prefix"/asttable --information \ 1100 "$table_file" $hdu_option \ 1101 | awk 'NR>2 && /^[0-9]/ { \ 1102 if($2=="n/a") print $1; else print $2 \ 1103 }' \ 1104 | grep ^$tomatch) 1105 1106 # Add the necessary columns in a comma-separated manner. 1107 _gnuastro_autocomplete_compreply_comma_when_matched \ 1108 "$columns" "$tomatch" "$fullmatch" "$continuematch" 1109} 1110