From patchwork Sun Jan 29 16:21:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 63881 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 00EB038493FB for ; Sun, 29 Jan 2023 16:22:34 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from progateway7-pub.mail.pro1.eigbox.com (gproxy5-pub.mail.unifiedlayer.com [67.222.38.55]) by sourceware.org (Postfix) with ESMTPS id 34FA33858C50 for ; Sun, 29 Jan 2023 16:21:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 34FA33858C50 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=tromey.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tromey.com Received: from cmgw15.mail.unifiedlayer.com (unknown [10.0.90.130]) by progateway7.mail.pro1.eigbox.com (Postfix) with ESMTP id 08C3F1004743C for ; Sun, 29 Jan 2023 16:21:23 +0000 (UTC) Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with ESMTP id MAQkpffAel2fvMAQkptrH9; Sun, 29 Jan 2023 16:21:23 +0000 X-Authority-Reason: nr=8 X-Authority-Analysis: v=2.4 cv=NIUQR22g c=1 sm=1 tr=0 ts=63d69d03 a=ApxJNpeYhEAb1aAlGBBbmA==:117 a=ApxJNpeYhEAb1aAlGBBbmA==:17 a=dLZJa+xiwSxG16/P+YVxDGlgEgI=:19 a=RvmDmJFTN0MA:10:nop_rcvd_month_year a=Qbun_eYptAEA:10:endurance_base64_authed_username_1 a=CCpqsmhAAAAA:8 a=Dyivgxzs2-QuVFkx6-cA:9 a=ul9cdbp4aOFLsgKbc677:22 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tromey.com; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=JCQTNgmC3wz20hRoLopkzppSb8Sq2fPBPstMnDV+K1g=; b=ZMFcXSG4x0j/+Ylf31HBngfTdF mjN/HHPy1pTnTuc8LQstpJ7fgDh02nlWrvktNgl00yzZFSYYxE3ociGc/h5f/uAekkn8Y7Nlu1CpU DILcXei8JCY5oHjXN4mFacpnW; Received: from 75-166-146-144.hlrn.qwest.net ([75.166.146.144]:59376 helo=localhost.localdomain) by box5379.bluehost.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1pMAQk-001fCo-Mk; Sun, 29 Jan 2023 09:21:22 -0700 From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH 3/5] Add "save user" command Date: Sun, 29 Jan 2023 09:21:03 -0700 Message-Id: <20230129162105.526266-4-tom@tromey.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230129162105.526266-1-tom@tromey.com> References: <20230129162105.526266-1-tom@tromey.com> MIME-Version: 1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - box5379.bluehost.com X-AntiAbuse: Original Domain - sourceware.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - tromey.com X-BWhitelist: no X-Source-IP: 75.166.146.144 X-Source-L: No X-Exim-ID: 1pMAQk-001fCo-Mk X-Source: X-Source-Args: X-Source-Dir: X-Source-Sender: 75-166-146-144.hlrn.qwest.net (localhost.localdomain) [75.166.146.144]:59376 X-Source-Auth: tom+tromey.com X-Email-Count: 4 X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTM3OS5ibHVlaG9zdC5jb20= X-Local-Domain: yes X-Spam-Status: No, score=-3027.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, JMQ_SPF_NEUTRAL, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, 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.29 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 Sender: "Gdb-patches" PR cli/19395 points out that it would sometimes be convenient to save one's user-defined commands to a file. This patch implements this feature. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=19395 --- gdb/NEWS | 3 ++ gdb/cli/cli-cmds.c | 72 +++++++++++++++++++++++++++++++++++++-------- gdb/doc/gdb.texinfo | 6 ++++ 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 2bc1672632a..3b7d768732c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -17,6 +17,9 @@ maintenance print record-instruction [ N ] prints how GDB would undo the N-th previous instruction, and if N is positive, it prints how GDB will redo the N-th following instruction. +save user FILENAME + Save all user-defined commands to the given file. + * MI changes ** mi now reports 'no-history' as a stop reason when hitting the end of the diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 61f890a7dae..3f488d1a544 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -51,6 +51,7 @@ #include "cli/cli-style.h" #include "cli/cli-utils.h" #include "cli/cli-style.h" +#include "cli-out.h" #include "extension.h" #include "gdbsupport/pathstuff.h" @@ -1646,24 +1647,41 @@ make_command (const char *arg, int from_tty) /* Print the definition of user command C to STREAM. Or, if C is a prefix command, show the definitions of all user commands under C (recursively). PREFIX and NAME combined are the name of the - current command. */ + current command. DEF is true if the output should be written as a + source-able script. */ static void show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name, - struct ui_file *stream) + struct ui_file *stream, struct ui_out *uiout, bool def) { if (cli_user_command_p (c)) { struct command_line *cmdlines = c->user_commands.get (); - gdb_printf (stream, "User %scommand \"", - c->is_prefix () ? "prefix" : ""); - fprintf_styled (stream, title_style.style (), "%s%s", - prefix, name); - gdb_printf (stream, "\":\n"); + if (def) + gdb_printf (stream, "define%s %s%s\n", + c->is_prefix () ? "-prefix" : "", + prefix, name); + else + { + gdb_printf (stream, "User %scommand \"", + c->is_prefix () ? "prefix" : ""); + fprintf_styled (stream, title_style.style (), "%s%s", + prefix, name); + gdb_printf (stream, "\":\n"); + } if (cmdlines) { - print_command_lines (current_uiout, cmdlines, 1); - gdb_puts ("\n", stream); + print_command_lines (uiout, cmdlines, 1); + if (!def) + gdb_puts ("\n", stream); + } + if (def) + { + gdb_puts ("end\n", stream); + + if (!c->is_prefix () && !streq (c->doc, "User-defined.")) + gdb_printf (stream, "document %s%s\n%s\nend\n", + prefix, name, c->doc); } } @@ -1673,9 +1691,8 @@ show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name, for (c = *c->subcommands; c != NULL; c = c->next) if (c->theclass == class_user || c->is_prefix ()) - show_user_1 (c, prefixname.c_str (), c->name, gdb_stdout); + show_user_1 (c, prefixname.c_str (), c->name, stream, uiout, def); } - } static void @@ -1690,18 +1707,40 @@ show_user (const char *args, int from_tty) c = lookup_cmd (&comname, cmdlist, "", NULL, 0, 1); if (!cli_user_command_p (c)) error (_("Not a user command.")); - show_user_1 (c, "", args, gdb_stdout); + show_user_1 (c, "", args, gdb_stdout, current_uiout, false); } else { for (c = cmdlist; c; c = c->next) { if (cli_user_command_p (c) || c->is_prefix ()) - show_user_1 (c, "", c->name, gdb_stdout); + show_user_1 (c, "", c->name, gdb_stdout, current_uiout, false); } } } +/* The "save user" command. */ + +static void +save_user_command (const char *filename, int from_tty) +{ + if (filename == nullptr || *filename == '\0') + error (_("Argument required (file name in which to save)")); + + gdb::unique_xmalloc_ptr expanded_filename (tilde_expand (filename)); + stdio_file fp; + if (!fp.open (expanded_filename.get (), "w")) + error (_("Unable to open file '%s' for saving (%s)"), + expanded_filename.get (), safe_strerror (errno)); + + cli_ui_out uiout (&fp); + for (struct cmd_list_element *c = cmdlist; c != nullptr; c = c->next) + { + if (cli_user_command_p (c) || c->is_prefix ()) + show_user_1 (c, "", c->name, &fp, &uiout, true); + } +} + /* Return true if COMMAND or any of its sub-commands is a user defined command. This is a helper function for show_user_completer. */ @@ -2762,6 +2801,13 @@ Usage: apropos [-v] REGEXP\n\ Flag -v indicates to produce a verbose output, showing full documentation\n\ of the matching commands.")); + c = add_cmd ("user", no_class, save_user_command, _("\ +Save current user-defined commands as a script.\n\ +Usage: save user FILE\n\ +Use the 'source' command in another debug session to restore them."), + &save_cmdlist); + set_cmd_completer (c, filename_completer); + add_setshow_uinteger_cmd ("max-user-call-depth", no_class, &max_user_call_depth, _("\ Set the max call depth for non-python/scheme user-defined commands."), _("\ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index b5fad2cb16e..37db4785fd2 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -28230,6 +28230,12 @@ The value of @code{max-user-call-depth} controls how many recursion levels are allowed in user-defined commands before @value{GDBN} suspects an infinite recursion and aborts the command. This does not apply to user-defined python commands. + +@kindex save user +@item save user @var{filename} +Save all user-defined commands to the file @var{filename}. This +command writes out the user-defined commands as a script that can be +re-read into @value{GDBN} using the @code{source} command. @end table In addition to the above commands, user-defined commands frequently