From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 296AA386100F for ; Tue, 1 Oct 2024 12:49:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 296AA386100F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 296AA386100F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727786958; cv=none; b=dVPa9lh1T0Ttx6TMbuPGJi9pYJvZG0kly6jKG4EcE7IIJHqeHOGa358uSJ5Rhj22T2nAAMofLGlGGXJl3PRmjFlsrzFEDxmIW3kRiPbH+b+LLUSefZ8KiXuDvHyrFGlGbYdtXFsEzf2QupU5Fjbb+3pzpH3OQ4EhjZwWCJ1g4EQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727786958; c=relaxed/simple; bh=f8OjWZqfbq232cIpp32DbmqB0+jd8wMQoeyda8F6Vtk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=jDk2oRWLzcldcFU5Ft32gyRGMooKgLjvyQCXrj9hOwvQLQkR/+jlVOUSOeLyALQ05BkYHfFPdFOptxmlYw9T1q9ytKMd69nkUQoGLcnbqPJz6R2ts474ARstZD9JkYwVkxWYrnRKRTSxeqZL12lgL+mWh9mpkqc6cTlieJ0sXXw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727786954; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FZJpiX/J/0JVT4WzHO9NTSHMy4JJNlVTqHg1AU/9b+4=; b=asXHXM3tN9BtBzLZtK60us+BRJHjTLwEIrePJO7mDPuzcjy0DjgX4cb3TD8IQPd+1lIQ4L Rz100gEw5K3gUCqgquRFO3HGDC1m/bVuLt+nUm95ke+tmPkMUWKaq/wodmRY1ZLX/HGXKY tcOkilZYeARDYfLrfYImR/MZzb77174= Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-554-IH4UEG5zPUKzYtxTbnGSSw-1; Tue, 01 Oct 2024 08:49:13 -0400 X-MC-Unique: IH4UEG5zPUKzYtxTbnGSSw-1 Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-a8d3085ab6fso381356666b.0 for ; Tue, 01 Oct 2024 05:49:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727786952; x=1728391752; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FZJpiX/J/0JVT4WzHO9NTSHMy4JJNlVTqHg1AU/9b+4=; b=pLthtZKW49Vb8iyDxgYhjtxIrSGAZ0t9ROSvTLu/r3DVNmU7F1bPUq/rJSktmDx/tD e3BdWS15oxncAHgFCwmCgxdE9bmg8iX8HAnm3lcWIj3h3jdYCFuaSNcN0oB7Dqhm1DDm bgnT1xcxUDzFJwrfpKs8+7vvm9Y2FidHUjib1r1NohylXFsNHuapICDk6oa0J4XD3jEG jqsPWJJ98J16bYz7zUhPfyJ4X/YQbydMY2nm2u7WIL118GTO1mxW63qU+SyQwJ+yK95P 69Qaq89z5LBqiqnayWM35AlqnT2XNlzt1m029LjFOHleE45L3YlOnZdEXLkmejcNpU+n 7WIw== X-Gm-Message-State: AOJu0YxV/IsZ3kuK3li+Xb2z4/G1C8nFXAyv8C5tyauz51RM/4CRaspc GqjukRlQWNxGLTl8wvV3lDvgw6PBlar1gL6rGP810MmcoSfvn8ogBUKWdl7HTw3tdBRLRJjmtwu wAbxj4AxlTXmQLbM/9PheSLrpehde38Z596YLeOOXeaYYE+1ouPt7VBYH4bZNsUNeeImsw5KaKV HrwaVHoGJaOl/1p0tVSN4MXIlMHGW0hucCFs+MtwOY1zM= X-Received: by 2002:a17:907:3f22:b0:a86:8ff8:1dd8 with SMTP id a640c23a62f3a-a93c4a61db7mr1863745866b.46.1727786951563; Tue, 01 Oct 2024 05:49:11 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEktfrs9c5Hkq4VxadGh4VBSYY+70/5BFAkkcsl+aPyr3m+hKaIdt/zk47Tp3Hmp/5gsdvjQQ== X-Received: by 2002:a17:907:3f22:b0:a86:8ff8:1dd8 with SMTP id a640c23a62f3a-a93c4a61db7mr1863742466b.46.1727786950901; Tue, 01 Oct 2024 05:49:10 -0700 (PDT) Received: from localhost (243.223.159.143.dyn.plus.net. [143.159.223.243]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a93c2946bddsm702420866b.123.2024.10.01.05.49.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Oct 2024 05:49:10 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 1/2] gdb: add filename option support Date: Tue, 1 Oct 2024 13:49:05 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This commit adds support for filename options to GDB's option sub-system (see cli/cli-option.{c,h}). The new filename options support quoted and escaped filenames, and tab completion is fully supported. This commit adds the new option, and adds these options to the 'maintenance test-options' command as '-filename', along with some tests that exercise this new option. I've split the -filename testing into two. In gdb.base/options.exp we use the -filename option with some arbitrary strings. This tests that GDB can correctly extract the value from a filename option, and that GDB can complete other options after a filename option. However, these tests don't actually pass real filenames, nor do they test filename completion. In gdb.base/filename-completion.exp I have added some tests that test the -filename option with real filenames, and exercise filename tab completion. This commit doesn't include any real uses of the new filename options, that will come in the next commit. --- gdb/cli/cli-option.c | 90 +++++++++++++++++- gdb/cli/cli-option.h | 20 ++++ gdb/maint-test-options.c | 28 ++++-- .../gdb.base/filename-completion.exp | 7 ++ gdb/testsuite/gdb.base/options.exp | 95 +++++++++++++++++-- 5 files changed, 222 insertions(+), 18 deletions(-) diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c index 05539285c80..9eb9ff81154 100644 --- a/gdb/cli/cli-option.c +++ b/gdb/cli/cli-option.c @@ -43,7 +43,7 @@ union option_value /* For var_enum options. */ const char *enumeration; - /* For var_string options. This is malloc-allocated. */ + /* For var_string and var_filename options. This is allocated with new. */ std::string *string; }; @@ -85,7 +85,7 @@ struct option_def_and_value { if (value.has_value ()) { - if (option.type == var_string) + if (option.type == var_string || option.type == var_filename) delete value->string; } } @@ -102,7 +102,7 @@ struct option_def_and_value { if (value.has_value ()) { - if (option.type == var_string) + if (option.type == var_string || option.type == var_filename) value->string = nullptr; } } @@ -452,6 +452,78 @@ parse_option (gdb::array_view options_group, return option_def_and_value {*match, match_ctx, val}; } + case var_filename: + { + if (check_for_argument (args, "--")) + { + /* Treat e.g., "maint test-options -filename --" as if there + was no argument after "-filename". */ + error (_("-%s requires an argument"), match->name); + } + + const char *arg_start = *args; + std::string str = extract_string_maybe_quoted (args); + + /* If we are performing completion, and extracting STR moved ARGS + to the end of the line, then the user is trying to complete the + filename value. + + If ARGS didn't make it to the end of the line then the filename + value is already complete and the user is trying to complete + something later on the line. */ + if (completion != nullptr && **args == '\0') + { + /* Preserve the current custom word point. If the call to + advance_to_filename_maybe_quoted_complete_word_point below + skips to the end of the command line then the custom word + point will have been updated even though we generate no + completions. + + However, *ARGS will also have been updated, and the general + option completion code (which we will return too) also + updates the custom word point based on the adjustment made + to *ARGS. + + And so, if we don't find any completions, we should restore + the custom word point value, this leaves the generic option + completion code free to make its own adjustments. */ + int prev_word_pt = completion->tracker.custom_word_point (); + + /* From ARG_START move forward to the start of the completion + word, this will skip over any opening quote if there is + one. + + If the word to complete is fully quoted, i.e. has an + opening and closing quote, then this will skip over the + word entirely and leave WORD pointing to the end of the + input string. */ + const char *word + = advance_to_filename_maybe_quoted_complete_word_point + (completion->tracker, arg_start); + + if (word == arg_start || *word != '\0') + { + filename_maybe_quoted_completer (nullptr, completion->tracker, + arg_start, word); + + if (completion->tracker.have_completions ()) + return {}; + } + + /* No completions. Restore the custom word point. See the + comment above for why this is needed. */ + completion->tracker.set_custom_word_point (prev_word_pt); + } + + /* Check we did manage to extract something. */ + if (*args == arg_start) + error (_("-%s requires an argument"), match->name); + + option_value val; + val.string = new std::string (std::move (str)); + return option_def_and_value {*match, match_ctx, val}; + } + default: /* Not yet. */ gdb_assert_not_reached ("option type not supported"); @@ -612,6 +684,7 @@ save_option_value_in_ctx (std::optional &ov) = ov->value->enumeration; break; case var_string: + case var_filename: *ov->option.var_address.string (ov->option, ov->ctx) = std::move (*ov->value->string); break; @@ -701,6 +774,8 @@ get_val_type_str (const option_def &opt, std::string &buffer) } case var_string: return "STRING"; + case var_filename: + return "FILENAME"; default: return nullptr; } @@ -856,6 +931,15 @@ add_setshow_cmds_for_options (command_class cmd_class, nullptr, option.show_cmd_cb, set_list, show_list); } + else if (option.type == var_filename) + { + add_setshow_filename_cmd (option.name, cmd_class, + option.var_address.string (option, data), + option.set_doc, option.show_doc, + option.help_doc, + nullptr, option.show_cmd_cb, + set_list, show_list); + } else gdb_assert_not_reached ("option type not handled"); } diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h index bbe281d9721..26307a5d1e9 100644 --- a/gdb/cli/cli-option.h +++ b/gdb/cli/cli-option.h @@ -308,6 +308,26 @@ struct string_option_def : option_def } }; +/* A var_filename command line option. */ + +template +struct filename_option_def : option_def +{ + filename_option_def (const char *long_option_, + std::string *(*get_var_address_cb_) (Context *), + show_value_ftype *show_cmd_cb_, + const char *set_doc_, + const char *show_doc_ = nullptr, + const char *help_doc_ = nullptr) + : option_def (long_option_, var_filename, nullptr, + (erased_get_var_address_ftype *) get_var_address_cb_, + show_cmd_cb_, + set_doc_, show_doc_, help_doc_) + { + var_address.string = detail::get_var_address; + } +}; + /* A group of options that all share the same context pointer to pass to the options' get-current-value callbacks. */ struct option_def_group diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c index 48b68f91084..9d768177798 100644 --- a/gdb/maint-test-options.c +++ b/gdb/maint-test-options.c @@ -57,12 +57,13 @@ readline, for proper testing of TAB completion. These maintenance commands support options of all the different - available kinds of commands (boolean, enum, flag, string, uinteger): + available kinds of commands (boolean, enum, flag, string, filename, + uinteger): (gdb) maint test-options require-delimiter -[TAB] - -bool -pinteger-unlimited -xx1 - -enum -string -xx2 - -flag -uinteger-unlimited + -bool -flag -uinteger-unlimited + -enum -pinteger-unlimited -xx1 + -filename -string -xx2 (gdb) maint test-options require-delimiter -bool o[TAB] off on @@ -77,14 +78,14 @@ Invoking the commands makes them print out the options parsed: (gdb) maint test-options unknown-is-error -flag -enum yyy cmdarg - -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg + -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -filename '' -- cmdarg (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg - -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -- -flag -enum yyy cmdarg + -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -filename '' -- -flag -enum yyy cmdarg (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg -- Unrecognized option at: cmdarg -- (gdb) maint test-options require-delimiter -flag -enum yyy -- cmdarg - -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg + -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -filename '' -- cmdarg The "maint show test-options-completion-result" command exists in order to do something similar for completion: @@ -135,6 +136,7 @@ struct test_options_opts unsigned int uint_unl_opt = 0; int pint_unl_opt = 0; std::string string_opt; + std::string filename_opt; test_options_opts () = default; @@ -146,7 +148,8 @@ struct test_options_opts { gdb_printf (file, _("-flag %d -xx1 %d -xx2 %d -bool %d " - "-enum %s -uint-unl %s -pint-unl %s -string '%s' -- %s\n"), + "-enum %s -uint-unl %s -pint-unl %s -string '%s' " + "-filename '%s' -- %s\n"), flag_opt, xx1_opt, xx2_opt, @@ -159,6 +162,7 @@ struct test_options_opts ? "unlimited" : plongest (pint_unl_opt)), string_opt.c_str (), + filename_opt.c_str (), args); } }; @@ -233,6 +237,14 @@ static const gdb::option::option_def test_options_option_defs[] = { nullptr, /* show_cmd_cb */ N_("A string option."), }, + + /* A filename option. */ + gdb::option::filename_option_def { + "filename", + [] (test_options_opts *opts) { return &opts->filename_opt; }, + nullptr, /* show_cmd_cb */ + N_("A filename option."), + }, }; /* Create an option_def_group for the test_options_opts options, with diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index 85fac35d7c8..a99781965e7 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -412,6 +412,13 @@ proc run_quoting_and_escaping_tests { root } { run_mid_line_completion_tests $root $cmd } + + foreach sub_cmd { require-delimiter unknown-is-error unknown-is-operand } { + set cmd "maintenance test-options $sub_cmd -filename" + with_test_prefix "cmd=$cmd" { + run_quoting_and_escaping_tests_1 $root $cmd + } + } } # Helper for run_unquoted_tests. ROOT is the root directory as setup diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index 841e603764c..e1ad61e6470 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -99,21 +99,21 @@ proc make_cmd {variant} { # operand. proc expect_none {operand} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -- $operand" + -string '' -filename '' -- $operand" } # Return a string for the expected result of running "maint # test-options xxx", with -flag set. OPERAND is the expected operand. proc expect_flag {operand} { return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -- $operand" + -string '' -filename '' -- $operand" } # Return a string for the expected result of running "maint # test-options xxx", with -bool set. OPERAND is the expected operand. proc expect_bool {operand} { return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -- $operand" + -string '' -filename '' -- $operand" } # Return a string for the expected result of running "maint @@ -123,10 +123,10 @@ proc expect_bool {operand} { proc expect_integer {option val operand} { if {$option == "uinteger-unlimited"} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl $val\ - -pint-unl 0 -string '' -- $operand" + -pint-unl 0 -string '' -filename '' -- $operand" } elseif {$option == "pinteger-unlimited"} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0\ - -pint-unl $val -string '' -- $operand" + -pint-unl $val -string '' -filename '' -- $operand" } else { error "unsupported option: $option" } @@ -144,12 +144,28 @@ proc expect_string {str operand} { set str [string range $str 1 end-1] } return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '$str' -- $operand" + -string '$str' -filename '' -- $operand" +} + +# Return a string for the expected result of running "maint +# test-options xxx", with -filename set to $STR. OPERAND is the +# expected operand. +proc expect_filename {str operand} { + # Dequote the string in the expected output. + if { ( [string range $str 0 0] == "\"" + && [string range $str end end] == "\"") + || ([string range $str 0 0] == "'" + && [string range $str end end] == "'")} { + set str [string range $str 1 end-1] + } + return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ + -string '' -filename '$str' -- $operand" } set all_options { "-bool" "-enum" + "-filename" "-flag" "-pinteger-unlimited" "-string" @@ -612,7 +628,7 @@ proc_with_prefix test-flag {variant} { # Extract twice the same flag, separated by one space. gdb_test "$cmd -xx1 -xx2 -xx1 -xx2 -xx1 -- non flags args" \ "-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -- non flags args" + -string '' -filename '' -- non flags args" # Extract 2 known flags in front of unknown flags. gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \ @@ -1031,6 +1047,70 @@ proc_with_prefix test-string {variant} { } } +# Filename option tests. These tests only focus on how GDB parses the +# filename option, and ensures that GDB can complete things after the +# filename value. The actual strings passed as filenames in this proc +# are not actual files that exist on disk. +# +# Filename options do also support completion. For testing of this +# aspect see the gdb.base/filename-completion.exp script. +proc_with_prefix test-filename {variant} { + global all_options + + set cmd [make_cmd $variant] + + # Check that "-" where a value is expected does not show the + # command's options. I.e., a filename's value is not optional. + # Check both completion and running the command. + res_test_gdb_complete_none \ + "1 [expect_none ""]" \ + "$cmd -filename -" + gdb_test "$cmd -filename --" \ + "-filename requires an argument" + if {$variant == "require-delimiter"} { + gdb_test "$cmd -filename" [expect_none "-filename"] + } else { + gdb_test "$cmd -filename" \ + "-filename requires an argument" + } + + foreach_with_prefix str { + "STR" + "\"STR\"" + "\\\"STR" + "'STR'" + "\\'STR" + "\"STR AAA\"" + "'STR BBB'" + "\"STR 'CCC' DDD\"" + "'STR \"EEE\" FFF'" + "\"STR \\\"GGG\\\" HHH\"" + "'STR \\\'III\\\' JJJ'" + } { + res_test_gdb_complete_none \ + "1 [expect_none ""]" \ + "$cmd -filename ${str}" + gdb_test "$cmd -filename ${str} --" [expect_filename "${str}" ""] + + # Completing at "-" after parsing STR should list all options. + res_test_gdb_complete_multiple \ + "1 [expect_filename "${str}" "-"]" \ + "$cmd -filename ${str} " "-" "" $all_options + + # Check that only $STR is considered part of the filename's value. + # I.e., that we stop parsing the filename at the first + # whitespace or after the closing quote of $STR. + if {$variant == "require-delimiter"} { + res_test_gdb_complete_none \ + "1 [expect_filename "${str}" "BAR"]" \ + "$cmd -filename ${str} BAR" + } else { + res_test_gdb_complete_none "0 BAR" "$cmd -filename ${str} BAR" + } + gdb_test "$cmd -filename ${str} BAR --" "Unrecognized option at: BAR --" + } +} + # Run the options framework tests first. foreach_with_prefix cmd { "require-delimiter" @@ -1045,6 +1125,7 @@ foreach_with_prefix cmd { } test-enum $cmd test-string $cmd + test-filename $cmd } # Run the print integration tests, both as "standalone", and under -- 2.25.4