This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

RFC: Inferior command line arguments


Tonight I did some hacking to let one set the inferior command line
arguments from the gdb command line.  This feature has been discussed
at length at least twice (most recently in March-April 2001 on
gdb-patches).  I would like to get some feedback on my general
approach before I do the polishing.

For the user the feature looks like this:

    gdb --args program arg1 arg2 ... argN

Actually, --args can appear anywhere:

    gdb program --args arg1 arg2 ... argN

Note that one idea that was discussed, namely using `--' instead of
`--args' here, cannot work due to how command-line processing is
done.  There is no visible difference to gdb in these command lines:

    gdb program -- corefile
    gdb program corefile

That is why I introduced --args.


I added a new target method which is given an argc/argv vector and
which then returns a string properly quoted for the target.  For Unix
systems this means quoting shell metacharacters.  I didn't do anything
for other systems (yet -- I'll need help anyway).

When processing the `--args' command line we simply stash the
arguments in infcmd.c.  We can't properly construct a command-line
string from the argument vector until the target has been set.

Finally I changed get_inferior_args() to compute the real arguments
when appropriate.

I've appended my current untested patch (it compiles, but I haven't
started testing/debugging yet).


What I'm looking for is some assurance that this approach isn't just a
waste of time.  Also, advice on nonobvious things is useful -- for
instance, do I need to edit every target_ops structure?  My plan right
now is to only change those which set to_create_inferior.

If this is considered the best approach, I'll finish the polishing:
adding texinfo, --help output, and fixing the many target_ops.

Thanks,
Tom


Index: ChangeLog
from  Tom Tromey  <tromey@redhat.com>

	* infcmd.c (_initialize_infcmd): Set sfunc on `set args' command.
	(inferior_argc, inferior_argv): New globals.
	(notice_args_set): New function.
	(set_inferior_args): Clear inferior_argc and inferior_argv.
	(set_inferior_args_vector): New function.
	(get_inferior_args): Handle inferior argument vector.
	* command.h: Typo fix.
	* main.c (captured_main): Added --args option.
	* inferior.h (construct_inferior_arguments): Declare.
	* fork-child.c (construct_inferior_arguments): New function.
	* inftarg.c (child_construct_inferior_arguments): New function.
	(init_child_ops): Use it.
	* exec.c (init_exec_ops): Initialize
	to_construct_inferior_arguments.
	* corelow.c (init_core_ops): Initialize
	to_construct_inferior_arguments.
	* target.c (find_default_construct_inferior_arguments): New
	function.
	* target.h (target_ops): Added to_construct_inferior_arguments.
	(target_construct_inferior_arguments): New macro.
	(find_default_construct_inferior_arguments): Declare.

Index: command.h
===================================================================
RCS file: /cvs/src/src/gdb/command.h,v
retrieving revision 1.18
diff -u -r1.18 command.h
--- command.h 2001/07/16 14:46:34 1.18
+++ command.h 2001/09/28 03:55:03
@@ -134,7 +134,7 @@
 	/* If type is not_set_cmd, call it like this:  */
 	void (*cfunc) (char *args, int from_tty);
 
-	/* If type is cmd_set or show_cmd, first set the variables, and
+	/* If type is set_cmd or show_cmd, first set the variables, and
 	   then call this.  */
 	void (*sfunc) (char *args, int from_tty, struct cmd_list_element * c);
       }
Index: corelow.c
===================================================================
RCS file: /cvs/src/src/gdb/corelow.c,v
retrieving revision 1.17
diff -u -r1.17 corelow.c
--- corelow.c 2001/05/15 00:03:36 1.17
+++ corelow.c 2001/09/28 03:55:03
@@ -511,6 +511,7 @@
   core_ops.to_insert_breakpoint = ignore;
   core_ops.to_remove_breakpoint = ignore;
   core_ops.to_create_inferior = find_default_create_inferior;
+  core_ops.to_construct_inferior_arguments = find_default_construct_inferior_arguments;
   core_ops.to_clone_and_follow_inferior = find_default_clone_and_follow_inferior;
   core_ops.to_thread_alive = core_file_thread_alive;
   core_ops.to_stratum = core_stratum;
Index: exec.c
===================================================================
RCS file: /cvs/src/src/gdb/exec.c,v
retrieving revision 1.13
diff -u -r1.13 exec.c
--- exec.c 2001/03/22 23:58:37 1.13
+++ exec.c 2001/09/28 03:55:04
@@ -706,6 +706,7 @@
   exec_ops.to_insert_breakpoint = ignore;
   exec_ops.to_remove_breakpoint = ignore;
   exec_ops.to_create_inferior = find_default_create_inferior;
