+2017-06-20 Sergio Durigan Junior <sergiodj@redhat.com>
+
+ * Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
+ 'unittests/environ-selftests.c'.
+ (SUBDIR_UNITTESTS_OBS): Add 'environ-selftests.o'.
+ * charset.c (find_charset_names): Declare object 'iconv_env'.
+ Update code to use 'iconv_env' object. Remove call to
+ 'free_environ'.
+ * common/environ.c: Include <utility>.
+ (make_environ): Delete function.
+ (free_environ): Delete function.
+ (gdb_environ::clear): New function.
+ (gdb_environ::operator=): New function.
+ (gdb_environ::get): Likewise.
+ (environ_vector): Delete function.
+ (set_in_environ): Delete function.
+ (gdb_environ::set): New function.
+ (unset_in_environ): Delete function.
+ (gdb_environ::unset): New function.
+ (gdb_environ::envp): Likewise.
+ * common/environ.h: Include <vector>.
+ (struct gdb_environ): Delete; transform into...
+ (class gdb_environ): ... this class.
+ (free_environ): Delete prototype.
+ (init_environ, get_in_environ, set_in_environ, unset_in_environ,
+ environ_vector): Likewise.
+ * infcmd.c (run_command_1): Update code to call
+ 'envp' from 'gdb_environ' class.
+ (environment_info): Update code to call methods from 'gdb_environ'
+ class.
+ (unset_environment_command): Likewise.
+ (path_info): Likewise.
+ (path_command): Likewise.
+ * inferior.c (inferior::~inferior): Delete call to 'free_environ'.
+ (inferior::inferior): Initialize 'environment' using the host's
+ information.
+ * inferior.h: Remove forward declaration of 'struct gdb_environ'.
+ Include "environ.h".
+ (class inferior) <environment>: Change type from 'struct
+ gdb_environ' to 'gdb_environ'.
+ * mi/mi-cmd-env.c (mi_cmd_env_path): Update code to call
+ methods from 'gdb_environ' class.
+ * solib.c (solib_find_1): Likewise
+ * unittests/environ-selftests.c: New file.
+
2017-06-20 Yao Qi <yao.qi@linaro.org>
* features/i386/i386-linux.xml: Exchange the order of including
SUBDIR_PYTHON_CFLAGS =
SUBDIR_UNITTESTS_SRCS = \
+ unittests/environ-selftests.c \
unittests/function-view-selftests.c \
unittests/offset-type-selftests.c \
unittests/optional-selftests.c \
unittests/scoped_restore-selftests.c
SUBDIR_UNITTESTS_OBS = \
+ environ-selftests.o \
function-view-selftests.o \
offset-type-selftests.o \
optional-selftests.o \
int err, status;
int fail = 1;
int flags;
- struct gdb_environ *iconv_env;
+ gdb_environ iconv_env = gdb_environ::from_host_environ ();
char *iconv_program;
/* Older iconvs, e.g. 2.2.2, don't omit the intro text if stdout is
not a tty. We need to recognize it and ignore it. This text is
subject to translation, so force LANGUAGE=C. */
- iconv_env = make_environ ();
- init_environ (iconv_env);
- set_in_environ (iconv_env, "LANGUAGE", "C");
- set_in_environ (iconv_env, "LC_ALL", "C");
+ iconv_env.set ("LANGUAGE", "C");
+ iconv_env.set ("LC_ALL", "C");
child = pex_init (PEX_USE_PIPES, "iconv", NULL);
/* Note that we simply ignore errors here. */
if (!pex_run_in_environment (child, flags,
args[0], const_cast<char **> (args),
- environ_vector (iconv_env),
+ iconv_env.envp (),
NULL, NULL, &err))
{
FILE *in = pex_read_output (child, 0);
xfree (iconv_program);
pex_free (child);
- free_environ (iconv_env);
if (fail)
{
#include "common-defs.h"
#include "environ.h"
#include <algorithm>
-\f
+#include <utility>
-/* Return a new environment object. */
+/* See common/environ.h. */
-struct gdb_environ *
-make_environ (void)
+gdb_environ &
+gdb_environ::operator= (gdb_environ &&e)
{
- struct gdb_environ *e;
-
- e = XNEW (struct gdb_environ);
-
- e->allocated = 10;
- e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *));
- e->vector[0] = 0;
- return e;
+ /* Are we self-moving? */
+ if (&e == this)
+ return *this;
+
+ m_environ_vector = std::move (e.m_environ_vector);
+ e.m_environ_vector.clear ();
+ e.m_environ_vector.push_back (NULL);
+ return *this;
}
-/* Free an environment and all the strings in it. */
+/* See common/environ.h. */
-void
-free_environ (struct gdb_environ *e)
-{
- char **vector = e->vector;
-
- while (*vector)
- xfree (*vector++);
-
- xfree (e->vector);
- xfree (e);
-}
-
-/* Copy the environment given to this process into E.
- Also copies all the strings in it, so we can be sure
- that all strings in these environments are safe to free. */
-
-void
-init_environ (struct gdb_environ *e)
+gdb_environ gdb_environ::from_host_environ ()
{
extern char **environ;
- int i;
+ gdb_environ e;
if (environ == NULL)
- return;
-
- for (i = 0; environ[i]; i++) /*EMPTY */ ;
+ return e;
- if (e->allocated < i)
+ for (int i = 0; environ[i] != NULL; ++i)
{
- e->allocated = std::max (i, e->allocated + 10);
- e->vector = (char **) xrealloc ((char *) e->vector,
- (e->allocated + 1) * sizeof (char *));
+ /* Make sure we add the element before the last (NULL). */
+ e.m_environ_vector.insert (e.m_environ_vector.end () - 1,
+ xstrdup (environ[i]));
}
- memcpy (e->vector, environ, (i + 1) * sizeof (char *));
+ return e;
+}
- while (--i >= 0)
- {
- int len = strlen (e->vector[i]);
- char *newobj = (char *) xmalloc (len + 1);
+/* See common/environ.h. */
- memcpy (newobj, e->vector[i], len + 1);
- e->vector[i] = newobj;
- }
+void
+gdb_environ::clear ()
+{
+ for (char *v : m_environ_vector)
+ xfree (v);
+ m_environ_vector.clear ();
+ /* Always add the NULL element. */
+ m_environ_vector.push_back (NULL);
}
-/* Return the vector of environment E.
- This is used to get something to pass to execve. */
+/* Helper function to check if STRING contains an environment variable
+ assignment of VAR, i.e., if STRING starts with 'VAR='. Return true
+ if it contains, false otherwise. */
-char **
-environ_vector (struct gdb_environ *e)
+static bool
+match_var_in_string (char *string, const char *var, size_t var_len)
{
- return e->vector;
+ if (strncmp (string, var, var_len) == 0 && string[var_len] == '=')
+ return true;
+
+ return false;
}
-\f
-/* Return the value in environment E of variable VAR. */
-char *
-get_in_environ (const struct gdb_environ *e, const char *var)
+/* See common/environ.h. */
+
+const char *
+gdb_environ::get (const char *var) const
{
- int len = strlen (var);
- char **vector = e->vector;
- char *s;
+ size_t len = strlen (var);
- for (; (s = *vector) != NULL; vector++)
- if (strncmp (s, var, len) == 0 && s[len] == '=')
- return &s[len + 1];
+ for (char *el : m_environ_vector)
+ if (el != NULL && match_var_in_string (el, var, len))
+ return &el[len + 1];
- return 0;
+ return NULL;
}
-/* Store the value in E of VAR as VALUE. */
+/* See common/environ.h. */
void
-set_in_environ (struct gdb_environ *e, const char *var, const char *value)
+gdb_environ::set (const char *var, const char *value)
{
- int i;
- int len = strlen (var);
- char **vector = e->vector;
- char *s;
+ /* We have to unset the variable in the vector if it exists. */
+ unset (var);
- for (i = 0; (s = vector[i]) != NULL; i++)
- if (strncmp (s, var, len) == 0 && s[len] == '=')
- break;
-
- if (s == 0)
- {
- if (i == e->allocated)
- {
- e->allocated += 10;
- vector = (char **) xrealloc ((char *) vector,
- (e->allocated + 1) * sizeof (char *));
- e->vector = vector;
- }
- vector[i + 1] = 0;
- }
- else
- xfree (s);
-
- s = (char *) xmalloc (len + strlen (value) + 2);
- strcpy (s, var);
- strcat (s, "=");
- strcat (s, value);
- vector[i] = s;
-
- /* This used to handle setting the PATH and GNUTARGET variables
- specially. The latter has been replaced by "set gnutarget"
- (which has worked since GDB 4.11). The former affects searching
- the PATH to find SHELL, and searching the PATH to find the
- argument of "symbol-file" or "exec-file". Maybe we should have
- some kind of "set exec-path" for that. But in any event, having
- "set env" affect anything besides the inferior is a bad idea.
- What if we want to change the environment we pass to the program
- without afecting GDB's behavior? */
-
- return;
+ /* Insert the element before the last one, which is always NULL. */
+ m_environ_vector.insert (m_environ_vector.end () - 1,
+ concat (var, "=", value, NULL));
}
-/* Remove the setting for variable VAR from environment E. */
+/* See common/environ.h. */
void
-unset_in_environ (struct gdb_environ *e, const char *var)
+gdb_environ::unset (const char *var)
{
- int len = strlen (var);
- char **vector = e->vector;
- char *s;
+ size_t len = strlen (var);
+
+ /* We iterate until '.cend () - 1' because the last element is
+ always NULL. */
+ for (std::vector<char *>::const_iterator el = m_environ_vector.cbegin ();
+ el != m_environ_vector.cend () - 1;
+ ++el)
+ if (match_var_in_string (*el, var, len))
+ {
+ xfree (*el);
+ m_environ_vector.erase (el);
+ break;
+ }
+}
- for (; (s = *vector) != NULL; vector++)
- {
- if (strncmp (s, var, len) == 0 && s[len] == '=')
- {
- xfree (s);
- /* Walk through the vector, shuffling args down by one, including
- the NULL terminator. Can't use memcpy() here since the regions
- overlap, and memmove() might not be available. */
- while ((vector[0] = vector[1]) != NULL)
- {
- vector++;
- }
- break;
- }
- }
+/* See common/environ.h. */
+
+char **
+gdb_environ::envp () const
+{
+ return const_cast<char **> (&m_environ_vector[0]);
}
#if !defined (ENVIRON_H)
#define ENVIRON_H 1
-/* We manipulate environments represented as these structures. */
+#include <vector>
-struct gdb_environ
+/* Class that represents the environment variables as seen by the
+ inferior. */
+
+class gdb_environ
+{
+public:
+ /* Regular constructor and destructor. */
+ gdb_environ ()
+ {
+ /* Make sure that the vector contains at least a NULL element.
+ If/when we add more variables to it, NULL will always be the
+ last element. */
+ m_environ_vector.push_back (NULL);
+ }
+
+ ~gdb_environ ()
{
- /* Number of usable slots allocated in VECTOR.
- VECTOR always has one slot not counted here,
- to hold the terminating zero. */
- int allocated;
- /* A vector of slots, ALLOCATED + 1 of them.
- The first few slots contain strings "VAR=VALUE"
- and the next one contains zero.
- Then come some unused slots. */
- char **vector;
- };
+ clear ();
+ }
+
+ /* Move constructor. */
+ gdb_environ (gdb_environ &&e)
+ : m_environ_vector (std::move (e.m_environ_vector))
+ {
+ /* Make sure that the moved-from vector is left at a valid
+ state (only one NULL element). */
+ e.m_environ_vector.clear ();
+ e.m_environ_vector.push_back (NULL);
+ }
+
+ /* Move assignment. */
+ gdb_environ &operator= (gdb_environ &&e);
-extern struct gdb_environ *make_environ (void);
+ /* Create a gdb_environ object using the host's environment
+ variables. */
+ static gdb_environ from_host_environ ();
-extern void free_environ (struct gdb_environ *);
+ /* Clear the environment variables stored in the object. */
+ void clear ();
-extern void init_environ (struct gdb_environ *);
+ /* Return the value in the environment for the variable VAR. The
+ returned pointer is only valid as long as the gdb_environ object
+ is not modified. */
+ const char *get (const char *var) const;
-extern char *get_in_environ (const struct gdb_environ *, const char *);
+ /* Store VAR=VALUE in the environment. */
+ void set (const char *var, const char *value);
-extern void set_in_environ (struct gdb_environ *, const char *, const char *);
+ /* Unset VAR in environment. */
+ void unset (const char *var);
-extern void unset_in_environ (struct gdb_environ *, const char *);
+ /* Return the environment vector represented as a 'char **'. */
+ char **envp () const;
-extern char **environ_vector (struct gdb_environ *);
+private:
+ /* A vector containing the environment variables. */
+ std::vector<char *> m_environ_vector;
+};
#endif /* defined (ENVIRON_H) */
+2017-06-20 Sergio Durigan Junior <sergiodj@redhat.com>
+
+ * linux-low.c (linux_create_inferior): Adjust code to access the
+ environment information via 'gdb_environ' class.
+ * lynx-low.c (lynx_create_inferior): Likewise.
+ * server.c (our_environ): Make it an instance of 'gdb_environ'.
+ (get_environ): Return a pointer to 'our_environ'.
+ (captured_main): Initialize 'our_environ'.
+ * server.h (get_environ): Adjust prototype.
+ * spu-low.c (spu_create_inferior): Adjust code to access the
+ environment information via 'gdb_environ' class.
+
2017-06-17 Simon Marchi <simon.marchi@ericsson.com>
* linux-low.c (linux_read_memory, linux_write_memory): Remove
pid = fork_inferior (program,
str_program_args.c_str (),
- environ_vector (get_environ ()), linux_ptrace_fun,
+ get_environ ()->envp (), linux_ptrace_fun,
NULL, NULL, NULL, NULL);
do_cleanups (restore_personality);
pid = fork_inferior (program,
str_program_args.c_str (),
- environ_vector (get_environ ()), lynx_ptrace_fun,
+ get_environ ()->envp (), lynx_ptrace_fun,
NULL, NULL, NULL, NULL);
post_fork_inferior (pid, program);
/* The environment to pass to the inferior when creating it. */
-struct gdb_environ *our_environ = NULL;
+static gdb_environ our_environ;
/* Start the inferior using a shell. */
/* See server.h. */
-struct gdb_environ *
+gdb_environ *
get_environ ()
{
- return our_environ;
+ return &our_environ;
}
static int
}
/* Gather information about the environment. */
- our_environ = make_environ ();
- init_environ (our_environ);
+ our_environ = gdb_environ::from_host_environ ();
initialize_async_io ();
initialize_low ();
#include "mem-break.h"
#include "gdbthread.h"
#include "inferiors.h"
+#include "environ.h"
/* Target-specific functions */
inferior and PROGRAM is its name. */
extern void post_fork_inferior (int pid, const char *program);
-/* Get the 'struct gdb_environ *' being used in the current
- session. */
-extern struct gdb_environ *get_environ ();
+/* Get the gdb_environ being used in the current session. */
+extern gdb_environ *get_environ ();
extern target_waitstatus last_status;
extern ptid_t last_ptid;
pid = fork_inferior (program,
str_program_args.c_str (),
- environ_vector (get_environ ()), spu_ptrace_fun,
+ get_environ ()->envp (), spu_ptrace_fun,
NULL, NULL, NULL, NULL);
post_fork_inferior (pid, program);
the value now. */
run_target->to_create_inferior (run_target, exec_file,
std::string (get_inferior_args ()),
- environ_vector (current_inferior ()->environment),
+ current_inferior ()->environment.envp (),
from_tty);
/* to_create_inferior should push the target, so after this point we
shouldn't refer to run_target again. */
{
if (var)
{
- char *val = get_in_environ (current_inferior ()->environment, var);
+ const char *val = current_inferior ()->environment.get (var);
if (val)
{
}
else
{
- char **vector = environ_vector (current_inferior ()->environment);
+ char **envp = current_inferior ()->environment.envp ();
- while (*vector)
+ for (int idx = 0; envp[idx] != NULL; ++idx)
{
- puts_filtered (*vector++);
+ puts_filtered (envp[idx]);
puts_filtered ("\n");
}
}
printf_filtered (_("Setting environment variable "
"\"%s\" to null value.\n"),
var);
- set_in_environ (current_inferior ()->environment, var, "");
+ current_inferior ()->environment.set (var, "");
}
else
- set_in_environ (current_inferior ()->environment, var, val);
+ current_inferior ()->environment.set (var, val);
xfree (var);
}
/* If there is no argument, delete all environment variables.
Ask for confirmation if reading from the terminal. */
if (!from_tty || query (_("Delete all environment variables? ")))
- {
- free_environ (current_inferior ()->environment);
- current_inferior ()->environment = make_environ ();
- }
+ current_inferior ()->environment = gdb_environ::from_host_environ ();
}
else
- unset_in_environ (current_inferior ()->environment, var);
+ current_inferior ()->environment.unset (var);
}
/* Handle the execution path (PATH variable). */
path_info (char *args, int from_tty)
{
puts_filtered ("Executable and object file path: ");
- puts_filtered (get_in_environ (current_inferior ()->environment,
- path_var_name));
+ puts_filtered (current_inferior ()->environment.get (path_var_name));
puts_filtered ("\n");
}
const char *env;
dont_repeat ();
- env = get_in_environ (current_inferior ()->environment, path_var_name);
+ env = current_inferior ()->environment.get (path_var_name);
/* Can be null if path is not set. */
if (!env)
env = "";
exec_path = xstrdup (env);
mod_path (dirname, &exec_path);
- set_in_environ (current_inferior ()->environment, path_var_name, exec_path);
+ current_inferior ()->environment.set (path_var_name, exec_path);
xfree (exec_path);
if (from_tty)
path_info ((char *) NULL, from_tty);
inferior_free_data (inf);
xfree (inf->args);
xfree (inf->terminal);
- free_environ (inf->environment);
target_desc_info_free (inf->tdesc_info);
xfree (inf->priv);
}
inferior::inferior (int pid_)
: num (++highest_inferior_num),
pid (pid_),
- environment (make_environ ()),
+ environment (gdb_environ::from_host_environ ()),
registry_data ()
{
- init_environ (this->environment);
inferior_alloc_data (this);
}
struct ui_out;
struct terminal_info;
struct target_desc_info;
-struct gdb_environ;
struct continuation;
struct inferior;
/* For struct frame_id. */
#include "frame.h"
+/* For gdb_environ. */
+#include "environ.h"
+
#include "progspace.h"
#include "registry.h"
/* Environment to use for running inferior,
in format described in environ.h. */
- gdb_environ *environment = NULL;
+ gdb_environ environment;
/* True if this child process was attached rather than forked. */
bool attach_flag = false;
else
{
/* Otherwise, get current path to modify. */
- env = get_in_environ (current_inferior ()->environment, path_var_name);
+ env = current_inferior ()->environment.get (path_var_name);
/* Can be null if path is not set. */
if (!env)
for (i = argc - 1; i >= 0; --i)
env_mod_path (argv[i], &exec_path);
- set_in_environ (current_inferior ()->environment, path_var_name, exec_path);
+ current_inferior ()->environment.set (path_var_name, exec_path);
xfree (exec_path);
- env = get_in_environ (current_inferior ()->environment, path_var_name);
+ env = current_inferior ()->environment.get (path_var_name);
uiout->field_string ("path", env);
}
/* If not found, next search the inferior's $PATH environment variable. */
if (found_file < 0 && sysroot == NULL)
- found_file = openp (get_in_environ (current_inferior ()->environment,
- "PATH"),
+ found_file = openp (current_inferior ()->environment.get ("PATH"),
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH, in_pathname,
O_RDONLY | O_BINARY, &temp_pathname);
/* If not found, and we're looking for a solib, next search the
inferior's $LD_LIBRARY_PATH environment variable. */
if (is_solib && found_file < 0 && sysroot == NULL)
- found_file = openp (get_in_environ (current_inferior ()->environment,
- "LD_LIBRARY_PATH"),
+ found_file = openp (current_inferior ()->environment.get
+ ("LD_LIBRARY_PATH"),
OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH, in_pathname,
O_RDONLY | O_BINARY, &temp_pathname);
--- /dev/null
+/* Self tests for gdb_environ for GDB, the GNU debugger.
+
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "selftest.h"
+#include "common/environ.h"
+
+namespace selftests {
+namespace gdb_environ_tests {
+
+static void
+run_tests ()
+{
+ /* Set a test environment variable. This will be unset at the end
+ of this function. */
+ if (setenv ("GDB_SELFTEST_ENVIRON", "1", 1) != 0)
+ error (_("Could not set environment variable for testing."));
+
+ gdb_environ env;
+
+ /* When the vector is initialized, there should always be one NULL
+ element in it. */
+ SELF_CHECK (env.envp ()[0] == NULL);
+
+ /* Make sure that there is no other element. */
+ SELF_CHECK (env.get ("PWD") == NULL);
+
+ /* Check if unset followed by a set in an empty vector works. */
+ env.set ("PWD", "test");
+ SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0);
+ /* The second element must be NULL. */
+ SELF_CHECK (env.envp ()[1] == NULL);
+ env.unset ("PWD");
+ SELF_CHECK (env.envp ()[0] == NULL);
+
+ /* Initialize the environment vector using the host's environ. */
+ env = gdb_environ::from_host_environ ();
+
+ /* Our test environment variable should be present at the
+ vector. */
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
+
+ /* Set our test variable to another value. */
+ env.set ("GDB_SELFTEST_ENVIRON", "test");
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "test") == 0);
+
+ /* And unset our test variable. The variable still exists in the
+ host's environment, but doesn't exist in our vector. */
+ env.unset ("GDB_SELFTEST_ENVIRON");
+ SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
+
+ /* Re-set the test variable. */
+ env.set ("GDB_SELFTEST_ENVIRON", "1");
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
+
+ /* When we clear our environ vector, there should be only one
+ element on it (NULL), and we shouldn't be able to get our test
+ variable. */
+ env.clear ();
+ SELF_CHECK (env.envp ()[0] == NULL);
+ SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
+
+ /* Reinitialize our environ vector using the host environ. We
+ should be able to see one (and only one) instance of the test
+ variable. */
+ env = gdb_environ::from_host_environ ();
+ char **envp = env.envp ();
+ int num_found = 0;
+
+ for (size_t i = 0; envp[i] != NULL; ++i)
+ if (strcmp (envp[i], "GDB_SELFTEST_ENVIRON=1") == 0)
+ ++num_found;
+ SELF_CHECK (num_found == 1);
+
+ /* Get rid of our test variable. */
+ unsetenv ("GDB_SELFTEST_ENVIRON");
+
+ /* Test the case when we set a variable A, then set a variable B,
+ then unset A, and make sure that we cannot find A in the environ
+ vector, but can still find B. */
+ env.set ("GDB_SELFTEST_ENVIRON_1", "aaa");
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_1"), "aaa") == 0);
+
+ env.set ("GDB_SELFTEST_ENVIRON_2", "bbb");
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
+
+ env.unset ("GDB_SELFTEST_ENVIRON_1");
+ SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON_1") == NULL);
+ SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
+
+ env.clear ();
+
+ /* Test that after a std::move the moved-from object is left at a
+ valid state (i.e., its only element is NULL). */
+ env.set ("A", "1");
+ SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
+ gdb_environ env2;
+ env2 = std::move (env);
+ SELF_CHECK (env.envp ()[0] == NULL);
+ SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
+ SELF_CHECK (env2.envp ()[1] == NULL);
+ env.set ("B", "2");
+ SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
+ SELF_CHECK (env.envp ()[1] == NULL);
+
+ /* Test that the move constructor leaves everything at a valid
+ state. */
+ env.clear ();
+ env.set ("A", "1");
+ SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
+ gdb_environ env3 = std::move (env);
+ SELF_CHECK (env.envp ()[0] == NULL);
+ SELF_CHECK (strcmp (env3.get ("A"), "1") == 0);
+ SELF_CHECK (env3.envp ()[1] == NULL);
+ env.set ("B", "2");
+ SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
+ SELF_CHECK (env.envp ()[1] == NULL);
+
+ /* Test self-move. */
+ env.clear ();
+ env.set ("A", "1");
+ SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
+ env = std::move (env);
+ SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
+ SELF_CHECK (strcmp (env.envp ()[0], "A=1") == 0);
+ SELF_CHECK (env.envp ()[1] == NULL);
+}
+} /* namespace gdb_environ */
+} /* namespace selftests */
+
+void
+_initialize_environ_selftests ()
+{
+ register_self_test (selftests::gdb_environ_tests::run_tests);
+}