[2/2] gdb: Allow parenthesis to group arguments to user-defined commands

Message ID 1535800196.10641.1.camel@skynet.be
State New, archived
Headers

Commit Message

Philippe Waroquiers Sept. 1, 2018, 11:09 a.m. UTC
  On Fri, 2018-08-31 at 14:59 -0600, Tom Tromey wrote:
> > > > > > "Philippe" == Philippe Waroquiers <philippe.waroquiers@skynet.be> writes:
> 
> Philippe> For what concerns the parenthesis based solution, it looks to not work
> Philippe> for some cases.
> 
> Philippe> E.g. if I want to pass one argument (I am using single quotes to show the args
> Philippe> I want in the below examples):
> Philippe>     'a b) (c d'
> Philippe> then I need to use:
> Philippe>     some_user_defined (a b) (c d)
> Philippe> but that is the same notation as if I want to pass 2 arguments
> Philippe>     'a b'  and 'c d'
> 
> Can you think of an actual example where this would be useful?
> My feeling is that there isn't one, though I'd be interested in
> tricky-but-plausible counter-examples.

For example, I want a user defined command
that receives a bunch of REGEXPs,
and then for each regexp, the user defined command calls
   info types $arg0
   info types $arg1
   info types $arg2
More generally, any kind of user defined command might
need to receive whatever args. 

IMO, quoting with quotes is also more usual (at least in shells)
and single quotes are already used in gdb (as pointed out by
Pedro).
See e.g. 
@cindex quotes in commands
@cindex completion of quoted strings
@cindex C@t{++} scope resolution
@cindex quoting names

(and of course, my brand new brilliant patch still
to be reviewed :) that implements 
  info [args|functions|locals|variables] [-q] [-t TYPEREGEXP] [NAMEREGEXP]
is using optional single quotes for TYPEREGEXP, following Pedro's
initial comments).


So, it looks strange to me to add a new user defined argument quoting
mechanism because the current mechanism (using quotes) has quirks,
but the new mechanism itself starts its life with quirks/limitations.


What about the alternative solution to allow a user defined
command to use $argu0, $argu1, $argu2, ... to do unquoted
expansion when needed ?

$arguX does unquoted expansion if there are quotes, otherwise
does normal expansion.
With the patch below, here is the behaviour:

define show_args
  printf "nargs=%d:", $argc
  set $i = 0
  while $i < $argc
    printf " "
    eval "echo [$arg%d] [$argu%d]", $i, $i
    set $i = $i + 1
  end
  printf "\n"
end

(gdb) show_args 'a b' 'c d' e '   f g   '
nargs=4: ['a b'] [a b] ['c d'] [c d] [e] [e] ['   f g   '] [   f g   ]

As far as I can see, the above should allow to solve the original
problem of Andrew, but without constraints on what can be quoted.
This is backward compatible, unless someone already used
somewhere $argu followed by one or more digits in a
user defined command
(and if we want to reduce further the probability of backward
incompatibility, we could use something like:
   $argunquoted0, $argunquoted1, ...
but that seems very long :).


Patch:
> 
> This feeling is why I'm generally ok with Andrew's idea.
> 
> Philippe> And it would be nice if the mechanism used to quote args would
> Philippe> be compatible between user defined commands and native gdb commands.
> 
> There is no universal quoting in gdb.
Yes, and we might maybe avoid to add a new half working new mechanism :).

>   Instead there are 3 common cases,
> plus the extras (neglecting MI, which is totally different and not
> relevant here):
> 
> 1. Commands that take an expression.  These are things like "print".
>    Expressions are passed to the language parser, but language parsers
>    have to follow some gdb rules: optionally terminate parsing at a
>    top-level (unparenthesized) comma, and also terminate parsing at "if"
>    or some other things (there is a mildly odd case for Ada tasks).
> 
> 2. Commands that take a linespec.  These can also take an expression via
>    the "*" linespec, so linespecs end up following some expression
>    rules.  Often there are expressions after the linespec too, like
>      break LINESPEC if EXPR
>    or
>      dprintf LINESPEC, "string", EXPR, EXPR...
> 
> 3. Commands that use gdb_argv (or libiberty's buildargv, which is the
>    same thing).  These respect some simple quoting.  However, the
>    quoting is very simple, not like what you might expect from the
>    shell, for example I don't think you can quote an embedded quotation
>    mark of the same kind (that is, no "this has \"quotes\"").
As far as I can see, gdb_argv is escaping single and double quotes, e.g.
(gdb) handle 10 'ignore this \'bidule'
Unrecognized or ambiguous flag word: "ignore this 'bidule".
(gdb) 