+  exec_ops.to_construct_inferior_arguments = find_default_construct_inferior_arguments;
   exec_ops.to_clone_and_follow_inferior = find_default_clone_and_follow_inferior;
   exec_ops.to_stratum = file_stratum;
   exec_ops.to_has_memory = 1;
Index: fork-child.c
===================================================================
RCS file: /cvs/src/src/gdb/fork-child.c,v
retrieving revision 1.13
diff -u -r1.13 fork-child.c
--- fork-child.c 2001/05/04 04:15:24 1.13
+++ fork-child.c 2001/09/28 03:55:04
@@ -570,3 +570,74 @@
 #endif /* STARTUP_INFERIOR */
   stop_soon_quietly = 0;
 }
+
+/* Compute command-line string given argument vector.  This does the
+   same shell processing as fork_inferior.  */
+
+char *
+construct_inferior_arguments (int argc, char **argv)
+{
+  char *result;
+
+  if (STARTUP_WITH_SHELL)
+    {
+      /* This holds all the characters considered special to the
+	 typical Unix shells.  We include `^' because the SunOS
+	 /bin/sh treats it as a synonym for `|'.  */
+      char *special = "\"!#$&*()\\|[]{}<>?'\"`~^; \t\n";
+      int i;
+      int length = 0;
+      char *out, *cp;
+
+      /* We over-compute the size.  It shouldn't matter.  */
+      for (i = 0; i < argc; ++i)
+	length += 2 * strlen (argv[i]) + 1;
+
+      result = (char *) xmalloc (length);
+      out = result;
+
+      for (i = 0; i < argc; ++i)
+	{
+	  if (i > 0)
+	    *out++ = ' ';
+
+	  for (cp = argv[i]; *cp; ++cp)
+	    {
+	      if (strchr (special, *cp) != NULL)
+		*out++ = '\\';
+	      *out++ = *cp;
+	    }
+	}
+      *out = '\0';
+    }
+  else
+    {
+      /* In this case we can't handle arguments that contain spaces,
+	 tabs, or newlines -- see breakup_args().  */
+      int i;
+      int length = 0;
+
+      for (i = 0; i < argc; ++i)
+	{
+	  char *cp = strchr (argv[i], ' ');
+	  if (cp == NULL)
+	    cp = strchr (argv[i], '\t');
+	  if (cp == NULL)
+	    cp = strchr (argv[i], '\n');
+	  if (cp != NULL)
+	    error ("can't handle command-line argument containing whitespace");
+	  length += strlen (argv[i]) + 1;
+	}
+
+      result = (char *) xmalloc (length);
+      result[0] = '\0';
+      for (i = 0; i < argc; ++i)
+	{
+	  if (i > 0)
+	    strcat (result, " ");
+	  strcat (result, argv[i]);
+	}
+    }
+
+  return result;
+}
Index: infcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/infcmd.c,v
retrieving revision 1.30
diff -u -r1.30 infcmd.c
--- infcmd.c 2001/08/02 11:58:29 1.30
+++ infcmd.c 2001/09/28 03:55:06
@@ -123,6 +123,12 @@
 
 static char *inferior_args;
 
+/* The inferior arguments as a vector.  If INFERIOR_ARGC is nonzero,
+   then we must compute INFERIOR_ARGS from this (via the target).  */
+
+static int inferior_argc;
+static char **inferior_argv;
+
 /* File name for default use for standard in/out in the inferior.  */
 
 char *inferior_io_terminal;
@@ -199,6 +205,14 @@
 char *
 get_inferior_args (void)
 {
+  if (inferior_argc != 0)
+    {
+      char *n = find_default_construct_inferior_arguments (inferior_argc,
+							   inferior_argv);
+      char *old = set_inferior_args (n);
+      xfree (old);
+    }
+
   return inferior_args;
 }
 
@@ -208,10 +222,27 @@
   char *saved_args = inferior_args;
 
   inferior_args = newargs;
+  inferior_argc = 0;
+  inferior_argv = 0;
 
   return saved_args;
 }
 
+void
+set_inferior_args_vector (int argc, char **argv)
+{
+  inferior_argc = argc;
+  inferior_argv = argv;
+}
+
+/* Notice when `set args' is run.  */
+static void
+notice_args_set (char *args, int from_tty, struct cmd_list_element *c)
+{
+  inferior_argc = 0;
+  inferior_argv = 0;
+}
+
 /* This function detects whether or not a '&' character (indicating
    background execution) has been added as *the last* of the arguments ARGS
    of a command. If it has, it removes it and returns 1. Otherwise it
@@ -331,7 +362,9 @@
       if (exec_file)
 	ui_out_field_string (uiout, "execfile", exec_file);
       ui_out_spaces (uiout, 1);
-      ui_out_field_string (uiout, "infargs", inferior_args);
+      /* We call get_inferior_args() because we might need to compute
+	 the value now.  */
+      ui_out_field_string (uiout, "infargs", get_inferior_args ());
       ui_out_text (uiout, "\n");
       ui_out_flush (uiout);
 #else
