From patchwork Fri Mar 29 11:42:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87812 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 5B0D93858C55 for ; Fri, 29 Mar 2024 11:43:12 +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 ESMTPS id F1BE63858D28 for ; Fri, 29 Mar 2024 11:42:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F1BE63858D28 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 F1BE63858D28 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=1711712562; cv=none; b=vqTAJEfvyc7pTDTBiKC1c8J2K1t46F6HKCC0k7uEYALIhTlQMKGBdhGmsDcrFijJzfi+KUENl99egVYJR65pA7pE+pHBBMfiL1KiYrZBiYTWKJRwL3UNQMlMid7jUjvckd9yMutkQkyo4BYhdiLG/5ly+sYMELE//cUmbUFnVog= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712562; c=relaxed/simple; bh=sXsBC3Uz2JBl1Qru4bsWgSENlDwIFOMK76JfM3Rh2TU=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=xynEL84GGKo8FR2ECsu950v4v7X6pxpSC3Mbk0o91TOpGJNtfWkyLYESvldLSRhtHswxiAOztJX983TO4cjBHyL+L19E3kXpkzJi2SxBYrgQC33bGKg1ztppxgsYINUN5GvClS26GQL8fCbas9I1LDxrQf28RYJilPsmPZAfX0A= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712559; 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=74UtgUnjzOsWpAVWoqjkzLOBBp6kPCNwfk2+nQX/soE=; b=ZPz8fAYrvpKSevzn8Rt4oZf23eHbWGd18nxFWEnas8AsfWNrhaJVkUEo5Tjbzn6KeQnKpz Evv80fnybmQzaJAo5CBmHuKnVGosNOKBjzaiUiHQ4IQ6g18UsEWEUhQGBeCG1pjWIxtFst tBoATyXgRC/lKRk11OSFGeibvrdK70c= Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-577-MzhCx5uNOm-Qggbq2NS1fg-1; Fri, 29 Mar 2024 07:42:37 -0400 X-MC-Unique: MzhCx5uNOm-Qggbq2NS1fg-1 Received: by mail-ej1-f69.google.com with SMTP id a640c23a62f3a-a4dfdbdaf06so103077066b.3 for ; Fri, 29 Mar 2024 04:42:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712555; x=1712317355; 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=74UtgUnjzOsWpAVWoqjkzLOBBp6kPCNwfk2+nQX/soE=; b=gUodiMVUCLerwpUu/JX6YV76eGsVQ3QePukpfPM0SyLnArZDnjHh44FFlnOLVFg8nA FLBzQpV0D6Vv+Nks4TD3q4P+vljsdfv9aaWc3lLHr3su5KbGmzSQAv3z780J5HFhb/6a 9A9S6BzJtN9EDwH171hAgk2J9C95tYWt8QEHYqcevcnn6cEPftgBWkjQYegdzS8yrchf ao0kh+p72nzbmHbJenBzoARWoJIPA39WvuRX60qVDzVTeO+sAP35Wiq6yO278VCWep1d Lt8ALFsE9QjcKTwR4RVe9rNKPZowP3Z22Scair2x57wZQ+e6FYAPx5nEtcx0ys5u0Mpq 0gbg== X-Gm-Message-State: AOJu0YyxKXDxlryg7l5+4Fi3Vtt1DumoXIEhZW6qQO1kStaYEDKbyYU8 bV5lnm8aE9v8jbT8xgNfQ+W+OE6lH1zK4X+5UusGU4JCTqCt5bb0An6CmFsAt3zxiJmCVfx8dzK 01rehIO2HtO4NS3wg3l9Y7txbz14fVn+56WdfumekQlRBOx3xb6XLrHQHuCXYlKsnTx3z0RJQ+X mapXvd18vOiRQE0DKLR8iXeJ/lOOG2RI0saBlRis1GiM4= X-Received: by 2002:a05:6402:268f:b0:56b:f54a:6eee with SMTP id w15-20020a056402268f00b0056bf54a6eeemr1587968edd.0.1711712555381; Fri, 29 Mar 2024 04:42:35 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH3vI9kLWx22cozrScc+aVTJB38OlxVt9uvGpbtSnjwm5dzBx+8uNMyPCCtok7tBZxP7vcIyA== X-Received: by 2002:a05:6402:268f:b0:56b:f54a:6eee with SMTP id w15-20020a056402268f00b0056bf54a6eeemr1587946edd.0.1711712554769; Fri, 29 Mar 2024 04:42:34 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id cf2-20020a0564020b8200b0056b0af78d80sm1945221edb.34.2024.03.29.04.42.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:34 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 1/6] gdb: improve escaping when completing filenames Date: Fri, 29 Mar 2024 11:42:27 +0000 Message-Id: <0543026b57b831d545d99e325d6e6c3ed9c4c968.1711712401.git.aburgess@redhat.com> 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_H4, 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 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 is a step towards improving filename quoting when completing filenames. I've struggled a bit trying to split this series into chunks. There's a lot of dependencies between different parts of the completion system, and trying to get this working correctly is pretty messy. This first step is really about implementing 3 readline hooks: rl_char_is_quoted_p - Is a particular character quoted within readline's input buffer? rl_filename_dequoting_function - Remove quoting characters from a filename. rl_filename_quoting_function - Add quoting characters to a filename. See 'info readline' for more details. There's still a bunch of stuff that doesn't work after this commit, mostly around the 'complete' command which of course doesn't go through readline, so doesn't benefit from all of these new functions yet, I'll add some of this in a later commit. Tab completion is now slightly improved though, it is possible to tab-complete a filename that includes a double or single quote, either in an unquoted string or within a string surrounded by single or double quotes, backslash escaping is used when necessary. There are some additional tests to cover the new functionality. --- gdb/completer.c | 163 +++++++++++++++++- .../gdb.base/filename-completion.exp | 34 ++++ 2 files changed, 194 insertions(+), 3 deletions(-) diff --git a/gdb/completer.c b/gdb/completer.c index 8e34e30f46b..4cda5f3a383 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -204,6 +204,153 @@ noop_completer (struct cmd_list_element *ignore, { } +/* Return 1 if the character at EINDEX in STRING is quoted (there is an + unclosed quoted string), or if the character at EINDEX is quoted by a + backslash. */ + +static int +gdb_completer_file_name_char_is_quoted (char *string, int eindex) +{ + for (int i = 0; i <= eindex && string[i] != '\0'; ) + { + char c = string[i]; + + if (c == '\\') + { + /* The backslash itself is not quoted. */ + if (i >= eindex) + return 0; + ++i; + /* But the next character is. */ + if (i >= eindex) + return 1; + if (string[i] == '\0') + return 0; + ++i; + continue; + } + else if (strchr (rl_completer_quote_characters, c) != nullptr) + { + /* This assumes that extract_string_maybe_quoted can handle a + string quoted with character C. Currently this is true as the + only characters we put in rl_completer_quote_characters are + single and/or double quotes, both of which + extract_string_maybe_quoted can handle. */ + const char *tmp = &string[i]; + (void) extract_string_maybe_quoted (&tmp); + i = tmp - string; + + /* Consider any character within the string we just skipped over + as quoted, though this might not be completely correct; the + opening and closing quotes are not themselves quoted. But so + far this doesn't seem to have caused any issues. */ + if (i >= eindex) + return 1; + } + else + ++i; + } + + return 0; +} + +/* Removing character escaping from FILENAME. QUOTE_CHAR is the quote + character around FILENAME or the null-character if there is no quoting + around FILENAME. */ + +static char * +gdb_completer_file_name_dequote (char *filename, int quote_char) +{ + std::string tmp; + + if (quote_char == '\'') + { + /* There is no backslash escaping within a single quoted string. In + this case we can just return the input string. */ + tmp = filename; + } + else if (quote_char == '"') + { + /* Remove escaping from a double quoted string. */ + for (const char *input = filename; + *input != '\0'; + ++input) + { + if (input[0] == '\\' + && input[1] != '\0' + && strchr ("\"\\", input[1]) != nullptr) + ++input; + tmp += *input; + } + } + else + { + /* Remove escaping from an unquoted string. */ + for (const char *input = filename; + *input != '\0'; + ++input) + { + /* We allow anything to be escaped in an unquoted string. */ + if (*input == '\\') + { + ++input; + if (*input == '\0') + break; + } + + tmp += *input; + } + } + + return strdup (tmp.c_str ()); +} + +/* Apply character escaping to the file name in TEXT. QUOTE_PTR points to + the quote character surrounding TEXT, or points to the null-character if + there are no quotes around TEXT. MATCH_TYPE will be one of the readline + constants SINGLE_MATCH or MULTI_MATCH depending on if there is one or + many completions. */ + +static char * +gdb_completer_file_name_quote (char *text, int match_type, char *quote_ptr) +{ + std::string str; + + if (*quote_ptr == '\'') + { + /* There is no backslash escaping permitted within a single quoted + string, so in this case we can just return the input sting. */ + str = text; + } + else if (*quote_ptr == '"') + { + /* Add escaping for a double quoted filename. */ + for (const char *input = text; + *input != '\0'; + ++input) + { + if (strchr ("\"\\", *input) != nullptr) + str += '\\'; + str += *input; + } + } + else + { + /* Add escaping for an unquoted filename. */ + for (const char *input = text; + *input != '\0'; + ++input) + { + if (strchr (" \t\n\\\"'", *input) + != nullptr) + str += '\\'; + str += *input; + } + } + + return strdup (str.c_str ()); +} + /* Complete on filenames. */ void @@ -211,6 +358,7 @@ filename_completer (struct cmd_list_element *ignore, completion_tracker &tracker, const char *text, const char *word) { + rl_char_is_quoted_p = gdb_completer_file_name_char_is_quoted; rl_completer_quote_characters = gdb_completer_file_name_quote_characters; int subsequent_name = 0; @@ -262,6 +410,7 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore, (gdb_completer_file_name_break_characters); rl_completer_quote_characters = gdb_completer_file_name_quote_characters; + rl_char_is_quoted_p = gdb_completer_file_name_char_is_quoted; } /* Find the bounds of the current word for completion purposes, and @@ -1261,6 +1410,7 @@ complete_line_internal_1 (completion_tracker &tracker, completing file names then we can switch to the file name quote character set (i.e., both single- and double-quotes). */ rl_completer_quote_characters = gdb_completer_expression_quote_characters; + rl_char_is_quoted_p = nullptr; /* Decide whether to complete on a list of gdb commands or on symbols. */ @@ -2153,9 +2303,11 @@ completion_tracker::build_completion_result (const char *text, /* Build replacement word, based on the LCD. */ recompute_lowest_common_denominator (); - match_list[0] - = expand_preserving_ws (text, end - start, - m_lowest_common_denominator); + if (rl_filename_completion_desired) + match_list[0] = xstrdup (m_lowest_common_denominator); + else + match_list[0] + = expand_preserving_ws (text, end - start, m_lowest_common_denominator); if (m_lowest_common_denominator_unique) { @@ -3018,6 +3170,11 @@ _initialize_completer () rl_attempted_completion_function = gdb_rl_attempted_completion_function; set_rl_completer_word_break_characters (default_word_break_characters ()); + /* Setup readline globals relating to filename completion. */ + rl_filename_quote_characters = " \t\n\\\"'"; + rl_filename_dequoting_function = gdb_completer_file_name_dequote; + rl_filename_quoting_function = gdb_completer_file_name_quote; + add_setshow_zuinteger_unlimited_cmd ("max-completions", no_class, &max_completions, _("\ Set maximum number of completion candidates."), _("\ diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index b700977cec5..f23e8671f40 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -40,6 +40,9 @@ proc setup_directory_tree {} { remote_exec host "touch \"${root}/aaa/aa bb\"" remote_exec host "touch \"${root}/aaa/aa cc\"" + remote_exec host "touch \"${root}/bb1/aa\\\"bb\"" + remote_exec host "touch \"${root}/bb1/aa'bb\"" + return $root } @@ -89,6 +92,37 @@ proc run_tests { root } { "aa cc" } "" "${qc}" false \ "expand filenames containing spaces" + + test_gdb_complete_multiple "file ${qc}${root}/bb1/" \ + "a" "a" { + "aa\"bb" + "aa'bb" + } "" "${qc}" false \ + "expand filenames containing quotes" + } else { + set sp "\\ " + + test_gdb_complete_tab_multiple "file ${qc}${root}/aaa/a" \ + "a${sp}" { + "aa bb" + "aa cc" + } false \ + "expand filenames containing spaces" + + test_gdb_complete_tab_multiple "file ${qc}${root}/bb1/a" \ + "a" { + "aa\"bb" + "aa'bb" + } false \ + "expand filenames containing quotes" + + test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa\\\"" \ + "file ${qc}${root}/bb1/aa\\\\\"bb${qc}" " " \ + "expand unique filename containing double quotes" + + test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa\\'" \ + "file ${qc}${root}/bb1/aa\\\\'bb${qc}" " " \ + "expand unique filename containing single quote" } } } From patchwork Fri Mar 29 11:42:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87814 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 6736E3858C5E for ; Fri, 29 Mar 2024 11:43:51 +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.133.124]) by sourceware.org (Postfix) with ESMTPS id B5E033858D34 for ; Fri, 29 Mar 2024 11:42:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B5E033858D34 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 B5E033858D34 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=1711712563; cv=none; b=x7lVptIevHQZOQprVvIk2YCSLqKmSUMzY4P/K/qVDX8UdcniiwWfDWVFhFMN9Xk//86ewAxuER2pCwthjW2OucrUur88x2ZUBtxDIlKrM6UAmC+VjYcXVWuFTdzbdHdBhn+FCradosVTFwP3CkbXdRs9A3ZZRwLTzcdA6pQPh4U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712563; c=relaxed/simple; bh=JwaU5YMevt74UPj+f2vmxFi/ArEvEpd0oO9s3myO1Dk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=ltx/WS9aY2FZwcxvjcX0bI0NvU7PF/HIYtJurUK2qrU8gouiNRUGYizKY3ewzLW8eNGSAnBuUrfUlv8VQO08yHDyb2YJfV//uKTxq0mthi5PEbb4konzUo0TQ3tY988VQUpbcX2vid7zmssyNQ+ER8+zif8/gQV43HBfizmCQnk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712561; 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=0HE1hYNohM+n9QQ2EZmVfMv59/0dMXCrUDGCE1IygHU=; b=BMwO29oS5SkV71XHUdnH7Chk0+7qMQw67KE4YMul/XQKNyISnpACIrIsd5vTmxBTYujU4e iCrigcZ7HJycT/NVJTKhgJ20N8AY8IiClihzTG5g6trWaiHw4apwJ9p74jjhB/OKDbrX8G 5zC4jlI+AIQ6TtNtyogtIMD7YdzcU4k= Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-454-wSvqatHnM-mZdtD7EUX5qw-1; Fri, 29 Mar 2024 07:42:40 -0400 X-MC-Unique: wSvqatHnM-mZdtD7EUX5qw-1 Received: by mail-lf1-f70.google.com with SMTP id 2adb3069b0e04-515c5796481so1306442e87.0 for ; Fri, 29 Mar 2024 04:42:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712557; x=1712317357; 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=0HE1hYNohM+n9QQ2EZmVfMv59/0dMXCrUDGCE1IygHU=; b=B369SeCBi0CbAijX6+iJ4mwqN0zELUcqQFJ0Xwm9wKVVlIUuyvEwI3N4zC55DPXcnZ Ypxujd1sG0IUGVNWsZWqHj3HSEhTN5lErtY76o9SnVergbFblOU437qv8Fr1dqVYPO8o Dmn26optqRlN36s5YvneGtyKIGmEQFgLqjaEEKN68MYBVMyUmyAYa+mkprwqluCNwX8M e5y15bLanNmne85FlGpBOm47cvpcW/ppSyrsGKesa5KvsDAym+eY/X7jSWkAoX5W6TA2 a8tkAVGiOepgYAYZcw2lSaQTkLOPcS+oclHRUAq4QvPPG2UOfcKNZjQRLM4pu98R/lCA TIgw== X-Gm-Message-State: AOJu0YwO/AEnYVMaCnjoe17qpRe/KYaGuLC3bME0UsKhjs4Cb7WBz1II L8tt2Pk34okjwAH8v+fbf3/H9Gkoyhz/HA5SYhkq5HUoFoMPGHEeZu3kS9VSH4ndMmpwALvXtvo G9mVZEHXQlQdP867ZZ+J2XWXsxuQZGIbOsU4s/ubnJEls19yWY03FpnMqFyltE1+vGdnGKNgiap HpYa1h4wTUkfu31Z6Zkx8gu7FvclN+8vxcF9R7vDNBbC0= X-Received: by 2002:ac2:4181:0:b0:515:b630:c3b9 with SMTP id z1-20020ac24181000000b00515b630c3b9mr1532736lfh.16.1711712557299; Fri, 29 Mar 2024 04:42:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHn3Xt2BdCPXITxwj2MQGv5UMrC0Tx4SKOL92tPad6tQIhRhYz0CRsPwbstwVv4Xi5AlUzuVg== X-Received: by 2002:ac2:4181:0:b0:515:b630:c3b9 with SMTP id z1-20020ac24181000000b00515b630c3b9mr1532718lfh.16.1711712556581; Fri, 29 Mar 2024 04:42:36 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id g16-20020a17090613d000b00a45ff5a30cesm1842036ejc.183.2024.03.29.04.42.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:35 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 2/6] gdb: move display of completion results into completion_result class Date: Fri, 29 Mar 2024 11:42:28 +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_H4, 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 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 When using the 'complete' command to complete file names we have some problems. The suggested completion should include any required escaping, so if there is a filename '/tmp/aa"bb' (without the single quotes), then this should be displayed in the completion output like: (gdb) complete file /tmp/aa file /tmp/aa\"bb This doesn't currently happen. We already have some code in filename_completer (completer.c) that handles adding the trailing '/' character if the completion result is a directory, so we might be tempted to add any needed escaping in that function, however if we do then we run into a problem. If there are multiple completion results from a 'complete' command, then in complete_command (cli/cli-cmds.c) we sort the completion results prior to printing them. If those results have already had the escaping added then the sort will be done on the text including the escape characters. This means that results from the 'complete' command will appear in a different order than readline would present them; readline sorts the results and then adds and required escaping. I think that the 'complete' command should behave the same as readline, sort the entries and then add the escaping. This means that we need to sort before adding the escaping. There is a second problem when using the 'complete' command with file names, that is trailing quote characters. The addition of a trailing quote character is a bit complex due (I think) to the structure of readline itself. Adding the quote character currently occurs in two places in GDB. In completion_tracker::build_completion_result (completer.c) we add the trailing quote in the case where we only have a single result, and in complete_command (cli/cli-cmds.c) we add the trailing quote character if we have more than one result. With these two cases we ensure that the 'complete' command always adds a trailing quote (when necessary) to the results it displays. However, I think that for filename completion we don't always want a trailing quote. Consider if we have a file '/tmp/xxx/foo.c' (without the quotes), and then we do: (gdb) complete file /tmp/xx What should the result of this be? Well, if we use TAB completion like this: (gdb) file /tmp/xx (gdb) file /tmp/xxx/ then readline completes the directory 'xxx/', but doesn't try to complete the filename within the xxx/ directory until we press again. So I think that the results of the 'complete' command should share this behaviour: (gdb) complete file /tmp/xx file /tmp/xxx/ And this is what we get right now, but what if the user adds some opening quotes? Right now we get this: (gdb) complete file "/tmp/xx file "/tmp/xxx/ Which I think is correct, there's no trailing quote added. This is because in completion_tracker::build_completion_result the completion_tracker object doesn't know that the double quote is the quote_char(). However, this causes a problem, if we do this: (gdb) complete file "/tmp/xxx/f file "/tmp/xxx/foo.c We're still missing the trailing quote, even though in this case, when we've expanded a complete filename, adding the trailing quote would be the correct thing to do. The cause is, of course, the same, the completion_tracker is unaware of what the quote character is, and so doesn't add the quote when needed. However, the problem gets worse, if we create an additional file '/tmp/xxa/bar.c', notice that this is in a different directory. So now when we do this: (gdb) complete file "/tmp/xx file "/tmp/xxa/" file "/tmp/xxx/" Now the trailing quote has been added even though we haven't completed a full filename. This is because in complete_command (cli/cli-cmds.c) we do know what the quote character is, and the trailing quote is always added. There are multiple problems here, but at least part of the problem will be solved by making the result printing in complete_command smarter. My plan is to allow different completion functions to modify how the 'complete' results are printed. This commit doesn't solve any of the problems listed above. Instead this commit is just a refactor. I've moved the result printing logic out of complete_command and created a new function completion_result::print_matches. And that's it. Nothing has functionally changed yet, that will come in later commits when the print_matches function is made smarter. There should be no user visible changes after this commit. --- gdb/cli/cli-cmds.c | 26 +------------------------- gdb/completer.c | 33 +++++++++++++++++++++++++++++++++ gdb/completer.h | 13 +++++++++++++ 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index df11f956245..0b621f65917 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -424,31 +424,7 @@ complete_command (const char *arg, int from_tty) { std::string arg_prefix (arg, word - arg); - if (result.number_matches == 1) - printf_unfiltered ("%s%s\n", arg_prefix.c_str (), result.match_list[0]); - else - { - result.sort_match_list (); - - for (size_t i = 0; i < result.number_matches; i++) - { - printf_unfiltered ("%s%s", - arg_prefix.c_str (), - result.match_list[i + 1]); - if (quote_char) - printf_unfiltered ("%c", quote_char); - printf_unfiltered ("\n"); - } - } - - if (result.number_matches == max_completions) - { - /* ARG_PREFIX and WORD are included in the output so that emacs - will include the message in the output. */ - printf_unfiltered (_("%s%s %s\n"), - arg_prefix.c_str (), word, - get_max_completions_reached_message ()); - } + result.print_matches (arg_prefix, word, quote_char); } } diff --git a/gdb/completer.c b/gdb/completer.c index 4cda5f3a383..9b4041da01a 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -2443,6 +2443,39 @@ completion_result::reset_match_list () } } +/* See completer.h */ + +void +completion_result::print_matches (const std::string &prefix, + const char *word, int quote_char) +{ + if (this->number_matches == 1) + printf_unfiltered ("%s%s\n", prefix.c_str (), this->match_list[0]); + else + { + this->sort_match_list (); + + for (size_t i = 0; i < this->number_matches; i++) + { + printf_unfiltered ("%s%s", prefix.c_str (), + this->match_list[i + 1]); + if (quote_char) + printf_unfiltered ("%c", quote_char); + printf_unfiltered ("\n"); + } + } + + if (this->number_matches == max_completions) + { + /* PREFIX and WORD are included in the output so that emacs will + include the message in the output. */ + printf_unfiltered (_("%s%s %s\n"), + prefix.c_str (), word, + get_max_completions_reached_message ()); + } + +} + /* Helper for gdb_rl_attempted_completion_function, which does most of the work. This is called by readline to build the match list array and to determine the lowest common denominator. The real matches diff --git a/gdb/completer.h b/gdb/completer.h index 98a12f3907c..4419c8f6d30 100644 --- a/gdb/completer.h +++ b/gdb/completer.h @@ -268,6 +268,19 @@ struct completion_result /* Sort the match list. */ void sort_match_list (); + /* Called to display all matches (used by the 'complete' command). + PREFIX is everything before the completion word. WORD is the word + being completed, this is only used if we reach the maximum number of + completions, otherwise, each line of output consists of PREFIX + followed by one of the possible completion words. + + The QUOTE_CHAR is appended after each possible completion word and + should be the quote character that appears before the completion word, + or the null-character if there is no quote before the completion + word. */ + void print_matches (const std::string &prefix, const char *word, + int quote_char); + private: /* Destroy the match list array and its contents. */ void reset_match_list (); From patchwork Fri Mar 29 11:42:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87813 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 36BBD3858435 for ; Fri, 29 Mar 2024 11:43:41 +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 ESMTPS id CF687385840D for ; Fri, 29 Mar 2024 11:42:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CF687385840D 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 CF687385840D 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=1711712567; cv=none; b=xa8CyMuA3cpbQ32tdMSYrgwsFsNsSN7l/MneoOIWeDbu7UeC7et29hwU3KBfOfzHOJkTVHSzy0iGlKGpDSqKSSDIopmJJHqvx/6uIyaq/DQdtqC7vz0RTIW0h7lIft1eRwG5+8DGofIMEhT5VPF9HALNMC/e4ayJOBlLbV7pWCk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712567; c=relaxed/simple; bh=sU0Mk4dOxYF6gBBomXwAYcsCLgaMs6rWE1+m5HCk74I=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=SrcFm5OBuVNcXwBDakhllyfG7LdpbJNF9KkCrxILIENixZHRZrYEJYoWDvAFx5Yc2pi/zKSC8yLeY3IsSmVXMnz6OPJLZxRvcoeIL0BBco02OK5noaMsGP/TOEwqaz62PVmF+9RAa4aNykjp4pMS5FbsRIOQZik8LGE7u762p5A= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712563; 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=b9dnuwC4RR7jwlKBShO3OMAYIDAxPmmgBy+UZLxHkrM=; b=eXUewhHzBRH1A6caAS325uj6RoyR+raMhDe1R68SSyquDoJacJYMxwQjS5vHKj5z+2S1mS YvQLzzB7Uxa84IpUR9ymRoJehLz2i3L0pVKTGgUEzKmIqNwXTn6mwHsOdyToEmzKdIOIbu QCEoyqszV7CIslmP3mN/xxzqu9NvEYU= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-263-Xs4kjU9bNpuBohx1VnZpZQ-1; Fri, 29 Mar 2024 07:42:40 -0400 X-MC-Unique: Xs4kjU9bNpuBohx1VnZpZQ-1 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-a46852c2239so125694766b.1 for ; Fri, 29 Mar 2024 04:42:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712558; x=1712317358; 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=b9dnuwC4RR7jwlKBShO3OMAYIDAxPmmgBy+UZLxHkrM=; b=tVeBZhqNWtXSfMc79Zt0foNvghYNzEi+e7JUzHL2CUfKNbAEouH2Z4maGNvBZH7jPV lThfRTAMs0cfeMZm3OknCsV4T7v3jcwqyfhynKUUPILakyeRMUodgChq+sPFTmDFnSQO 8yJ6tct2ZjH4TXICENV9ejjqNLgMjkDJfuTv78R1zc01bG2tdMS3gq+0bAGSq8Qr7EM4 WV2ohQVke6c3PlVx111I+5vzlJtrgmeKVwP9umJ58q/cx2YW07TzJJbsasYimuS8ufei o00w7gOEnsMw5SOWj2pQC2M4wCgX9/yJ2iJX2l473jz9UiXgpDqvnAFIHBw4BJNye43E GIow== X-Gm-Message-State: AOJu0YxubNGx8iuzxrIzboaKq8M34mhmYf+FNqIsy1AKXAv4E5Q85iyj 3MJ5s04fgNtTV0Z7Wl2+avVK1BFq0TVuxNNEvp0pFndj+ZZxR3lBgjX5UQ7NryraHm6YO1T40ND V47I0kOWMtvwbMqEffyUmH6gt+kZ/k/o/lMsqx1H2vRXqR/NhpvcVYpE7JstCrpwD6kArP49QEh VKg2Dunjwu4IwLEcXNbZ3/GEpG3YTCVp0ORVC0514/OpU= X-Received: by 2002:a17:907:10d8:b0:a46:92a1:6459 with SMTP id rv24-20020a17090710d800b00a4692a16459mr1344786ejb.17.1711712557976; Fri, 29 Mar 2024 04:42:37 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE85G0ELc+Ajh95BsSBndUaAUSsB7bn/9ciY1b9ur+jfgs4QPxAMnZxfs7KpowEKPgzK9DiXw== X-Received: by 2002:a17:907:10d8:b0:a46:92a1:6459 with SMTP id rv24-20020a17090710d800b00a4692a16459mr1344766ejb.17.1711712557462; Fri, 29 Mar 2024 04:42:37 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id xg6-20020a170907320600b00a4e38a06c86sm468400ejb.66.2024.03.29.04.42.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:37 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 3/6] gdb: simplify completion_result::print_matches Date: Fri, 29 Mar 2024 11:42:29 +0000 Message-Id: <8f677c4387cb1b14d736112bd33e799e01df9167.1711712401.git.aburgess@redhat.com> 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_H4, 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 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 Simplify completion_result::print_matches by removing one of the code paths. Now, every time we call ::print_matches we always add the trailing quote. Previously, when using the 'complete' command, if there was only one result then trailing quote was added in ::build_completion_result, but when we had multiple results the trailing quote was added in ::print_matches. As a consequence, ::print_matches had to understand not to add the trailing quote for the single result case. After this commit we don't add the trailing quote in ::build_completion_result, instead ::print_matches always adds the trailing quote, which makes ::print_matches simpler. However, there is a slight problem. When completion is being driven by readline, and not by the 'complete' command, we still need to manually add the trailing quote in the single result case, and as the printing is done by readline we can't add the quote at the time of printing, and so, in ::build_completion_result, we still add the trailing quote, but only when completion is being done for readline. And this does cause a small problem. When completing a filename, if the completion results in a directory name then, when using the 'complete' command, GDB should not be adding a trailing quote. For example, if we have the file /tmp/xxx/foo.c, then what we should see is this: (gdb) complete file '/tmp/xx file 'tmp/xxx/ But what we actually see after this commit is this: (gdb) complete file '/tmp/xx file 'tmp/xxx/' Previously we didn't get the trailing quote in this case, as when there is only a single result, the quote was added in ::build_completion_result, and for filename completion, GDB didn't know what the quote character was in ::build_completion_result, so no quote was added. Now that the trailing quote is always added in ::print_matches, and GDB does know the quote character at this point, so we are now getting the trailing quote, which is not correct. This is a regression, but really, GDB is now broken in a consistent way, if we create the file /tmp/xxa/bar.c, then previously if we did this: (gdb) complete file '/tmp/xx file '/tmp/xxa/' file '/tmp/xxx/' Notice how we get the trailing quote in this case, this is the before patch behaviour, and is also wrong. A later commit will fix things so that the trailing quote is not added in this filename completion case, but for now I'm going to accept this small regression. This change in behaviour caused some failures in one of the completion tests, I've tweaked the test case to expect the trailing quote as part of this commit, but will revert this in a later commit in this series. I've also added an extra test for when the 'complete' command does complete to a single complete filename, in which case the trailing quote is expected. --- gdb/completer.c | 62 +++++++++---------- .../gdb.base/filename-completion.exp | 17 ++++- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/gdb/completer.c b/gdb/completer.c index 9b4041da01a..2b3972213d8 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -2311,23 +2311,30 @@ completion_tracker::build_completion_result (const char *text, if (m_lowest_common_denominator_unique) { - /* We don't rely on readline appending the quote char as - delimiter as then readline wouldn't append the ' ' after the - completion. */ - char buf[2] = { (char) quote_char () }; - - match_list[0] = reconcat (match_list[0], match_list[0], - buf, (char *) NULL); - match_list[1] = NULL; - - /* If the tracker wants to, or we already have a space at the - end of the match, tell readline to skip appending - another. */ - char *match = match_list[0]; - bool completion_suppress_append - = (suppress_append_ws () - || (match[0] != '\0' - && match[strlen (match) - 1] == ' ')); + bool completion_suppress_append; + + if (from_readline ()) + { + /* We don't rely on readline appending the quote char as + delimiter as then readline wouldn't append the ' ' after the + completion. */ + char buf[2] = { (char) quote_char () }; + + match_list[0] = reconcat (match_list[0], match_list[0], buf, + (char *) nullptr); + + /* If the tracker wants to, or we already have a space at the end + of the match, tell readline to skip appending another. */ + char *match = match_list[0]; + completion_suppress_append + = (suppress_append_ws () + || (match[0] != '\0' + && match[strlen (match) - 1] == ' ')); + } + else + completion_suppress_append = false; + + match_list[1] = nullptr; return completion_result (match_list, 1, completion_suppress_append); } @@ -2449,21 +2456,14 @@ void completion_result::print_matches (const std::string &prefix, const char *word, int quote_char) { - if (this->number_matches == 1) - printf_unfiltered ("%s%s\n", prefix.c_str (), this->match_list[0]); - else - { - this->sort_match_list (); + this->sort_match_list (); - for (size_t i = 0; i < this->number_matches; i++) - { - printf_unfiltered ("%s%s", prefix.c_str (), - this->match_list[i + 1]); - if (quote_char) - printf_unfiltered ("%c", quote_char); - printf_unfiltered ("\n"); - } - } + char buf[2] = { (char) quote_char, '\0' }; + size_t off = this->number_matches == 1 ? 0 : 1; + + for (size_t i = 0; i < this->number_matches; i++) + printf_unfiltered ("%s%s%s\n", prefix.c_str (), + this->match_list[i + off], buf); if (this->number_matches == max_completions) { diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index f23e8671f40..66e5f411795 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -63,8 +63,21 @@ proc run_tests { root } { test_gdb_complete_none "file ${qc}${root}/xx" \ "expand a non-existent filename" - test_gdb_complete_unique "file ${qc}${root}/a" \ - "file ${qc}${root}/aaa/" "" false \ + # The following test is split into separate cmd and tab calls as the + # cmd versions will add a closing quote. It shouldn't be doing + # this; this will be fixed in a later commit. + test_gdb_complete_cmd_unique "file ${qc}${root}/a" \ + "file ${qc}${root}/aaa/${qc}" \ + "expand a unique directory name" + + if { [readline_is_used] } { + test_gdb_complete_tab_unique "file ${qc}${root}/a" \ + "file ${qc}${root}/aaa/" "" \ + "expand a unique directory name" + } + + test_gdb_complete_unique "file ${qc}${root}/cc2" \ + "file ${qc}${root}/cc2${qc}" " " false \ "expand a unique filename" test_gdb_complete_multiple "file ${qc}${root}/" \ From patchwork Fri Mar 29 11:42:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87815 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 ABAC83858D32 for ; Fri, 29 Mar 2024 11:43:57 +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.133.124]) by sourceware.org (Postfix) with ESMTPS id 506FD3858C98 for ; Fri, 29 Mar 2024 11:42:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 506FD3858C98 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 506FD3858C98 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=1711712568; cv=none; b=krASl0/817vr+qB5SBBUW9vAqHUF72mRZOP0Gd50G+CFmas+Si8UAPsa+1TvtkN0m5KMJG00IrKF4FnxAmFe1PITPCJ84EZsTc79lw0YengO7xCA5PbMOAcXi48MWx4VC0PiM1ygliCjbxYvpev6vwQg7WB58ztqU5xn9Ad49gc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712568; c=relaxed/simple; bh=5kDCsYzNA+KbA7VOTce/EdiGODLtjvK1EgQUz1eKhiE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=dzEDuXBNbeqtk3ibfxBk3BDw4zytBgXXPcnZU8y1JezO0ieVq2pEbm3CV/aoUCzw8xqnKY/jy3LDsP2q7Re49JL+EJt63sq+u4V1P3wmtBhwrjQ+GnssaL9gHyjkDT91JYSsMvqbT5TgjC7Ogx4GLTyx4p2sDwZTlnk41+aHa5g= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712564; 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=yhoVyq3lrrCQwQvDHRVakjiALIz0zJSJwPbQvwUL9n4=; b=Inxy6oD975TEUBVVNwe3KtFsltwMqZCKyYlG25TcbAWlcSRKPDmKkmcvoQTxNvV1UIgBCO 5npjj9cdTLTv7P2J73oz8AIR+IbcfFAOyGXbPcPgMJ7W0rqQWUDuoO/mxUPd1YYJdOwlW4 M54I7XZ6OJ6SQyg6f5RM2QJrEcVmb8A= Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-142-w7RLDVtZNPiZ4WzkYfzQvA-1; Fri, 29 Mar 2024 07:42:42 -0400 X-MC-Unique: w7RLDVtZNPiZ4WzkYfzQvA-1 Received: by mail-ed1-f71.google.com with SMTP id 4fb4d7f45d1cf-56bf2d59fceso1656096a12.3 for ; Fri, 29 Mar 2024 04:42:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712561; x=1712317361; 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=yhoVyq3lrrCQwQvDHRVakjiALIz0zJSJwPbQvwUL9n4=; b=Jfa4rVuPiCpQbFoZaVPZg98G7Y8DZH6bNjuGVGME6iceDLUTCM0vDQBS1M6gkJ/vxi wlavqSpztHz4oK/Km85qK2woXrSHfCoIsnxYaOEHKYa8oVBE2ZloMGPRFyxpZCL4DpDb WqPFUXm+8xmM/Duo5EjbipodbP4GUkljMRocziXmwAif8FbAn0J9mHQkgip1Y63s2ivz LZbv49Mj9Ie+8SVIOiQFVrBLXguNPQ3+hy3uoq6P5v4menboEJikGszKCTbco0APOffT Yvi6ThFYYSncX1pS/BC1kg8MoPPd3OHse3/xHnQq3mv+MxE7CSJACkoK9W4BQhviV0tN yLTA== X-Gm-Message-State: AOJu0Yzq3Nz7kDIQF3rQcYYpir2TplLua143khKQ1q+wYYU7eBteZIme hA4XhTzdOirIzZ4K6SFffet6vUIjPqKnStO6eKGGOdfM0b1PXI3+kTkcp9QwIGSu3CH/aHJRy3Y MHGAQUZUlx1keRwqbDy8Rme0UjPHPdNI7tU34krbui0NVYzvq3zNtg16AJGVdeCdmpC7sy+gKRk aCSjfE0/L4SMOu4QXPxhHRhiec2FPEkU99vt03YiU5Bts= X-Received: by 2002:a50:a44a:0:b0:56c:d47:e403 with SMTP id v10-20020a50a44a000000b0056c0d47e403mr1564967edb.28.1711712560577; Fri, 29 Mar 2024 04:42:40 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGL8hkpbQER01w2cp0Lo9IwobRHPgGIqa3muucP450bF/C/iizyR5Gh9GhZupbXIEasZL42DA== X-Received: by 2002:a50:a44a:0:b0:56c:d47:e403 with SMTP id v10-20020a50a44a000000b0056c0d47e403mr1564957edb.28.1711712559949; Fri, 29 Mar 2024 04:42:39 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id di19-20020a056402319300b0056a2b87787dsm1967372edb.37.2024.03.29.04.42.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:38 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 4/6] gdb: add match formatter mechanism for 'complete' command output Date: Fri, 29 Mar 2024 11:42:30 +0000 Message-Id: <6adc14efeac88ecd9501c0c8c53b622099333792.1711712401.git.aburgess@redhat.com> 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_H4, 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 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 solves the problem that was made worse in the previous commit. When completing a filename with the 'complete' command GDB will always add a trailing quote character, even if the completion is a directory name, in which case it would be better if the trailing quote was not added. Consider: (gdb) complete file '/tmp/xx file '/tmp/xxx/' The completion offered here is really only a partial completion, we've completed up to the end of the next directory name, but, until we have a filename then the completion is not finished and the trailing quote should not be added. This would match the readline behaviour, e.g.: (gdb) file '/tmp/xx (gdb) file '/tmp/xxx/ In this case readline completes the directory name, but doesn't add the trailing quote character. To achieve this, in this commit, I've added a new function pointer member variable completion_result::m_match_formatter. This contains a pointer to a callback function which is used by the 'complete' command to format each completion result. The default behaviour of this callback function is to just append the quote character (the character from before the completion string) to the end of the completion result. However, for filename completion we override the default value of m_match_formatter, this new function checks if the completion result is a directory or not. If the completion result is a directory then the closing quote is not added, instead we add a trailing '/' character. The code to add a trailing '/' character already exists within the filename_completer function. This is no longer needed in this location, instead this code is move into the formatter callback. Tests are updated to handle the changes in functionality. --- gdb/completer.c | 92 ++++++++++++++----- gdb/completer.h | 42 ++++++++- .../gdb.base/filename-completion.exp | 66 +++++++++---- 3 files changed, 155 insertions(+), 45 deletions(-) diff --git a/gdb/completer.c b/gdb/completer.c index 2b3972213d8..785fb09b4d7 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -351,6 +351,34 @@ gdb_completer_file_name_quote (char *text, int match_type, char *quote_ptr) return strdup (str.c_str ()); } +/* The function is used to update the completion word MATCH before + displaying it to the user in the 'complete' command output, this + particular function is only used when MATCH has been supplied by the + filename_completer function (and so MATCH is a filename or directory + name). + + This function checks to see if the completion word MATCH is a directory, + in which case a trailing "/" (forward-slash) is added, otherwise + QUOTE_CHAR is added as a trailing quote. + + Return the updated completion word as a string. */ + +static std::string +filename_match_formatter (const char *match, char quote_char) +{ + std::string expanded = gdb_tilde_expand (match); + struct stat finfo; + const bool isdir = (stat (expanded.c_str (), &finfo) == 0 + && S_ISDIR (finfo.st_mode)); + std::string result (match); + if (isdir) + result += "/"; + else + result += quote_char; + + return result; +} + /* Complete on filenames. */ void @@ -361,6 +389,8 @@ filename_completer (struct cmd_list_element *ignore, rl_char_is_quoted_p = gdb_completer_file_name_char_is_quoted; rl_completer_quote_characters = gdb_completer_file_name_quote_characters; + tracker.set_match_format_func (filename_match_formatter); + int subsequent_name = 0; while (1) { @@ -379,20 +409,6 @@ filename_completer (struct cmd_list_element *ignore, if (p[strlen (p) - 1] == '~') continue; - /* Readline appends a trailing '/' if the completion is a - directory. If this completion request originated from outside - readline (e.g. GDB's 'complete' command), then we append the - trailing '/' ourselves now. */ - if (!tracker.from_readline ()) - { - std::string expanded = gdb_tilde_expand (p_rl.get ()); - struct stat finfo; - const bool isdir = (stat (expanded.c_str (), &finfo) == 0 - && S_ISDIR (finfo.st_mode)); - if (isdir) - p_rl.reset (concat (p_rl.get (), "/", nullptr)); - } - tracker.add_completion (make_completion_match_str (std::move (p_rl), word, word)); } @@ -1630,10 +1646,25 @@ int max_completions = 200; /* Initial size of the table. It automagically grows from here. */ #define INITIAL_COMPLETION_HTAB_SIZE 200 +/* The function is used to update the completion word MATCH before + displaying it to the user in the 'complete' command output. This + default function is used in all cases except those where a completion + function overrides this function by calling set_match_format_func. + + This function returns MATCH with QUOTE_CHAR appended. If QUOTE_CHAR is + the null-character then the returned string will just contain MATCH. */ + +static std::string +default_match_formatter (const char *match, char quote_char) +{ + return std::string (match) + quote_char; +} + /* See completer.h. */ completion_tracker::completion_tracker (bool from_readline) - : m_from_readline (from_readline) + : m_from_readline (from_readline), + m_match_format_func (default_match_formatter) { discard_completions (); } @@ -2336,7 +2367,8 @@ completion_tracker::build_completion_result (const char *text, match_list[1] = nullptr; - return completion_result (match_list, 1, completion_suppress_append); + return completion_result (match_list, 1, completion_suppress_append, + m_match_format_func); } else { @@ -2373,7 +2405,8 @@ completion_tracker::build_completion_result (const char *text, htab_traverse_noresize (m_entries_hash.get (), func, &builder); match_list[builder.index] = NULL; - return completion_result (match_list, builder.index - 1, false); + return completion_result (match_list, builder.index - 1, false, + m_match_format_func); } } @@ -2381,17 +2414,20 @@ completion_tracker::build_completion_result (const char *text, completion_result::completion_result () : match_list (NULL), number_matches (0), - completion_suppress_append (false) + completion_suppress_append (false), + m_match_formatter (nullptr) {} /* See completer.h */ completion_result::completion_result (char **match_list_, size_t number_matches_, - bool completion_suppress_append_) + bool completion_suppress_append_, + match_format_func_t match_formatter_) : match_list (match_list_), number_matches (number_matches_), - completion_suppress_append (completion_suppress_append_) + completion_suppress_append (completion_suppress_append_), + m_match_formatter (match_formatter_) {} /* See completer.h */ @@ -2405,10 +2441,12 @@ completion_result::~completion_result () completion_result::completion_result (completion_result &&rhs) noexcept : match_list (rhs.match_list), - number_matches (rhs.number_matches) + number_matches (rhs.number_matches), + m_match_formatter (rhs.m_match_formatter) { rhs.match_list = NULL; rhs.number_matches = 0; + rhs.m_match_formatter = nullptr; } /* See completer.h */ @@ -2458,12 +2496,18 @@ completion_result::print_matches (const std::string &prefix, { this->sort_match_list (); - char buf[2] = { (char) quote_char, '\0' }; size_t off = this->number_matches == 1 ? 0 : 1; for (size_t i = 0; i < this->number_matches; i++) - printf_unfiltered ("%s%s%s\n", prefix.c_str (), - this->match_list[i + off], buf); + { + gdb_assert (this->m_match_formatter != nullptr); + std::string formatted_match + = this->m_match_formatter (this->match_list[i + off], + (char) quote_char); + + printf_unfiltered ("%s%s\n", prefix.c_str (), + formatted_match.c_str ()); + } if (this->number_matches == max_completions) { diff --git a/gdb/completer.h b/gdb/completer.h index 4419c8f6d30..8965ecacc44 100644 --- a/gdb/completer.h +++ b/gdb/completer.h @@ -247,12 +247,24 @@ struct completion_match_result struct completion_result { + /* The type of a function that is used to format completion results when + using the 'complete' command. MATCH is the completion word to be + printed, and QUOTE_CHAR is a trailing quote character to (possibly) + add at the end of MATCH. QUOTE_CHAR can be the null-character in + which case no trailing quote should be added. + + Return the possibly modified completion match word which should be + presented to the user. */ + using match_format_func_t = std::string (*) (const char *match, + char quote_char); + /* Create an empty result. */ completion_result (); /* Create a result. */ completion_result (char **match_list, size_t number_matches, - bool completion_suppress_append); + bool completion_suppress_append, + match_format_func_t match_format_func); /* Destroy a result. */ ~completion_result (); @@ -274,10 +286,15 @@ struct completion_result completions, otherwise, each line of output consists of PREFIX followed by one of the possible completion words. - The QUOTE_CHAR is appended after each possible completion word and - should be the quote character that appears before the completion word, - or the null-character if there is no quote before the completion - word. */ + The QUOTE_CHAR is usually appended after each possible completion + word and should be the quote character that appears before the + completion word, or the null-character if there is no quote before + the completion word. + + The QUOTE_CHAR is not always appended to the completion output. For + example, filename completions will not append QUOTE_CHAR if the + completion is a directory name. This is all handled by calling this + function. */ void print_matches (const std::string &prefix, const char *word, int quote_char); @@ -305,6 +322,12 @@ struct completion_result /* Whether readline should suppress appending a whitespace, when there's only one possible completion. */ bool completion_suppress_append; + +private: + /* A function which formats a single completion match ready for display + as part of the 'complete' command output. Different completion + functions can set different formatter functions. */ + match_format_func_t m_match_formatter; }; /* Object used by completers to build a completion match list to hand @@ -441,6 +464,11 @@ class completion_tracker bool from_readline () const { return m_from_readline; } + /* Set the function used to format the completion word before displaying + it to the user to F, this is used by the 'complete' command. */ + void set_match_format_func (completion_result::match_format_func_t f) + { m_match_format_func = f; } + private: /* The type that we place into the m_entries_hash hash table. */ @@ -535,6 +563,10 @@ class completion_tracker interactively. The 'complete' command is a way to generate completions not to be displayed by readline. */ bool m_from_readline; + + /* The function used to format the completion word before it is printed + in the 'complete' command output. */ + completion_result::match_format_func_t m_match_format_func; }; /* Return a string to hand off to readline as a completion match diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index 66e5f411795..d7c99e1340d 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -46,6 +46,49 @@ proc setup_directory_tree {} { return $root } +# This proc started as a copy of test_gdb_complete_multiple, however, this +# version does some extra work. See the original test_gdb_complete_multiple +# for a description of all the arguments. +# +# When using the 'complete' command with filenames, GDB will add a trailing +# quote for filenames, and a trailing "/" for directory names. As the +# trailing "/" is also added in the tab-completion output the +# COMPLETION_LIST will include the "/" character, but the trailing quote is +# only added when using the 'complete' command. +# +# Pass the trailing quote will be passed as END_QUOTE_CHAR, this proc will +# run the tab completion test, and will then add the trailing quote to those +# entries in COMPLETION_LIST that don't have a trailing "/" before running +# the 'complete' command test. +proc test_gdb_complete_filename_multiple { + cmd_prefix completion_word add_completed_line completion_list + {start_quote_char ""} {end_quote_char ""} {max_completions false} + {testname ""} +} { + if { [readline_is_used] } { + test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" \ + $add_completed_line $completion_list $max_completions $testname + } + + if { $start_quote_char eq "" && $end_quote_char ne "" } { + set updated_completion_list {} + + foreach entry $completion_list { + if {[string range $entry end end] ne "/"} { + set entry $entry$end_quote_char + } + lappend updated_completion_list $entry + } + + set completion_list $updated_completion_list + set end_quote_char "" + } + + test_gdb_complete_cmd_multiple $cmd_prefix $completion_word \ + $completion_list $start_quote_char $end_quote_char $max_completions \ + $testname +} + # Run filename completetion tests. ROOT is the base directory as # returned from setup_directory_tree, though, if ROOT is a # sub-directory of the user's home directory ROOT might have been @@ -63,31 +106,22 @@ proc run_tests { root } { test_gdb_complete_none "file ${qc}${root}/xx" \ "expand a non-existent filename" - # The following test is split into separate cmd and tab calls as the - # cmd versions will add a closing quote. It shouldn't be doing - # this; this will be fixed in a later commit. - test_gdb_complete_cmd_unique "file ${qc}${root}/a" \ - "file ${qc}${root}/aaa/${qc}" \ + test_gdb_complete_unique "file ${qc}${root}/a" \ + "file ${qc}${root}/aaa/" "" false \ "expand a unique directory name" - if { [readline_is_used] } { - test_gdb_complete_tab_unique "file ${qc}${root}/a" \ - "file ${qc}${root}/aaa/" "" \ - "expand a unique directory name" - } - test_gdb_complete_unique "file ${qc}${root}/cc2" \ "file ${qc}${root}/cc2${qc}" " " false \ "expand a unique filename" - test_gdb_complete_multiple "file ${qc}${root}/" \ + test_gdb_complete_filename_multiple "file ${qc}${root}/" \ "b" "b" { "bb1/" "bb2/" - } "" "${qc}" false \ + } "" "" false \ "expand multiple directory names" - test_gdb_complete_multiple "file ${qc}${root}/" \ + test_gdb_complete_filename_multiple "file ${qc}${root}/" \ "c" "c" { "cc1/" "cc2" @@ -99,14 +133,14 @@ proc run_tests { root } { if { $qc ne "" } { set sp " " - test_gdb_complete_multiple "file ${qc}${root}/aaa/" \ + test_gdb_complete_filename_multiple "file ${qc}${root}/aaa/" \ "a" "a${sp}" { "aa bb" "aa cc" } "" "${qc}" false \ "expand filenames containing spaces" - test_gdb_complete_multiple "file ${qc}${root}/bb1/" \ + test_gdb_complete_filename_multiple "file ${qc}${root}/bb1/" \ "a" "a" { "aa\"bb" "aa'bb" From patchwork Fri Mar 29 11:42:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87817 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 981663858D37 for ; Fri, 29 Mar 2024 11:44:36 +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.133.124]) by sourceware.org (Postfix) with ESMTPS id 025313858C36 for ; Fri, 29 Mar 2024 11:44:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 025313858C36 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 025313858C36 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=1711712645; cv=none; b=quFNNNdOyj/MuxuQ0i8ZD4j1gb7x3dgAPjrFmsIrCqf4wvNfeEWQ56Z3mfs/bSZ6YRWhSvZDkTB5vuGqkTULPDTNbwrKcLxAlaauA0z62JA51DFOuuwadyI8jymv7+IWMHRe2miDCOs/0QvdV6owDw2dfxlHfHKpDcnCM5tWm9o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712645; c=relaxed/simple; bh=O05KYAyiAA9TDGPDjBFvAl+HbznTvFTyHPxL4WjjskA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=QJ3xAcaY+gqVMiYbGysoO8jJA1UHDApm2QtCrJFcD9R9m8ZLrc1ocFChqWwrd3Woy3o6W5WIT4CxKrbtYTftnajpUjx6sM50ldkXeA1q+GLAROkWCb8WvhvOLgU7aViX/Ey0YvBIewj+3TSnmPYhgx0UHuDR3RiQnOA9bDpp7MM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712641; 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=9OFQf6c/NiIg3Fp+I3EYGzy1L+TBbcHj69SmfoVh9NI=; b=IL2aVMbrJIbDhINGdVOOiLpvN7dNas6l9A6DgR4ajF0lBSahIewnb5FXLqqo6Lk75QnIQd Q/70EKramXmF4cJk6+UU+psIjUPV03qSK49laa4Cvm9r2BbVFEHL38dXeVOGuRAZNQYZw8 Zz7I+24s7vNKiy9skPNUo6ArLg7dsWM= Received: from mail-lf1-f69.google.com (mail-lf1-f69.google.com [209.85.167.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-688-7jO6i-00OtKKZ8yNX9qtxw-1; Fri, 29 Mar 2024 07:42:43 -0400 X-MC-Unique: 7jO6i-00OtKKZ8yNX9qtxw-1 Received: by mail-lf1-f69.google.com with SMTP id 2adb3069b0e04-515c94f9ea8so1086182e87.1 for ; Fri, 29 Mar 2024 04:42:43 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712561; x=1712317361; 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=9OFQf6c/NiIg3Fp+I3EYGzy1L+TBbcHj69SmfoVh9NI=; b=qM6GqhMewlIm4maiObmDaDazoxH/EQx+xJ2ozBoFLbkQWYWVYH+2W2vzxgijiLB60x Absi0qpjS5B0gLk+V3kWpj6iwCSIgrn9kWqwKibMojrwWYSTpMU8sxaXZxyg82dCEDx7 QIubR7S+1YikLjMmvuIkOeXO84kLrTHdSR8USEVwCwiSfQLLauV9cxCii9m2NlKSf9/5 xGFUlCC2fd/qghYG9qM/UAP1ESMECA4SHWQNtjoQ1YVGSxQentWpgLSzgVWcpmQgpU0d 8l2eCBfkWnZyO1gGCMx0Ws7gZHqAMMwIejLlIG07sr0I5zBFK34FH6G31f22LgWPyuoU azUw== X-Gm-Message-State: AOJu0YzJk5wECn3ONqVByjkeasG9hdwKLmEGFlZganV5n7v3mOhWa2Bl k3Y34tXhwD2XJ3JcZX898YlPb5uWgSClJ6BHszgMxWREdEXQhOPjLGICy15pXQnJxU0mDJielb3 NT6rcx5eDgBPhzEZOHnFDOhpBNuPEsraP+cOdvdVvMZpJst3OwLjLpGoXZypiwpnXNpsRQybDcO E6n+TIaVAxUf8TPY+35AvZMM/0pku6YqlVktWPIo20KTg= X-Received: by 2002:a05:6512:475:b0:515:cf44:904e with SMTP id x21-20020a056512047500b00515cf44904emr1308297lfd.46.1711712561551; Fri, 29 Mar 2024 04:42:41 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEaWjo66ZwxFlu5r+0GmtdRRfH8Q4c20lC/spZ1+ouKLHUjW00Z5y+8l8W78uC9WngPyiYYKw== X-Received: by 2002:a05:6512:475:b0:515:cf44:904e with SMTP id x21-20020a056512047500b00515cf44904emr1308285lfd.46.1711712561081; Fri, 29 Mar 2024 04:42:41 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id ef15-20020a05640228cf00b0056c1380a972sm1911388edb.74.2024.03.29.04.42.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:40 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 5/6] gdb: apply escaping to filenames in 'complete' results Date: Fri, 29 Mar 2024 11:42:31 +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_H4, 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 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 Building on the mechanism added in the previous commit(s), this commit applies escaping to filenames in the 'complete' command output. Consider a file: /tmp/xxx/aa"bb -- that is a filename that contains a double quote, currently the 'complete' command output looks like this: (gdb) complete file /tmp/xxx/a file /tmp/xxx/aa"bb Notice that the double quote in the output is not escaped. If we passed this same output back to GDB then the double quote will be treated as the start of a string. After this commit then the output looks like this: (gdb) complete file /tmp/xxx/a file /tmp/xxx/aa\"bb The double quote is now escaped. If we feed this output back to GDB then GDB will treat this as a single filename that contains a double quote, exactly what we want. To achieve this I've done a little refactoring, splitting out the core of gdb_completer_file_name_quote, and then added a new call from the filename_match_formatter function. There are updates to the tests to cover this new functionality. --- gdb/completer.c | 35 ++++++-- .../gdb.base/filename-completion.exp | 89 +++++++++++-------- 2 files changed, 80 insertions(+), 44 deletions(-) diff --git a/gdb/completer.c b/gdb/completer.c index 785fb09b4d7..710c8c206cb 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -305,24 +305,24 @@ gdb_completer_file_name_dequote (char *filename, int quote_char) return strdup (tmp.c_str ()); } -/* Apply character escaping to the file name in TEXT. QUOTE_PTR points to - the quote character surrounding TEXT, or points to the null-character if - there are no quotes around TEXT. MATCH_TYPE will be one of the readline - constants SINGLE_MATCH or MULTI_MATCH depending on if there is one or - many completions. */ +/* Apply character escaping to the filename in TEXT and return a newly + allocated buffer containing the possibly updated filename. + + QUOTE_CHAR is the quote character surrounding TEXT, or the + null-character if there are no quotes around TEXT. */ static char * -gdb_completer_file_name_quote (char *text, int match_type, char *quote_ptr) +gdb_completer_file_name_quote_1 (const char *text, char quote_char) { std::string str; - if (*quote_ptr == '\'') + if (quote_char == '\'') { /* There is no backslash escaping permitted within a single quoted string, so in this case we can just return the input sting. */ str = text; } - else if (*quote_ptr == '"') + else if (quote_char == '"') { /* Add escaping for a double quoted filename. */ for (const char *input = text; @@ -351,6 +351,18 @@ gdb_completer_file_name_quote (char *text, int match_type, char *quote_ptr) return strdup (str.c_str ()); } +/* Apply character escaping to the filename in TEXT. QUOTE_PTR points to + the quote character surrounding TEXT, or points to the null-character if + there are no quotes around TEXT. MATCH_TYPE will be one of the readline + constants SINGLE_MATCH or MULTI_MATCH depending on if there is one or + many completions. */ + +static char * +gdb_completer_file_name_quote (char *text, int match_type, char *quote_ptr) +{ + return gdb_completer_file_name_quote_1 (text, *quote_ptr); +} + /* The function is used to update the completion word MATCH before displaying it to the user in the 'complete' command output, this particular function is only used when MATCH has been supplied by the @@ -370,7 +382,12 @@ filename_match_formatter (const char *match, char quote_char) struct stat finfo; const bool isdir = (stat (expanded.c_str (), &finfo) == 0 && S_ISDIR (finfo.st_mode)); - std::string result (match); + + gdb::unique_xmalloc_ptr quoted_match + (gdb_completer_file_name_quote_1 (match, quote_char)); + + std::string result (quoted_match.get ()); + if (isdir) result += "/"; else diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index d7c99e1340d..f8a48269528 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -70,10 +70,22 @@ proc test_gdb_complete_filename_multiple { $add_completed_line $completion_list $max_completions $testname } - if { $start_quote_char eq "" && $end_quote_char ne "" } { + if { $start_quote_char eq "" } { set updated_completion_list {} foreach entry $completion_list { + # If ENTRY is quoted with double quotes, then any double + # quotes within the entry need to be escaped. + if { $end_quote_char eq "\"" } { + regsub -all "\"" $entry "\\\"" entry + } + + if { $end_quote_char eq "" } { + regsub -all " " $entry "\\ " entry + regsub -all "\"" $entry "\\\"" entry + regsub -all "'" $entry "\\'" entry + } + if {[string range $entry end end] ne "/"} { set entry $entry$end_quote_char } @@ -128,47 +140,54 @@ proc run_tests { root } { } "" "${qc}" false \ "expand mixed directory and file names" - # GDB does not currently escape word break characters - # (e.g. white space) correctly in unquoted filenames. if { $qc ne "" } { set sp " " - - test_gdb_complete_filename_multiple "file ${qc}${root}/aaa/" \ - "a" "a${sp}" { - "aa bb" - "aa cc" - } "" "${qc}" false \ - "expand filenames containing spaces" - - test_gdb_complete_filename_multiple "file ${qc}${root}/bb1/" \ - "a" "a" { - "aa\"bb" - "aa'bb" - } "" "${qc}" false \ - "expand filenames containing quotes" } else { set sp "\\ " + } - test_gdb_complete_tab_multiple "file ${qc}${root}/aaa/a" \ - "a${sp}" { - "aa bb" - "aa cc" - } false \ - "expand filenames containing spaces" - - test_gdb_complete_tab_multiple "file ${qc}${root}/bb1/a" \ - "a" { - "aa\"bb" - "aa'bb" - } false \ - "expand filenames containing quotes" - - test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa\\\"" \ - "file ${qc}${root}/bb1/aa\\\\\"bb${qc}" " " \ + if { $qc eq "'" } { + set dq "\"" + set dq_re "\"" + } else { + set dq "\\\"" + set dq_re "\\\\\"" + } + + test_gdb_complete_filename_multiple "file ${qc}${root}/aaa/" \ + "a" "a${sp}" { + "aa bb" + "aa cc" + } "" "${qc}" false \ + "expand filenames containing spaces" + + test_gdb_complete_filename_multiple "file ${qc}${root}/bb1/" \ + "a" "a" { + "aa\"bb" + "aa'bb" + } "" "${qc}" false \ + "expand filenames containing quotes" + + test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa${dq}" \ + "file ${qc}${root}/bb1/aa${dq_re}bb${qc}" " " \ "expand unique filename containing double quotes" - test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa\\'" \ - "file ${qc}${root}/bb1/aa\\\\'bb${qc}" " " \ + # It is not possible to include a single quote character + # within a single quoted string. However, GDB does not do + # anything smart if a user tries to do this. Avoid testing + # this case. Maybe in the future we'll figure a way to avoid + # this situation. + if { $qc ne "'" } { + if { $qc eq "" } { + set sq "\\'" + set sq_re "\\\\'" + } else { + set sq "'" + set sq_re "'" + } + + test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa${sq}" \ + "file ${qc}${root}/bb1/aa${sq_re}bb${qc}" " " \ "expand unique filename containing single quote" } } From patchwork Fri Mar 29 11:42:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 87816 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 767223858D39 for ; Fri, 29 Mar 2024 11:44:20 +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 ESMTPS id 801773858401 for ; Fri, 29 Mar 2024 11:42:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 801773858401 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 801773858401 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=1711712572; cv=none; b=kUuGDD4wOwreW3zkIsQtTyzLj6dvUaHqVFlb+ZbmRnA1NYfl8m1yHycnKuM0Q8E7Z/ZLQKPrxu11LpPRBjrZrweu4okPMK1nsic3WL6B7wx4W/cgbMVYxFcR4k+zknyrsmEMsGJbAZ8lqjQxWTntXcwWRVzvaVLCw0u/BC4sAxs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711712572; c=relaxed/simple; bh=2GpZJcO8LknM81ozDck7VdAwIq3FW6526Di7CD6vnEM=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=iW+BGx6y7pKlC3mCKfSX3K/mTbJlwKIb1AE3VycEvXnCP6vslLeAdeX9xO8P+2ks1iZnbMv3GcgtYyquXwDG/GQFxLVo66Kwmk7a94A2CQYnNKXpiaWzRf6B9W/FaX1T3ewqQG7bjGbFE6Z3UZBBMj2DZElCzKQ4NSLZZAQJa+Y= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711712567; 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=XittYiVyRI1Yh/6iznxWAbB0B1avYc8owUO5vG6ZcSg=; b=hf8946JtAWu0IzkJtFZJEXTOSL7ucRJftWna3h8+3qILDszxVb35Xp619HdqajsAikECxI xW4zOaJnFa60KkXKsFyePqVUBwa0G9hay3IpSLxJgAfFkTR6uHo2x8WtLkV6SZdrUN/2Wv vYn/rVRuAOm33SxGfZRJQ6ZYVqSbSY4= Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-605-lxAlcwxvO8GZQblGJcNeew-1; Fri, 29 Mar 2024 07:42:45 -0400 X-MC-Unique: lxAlcwxvO8GZQblGJcNeew-1 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-a46d59152c9so145798466b.0 for ; Fri, 29 Mar 2024 04:42:45 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711712563; x=1712317363; 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=XittYiVyRI1Yh/6iznxWAbB0B1avYc8owUO5vG6ZcSg=; b=e2lBF1oOjoLfChdqGzAczf4hKls1k7xTHpA4iRMoU8vapAUH0TTAhjKtl2pWa4KYoJ 8N76XKCD7LPnxLINhhCI8/fd/59/rBbvBCguozyPnbhnOrHMQrKLAIEbD0S506xrYNJX Tm6Ue5tVPLiYadsHgKIR+aNcEQW1147vf2v/cCVkTCdPjizojSEmJiWfDNSDY2NQMWV2 TjC8AEAn1e/TLnY4CG3rv26qPGEbkG8LWRftUJDP7djeoujwBlpm6KjXDjJGwItdOcTO RA9/QyS0tkfmqavokc1nVHYa57EgT/KPPzRlfrGe4x9q17/dzEGEgHNVXFM9f7PdwZ0q ++RQ== X-Gm-Message-State: AOJu0YwDLGVKpJPVCU2mBB7DOFSlYt1ENS0LTnVRPibeh1wGwt8fa69h d8bVDMhqS3YecFvZDeO2h6eZvsuygf2hPkRfbj8rcUc6J5g5xbNiePlpUdpm/KHrov/QsCEmXhv wMdkyH8L+qB96sCAmtwySGy+5lzEVTkFZqAL8gSKn5KUS4wf/HIGMa06J/eToWjYOWW5P0JXEEp wS5fzCCNcAMyIVHtfVGRs4SmPdEvbkbtX7tJCVDOZq76Q= X-Received: by 2002:a17:906:2b55:b0:a47:348d:cc6f with SMTP id b21-20020a1709062b5500b00a47348dcc6fmr1308622ejg.3.1711712562674; Fri, 29 Mar 2024 04:42:42 -0700 (PDT) X-Google-Smtp-Source: AGHT+IG93KFrLL9iCvrem87yweUZO/OdX87KIfvLjj8CwZQkC7hSQTs2vnw8uVwNe5+vIzb2rHOmDQ== X-Received: by 2002:a17:906:2b55:b0:a47:348d:cc6f with SMTP id b21-20020a1709062b5500b00a47348dcc6fmr1308605ejg.3.1711712561968; Fri, 29 Mar 2024 04:42:41 -0700 (PDT) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id ho19-20020a1709070e9300b00a473631e261sm1834434ejc.28.2024.03.29.04.42.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Mar 2024 04:42:41 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 6/6] gdb: improve gdb_rl_find_completion_word for quoted words Date: Fri, 29 Mar 2024 11:42:32 +0000 Message-Id: <3735f2a98f16b040fe5aac440cde075f3f7b1997.1711712401.git.aburgess@redhat.com> 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_H4, 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 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 The function gdb_rl_find_completion_word is very similar to the readline function _rl_find_completion_word, but was either an older version of that function, or was trimmed when copying to remove code which was considered unnecessary. We maintain this copy because the _rl_find_completion_word function is not part of the public readline API, and we need to replicate the functionality of that function as part of the 'complete' command. Within gdb_rl_find_completion_word when looking for the completion word, if we don't find a unclosed quoted string (which would become the completion word) then we scan backwards looking for a word break character. For example, given: (gdb) complete file /tmp/foo There is no unclosed quoted string so we end up scanning backwards from the end looking for a word break character. In this case the space after 'file' and before '/tmp/foo' is found, so '/tmp/foo' becomes the completion word. However, given this: (gdb) complete file /tmp/foo\" There is still no unclosed quoted string, however, when we can backwards the '"' (double quotes) are treated as a word break character, and so we end up using the empty string as the completion word. The readline function _rl_find_completion_word avoids this mistake by using the rl_char_is_quoted_p hook. This function will return true for the double quote character as it is preceded by a backslash. An earlier commit in this series supplied a rl_char_is_quoted_p function for the filename completion case, however, gdb_rl_find_completion_word doesn't call rl_char_is_quoted_p so this doesn't help for the 'complete' case. In this commit I've copied the code to call rl_char_is_quoted_p from _rl_find_completion_word into gdb_rl_find_completion_word. This half solves the problem. In the case: (gdb) complete file /tmp/foo\" We do now try to complete on the string '/tmp/foo\"', however, when we reach filename_completer we call back into readline to actually perform filename completion. However, at this point the WORD variable points to a string that still contains the backslash. The backslash isn't part of the actual filename, that's just an escape character. Our expectation is that readline will remove the backslash when looking for matching filenames. However, readline contains an optimisation to avoid unnecessary work trying to remove escape characters. The readline variable rl_completion_found_quote is set in the readline function gen_completion_matches before the generation of completion matches. This variable is set to true (non-zero) if there is (or might be) escape characters within the completion word. The function rl_filename_completion_function, which generates the filename matches, only removes escape characters when rl_completion_found_quote is true. When GDB generates completions through readline (e.g. tab completion) then rl_completion_found_quote is set correctly. But when we use the 'complete' command we don't pass through readline, and so gen_completion_matches is never called and rl_completion_found_quote is not set. In this case when we call rl_filename_completion_function readline doesn't remove the escapes from the completion word, and so in our case above, readline looks for completions of the exact filename '/tmp/foo\"', that is, the filename including the backslash. To work around this problem I've added a new flag to our function gdb_rl_find_completion_word which is set true when we find any quoting or escaping. This matches what readline does. Then in the 'complete' function we can set rl_completion_found_quote prior to generating completion matches. With this done the 'complete' command now works correctly when trying to complete filenames that contain escaped word break characters. The tests have been updated accordingly. --- gdb/completer.c | 60 +++++++++++++++---- .../gdb.base/filename-completion.exp | 12 ++-- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/gdb/completer.c b/gdb/completer.c index 710c8c206cb..924fd08b469 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -49,7 +49,8 @@ /* Forward declarations. */ static const char *completion_find_completion_word (completion_tracker &tracker, const char *text, - int *quote_char); + int *quote_char, + bool *found_any_quoting); static void set_rl_completer_word_break_characters (const char *break_chars); @@ -458,7 +459,9 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore, boundaries of the current word. QC, if non-null, is set to the opening quote character if we found an unclosed quoted substring, '\0' otherwise. DP, if non-null, is set to the value of the - delimiter character that caused a word break. */ + delimiter character that caused a word break. FOUND_ANY_QUOTING, if + non-null, is set to true if we found any quote characters (single or + double quotes, or a backslash) while finding the completion word. */ struct gdb_rl_completion_word_info { @@ -469,7 +472,7 @@ struct gdb_rl_completion_word_info static const char * gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, - int *qc, int *dp, + int *qc, int *dp, bool *found_any_quoting, const char *line_buffer) { int scan, end, delimiter, pass_next, isbrk; @@ -491,6 +494,7 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, end = point; delimiter = 0; quote_char = '\0'; + bool found_quote = false; brkchars = info->word_break_characters; @@ -516,6 +520,7 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, if (quote_char != '\'' && line_buffer[scan] == '\\') { pass_next = 1; + found_quote = true; continue; } @@ -536,6 +541,7 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, /* Found start of a quoted substring. */ quote_char = line_buffer[scan]; point = scan + 1; + found_quote = true; } } } @@ -549,8 +555,22 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, { scan = line_buffer[point]; - if (strchr (brkchars, scan) != 0) - break; + if (strchr (brkchars, scan) == 0) + continue; + + /* Call the application-specific function to tell us whether + this word break character is quoted and should be skipped. + The const_cast is needed here to comply with the readline + API. The only function we register for rl_char_is_quoted_p + treats the input buffer as 'const', so we're OK. */ + if (rl_char_is_quoted_p != nullptr && found_quote + && (*rl_char_is_quoted_p) (const_cast (line_buffer), + point)) + continue; + + /* Convoluted code, but it avoids an n^2 algorithm with calls + to char_is_quoted. */ + break; } } @@ -574,6 +594,8 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info, } } + if (found_any_quoting != nullptr) + *found_any_quoting = found_quote; if (qc != NULL) *qc = quote_char; if (dp != NULL) @@ -600,7 +622,7 @@ advance_to_completion_word (completion_tracker &tracker, int delimiter; const char *start - = gdb_rl_find_completion_word (&info, NULL, &delimiter, text); + = gdb_rl_find_completion_word (&info, nullptr, &delimiter, nullptr, text); tracker.advance_custom_word_point_by (start - text); @@ -673,7 +695,8 @@ complete_nested_command_line (completion_tracker &tracker, const char *text) int quote_char = '\0'; const char *word = completion_find_completion_word (tracker, text, - "e_char); + "e_char, + nullptr); if (tracker.use_custom_word_point ()) { @@ -1891,8 +1914,11 @@ complete (const char *line, char const **word, int *quote_char) try { + bool found_any_quoting = false; + *word = completion_find_completion_word (tracker_handle_brkchars, - line, quote_char); + line, quote_char, + &found_any_quoting); /* Completers that provide a custom word point in the handle_brkchars phase also compute their completions then. @@ -1902,6 +1928,12 @@ complete (const char *line, char const **word, int *quote_char) tracker = &tracker_handle_brkchars; else { + /* Setting this global matches what readline does within + gen_completion_matches. We need this set correctly in case + our completion function calls back into readline to perform + completion (e.g. filename_completer does this). */ + rl_completion_found_quote = found_any_quoting; + complete_line (tracker_handle_completions, *word, line, strlen (line)); tracker = &tracker_handle_completions; } @@ -2176,7 +2208,7 @@ gdb_completion_word_break_characters () noexcept static const char * completion_find_completion_word (completion_tracker &tracker, const char *text, - int *quote_char) + int *quote_char, bool *found_any_quoting) { size_t point = strlen (text); @@ -2186,6 +2218,13 @@ completion_find_completion_word (completion_tracker &tracker, const char *text, { gdb_assert (tracker.custom_word_point () > 0); *quote_char = tracker.quote_char (); + /* This isn't really correct, we're ignoring the case where we found + a backslash escaping a character. However, this isn't an issue + right now as we only rely on *FOUND_ANY_QUOTING being set when + performing filename completion, which doesn't go through this + path. */ + if (found_any_quoting != nullptr) + *found_any_quoting = *quote_char != '\0'; return text + tracker.custom_word_point (); } @@ -2195,7 +2234,8 @@ completion_find_completion_word (completion_tracker &tracker, const char *text, info.quote_characters = rl_completer_quote_characters; info.basic_quote_characters = rl_basic_quote_characters; - return gdb_rl_find_completion_word (&info, quote_char, NULL, text); + return gdb_rl_find_completion_word (&info, quote_char, nullptr, + found_any_quoting, text); } /* See completer.h. */ diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index f8a48269528..fd4c407ac23 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -148,10 +148,8 @@ proc run_tests { root } { if { $qc eq "'" } { set dq "\"" - set dq_re "\"" } else { set dq "\\\"" - set dq_re "\\\\\"" } test_gdb_complete_filename_multiple "file ${qc}${root}/aaa/" \ @@ -168,8 +166,8 @@ proc run_tests { root } { } "" "${qc}" false \ "expand filenames containing quotes" - test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa${dq}" \ - "file ${qc}${root}/bb1/aa${dq_re}bb${qc}" " " \ + test_gdb_complete_unique "file ${qc}${root}/bb1/aa${dq}" \ + "file ${qc}${root}/bb1/aa${dq}bb${qc}" " " false \ "expand unique filename containing double quotes" # It is not possible to include a single quote character @@ -180,14 +178,12 @@ proc run_tests { root } { if { $qc ne "'" } { if { $qc eq "" } { set sq "\\'" - set sq_re "\\\\'" } else { set sq "'" - set sq_re "'" } - test_gdb_complete_tab_unique "file ${qc}${root}/bb1/aa${sq}" \ - "file ${qc}${root}/bb1/aa${sq_re}bb${qc}" " " \ + test_gdb_complete_unique "file ${qc}${root}/bb1/aa${sq}" \ + "file ${qc}${root}/bb1/aa${sq}bb${qc}" " " false \ "expand unique filename containing single quote" } }