From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id E1431385842F for ; Tue, 9 Jan 2024 14:27:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E1431385842F 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 E1431385842F 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=1704810435; cv=none; b=kc7Zzeomt9F1PwSfQQc3tJHlBK3h4Paff+9MuRXJgEk6OzJvRNAhYc/6idniOleapl8g7kZLj/6Exd2k+aJE+Xa49MTe9M/sHJrD8pW1OoxgHLvzlkTE9y4K4KHEOMVKfLq5f5xxMJl6UlBINyQUN3NtO72Ey3xNTWF7p0ATYbI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704810435; c=relaxed/simple; bh=hboOMOSpj3b+INaK91JOGX6/Hp+wyvGCiickiijumz8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=moTicol4JboZID+dQ+9VWtUZjIgVsCHrTz2WLetPQiQjzfAREassze1dJRTedbfuuSgwP9/HflNRhS0QSYh5uVtwcdhCral2A1txGcHg3/h3xxD/Hb7kUo7tGY3pkm5xNEdPx5Rp35BlkIep8ychb7EwL6ywAsJk7jVhhhHI9fY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1704810430; 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=XwCI5z9M+cmGxRyGE+5iA8boBZSoiPOGIHJMIQRfk6c=; b=I1h9qP11kLEvEehSFMQ+gM1EX3yMkKrBj4m6g94aSxzGHI9tq3u7tMQmScciaNBbQE8ylB xbBxgNx4DIN1IUgjpW93D9fIuJju11+ccR9tbTjJaxxda98vR5OMwHBdWtoJKTEHrFGebR Y+KZjKWhrRHzuU9eV/SiG0uZIKTOWpg= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-117-dxJZM3vwPHiGb-pt4GzQJg-1; Tue, 09 Jan 2024 09:27:04 -0500 X-MC-Unique: dxJZM3vwPHiGb-pt4GzQJg-1 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-40e53200380so661655e9.3 for ; Tue, 09 Jan 2024 06:27:04 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704810422; x=1705415222; 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=XwCI5z9M+cmGxRyGE+5iA8boBZSoiPOGIHJMIQRfk6c=; b=JUyrY0M2dI9WE9+KRJRgNcA8GoKpqvwbHBZKJZ8dUTjvMBFdr/d/RdTDVSRmOpM4e3 R6UvgOGEtgimKgTqEzrLV9f1zKS47S1Lht2NPOx2G62R5CEitoxsM+ZYgiq7y1PwtqRh CBRFx4n7Ys1bQHQ7/gGCFb3MdEkyC82usY9S64enQ1ryHvfnwEtYG4besQmpwSR2/VBA LBQy7+v1+cls6CHDrqiFkkAAiQ1o1VTagCIWwKwT3CIh/kY87RUOX29hsL4VOny/07Yz D2uRkHM2VZqCdEChbpYzpibuoGJqR0wPEVC81Vm7BVVuKp14IpW2aX12LB2ZfK78LE1L a5OA== X-Gm-Message-State: AOJu0Yy9vyZFJhnlnyv1qq1CSE04yxM9YgbFi03zM44w+ZLUv0i6bLpJ /j5DCTRhBJa6kfAEEU1JrQWQmVf0jvfIVthV7hVmr9cyn1UkA1YZsdJgPwZhzkc7v7CjTErBzi0 Z9o3bKLrc5qLFwEzjaxnqCsX3NO+ySFtQJoSCWRAgxPUwipF9NZTgIA34FSI3wKpZNvz09PNRNk NxG97M4yMrIqWVLg== X-Received: by 2002:a05:600c:468b:b0:40c:366a:598a with SMTP id p11-20020a05600c468b00b0040c366a598amr2603748wmo.107.1704810422285; Tue, 09 Jan 2024 06:27:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IF9SElVpd6UbB+Ebs86V2i7/Z8ufe46O0ikP7YlAiJemqRLdFCh5D6NxRg9tbazhy4hDq1agQ== X-Received: by 2002:a05:600c:468b:b0:40c:366a:598a with SMTP id p11-20020a05600c468b00b0040c366a598amr2603742wmo.107.1704810421790; Tue, 09 Jan 2024 06:27:01 -0800 (PST) Received: from localhost (185.223.159.143.dyn.plus.net. [143.159.223.185]) by smtp.gmail.com with ESMTPSA id d7-20020adfe887000000b0033672971fabsm2528766wrm.115.2024.01.09.06.27.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 Jan 2024 06:27:01 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Michael Weghorn Subject: [PATCH 13/16] gdb: allow 'set args' and run commands to contain newlines Date: Tue, 9 Jan 2024 14:26:36 +0000 Message-Id: <4b226e07edabf10a704aef70fd9ef4a3f2a5bd34.1704809585.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 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-13.5 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,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: When starting GDB it is possible to set an inferior argument that contains a newline, for example: shell> gdb --args my.app "abc > def" ... (gdb) show args Argument list to give program being debugged when it is started is "abc' 'def". However, once GDB is started, the only way to install an argument containing a newline is to use the Python API. This commit changes that. After this commit 'set args' as well as 'run', 'start', and 'starti', will now accept multi-line inferior arguments, e.g.: (gdb) set args "abc > def" (gdb) show args Argument list to give program being debugged when it is started is ""abc def"". And also: (gdb) run "abc > def" ... etc ... Once GDB has presented the secondary prompt to gather the remaining inferior arguments then it is possible for the user to quit argument entry by sending SIGINT (usually, Ctrl-c). For the 'set args' case this will abort the argument change, leaving the arguments as they were previously. For the run style commands, this aborts the run command completely, the inferior is not changed, and the partially collected arguments are not installed. --- gdb/NEWS | 10 ++ gdb/doc/gdb.texinfo | 20 ++++ gdb/infcmd.c | 128 +++++++++++++++++++++-- gdb/testsuite/gdb.base/inferior-args.exp | 71 ++++++++++++- 4 files changed, 219 insertions(+), 10 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 80c766eeeda..2ba1899f78c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -51,6 +51,16 @@ set remote thread-options-packet show remote thread-options-packet Set/show the use of the thread options packet. +* Changed commands + +set args +run +start +starti + These commands now all allow for entering inferior arguments that + contain a newline character. The newline must be contained within a + single or double quoted argument. + * New features in the GDB remote stub, GDBserver ** The --remote-debug and --event-loop-debug command line options diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index abb07d74baf..2015293ee0d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2893,6 +2893,26 @@ using @code{set args} before the next @code{run} is the only way to run it again without arguments. +It is possible to set arguments containing a newline character. This +can be done by enclosing an argument within a single or double quote. +For example, start by entering: + +@smallexample +(@value{GDBP}) set args "ab +@end smallexample + +@noindent +and then enter the newline, @value{GDBN} gives a secondary prompt +@code{>} and allows you to continue entering the arguments: + +@smallexample +(@value{GDBP}) set args "ab +>cd" +(@value{GDBP}) show args +Argument list to give program being debugged when it is started is ""ab +cd"". +@end smallexample + @kindex show args @item show args Show the arguments to give your program when it is started. diff --git a/gdb/infcmd.c b/gdb/infcmd.c index a3c32792491..bad8736aad2 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -120,11 +120,112 @@ show_inferior_tty_command (struct ui_file *file, int from_tty, "is \"%s\".\n"), inferior_tty.c_str ()); } +/* Return true if the inferior argument string ARGS represents a "complete" + set of arguments. Arguments are considered complete so long as they + don't contain unbalanced single or double quoted strings. Unbalanced + means that a single or double quoted argument is started, but not + finished. */ + +static bool +args_complete_p (const std::string &args) +{ + const char *input = args.c_str (); + bool squote = false, dquote = false; + + while (*input != '\0') + { + input = skip_spaces (input); + + if (squote) + { + /* Inside a single quoted argument, look for the closing single + quote. */ + if (*input == '\'') + squote = false; + } + else if (dquote) + { + /* If we see either '\"' or '\\' within a double quoted argument + then skip both characters (one is skipped here, and the other + at the end of the loop). We need to skip the '\"' so that we + don't consider the '"' as closing the double quoted argument, + and we don't skip the entire '\\' then we'll only skip the + first '\', in which case we might see the second '\' as a '\"' + sequence, which would be wrong. */ + if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr) + ++input; + /* Otherwise, just look for the closing double quote. */ + else if (*input == '"') + dquote = false; + } + else + { + /* Outside of either a single or double quoted argument, we need + to check for '\"', '\'', and '\\'. The escaped quotes we + obviously need to skip so we don't think that we have started + a quoted argument. The '\\' we need to skip so we don't just + skip the first '\' and then incorrectly consider the second + '\' are part of a '\"' or '\'' sequence. */ + if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr) + ++input; + /* Otherwise, check for the start of a single or double quoted + argument. */ + else if (*input == '\'') + squote = true; + else if (*input == '"') + dquote = true; + } + + ++input; + } + + return (!dquote && !squote); +} + +/* ... */ + +static std::string +get_complete_args (std::string args) +{ + /* If the user wants an argument containing a newline then they need to + do so within quotes. Use args_complete_p to check if the ARGS string + contains balanced double and single quotes. If not then prompt the + user for additional arguments and append this to ARGS. */ + const char *prompt = nullptr; + while (!args_complete_p (args)) + { + if (prompt == nullptr) + { + prompt = getenv ("PS2"); + if (prompt == nullptr) + prompt = "> "; + } + + std::string buffer; + const char *content = command_line_input (buffer, prompt, "set_args"); + if (content == nullptr) + return {}; + + args += "\n" + buffer; + } + + return args; +} + /* Store the new value passed to 'set args'. */ static void -set_args_value (const std::string &args) +set_args_value (const std::string &args_in) { + std::string args; + + if (!args_in.empty ()) + { + args = get_complete_args (args_in); + if (args.empty ()) + return; + } + current_inferior ()->set_args (args); } @@ -376,6 +477,21 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) dont_repeat (); + gdb::unique_xmalloc_ptr stripped = strip_bg_char (args, &async_exec); + args = stripped.get (); + + std::string inf_args; + /* If there were other args, beside '&', process them. */ + if (args != nullptr) + { + /* If ARGS is only a partial argument string then this call will + interactively read more arguments from the user. If the user + quits then we shouldn't start the inferior. */ + inf_args = get_complete_args (args); + if (inf_args.empty ()) + return; + } + scoped_disable_commit_resumed disable_commit_resumed ("running"); kill_if_already_running (from_tty); @@ -397,9 +513,6 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) reopen_exec_file (); reread_symbols (from_tty); - gdb::unique_xmalloc_ptr stripped = strip_bg_char (args, &async_exec); - args = stripped.get (); - /* Do validation and preparation before possibly changing anything in the inferior. */ @@ -412,6 +525,9 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) /* Done. Can now set breakpoints, change inferior args, etc. */ + if (!inf_args.empty ()) + current_inferior ()->set_args (inf_args); + /* Insert temporary breakpoint in main function if requested. */ if (run_how == RUN_STOP_AT_MAIN) { @@ -433,10 +549,6 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) the user has to manually nuke all symbols between runs if they want them to go away (PR 2207). This is probably reasonable. */ - /* If there were other args, beside '&', process them. */ - if (args != nullptr) - current_inferior ()->set_args (args); - if (from_tty) { uiout->field_string (nullptr, "Starting program"); diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index 6c22ecb3c54..b259e4a4217 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -211,12 +211,15 @@ lappend test_desc_list [list "test four" \ [list "$hex \"'\"" \ "$hex \"\\\\\"\""]] -# Run all tests in the global TEST_DESC_LIST. +# Run all tests in the global TEST_DESC_LIST, as well as some tests of +# inferior arguments containing newlines. proc run_all_tests {} { + set all_methods { "start" "starti" "run" "set args" } + foreach desc $::test_desc_list { lassign $desc name stub_suitable args re_list with_test_prefix $name { - foreach_with_prefix set_method { "start" "starti" "run" "set args" } { + foreach_with_prefix set_method $all_methods { foreach_with_prefix startup_with_shell { on off } { do_test $set_method $startup_with_shell $args $re_list \ $stub_suitable @@ -224,6 +227,70 @@ proc run_all_tests {} { } } } + + # Check the multi-line argument entry. This isn't going to work when + # using the gdbstub, as the only way to set arguments in this case is + # via the gdbserver command line, which isn't what we're testing here. + if { ![use_gdb_stub] } { + foreach_with_prefix set_method $all_methods { + clean_restart $::binfile + + # First check that we can abort entering multi-line arguments. + set saw_prompt false + gdb_test_multiple "$set_method \"ab" "abort argument entry" { + -re "^$set_method \"ab\r\n" { + exp_continue + } + -re "^> $" { + set saw_prompt true + send_gdb "\004" + exp_continue + } + -re "quit\r\n$::gdb_prompt $" { + gdb_assert {$saw_prompt} \ + $gdb_test_name + } + } + + # Now place a breakpoint on main. + if { ![gdb_breakpoint "main" message] } { + fail "could not set breakpoint on main" + continue + } + + # And actually enter some multi-line arguments. + set saw_prompt false + gdb_test_multiple "$set_method \"xy" "complete argument entry" { + -re "^$set_method \"xy\r\n" { + exp_continue + } + -re "^> $" { + set saw_prompt true + send_gdb "12\"\n" + exp_continue + } + + -re "$::gdb_prompt $" { + gdb_assert { $saw_prompt } \ + $gdb_test_name + } + } + + # For the two methods that don't automatically run to main, + # poke the inferior along to main. + if { $set_method == "set args" } { + if { ![runto_main] } { + continue + } + } elseif { $set_method == "starti" } { + gdb_continue_to_breakpoint "b/p in main" + } + + # And check we correctly see the argument containing a newline. + gdb_test "print argc" " = 2" + gdb_test "print argv\[1\]" " = $::hex \"xy\\\\n12\"" + } + } } run_all_tests -- 2.25.4