Home | History | Annotate | Line # | Download | only in gdb.base
settings.exp revision 1.1.1.1
      1 # This testcase is part of GDB, the GNU debugger.
      2 
      3 # Copyright 2019-2020 Free Software Foundation, Inc.
      4 
      5 # This program is free software; you can redistribute it and/or modify
      6 # it under the terms of the GNU General Public License as published by
      7 # the Free Software Foundation; either version 3 of the License, or
      8 # (at your option) any later version.
      9 #
     10 # This program is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU General Public License
     16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 # Test the set/show commands framework.  The test uses the
     19 # "maintenance test-settings set/show xxx" subcommands to exercise
     20 # TAB-completion and setting processing.
     21 
     22 load_lib completion-support.exp
     23 
     24 standard_testfile .c
     25 
     26 if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
     27     return -1
     28 }
     29 
     30 clean_restart
     31 
     32 if { ![readline_is_used] } {
     33     untested "no tab completion support without readline"
     34     return -1
     35 }
     36 
     37 # Test the show command SHOW_CMD.  EXPECTED_RE is the expected output.
     38 # Also verifies that $_gdb_maint_setting_str produces an equivalent output,
     39 # matching it with EXPECTED_RE.  EXPECTED_RE double quotes are escaped
     40 # unless EXPECTED_RE_ESCAPED is true, indicating the quotes in EXPECTED_RE
     41 # are already escaped.
     42 # The value for the setting corresponding to SHOW_CMD is then reset
     43 # to RESET_VALUE, then set again to the value given by $_gdb_maint_setting_str
     44 # and $_gdb_maint_setting.  The default value of RESET_VALUE (0) should work for
     45 # most settings.  Note that we do not check that RESET_VALUE differs from
     46 # the expected value, as we assume different values will be verified.
     47 # The setting value must still be the one in force before calling show_setting.
     48 # In other words, this verifies that
     49 #   maint set test-settings <some_setting> $_gdb_maint_setting_str(<some_setting>)
     50 #   maint set test-settings <some_setting> $_gdb_maint_setting(<some_setting>)
     51 # do not change the setting value.
     52 # This procedure makes it easier to make the test
     53 # name/message unique, since we test the "show" commands many times.
     54 # EXPECTED_RE is made part of the test name.
     55 proc show_setting {show_cmd expected_re {expected_re_escaped 0} {reset_value 0}} {
     56     global gdb_prompt
     57 
     58     with_test_prefix "$show_cmd $expected_re" {
     59 	gdb_test "$show_cmd" $expected_re "show"
     60 
     61 	# Remove the first two words (such as "maint show") to have the
     62 	# setting name to use for $_gdb_maint_setting_str.
     63 	regsub "\[^ \]+ +\[^ \]+ +\(.*\)" $show_cmd "\\1" maint_setting
     64 	if {$expected_re_escaped} {
     65 	    set escaped_expected_re $expected_re
     66 	} else {
     67 	    regsub -all "\"" $expected_re "\\\\\\\"" escaped_expected_re
     68 	}
     69 	set test "print \$_gdb_maint_setting_str"
     70 	set setting_str_value "xxxYYYxxx"
     71 	gdb_test_multiple "print \$_gdb_maint_setting_str(\"$maint_setting\")" $test {
     72 	    -re " = \"\($escaped_expected_re\)\".*$gdb_prompt $" {
     73 		set setting_str_value $expect_out(1,string)
     74 		regsub -all "\\\\" $expect_out(1,string) "" setting_str_value
     75 		pass $test
     76 	    }
     77 	}
     78 
     79 	# Change the setting value to RESET_VALUE, set it back to setting_str_value
     80 	# and check we still have the original value.
     81 	gdb_test_no_output "maintenance set $maint_setting $reset_value" "str reset $reset_value"
     82 	gdb_test_no_output "maintenance set $maint_setting $setting_str_value" "str set again"
     83 	gdb_test "$show_cmd" $expected_re "str show after reset+set again"
     84 
     85 	# Same test, but with value captured from $_gdb_maint_setting.
     86 	set test "print \$_gdb_maint_setting"
     87 	set setting_value "xxxYYYxxx"
     88 	gdb_test_multiple "print \$_gdb_maint_setting(\"$maint_setting\")" $test {
     89 	    -re " = \"\(.*\)\".*$gdb_prompt $" {
     90 		set setting_value $expect_out(1,string)
     91 		regsub -all "\\\\" $expect_out(1,string) "" setting_value
     92 		pass $test
     93 	    }
     94 	    -re " = \(.*\)\r\n$gdb_prompt $" {
     95 		set setting_value $expect_out(1,string)
     96 		pass $test
     97 	    }
     98 	}
     99 
    100 	gdb_test_no_output "maintenance set $maint_setting $reset_value" "reset $reset_value"
    101 	gdb_test_no_output "maintenance set $maint_setting $setting_value" "set again"
    102 	gdb_test "$show_cmd" $expected_re "show after reset+set again"
    103     }
    104 }
    105 
    106 # Verifies that $_gdb_setting (SETTING) gives a value whose ptype matches EXPECTED.
    107 proc check_type {setting expected} {
    108     with_test_prefix "check_type $setting $expected" {
    109 	gdb_test "print \$_gdb_maint_setting(\"$setting\")"
    110 	gdb_test "ptype $" "$expected"
    111 
    112 	# Currently, GDB ptype <internal function> always tells it is type int.
    113 	# ptype should better report an error such as:
    114 	#   "No type information for GDB functions"
    115 	# Test 'type int', so as to make it fail if ptype is changed.
    116 	gdb_test "ptype \$_gdb_maint_setting(\"$setting\")" \
    117             "type = int"
    118     }
    119 }
    120 
    121 # var_Xinteger tests.  VARIANT determines which command/variant to
    122 # exercise.
    123 proc test-integer {variant} {
    124     set set_cmd "maint set test-settings $variant"
    125     set show_cmd "maint show test-settings $variant"
    126 
    127     # A bogus value.
    128     gdb_test "$set_cmd bogus" \
    129 	"No symbol table is loaded\\.  Use the \"file\" command\\."
    130 
    131     # Seemingly-valid but not quite valid value.
    132     gdb_test "$set_cmd 1a" \
    133 	"Invalid number \"1a\"\\."
    134 
    135     # Valid value followed by garbage.
    136     gdb_test "$set_cmd 1 1" \
    137 	"A syntax error in expression, near `1'\\."
    138 
    139     # Valid value followed by garbage.
    140     gdb_test "$set_cmd 1 x" \
    141 	"A syntax error in expression, near `x'\\."
    142 
    143     if {$variant == "zuinteger-unlimited"} {
    144 	# -1 means unlimited.  Other negative values are rejected.  -1
    145 	# -is tested further below, along the "unlimited" tests.
    146 	gdb_test "$set_cmd -2" "only -1 is allowed to set as unlimited"
    147 	check_type "test-settings $variant" "type = int"
    148     } elseif {$variant == "uinteger" || $variant == "zuinteger"} {
    149 	# Negative values are not accepted.
    150 	gdb_test "$set_cmd -1" "integer -1 out of range"
    151 	gdb_test "$set_cmd -2" "integer -2 out of range"
    152 	check_type "test-settings $variant" "type = unsigned int"
    153     } else {
    154 	# Negative values are not accepted.
    155 	gdb_test_no_output "$set_cmd -1"
    156 	show_setting "$show_cmd" "-1"
    157 	gdb_test_no_output "$set_cmd -2"
    158 	show_setting "$show_cmd" "-2"
    159 	check_type "test-settings $variant" "type = int"
    160     }
    161 
    162     # Regular integer is accepted.
    163     gdb_test_no_output "$set_cmd 999"
    164     show_setting "$show_cmd" "999"
    165 
    166     if {$variant == "zinteger" || $variant == "zuinteger"} {
    167 	# 0 means 0.
    168 	gdb_test_no_output "$set_cmd 0"
    169 	show_setting "$show_cmd" "0"
    170     } else {
    171 	# Either 0 or -1 mean unlimited.  Test both the number and
    172 	# "unlimited".  For the latter, test both full name and
    173 	# abbreviations.
    174 
    175 	if {$variant == "zuinteger-unlimited"} {
    176 	    gdb_test_no_output "$set_cmd -1"
    177 	} else {
    178 	    gdb_test_no_output "$set_cmd 0"
    179 	}
    180 	show_setting "$show_cmd" "unlimited"
    181 
    182 	foreach_with_prefix value {
    183 	    "u"
    184 	    "un"
    185 	    "unl"
    186 	    "unli"
    187 	    "unlim"
    188 	    "unlimi"
    189 	    "unlimit"
    190 	    "unlimite"
    191 	    "unlimited"
    192 	} {
    193 	    # Alternate between integer and unlimited, to make sure the
    194 	    # setting really took effect.
    195 	    gdb_test_no_output "$set_cmd 1"
    196 	    show_setting "$show_cmd" "1"
    197 
    198 	    gdb_test_no_output "$set_cmd $value"
    199 	    show_setting "$show_cmd" "unlimited"
    200 	}
    201     }
    202 
    203     if {$variant == "zuinteger"} {
    204 	test_gdb_complete_multiple "maint set test-settings " "zuinteger" "" {
    205 	    "zuinteger"
    206 	    "zuinteger-unlimited"
    207 	}
    208     } else {
    209 	test_gdb_complete_unique \
    210 	    "$set_cmd" \
    211 	    "$set_cmd"
    212     }
    213 
    214     if {$variant == "zinteger" || $variant == "zuinteger"} {
    215 	test_gdb_complete_none \
    216 	    "$set_cmd " \
    217     } else {
    218 	test_gdb_complete_unique \
    219 	    "$set_cmd " \
    220 	    "$set_cmd unlimited"
    221     }
    222 
    223     # Check junk after "unlimited".
    224     gdb_test "$set_cmd unlimitedu" "No symbol table is loaded.*"
    225 
    226     if {$variant == "zinteger" || $variant == "zuinteger"} {
    227 	gdb_test "$set_cmd unlimited u" "No symbol table is loaded.*"
    228 	gdb_test "$set_cmd unlimited 1" "No symbol table is loaded.*"
    229 	gdb_test "$set_cmd unlimited -1" "No symbol table is loaded.*"
    230     } else {
    231 	gdb_test "$set_cmd unlimited u" "Junk after \"unlimited\": u"
    232 	gdb_test "$set_cmd unlimited 1" "Junk after \"unlimited\": 1"
    233 	gdb_test "$set_cmd unlimited -1" "Junk after \"unlimited\": -1"
    234     }
    235 
    236     test_gdb_complete_none "$set_cmd unlimited "
    237     test_gdb_complete_none "$set_cmd unlimitedu"
    238     test_gdb_complete_none "$set_cmd unlimited u"
    239     test_gdb_complete_none "$set_cmd unlimited 1"
    240     test_gdb_complete_none "$set_cmd x"
    241     test_gdb_complete_none "$set_cmd x "
    242     test_gdb_complete_none "$set_cmd -1"
    243     test_gdb_complete_none "$set_cmd -1 "
    244     test_gdb_complete_none "$set_cmd 1 "
    245 
    246     # Check show command completion.
    247     if {$variant == "zuinteger"} {
    248 	test_gdb_complete_multiple "maintenance show test-settings " "zuinteger" "" {
    249 	    "zuinteger"
    250 	    "zuinteger-unlimited"
    251 	}
    252     } else {
    253 	test_gdb_complete_unique \
    254 	    "$show_cmd" \
    255 	    "$show_cmd"
    256     }
    257     test_gdb_complete_none "$show_cmd "
    258 }
    259 
    260 # boolean tests.
    261 proc_with_prefix test-boolean {} {
    262     # Use these variables to make sure we don't call the wrong command
    263     # by mistake.
    264     set set_cmd "maint set test-settings boolean"
    265     set show_cmd "maint show test-settings boolean"
    266 
    267     # A bogus value.
    268     gdb_test "$set_cmd bogus" \
    269 	"\"on\" or \"off\" expected\\."
    270 
    271     # Seemingly-valid but not quite valid value.
    272     gdb_test "$set_cmd on1" \
    273 	"\"on\" or \"off\" expected\\."
    274 
    275     # Valid value followed by garbage.
    276     gdb_test "$set_cmd on 1" \
    277 	"\"on\" or \"off\" expected\\."
    278 
    279     # Unlike auto-bool settings, "-1" is not accepted.
    280     gdb_test "$set_cmd -1" \
    281 	"\"on\" or \"off\" expected\\."
    282 
    283     # Nor "auto".
    284     gdb_test "$set_cmd auto" \
    285 	"\"on\" or \"off\" expected\\."
    286 
    287     # "o" is ambiguous.
    288     gdb_test "$set_cmd o" \
    289 	"\"on\" or \"off\" expected\\."
    290 
    291     # Various valid values.  Test both full value names and
    292     # abbreviations.
    293 
    294     # Note that unlike with auto-bool, empty value implies "on".
    295     foreach_with_prefix value {
    296 	""
    297 	"on"
    298 	"1"
    299 	"y"
    300 	"ye"
    301 	"yes"
    302 	"e"
    303 	"en"
    304 	"ena"
    305 	"enab"
    306 	"enabl"
    307 	"enable"
    308     } {
    309 	gdb_test_no_output "$set_cmd off"
    310 	show_setting "$show_cmd" "off"
    311 
    312 	gdb_test_no_output "$set_cmd $value"
    313 	show_setting "$show_cmd" "on"
    314     }
    315 
    316     check_type "test-settings boolean" "type = int"
    317 
    318     foreach_with_prefix value {
    319 	"of"
    320 	"off"
    321 	"0"
    322 	"n"
    323 	"no"
    324 	"d"
    325 	"di"
    326 	"dis"
    327 	"disa"
    328 	"disab"
    329 	"disabl"
    330 	"disable"
    331     } {
    332 	gdb_test_no_output "$set_cmd on"
    333 	show_setting "$show_cmd" "on"
    334 
    335 	gdb_test_no_output "$set_cmd $value"
    336 	show_setting "$show_cmd" "off"
    337     }
    338 
    339     test_gdb_complete_multiple "$set_cmd " "" "o" {
    340 	"off"
    341 	"on"
    342     }
    343 
    344     test_gdb_complete_unique \
    345 	"$set_cmd of" \
    346 	"$set_cmd off"
    347 
    348     test_gdb_complete_none "$set_cmd x"
    349 
    350     # Check that the show command doesn't complete anything.
    351     test_gdb_complete_unique \
    352 	"$show_cmd" \
    353 	"$show_cmd"
    354     test_gdb_complete_none "$show_cmd "
    355 }
    356 
    357 # auto-boolean tests.
    358 proc_with_prefix test-auto-boolean {} {
    359     # Use these variables to make sure we don't call the wrong command
    360     # by mistake.
    361     set set_cmd "maint set test-settings auto-boolean"
    362     set show_cmd "maint show test-settings auto-boolean"
    363 
    364     # A bogus value.
    365     gdb_test "$set_cmd bogus" \
    366 	"\"on\", \"off\" or \"auto\" expected\\."
    367 
    368     # Seemingly-valid but not quite valid value.
    369     gdb_test "$set_cmd on1" \
    370 	"\"on\", \"off\" or \"auto\" expected\\."
    371 
    372     # Valid value followed by garbage.
    373     gdb_test "$set_cmd on 1" \
    374 	"\"on\", \"off\" or \"auto\" expected\\."
    375 
    376     # "o" is ambiguous.
    377     gdb_test "$set_cmd o" \
    378 	"\"on\", \"off\" or \"auto\" expected\\."
    379 
    380     # Various valid values.  Test both full value names and
    381     # abbreviations.
    382 
    383     foreach_with_prefix value {
    384 	"on"
    385 	"1"
    386 	"y"
    387 	"ye"
    388 	"yes"
    389 	"e"
    390 	"en"
    391 	"ena"
    392 	"enab"
    393 	"enabl"
    394 	"enable"
    395     } {
    396 	gdb_test_no_output "$set_cmd off"
    397 	show_setting "$show_cmd" "off"
    398 
    399 	gdb_test_no_output "$set_cmd $value"
    400 	show_setting "$show_cmd" "on"
    401     }
    402 
    403     foreach_with_prefix value {
    404 	"of"
    405 	"off"
    406 	"0"
    407 	"n"
    408 	"no"
    409 	"d"
    410 	"di"
    411 	"dis"
    412 	"disa"
    413 	"disab"
    414 	"disabl"
    415 	"disable"
    416     } {
    417 	gdb_test_no_output "$set_cmd on"
    418 	show_setting "$show_cmd" "on"
    419 
    420 	gdb_test_no_output "$set_cmd $value"
    421 	show_setting "$show_cmd" "off"
    422     }
    423 
    424     foreach_with_prefix value {
    425 	"a"
    426 	"au"
    427 	"aut"
    428 	"auto"
    429 	"-1"
    430     } {
    431 	gdb_test_no_output "$set_cmd on"
    432 	show_setting "$show_cmd" "on"
    433 
    434 	gdb_test_no_output "$set_cmd $value"
    435 	show_setting "$show_cmd" "auto"
    436     }
    437 
    438     check_type "test-settings auto-boolean" "type = int"
    439 
    440     # "-" is not accepted as abbreviation of "-1".
    441     gdb_test "$set_cmd -" \
    442 	"\"on\", \"off\" or \"auto\" expected\\."
    443 
    444     test_gdb_complete_multiple "$set_cmd " "" "" {
    445 	"auto"
    446 	"off"
    447 	"on"
    448     }
    449 
    450     test_gdb_complete_unique \
    451 	"$set_cmd of" \
    452 	"$set_cmd off"
    453 
    454     test_gdb_complete_none "$set_cmd x"
    455 
    456     # Check that the show command doesn't complete anything.
    457     test_gdb_complete_unique \
    458 	"$show_cmd" \
    459 	"$show_cmd"
    460     test_gdb_complete_none "$show_cmd "
    461 }
    462 
    463 # Enum option tests.
    464 proc_with_prefix test-enum {} {
    465     # Use these variables to make sure we don't call the wrong command
    466     # by mistake.
    467     set set_cmd "maint set test-settings enum"
    468     set show_cmd "maint show test-settings enum"
    469 
    470     # Missing value.
    471     gdb_test "$set_cmd" \
    472 	"Requires an argument\\. Valid arguments are xxx, yyy, zzz\\."
    473 
    474     # A bogus value.
    475     gdb_test "$set_cmd bogus" \
    476 	"Undefined item: \"bogus\"."
    477 
    478     # Seemingly-valid but not quite valid value.
    479     gdb_test "$set_cmd xxx1" \
    480 	"Undefined item: \"xxx1\"."
    481 
    482     # Valid value followed by garbage.
    483     gdb_test "$set_cmd xxx 1" \
    484 	"Junk after item \"xxx\": 1"
    485     # Valid value followed by garbage, with extra spaces.
    486     gdb_test "$set_cmd xxx      1" \
    487 	"Junk after item \"xxx\": 1"
    488     # Abbreviated value followed by garbage.
    489     gdb_test "$set_cmd xx 1" \
    490 	"Junk after item \"xx\": 1"
    491 
    492     # Various valid values.  Test both full value names and
    493     # abbreviations.
    494     gdb_test_no_output "$set_cmd x"
    495     show_setting "$show_cmd" "xxx" 0 "zzz"
    496     gdb_test_no_output "$set_cmd yy"
    497     show_setting "$show_cmd" "yyy" 0 "zzz"
    498     gdb_test_no_output "$set_cmd zzz"
    499     show_setting "$show_cmd" "zzz" 0 "yyy"
    500 
    501     check_type "test-settings enum" "type = char \\\[3\\\]"
    502 
    503     test_gdb_complete_multiple "$set_cmd " "" "" {
    504 	"xxx"
    505 	"yyy"
    506 	"zzz"
    507     }
    508 
    509     test_gdb_complete_unique \
    510 	"$set_cmd x" \
    511 	"$set_cmd xxx"
    512 
    513     test_gdb_complete_none "$set_cmd a"
    514 
    515     # Check that the show command doesn't complete anything.
    516     test_gdb_complete_unique \
    517 	"$show_cmd" \
    518 	"$show_cmd"
    519     test_gdb_complete_none "$show_cmd "
    520 }
    521 
    522 # string settings tests.
    523 proc test-string {variant} {
    524     global gdb_prompt
    525     global srcfile binfile
    526 
    527     # Load symbols for the completion test below.
    528     clean_restart $binfile
    529 
    530     # Use these variables to make sure we don't call the wrong command
    531     # by mistake.
    532     set set_cmd "maint set test-settings $variant"
    533     set show_cmd "maint show test-settings $variant"
    534 
    535     # Checks that gdb doesn't crash if we haven't set the string yet.
    536     if {$variant != "filename"} {
    537 	gdb_test "$show_cmd" "^$show_cmd\r\n" "$show_cmd: show default"
    538     } else {
    539 	gdb_test "$show_cmd" "/foo/bar" "$show_cmd: show default"
    540     }
    541 
    542     # A string value.
    543     gdb_test_no_output "$set_cmd hello world"
    544     show_setting "$show_cmd" "hello world"
    545 
    546     check_type "test-settings $variant" "type = char \\\[\[1-9\]\[0-9\]*\\\]"
    547 
    548     # A quoted string value.
    549     if {$variant == "string"} {
    550 	gdb_test_no_output "$set_cmd \"hello world\""
    551 	show_setting "$show_cmd" "\\\\\"hello world\\\\\"" 1
    552     } else {
    553 	gdb_test_no_output "$set_cmd \"hello world\""
    554 	show_setting "$show_cmd" "\"hello world\""
    555     }
    556 
    557     # Test clearing the string.
    558     with_test_prefix "clear string" {
    559 	if {$variant == "filename"} {
    560 	    gdb_test "$set_cmd" \
    561 		"Argument required \\(filename to set it to\\.\\)\\."
    562 
    563 	    # Check the value didn't change.
    564 	    show_setting "$show_cmd" "\"hello world\""
    565 	} else {
    566 	    gdb_test_no_output "$set_cmd"
    567 	    gdb_test "$show_cmd" \
    568 		"^$show_cmd\r\n" "$show_cmd: empty second time"
    569 	}
    570     }
    571 
    572     # Test completion.
    573     if {$variant == "string" || $variant == "string-noescape" } {
    574 	# Make sure GDB doesn't try to complete on symbols, which
    575 	# doesn't make any sense.
    576 	test_gdb_complete_none "$set_cmd "
    577     } else {
    578 	# Complete on filename.
    579 
    580 	# See comments in gdb.base/completion.exp.
    581 
    582 	# We `cd' to ${srcdir}, and then do the completion relative to
    583 	# the current directory.
    584 
    585 	# ${srcdir} may be a relative path.  We want to make sure we
    586 	# end up in the right directory - so make sure we know where
    587 	# it is.
    588 	global srcdir
    589 	set mydir [pwd]
    590 	cd ${srcdir}
    591 	set fullsrcdir [pwd]
    592 	cd ${mydir}
    593 
    594 	gdb_test "cd ${fullsrcdir}" \
    595 	    "Working directory [string_to_regexp ${fullsrcdir}].*" \
    596 	    "cd to \${srcdir}"
    597 
    598 	set unique_file ../testsuite/gdb.base/comp-dir/subdir/dummy
    599 
    600 	test_gdb_complete_unique \
    601 	    "$set_cmd ${unique_file}" \
    602 	    "$set_cmd ${unique_file}.txt"
    603 
    604 	test_gdb_complete_none "$set_cmd ${unique_file}.abc"
    605     }
    606 
    607     # Check show command completion.
    608     if {$variant == "string"} {
    609 	test_gdb_complete_multiple "maint show test-settings " "string" "" {
    610 	    "string"
    611 	    "string-noescape"
    612 	}
    613     } else {
    614 	test_gdb_complete_unique \
    615 	    "$show_cmd" \
    616 	    "$show_cmd"
    617     }
    618     test_gdb_complete_none "$show_cmd "
    619 }
    620 
    621 foreach variant {
    622     uinteger
    623     integer
    624     zinteger
    625     zuinteger
    626     zuinteger-unlimited
    627 } {
    628     with_test_prefix "test-integer $variant" {
    629 	test-integer $variant
    630     }
    631 }
    632 
    633 test-boolean
    634 test-auto-boolean
    635 test-enum
    636 
    637 foreach variant {
    638     string
    639     string-noescape
    640     optional-filename
    641     filename
    642 } {
    643     with_test_prefix "test-string $variant" {
    644 	test-string $variant
    645     }
    646 }
    647