> 
> 4. Everything else.  gdb commands are just given a string and some do
>    whatever the author liked.
> 
> Philippe> I have not understood the reference given by Tom that expressions
> Philippe> are terminated with ,  and that parenthesis stops this termination.
> Philippe> Is that described somewhere in the doc ?
> Philippe> The doc (or an example if this is not documented) will help
> Philippe> me to understand.
> 
> I think it's largely undocumented, since some of these quirks are just
> constraints arising from gdb's implementation choices.
> 
> Not having any rhyme or reason to command argument parsing has good and
> bad facets.
> 
> The good is that the generally DWIM nature of commands means that
> usually you don't have to contort yourself to satisfy some parser.
> Like, you can "break foo.c:93" or "break function" rather than something
> like the old dbx "stop in" / "stop at" split.
> 
> The bad of course is that you may sometimes want to quote something and
> not have any idea of how to do it: because there's no consistency;
> because the gdb_argv idea was not really thought through (that's my
> conclusion, I don't know the actual story); or perhaps because you've
> just tripped across some command that was implemented in a particularly
> bad way.
> 
> Now, it would be great to fix this, at least for some subset of things.
> I find it hard to see a way forward, though.  Breaking compatibility
> (see my post upthread) is unpleasant, unless maybe it is done in a very
> dramatic way, coupled with a major version bump and some kind of huge
> warning for users -- but that's also very hard to implement and release.
> 
> One idea is to add a new standard way to parse arguments, for new
> commands.  But of course then gdb just has 5 ways to do it ... :(

:(

But as said above, for user defined command, we might maybe repair
what we have now, e.g. using the $argu approach.

For sure, the current $arg expansion with quoting is also
working (reasonably consistently, except with the fact that
you cannot jsut 'pass' a still quoted arg containing a ' to
another user defined command ?):

(gdb) show_args 'a b' 'c \d e' 'f \\g h' \i \\j 'k \' l'
nargs=6: ['a b'] [a b] ['c d e'] [c d e] ['f \g h'] [f \g h] [i] [i] [\j] [\j] ['k ' l'] [k ' l]

(so, today, a user defined command can correctly pass a quoted arg to another
user defined command, unless this quoted arg initially contained an escaped ').


So, in summary, I argue for $argu :).

Philippe
  

Comments

Tom Tromey Sept. 1, 2018, 2:20 p.m. UTC | #1
>>>>> "Philippe" == Philippe Waroquiers <philippe.waroquiers@skynet.be> writes:

Philippe> What about the alternative solution to allow a user defined
Philippe> command to use $argu0, $argu1, $argu2, ... to do unquoted
Philippe> expansion when needed ?

I think the problem is that people want to write user-defined commands
that act like other gdb commands, and in particular writing a
user-defined that takes an expression is, by far, the most common case.

But, this proposal would mean that "print" would have one style of
quoting, while "user-print" would have another, drastically less nice,
style.

Personally I think people should just write Python, since it is a much
better way.  But the CLI is more convenient, so here we are.

Philippe> As far as I can see, gdb_argv is escaping single and double quotes, e.g.
Philippe> (gdb) handle 10 'ignore this \'bidule'
Philippe> Unrecognized or ambiguous flag word: "ignore this 'bidule".
Philippe> (gdb) 

Yeah, I misremembered that.  Thanks.

Tom
  
Philippe Waroquiers Sept. 1, 2018, 3:36 p.m. UTC | #2
On Sat, 2018-09-01 at 08:20 -0600, Tom Tromey wrote:
> > > > > > "Philippe" == Philippe Waroquiers <philippe.waroquiers@skynet.be> writes:
> 
> Philippe> What about the alternative solution to allow a user defined
> Philippe> command to use $argu0, $argu1, $argu2, ... to do unquoted
> Philippe> expansion when needed ?
> 
> I think the problem is that people want to write user-defined commands
> that act like other gdb commands, and in particular writing a
> user-defined that takes an expression is, by far, the most common case.
> 
> But, this proposal would mean that "print" would have one style of
> quoting, while "user-print" would have another, drastically less nice,
> style.
Then maybe the conclusion is that we really need 2 different styles
of quoting:
 
 * a quote mechanism to quote expressions, which then must have
     a
'balanced' nr of open/close parenthesis.
   * a quote mechanism (based on '
quoting) for
     non expressions args.
     Single quotes are already used by
several native gdb
     commands (all gdb_argv based, and some others),
     but
is (currently) poorly supported for user defined
     commands, which must be
able to unquote an arg to pass
     it to native commands that do not expect
quoted args.
     
The $argu approach then should allow to write
user defined
commands that have the same behaviour
as the native commands.
E.g. I believe the
example 'info var for a bunch of REGEXP'
is not implementable today, would still
not be doable
with parenthesis quoting, but is I believe implementable
with the
$argu approach.
    
> 
> Personally I think people should just write Python, since it is a much
> better way.  But the CLI is more convenient, so here we are.

> 
> Philippe> As far as I can see, gdb_argv is escaping single and double quotes, e.g.
> Philippe> (gdb) handle 10 'ignore this \'bidule'
> Philippe> Unrecognized or ambiguous flag word: "ignore this 'bidule".
> Philippe> (gdb) 
> 
> Yeah, I misremembered that.  Thanks.
Well, before you pointed me at gdb_argv, I even did not know
it was existing :). So thanks to Andrew and you for this discussion,
I learned a lot about gdb from it.

Philippe
  

Patch

diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 8496fb85e6..bfeb92dac6 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -811,7 +811,9 @@  locate_arg (const char *p)
   while ((p = strchr (p, '$')))
     {
       if (startswith (p, "$arg")
-         && (isdigit (p[4]) || p[4] == 'c'))
+         && (isdigit (p[4])  /* E.g. $arg34.  */
+             || p[4] == 'c'  /* $argc  */
+             || (p[4] == 'u' && isdigit (p[5])))) /* E.g. $argu34.  */
        return p;
       p++;
     }
@@ -854,6 +856,10 @@  user_args::insert_args (const char *line) const
        {
          char *tmp;
          unsigned long i;
+         bool unquote = p[4] == 'u';
+
+         if (unquote)
+           p++;
 
          errno = 0;
          i = strtoul (p + 4, &tmp, 10);
@@ -863,7 +869,13 @@  user_args::insert_args (const char *line) const
            error (_("Missing argument %ld in user function."), i);
          else
            {
-             new_line.append (m_args[i].data (), m_args[i].length ());
+             if (unquote
+                 && m_args[i].length () >= 2
+                 && m_args[i].data () [0] == '\''
+                 && m_args[i].data () [m_args[i].length () - 1] == '\'')
+               new_line.append (m_args[i].data () + 1, m_args[i].length () - 2);
+             else
+               new_line.append (m_args[i].data (), m_args[i].length ());
              line = tmp;
            }
        }