From patchwork Sun Oct 27 09:15:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 99674 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 821EF3858C2B for ; Sun, 27 Oct 2024 09:16:05 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 531BB3858408 for ; Sun, 27 Oct 2024 09:15:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 531BB3858408 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 531BB3858408 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730020524; cv=none; b=eAtI4meW063t0W0KGX/eUw/vGYfqDaCvAFYdIWbDQKgz6WFG8KP0df28OfVQx1TRJ7sII9ugM4emCzqmTXsXQHBOCvbwt2JyvVNHp/RaEAsHHS9QcpdekPcScqWNXjULpRTtqL0OVeEZxt18n3/evbXVs+xqZqQxzHGI+1nvF7s= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730020524; c=relaxed/simple; bh=C4d/gDBx5Pw4m4IdTnzMHxu5V4aTkeDQt3iDsbhFbPA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=CCyosCa4fgmDjp0BHLP8664xuZ3tTMzjkOoIePVjiMZjEIVOlXOhogcgFmWakWkyaY3NL7gY6vUInVt4ZXv7ICvMfDMjv5z0dyXRXGnwRCuo2hPKtoi5GJWchiM2RkG3vcHuQPk4BQM7kST2GGKJguNxvgAJTukB1fjruZ3AjOw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1730020521; 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=frW8wTK4UoP/jPQ8qSceWxV0rN1OjIhaVQsLS4VoG1I=; b=eCA2kigIIGRUN5774ZpwyugNGIYFHjq2oftovfAlYNBOcdcpoLggNL+7TG5p2yFRXNjejT nxuriaUHmXMkj0JFFYtsnhD8E/Voqz8i191kUeYVqtSYFnagLjzqafCl9/nHxTcLSiVyrd 9JW0+K7eTNg3WFt1uUY6Kw8etOW5Sag= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-189-nYcZkgnAP4m0r-N5sH_LSA-1; Sun, 27 Oct 2024 05:15:13 -0400 X-MC-Unique: nYcZkgnAP4m0r-N5sH_LSA-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-4316300bb15so24280505e9.2 for ; Sun, 27 Oct 2024 02:15:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730020512; x=1730625312; 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=frW8wTK4UoP/jPQ8qSceWxV0rN1OjIhaVQsLS4VoG1I=; b=pOs+LgcRE/7MAV4Dswwd4dhyXejckQ/7DxDAz2GKwF9GnVYeJf4rftxly/Ep3QpJEM rhM4AEshAeQJZ8DvsoG/prLLLNrGUIeRyF4vPWqmtJuoSdaD66m3HKZvCyg0eVMGocNV HonvfJCovoqAeW2nrOJNOuzmA7oo/7aVnMgZzf4ItlN74ul/LcnOf9aQmjq+UO0O/qQb ebdPwgf6afQB/KE/CrVqtXKgAfxcaq4KKjv2C+5jeUwUuzP+XFiiKx5kBXnethA8MKHe MKCgGgyr0JA8AgTBLrkouqVPmlpi412iU9xvTb6vzBKropJRoW1MTb7F07eeBJ9d1jJf S4MQ== X-Gm-Message-State: AOJu0Yx4EpIX3QD0wj9qMHHmflOCXS+NF30EaixYKdlfzagOPmqhWxl7 xpX/YLCNczcwcV4VUxh8I1MdwWWFYt1hg+toQAJ1pqUduEptDiftif3UiN4NURcOGGqZ8tf/1ZP JP1kG8BeSO31J5kJZpEm3CjGVOHseAzvnGpdT9xYsrdLW6IqA88RhQqxzNkzlnIpaXlybvaWpN6 pde7twbaB4DWGNI2hcgH8fVvXM8RDSJ2pBTymdzByQ+ws= X-Received: by 2002:a05:6000:4d:b0:37d:4d72:dca3 with SMTP id ffacd0b85a97d-380611637f8mr3909692f8f.31.1730020511625; Sun, 27 Oct 2024 02:15:11 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGJWWwTjTMJz8YuknpGdthfzzArIyfOrBgVV3cIPHjR2y8y24UmctHi3FbPaVn+mCxL4sxitA== X-Received: by 2002:a05:6000:4d:b0:37d:4d72:dca3 with SMTP id ffacd0b85a97d-380611637f8mr3909665f8f.31.1730020510935; Sun, 27 Oct 2024 02:15:10 -0700 (PDT) Received: from localhost (197.209.200.146.dyn.plus.net. [146.200.209.197]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-38058b3c7f6sm6305645f8f.41.2024.10.27.02.15.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 02:15:10 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCHv2 1/2] gdb: add filename option support Date: Sun, 27 Oct 2024 09:15:04 +0000 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 X-Spam-Status: No, score=-12.0 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_H2, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org 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 389e2d736c5..6de312bc6a3 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