@@ -339,13 +372,17 @@
       if (exec_file)
 	puts_filtered (exec_file);
       puts_filtered (" ");
-      puts_filtered (inferior_args);
+      /* We call get_inferior_args() because we might need to compute
+	 the value now.  */
+      puts_filtered (get_inferior_args ());
       puts_filtered ("\n");
       gdb_flush (gdb_stdout);
 #endif
     }
 
-  target_create_inferior (exec_file, inferior_args,
+  /* We call get_inferior_args() because we might need to compute
+     the value now.  */
+  target_create_inferior (exec_file, get_inferior_args (),
 			  environ_vector (inferior_environ));
 }
 
@@ -1806,6 +1843,7 @@
 		   &setlist);
   add_show_from_set (c, &showlist);
   c->completer = filename_completer;
+  c->function.sfunc = notice_args_set;
 
   c = add_cmd
     ("environment", no_class, environment_info,
Index: inferior.h
===================================================================
RCS file: /cvs/src/src/gdb/inferior.h,v
retrieving revision 1.23
diff -u -r1.23 inferior.h
--- inferior.h 2001/05/15 00:03:36 1.23
+++ inferior.h 2001/09/28 03:55:07
@@ -270,6 +270,8 @@
 
 extern void startup_inferior (int);
 
+extern char *construct_inferior_arguments (int, char **);
+
 /* From inflow.c */
 
 extern void new_tty_prefork (char *);
@@ -306,6 +308,8 @@
 extern char *get_inferior_args (void);
 
 extern char *set_inferior_args (char *);
+
+extern void set_inferior_args_vector (int, char **);
 
 /* Last signal that the inferior received (why it stopped).  */
 
Index: inftarg.c
===================================================================
RCS file: /cvs/src/src/gdb/inftarg.c,v
retrieving revision 1.7
diff -u -r1.7 inftarg.c
--- inftarg.c 2001/05/04 04:15:25 1.7
+++ inftarg.c 2001/09/28 03:55:07
@@ -80,6 +80,8 @@
 
 static void child_create_inferior (char *, char *, char **);
 
+static char *child_construct_inferior_arguments (int, char **);
+
 static void child_mourn_inferior (void);
 
 static int child_can_run (void);
@@ -473,6 +475,21 @@
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0);
 }
 
+/* Given a vector of command-line arguments, return a newly allocated
+   string which, when passed to the create_inferior function, will be
+   parsed to yield the same vector.  Return NULL if this target does
+   not support command-line arguments.  This function should call
+   error() if the argument vector is not representable for this
+   target.
+   ARGC is the number of elements in the vector.
+   ARGV is an array of strings, one per argument.  */
+
+static char *
+child_construct_inferior_arguments (int argc, char **argv)
+{
+  return construct_inferior_arguments (argc, argv);
+}
+
 #if !defined(CHILD_POST_STARTUP_INFERIOR)
 void
 child_post_startup_inferior (ptid_t ptid)
@@ -777,6 +794,7 @@
   child_ops.to_terminal_info = child_terminal_info;
   child_ops.to_kill = kill_inferior;
   child_ops.to_create_inferior = child_create_inferior;
+  child_ops.to_construct_inferior_arguments = child_construct_inferior_arguments;
   child_ops.to_post_startup_inferior = child_post_startup_inferior;
   child_ops.to_acknowledge_created_inferior = child_acknowledge_created_inferior;
   child_ops.to_clone_and_follow_inferior = child_clone_and_follow_inferior;
Index: main.c
===================================================================
RCS file: /cvs/src/src/gdb/main.c,v
retrieving revision 1.12
diff -u -r1.12 main.c
--- main.c 2001/07/14 18:59:07 1.12
+++ main.c 2001/09/28 03:55:08
@@ -125,6 +125,7 @@
   int count;
   static int quiet = 0;
   static int batch = 0;
+  static int set_args = 0;
 
   /* Pointers to various arguments from command line.  */
   char *symarg = NULL;
@@ -264,6 +265,7 @@
       {"windows", no_argument, &use_windows, 1},
       {"statistics", no_argument, 0, 13},
       {"write", no_argument, &write_files, 1},
+      {"args", no_argument, &set_args, 1},
 /* Allow machine descriptions to add more options... */
 #ifdef ADDITIONAL_OPTIONS
       ADDITIONAL_OPTIONS
