From 8e87d595b7f07f28b8b36cb9f06f399a7f7249b8 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 24 Jul 2017 20:45:43 -0700 Subject: [PATCH] remove some uses of `$IFS` This is a step towards resolving issue #4156. It replaces uses of `$IFS` with other solutions. --- share/completions/dd.fish | 120 ++++++------- share/completions/iptables.fish | 162 +++++++++--------- share/completions/systemctl.fish | 48 +++--- share/completions/vagrant.fish | 24 +-- .../functions/__fish_complete_lpr_option.fish | 3 +- share/functions/__fish_git_prompt.fish | 3 +- share/functions/__fish_print_help.fish | 2 - share/functions/funced.fish | 16 +- tests/test_functions/mktemp.fish | 37 ++-- 9 files changed, 187 insertions(+), 228 deletions(-) diff --git a/share/completions/dd.fish b/share/completions/dd.fish index 9402661a5..33160a4e5 100644 --- a/share/completions/dd.fish +++ b/share/completions/dd.fish @@ -3,17 +3,12 @@ complete -c dd -d 'display help and exit' -xa '--help' complete -c dd -d 'output version information and exit' -xa '--version' function __fish_complete_dd --description 'Complete dd operands' - # set operand_string as a local variable containing the current command-line token. set -l operand_string (commandline -t) switch $operand_string - case 'if=*' 'of=*' - # the read command uses $IFS to tokenise stdin input - set -l IFS = - # $operand now contains the left side of the operator, $value the right - echo $operand_string | read -l operand value + string replace = ' ' -- $operand_string | read -l operand value for entry in $value* # if $entry is a directory, append a '/' @@ -25,78 +20,61 @@ function __fish_complete_dd --description 'Complete dd operands' end case 'iflag=*' 'oflag=*' - set -l IFS = - echo $operand_string | read -l operand value + string replace = ' ' -- $operand_string | read -l operand complete + string match -q '*,' -- $complete + or set complete '' - set -l IFS ' ' - echo $value | sed -e 's/\(.*\)\(,\)/\1 \2/' | read -l complete comma - - # check if there is only one option - if test $comma = '' - set complete '' - else - set complete $complete, - end - - printf "%s\t%s\n" "$operand=$complete""append" "append mode (makes sense only for output; conv=notrunc suggested)" - printf "%s\t%s\n" "$operand=$complete""direct" "use direct I/O for data" - printf "%s\t%s\n" "$operand=$complete""directory" "fail unless a directory" - printf "%s\t%s\n" "$operand=$complete""dsync" "use synchronized I/O for data" - printf "%s\t%s\n" "$operand=$complete""sync" "use synchronized I/O for data and metadata" - printf "%s\t%s\n" "$operand=$complete""fullblock" "accumulate full blocks of input (iflag only)" - printf "%s\t%s\n" "$operand=$complete""nonblock" "use non-blocking I/O" - printf "%s\t%s\n" "$operand=$complete""noatime" "do not update access time" - printf "%s\t%s\n" "$operand=$complete""nocache" "discard cached data" - printf "%s\t%s\n" "$operand=$complete""noctty" "do not assign controlling terminal from file" - printf "%s\t%s\n" "$operand=$complete""nofollow" "do not follow symbolic links" + printf "%s\t%s\n" "$operand=$complete""append" "append mode (makes sense only for output; conv=notrunc suggested)" + printf "%s\t%s\n" "$operand=$complete""direct" "use direct I/O for data" + printf "%s\t%s\n" "$operand=$complete""directory" "fail unless a directory" + printf "%s\t%s\n" "$operand=$complete""dsync" "use synchronized I/O for data" + printf "%s\t%s\n" "$operand=$complete""sync" "use synchronized I/O for data and metadata" + printf "%s\t%s\n" "$operand=$complete""fullblock" "accumulate full blocks of input (iflag only)" + printf "%s\t%s\n" "$operand=$complete""nonblock" "use non-blocking I/O" + printf "%s\t%s\n" "$operand=$complete""noatime" "do not update access time" + printf "%s\t%s\n" "$operand=$complete""nocache" "discard cached data" + printf "%s\t%s\n" "$operand=$complete""noctty" "do not assign controlling terminal from file" + printf "%s\t%s\n" "$operand=$complete""nofollow" "do not follow symbolic links" case 'conv=*' - set -l IFS = - echo $operand_string | read -l operand value + string replace = ' ' -- $operand_string | read -l operand complete + string match -q '*,' -- $complete + or set complete '' - set -l IFS ' ' - echo $value | sed -e 's/\(.*\)\(,\)/\1 \2/' | read -l complete comma - - if test $comma = '' - set complete '' - else - set complete $complete, - end - - printf "%s\t%s\n" "$operand=$complete""ascii" "from EBCDIC to ASCII" - printf "%s\t%s\n" "$operand=$complete""ebcdic" "from ASCII to EBCDIC" - printf "%s\t%s\n" "$operand=$complete""ibm" "from ASCII to alternate EBCDIC" - printf "%s\t%s\n" "$operand=$complete""block" "pad newline-terminated records with spaces to cbs-size" - printf "%s\t%s\n" "$operand=$complete""unblock" "replace trailing spaces in cbs-size records with newline" - printf "%s\t%s\n" "$operand=$complete""lcase" "change upper case to lower case" - printf "%s\t%s\n" "$operand=$complete""ucase" "change lower case to upper case" - printf "%s\t%s\n" "$operand=$complete""swab" "swap every pair of input bytes" - printf "%s\t%s\n" "$operand=$complete""sync" "pad every input block with NULs to ibs-size; with block or ublock use spaces" - printf "%s\t%s\n" "$operand=$complete""excl" "fail if the output file already exists" - printf "%s\t%s\n" "$operand=$complete""nocreat" "do not create the output file" - printf "%s\t%s\n" "$operand=$complete""notrunc" "do not truncate the output file" - printf "%s\t%s\n" "$operand=$complete""noerror" "continue after read errors" - printf "%s\t%s\n" "$operand=$complete""fdatasync" "physically write output file data before finishing" - printf "%s\t%s\n" "$operand=$complete""fsync" "physically write output file data and metadata before finishing" + printf "%s\t%s\n" "$operand=$complete""ascii" "from EBCDIC to ASCII" + printf "%s\t%s\n" "$operand=$complete""ebcdic" "from ASCII to EBCDIC" + printf "%s\t%s\n" "$operand=$complete""ibm" "from ASCII to alternate EBCDIC" + printf "%s\t%s\n" "$operand=$complete""block" "pad newline-terminated records with spaces to cbs-size" + printf "%s\t%s\n" "$operand=$complete""unblock" "replace trailing spaces in cbs-size records with newline" + printf "%s\t%s\n" "$operand=$complete""lcase" "change upper case to lower case" + printf "%s\t%s\n" "$operand=$complete""ucase" "change lower case to upper case" + printf "%s\t%s\n" "$operand=$complete""swab" "swap every pair of input bytes" + printf "%s\t%s\n" "$operand=$complete""sync" "pad every input block with NULs to ibs-size; with block or ublock use spaces" + printf "%s\t%s\n" "$operand=$complete""excl" "fail if the output file already exists" + printf "%s\t%s\n" "$operand=$complete""nocreat" "do not create the output file" + printf "%s\t%s\n" "$operand=$complete""notrunc" "do not truncate the output file" + printf "%s\t%s\n" "$operand=$complete""noerror" "continue after read errors" + printf "%s\t%s\n" "$operand=$complete""fdatasync" "physically write output file data before finishing" + printf "%s\t%s\n" "$operand=$complete""fsync" "physically write output file data and metadata before finishing" case 'status=*' - printf "%s\t%s\n" status=noxfer "suppress final transfer statistics" - printf "%s\t%s\n" status=none "suppress everything but errors" - printf "%s\t%s\n" status=progress "show periodic transfer statistics" + printf "%s\t%s\n" status=noxfer "suppress final transfer statistics" + printf "%s\t%s\n" status=none "suppress everything but errors" + printf "%s\t%s\n" status=progress "show periodic transfer statistics" case '*' - printf "%s=\t%s\n" bs "read and write up to BYTES bytes at a time" - printf "%s=\t%s\n" cbs "convert BYTES bytes at a time" - printf "%s=\t%s\n" conv "convert the file as per the comma separated symbol list" - printf "%s=\t%s\n" count "copy only BLOCKS input blocks" - printf "%s=\t%s\n" ibs "read up to BYTES bytes at a time (default 512)" - printf "%s=\t%s\n" if "read from FILE instead of stdin" - printf "%s=\t%s\n" iflag "read as per the comma separated symbol list" - printf "%s=\t%s\n" obs "write BYTES bytes at a time (default 512)" - printf "%s=\t%s\n" of "write to FILE instead of stdout" - printf "%s=\t%s\n" oflag "write as per the comma separated symbol list" - printf "%s=\t%s\n" seek "skip BLOCKS obs-sized blocks at the start of output" - printf "%s=\t%s\n" skip "skip BLOCKS ibs-sized blocks at the start of input" - printf "%s=\t%s\n" status "set the level of information to print to stderr" + printf "%s=\t%s\n" bs "read and write up to BYTES bytes at a time" + printf "%s=\t%s\n" cbs "convert BYTES bytes at a time" + printf "%s=\t%s\n" conv "convert the file as per the comma separated symbol list" + printf "%s=\t%s\n" count "copy only BLOCKS input blocks" + printf "%s=\t%s\n" ibs "read up to BYTES bytes at a time (default 512)" + printf "%s=\t%s\n" if "read from FILE instead of stdin" + printf "%s=\t%s\n" iflag "read as per the comma separated symbol list" + printf "%s=\t%s\n" obs "write BYTES bytes at a time (default 512)" + printf "%s=\t%s\n" of "write to FILE instead of stdout" + printf "%s=\t%s\n" oflag "write as per the comma separated symbol list" + printf "%s=\t%s\n" seek "skip BLOCKS obs-sized blocks at the start of output" + printf "%s=\t%s\n" skip "skip BLOCKS ibs-sized blocks at the start of input" + printf "%s=\t%s\n" status "set the level of information to print to stderr" end end diff --git a/share/completions/iptables.fish b/share/completions/iptables.fish index 1e6bdabf3..62653741d 100644 --- a/share/completions/iptables.fish +++ b/share/completions/iptables.fish @@ -1,103 +1,99 @@ set -l __fish_iptables_tables filter nat mangle raw security function __fish_iptables_current_table - set -l next_is_table 1 - for token in (commandline -oc) - switch $token - case "--table=*" - set -l IFS "=" - echo $token | while read a b - echo $b - end - return 0 - case "--table" - set next_is_table 0 - case "-*t*" - set next_is_table 0 - case "*" - if [ $next_is_table -eq 0 ] - echo $token - return 0 - end - end - end - return 1 + set -l next_is_table 1 + for token in (commandline -oc) + switch $token + case "--table=*" + echo (string split -m1 = -- $token)[2] + return 0 + case "--table" + set next_is_table 0 + case "-*t*" + set next_is_table 0 + case "*" + if [ $next_is_table -eq 0 ] + echo $token + return 0 + end + end + end + return 1 end function __fish_iptables_user_chains - # There can be user-defined chains so we need iptables' help - set -l tablearg - set -l table (__fish_iptables_current_table) - if __fish_iptables_current_table - set tablearg "--table=$table" - end - # This only works as root, so ignore errors - iptables $tablearg -L ^/dev/null | grep Chain | while read a b c - echo $b - end + # There can be user-defined chains so we need iptables' help + set -l tablearg + set -l table (__fish_iptables_current_table) + if __fish_iptables_current_table + set tablearg "--table=$table" + end + # This only works as root, so ignore errors + iptables $tablearg -L ^/dev/null | grep Chain | while read a b c + echo $b + end end function __fish_iptables_chains - set -l table (__fish_iptables_current_table) - [ -z $table ]; and set -l table "*" - set -l prerouting "PREROUTING For packets that are coming in" - set -l input "INPUT For packets destined to local sockets" - set -l output "OUTPUT For locally-generated packets" - set -l forward "FORWARD For packets being routed through" - set -l postrouting "POSTROUTING For packets that are about to go out" - switch $table - case "filter" - echo $input - echo $forward - echo $output - case "nat" - echo $prerouting - echo $output - echo $postrouting - case "mangle" - echo $prerouting - echo $input - echo $output - echo $forward - echo $postrouting - case "raw" - echo $prerouting - echo $output - case "security" - echo $input - echo $output - echo $forward - case '*' - echo $prerouting - echo $input - echo $output - echo $forward - echo $postrouting - end - __fish_iptables_user_chains + set -l table (__fish_iptables_current_table) + [ -z $table ] + and set -l table "*" + set -l prerouting "PREROUTING For packets that are coming in" + set -l input "INPUT For packets destined to local sockets" + set -l output "OUTPUT For locally-generated packets" + set -l forward "FORWARD For packets being routed through" + set -l postrouting "POSTROUTING For packets that are about to go out" + switch $table + case "filter" + echo $input + echo $forward + echo $output + case "nat" + echo $prerouting + echo $output + echo $postrouting + case "mangle" + echo $prerouting + echo $input + echo $output + echo $forward + echo $postrouting + case "raw" + echo $prerouting + echo $output + case "security" + echo $input + echo $output + echo $forward + case '*' + echo $prerouting + echo $input + echo $output + echo $forward + echo $postrouting + end + __fish_iptables_user_chains end function __fish_iptables_has_chain - # Remove descriptions - set -l IFS " " - set -l chains (__fish_iptables_chains | while read a b; echo $a; end) - set -e IFS - set -l cmdline (commandline -op) - for c in $chains - if contains -- $c $cmdline - return 0 - end - end - return 1 + # Remove descriptions + set -l chains (__fish_iptables_chains | string split -m1 " " | while read a b; echo $a; end) + set -l cmdline (commandline -op) + for c in $chains + if contains -- $c $cmdline + return 0 + end + end + return 1 end # A target is a user-defined chain, one of "ACCEPT DROP RETURN" or an extension (TODO) function __fish_iptables_targets - echo "ACCEPT" - echo "DROP" - echo "RETURN" - __fish_iptables_chains + echo "ACCEPT" + echo "DROP" + echo "RETURN" + __fish_iptables_chains end ### Commands diff --git a/share/completions/systemctl.fish b/share/completions/systemctl.fish index 7ec1e3308..1a949088b 100644 --- a/share/completions/systemctl.fish +++ b/share/completions/systemctl.fish @@ -1,9 +1,5 @@ set -l systemd_version (systemctl --version | string match "systemd*" | string replace -r "\D*(\d+)" '$1') -set -l commands list-units list-sockets start stop reload restart try-restart reload-or-restart reload-or-try-restart \ - isolate kill is-active is-failed status show get-cgroup-attr set-cgroup-attr unset-cgroup-attr set-cgroup help \ - reset-failed list-unit-files enable disable is-enabled reenable preset mask unmask link load list-jobs cancel dump \ - list-dependencies snapshot delete daemon-reload daemon-reexec show-environment set-environment unset-environment \ - default rescue emergency halt poweroff reboot kexec exit suspend hibernate hybrid-sleep switch-root +set -l commands list-units list-sockets start stop reload restart try-restart reload-or-restart reload-or-try-restart isolate kill is-active is-failed status show get-cgroup-attr set-cgroup-attr unset-cgroup-attr set-cgroup help reset-failed list-unit-files enable disable is-enabled reenable preset mask unmask link load list-jobs cancel dump list-dependencies snapshot delete daemon-reload daemon-reexec show-environment set-environment unset-environment default rescue emergency halt poweroff reboot kexec exit suspend hibernate hybrid-sleep switch-root if test $systemd_version -gt 208 set commands $commands cat if test $systemd_version -gt 217 @@ -13,21 +9,17 @@ end set -l types services sockets mounts service_paths targets automounts timers function __fish_systemd_properties - if type -q /usr/lib/systemd/systemd - set IFS "=" - /usr/lib/systemd/systemd --dump-configuration-items | while read key value - if not test -z $value - echo $key - end - end - else if type -q /lib/systemd/systemd # Debian has not merged /lib and /usr/lib - set IFS "=" - /lib/systemd/systemd --dump-configuration-items | while read key value - if not test -z $value - echo $key - end - end - end + if type -q /usr/lib/systemd/systemd + /usr/lib/systemd/systemd --dump-configuration-items | string split -m1 = | while read key value + test -n "$value" + and echo $key + end + else if type -q /lib/systemd/systemd # Debian has not merged /lib and /usr/lib + /lib/systemd/systemd --dump-configuration-items | string split -m1 = | while read key value + test -n "$value" + and echo $key + end + end end function __fish_systemctl_failed @@ -65,9 +57,9 @@ if test $systemd_version -gt 208 end for command in $commands_types - for t in $types - complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" - end + for t in $types + complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" + end end # Handle reset-failed specially because it doesn't apply to unit-files (only units that have been tried can have failed) and a second "--state=" argument doesn't override the earlier one. @@ -75,16 +67,16 @@ complete -f -c systemctl -n "__fish_seen_subcommand_from reset-failed" -a "(__fi # Enable/Disable: Only show units with matching state for t in services sockets timers service_paths - complete -f -c systemctl -n "__fish_seen_subcommand_from enable" -a "(eval __fish_systemctl_$t --state=disabled)" - complete -f -c systemctl -n "__fish_seen_subcommand_from disable" -a "(eval __fish_systemctl_$t --state=enabled)" + complete -f -c systemctl -n "__fish_seen_subcommand_from enable" -a "(eval __fish_systemctl_$t --state=disabled)" + complete -f -c systemctl -n "__fish_seen_subcommand_from disable" -a "(eval __fish_systemctl_$t --state=enabled)" end # These are useless for the other commands # .device in particular creates too much noise for t in devices slices scopes swaps - for command in status show list-dependencies - complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" - end + for command in status show list-dependencies + complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" + end end complete -f -c systemctl -n "__fish_seen_subcommand_from isolate" -a '(__fish_systemctl_targets)' -d 'Target' diff --git a/share/completions/vagrant.fish b/share/completions/vagrant.fish index ab46163cb..8bf09c893 100644 --- a/share/completions/vagrant.fish +++ b/share/completions/vagrant.fish @@ -1,30 +1,32 @@ # vagrant autocompletion function __fish_vagrant_no_command --description 'Test if vagrant has yet to be given the main command' - set -l cmd (commandline -opc) - test (count $cmd) -eq 1 + set -l cmd (commandline -opc) + test (count $cmd) -eq 1 end function __fish_vagrant_using_command - set -l cmd (commandline -opc) - set -q cmd[2]; and test "$argv[1]" = $cmd[2] + set -l cmd (commandline -opc) + set -q cmd[2] + and test "$argv[1]" = $cmd[2] end function __fish_vagrant_using_command_and_no_subcommand - set -l cmd (commandline -opc) - test (count $cmd) -eq 2; and test "$argv[1]" = "$cmd[2]" + set -l cmd (commandline -opc) + test (count $cmd) -eq 2 + and test "$argv[1]" = "$cmd[2]" end function __fish_vagrant_using_subcommand --argument-names cmd_main cmd_sub set -l cmd (commandline -opc) - set -q cmd[3]; and test "$cmd_main" = $cmd[2] -a "$cmd_sub" = $cmd[3] + set -q cmd[3] + and test "$cmd_main" = $cmd[2] -a "$cmd_sub" = $cmd[3] end function __fish_vagrant_boxes --description 'Lists all available Vagrant boxes' - set -l IFS \n\ \t - command vagrant box list | while read -l name _ - echo $name - end + command vagrant box list | while read -l name _ + echo $name + end end # --version and --help are always active diff --git a/share/functions/__fish_complete_lpr_option.fish b/share/functions/__fish_complete_lpr_option.fish index 6db627f84..eccb6bdb6 100644 --- a/share/functions/__fish_complete_lpr_option.fish +++ b/share/functions/__fish_complete_lpr_option.fish @@ -2,8 +2,7 @@ function __fish_complete_lpr_option --description 'Complete lpr option' set -l optstr (commandline -t) switch $optstr case '*=*' - set -l IFS = - echo $optstr | read -l opt val + string split -m1 = -- "$optstr" | read -l opt val set -l descr for l in (lpoptions -l ^/dev/null | string match -- "*$opt*" | string replace -r '.*/(.*):\s*(.*)$' '$1 $2' | string split " ") if not set -q descr[1] diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index fb0946580..d5e690cf1 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -265,8 +265,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi # Use fetch config to fix upstream set -l fetch_val (command git config "$cur_prefix".fetch) if test -n "$fetch_val" - set -l IFS : - echo "$fetch_val" | read -l trunk pattern + string split -m1 : -- "$fetch_val" | read -l trunk pattern set upstream (string replace -r -- "/$trunk\$" '' $pattern) /$upstream end end diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index e18506042..5c9604a7b 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -15,8 +15,6 @@ function __fish_print_help --description "Print help message for the specified f return end - set -l IFS \n\ \t - # Render help output, save output into the variable 'help' set -l help set -l cols diff --git a/share/functions/funced.fish b/share/functions/funced.fish index 924d269d8..687a6ffa1 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -56,25 +56,21 @@ function funced --description 'Edit function definition' end if test "$editor" = fish - set -l IFS if functions -q -- $funcname - # Shadow IFS here to avoid array splitting in command substitution - set init (functions -- $funcname | fish_indent --no-indent) + functions -- $funcname | fish_indent --no-indent | read -z init end set -l prompt 'printf "%s%s%s> " (set_color green) '$funcname' (set_color normal)' - # Unshadow IFS since the fish_title breaks otherwise - set -e IFS if read -p $prompt -c "$init" -s cmd - # Shadow IFS _again_ to avoid array splitting in command substitution - set -l IFS - eval (echo -n $cmd | fish_indent) + echo -n $cmd | fish_indent | read -lz cmd + eval "$cmd" end return 0 end - # OSX mktemp is rather restricted - no suffix, no way to automatically use TMPDIR - # Create a directory so we can use a ".fish" suffix for the file - makes editors pick up that it's a fish file + # OS X (macOS) `mktemp` is rather restricted - no suffix, no way to automatically use TMPDIR. + # Create a directory so we can use a ".fish" suffix for the file - makes editors pick up that + # it's a fish file. set -q TMPDIR or set -l TMPDIR /tmp set -l tmpdir (mktemp -d $TMPDIR/fish.XXXXXX) diff --git a/tests/test_functions/mktemp.fish b/tests/test_functions/mktemp.fish index 571898ac2..e32da29fc 100644 --- a/tests/test_functions/mktemp.fish +++ b/tests/test_functions/mktemp.fish @@ -14,19 +14,19 @@ function mktemp set -l opts while set -q argv[1] switch $argv[1] - case -d - set opts $opts d - case -t - set opts $opts t - case -- - set -e argv[1] - break - case '-*' - echo "mktemp: unknown flag $argv[1]" >&2 - _mktemp_help >&2 - exit 2 - case '*' - break + case -d + set opts $opts d + case -t + set opts $opts t + case -- + set -e argv[1] + break + case '-*' + echo "mktemp: unknown flag $argv[1]" >&2 + _mktemp_help >&2 + exit 2 + case '*' + break end set -e argv[1] end @@ -50,8 +50,7 @@ function mktemp # So let's outlaw them anywhere besides the end. # Similarly GNU sed requires at least 3 X's, BSD sed requires none. Let's require 3. begin - set -l IFS - printf '%s' "$template" | read -la chars + set -l chars (string split '' -- $template) set -l found_x for c in $chars if test $c = X @@ -75,10 +74,10 @@ function mktemp end if contains t $opts switch $template - case '/*' - echo "mktemp: invalid template '$template' with -t, template must not be absolute" >&2 - _mktemp_help >&2 - exit 1 + case '/*' + echo "mktemp: invalid template '$template' with -t, template must not be absolute" >&2 + _mktemp_help >&2 + exit 1 end if set -q TMPDIR[1]