@@ -275,6 +277,11 @@
       {
 	int option_index;
 
+	/* If SET_ARGS is true, then the last option we processed was
+	   --args, and so it is time to stop the loop.  */
+	if (set_args)
+	  break;
+
 	c = getopt_long_only (argc, argv, "",
 			      long_options, &option_index);
 	if (c == EOF)
@@ -446,25 +453,46 @@
       use_windows = 0;
 #endif
 
-    /* OK, that's all the options.  The other arguments are filenames.  */
-    count = 0;
-    for (; optind < argc; optind++)
-      switch (++count)
-	{
-	case 1:
-	  symarg = argv[optind];
-	  execarg = argv[optind];
-	  break;
-	case 2:
-	  /* FIXME: The documentation says this can be a "ProcID". as well. */
-	  corearg = argv[optind];
-	  break;
-	case 3:
-	  fprintf_unfiltered (gdb_stderr,
-			  "Excess command line arguments ignored. (%s%s)\n",
-			  argv[optind], (optind == argc - 1) ? "" : " ...");
-	  break;
-	}
+    if (set_args)
+      {
+	/* The remaining options are the command-line options for the
+	   inferior.  The first one is the sym/exec file, and the rest
+	   are arguments.  */
+	if (optind >= argc)
+	  {
+	    fprintf_unfiltered (gdb_stderr,
+				"%s: `--args' specified but no program specified\n",
+				argv[0]);
+	    exit (1);
+	  }
+	symarg = argv[optind];
+	execarg = argv[optind];
+	++optind;
+	set_inferior_args_vector (argc - optind, &argv[optind]);
+      }
+    else
+      {
+	/* OK, that's all the options.  The other arguments are filenames.  */
+	count = 0;
+	for (; optind < argc; optind++)
+	  switch (++count)
+	    {
+	    case 1:
+	      symarg = argv[optind];
+	      execarg = argv[optind];
+	      break;
+	    case 2:
+	      /* FIXME: The documentation says this can be a
+		 "ProcID". as well.  */
+	      corearg = argv[optind];
+	      break;
+	    case 3:
+	      fprintf_unfiltered (gdb_stderr,
+				  "Excess command line arguments ignored. (%s%s)\n",
+				  argv[optind], (optind == argc - 1) ? "" : " ...");
+	      break;
+	    }
+      }
     if (batch)
       quiet = 1;
   }
Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.28
diff -u -r1.28 target.c
--- target.c 2001/07/19 18:09:11 1.28
+++ target.c 2001/09/28 03:55:09
@@ -1195,6 +1195,16 @@
   return;
 }
 
+char *
+find_default_construct_inferior_arguments (int argc, char **argv)
+{
+  struct target_ops *t;
+  char *result;
+
+  t = find_default_run_target ("run");
+  return (t->to_construct_inferior_arguments) (argc, argv);
+}
+
 void
 find_default_clone_and_follow_inferior (int child_pid, int *followed_child)
 {
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.20
diff -u -r1.20 target.h
--- target.h 2001/08/11 00:59:29 1.20
+++ target.h 2001/09/28 03:55:10
@@ -261,6 +261,7 @@
     void (*to_load) (char *, int);
     int (*to_lookup_symbol) (char *, CORE_ADDR *);
     void (*to_create_inferior) (char *, char *, char **);
+    char *(*to_construct_inferior_arguments) (int, char **);
     void (*to_post_startup_inferior) (ptid_t);
     void (*to_acknowledge_created_inferior) (int);
     void (*to_clone_and_follow_inferior) (int, int *);
@@ -649,7 +650,18 @@
 #define	target_create_inferior(exec_file, args, env)	\
      (*current_target.to_create_inferior) (exec_file, args, env)
 
+/* Given a vector of command-line arguments, return a newly allocated
+   string which, when passed to the create_inferior function, will be
+   parsed to yield the same vector.  Return NULL if this target does
+   not support command-line arguments.  This function should call
+   error() if the argument vector is not representable for this
+   target.
+   ARGC is the number of elements in the vector.
+   ARGV is an array of strings, one per argument.  */
 
+#define target_construct_inferior_arguments(argc, argv) \
+     (*current_target.to_construct_inferior_arguments) (argc, argv)
+
 /* Some targets (such as ttrace-based HPUX) don't allow us to request
    notification of inferior events such as fork and vork immediately
    after the inferior is created.  (This because of how gdb gets an
@@ -1188,6 +1200,8 @@
 extern void find_default_require_detach (int, char *, int);
 
 extern void find_default_create_inferior (char *, char *, char **);
+
+extern char *find_default_construct_inferior_arguments (int, char **);
 
 extern void find_default_clone_and_follow_inferior (int, int *);
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]