diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 79d0f3c2c07..77001fadfb0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,48 @@ +2017-06-20 Sergio Durigan Junior + + * 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 . + (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 . + (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) : 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 * features/i386/i386-linux.xml: Exchange the order of including diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 153a5dd6fb8..b27f6982d4a 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -526,6 +526,7 @@ SUBDIR_PYTHON_LDFLAGS = SUBDIR_PYTHON_CFLAGS = SUBDIR_UNITTESTS_SRCS = \ + unittests/environ-selftests.c \ unittests/function-view-selftests.c \ unittests/offset-type-selftests.c \ unittests/optional-selftests.c \ @@ -533,6 +534,7 @@ SUBDIR_UNITTESTS_SRCS = \ unittests/scoped_restore-selftests.c SUBDIR_UNITTESTS_OBS = \ + environ-selftests.o \ function-view-selftests.o \ offset-type-selftests.o \ optional-selftests.o \ diff --git a/gdb/charset.c b/gdb/charset.c index dbe46a46a93..be95bbe11c5 100644 --- a/gdb/charset.c +++ b/gdb/charset.c @@ -794,16 +794,14 @@ find_charset_names (void) 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); @@ -827,7 +825,7 @@ find_charset_names (void) /* Note that we simply ignore errors here. */ if (!pex_run_in_environment (child, flags, args[0], const_cast (args), - environ_vector (iconv_env), + iconv_env.envp (), NULL, NULL, &err)) { FILE *in = pex_read_output (child, 0); @@ -901,7 +899,6 @@ find_charset_names (void) xfree (iconv_program); pex_free (child); - free_environ (iconv_env); if (fail) { diff --git a/gdb/common/environ.c b/gdb/common/environ.c index 3145d01afa5..20f8aba74b0 100644 --- a/gdb/common/environ.c +++ b/gdb/common/environ.c @@ -18,165 +18,119 @@ #include "common-defs.h" #include "environ.h" #include - +#include -/* 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; + /* Are we self-moving? */ + if (&e == this) + return *this; - e = XNEW (struct gdb_environ); + m_environ_vector = std::move (e.m_environ_vector); + e.m_environ_vector.clear (); + e.m_environ_vector.push_back (NULL); + return *this; +} + +/* See common/environ.h. */ + +gdb_environ gdb_environ::from_host_environ () +{ + extern char **environ; + gdb_environ e; + + if (environ == NULL) + return e; + + for (int i = 0; environ[i] != NULL; ++i) + { + /* Make sure we add the element before the last (NULL). */ + e.m_environ_vector.insert (e.m_environ_vector.end () - 1, + xstrdup (environ[i])); + } - e->allocated = 10; - e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *)); - e->vector[0] = 0; return e; } -/* Free an environment and all the strings in it. */ +/* See common/environ.h. */ void -free_environ (struct gdb_environ *e) +gdb_environ::clear () { - char **vector = e->vector; - - while (*vector) - xfree (*vector++); - - xfree (e->vector); - xfree (e); + for (char *v : m_environ_vector) + xfree (v); + m_environ_vector.clear (); + /* Always add the NULL element. */ + m_environ_vector.push_back (NULL); } -/* 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. */ +/* 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. */ + +static bool +match_var_in_string (char *string, const char *var, size_t var_len) +{ + if (strncmp (string, var, var_len) == 0 && string[var_len] == '=') + return true; + + return false; +} + +/* See common/environ.h. */ + +const char * +gdb_environ::get (const char *var) const +{ + size_t len = strlen (var); + + for (char *el : m_environ_vector) + if (el != NULL && match_var_in_string (el, var, len)) + return &el[len + 1]; + + return NULL; +} + +/* See common/environ.h. */ void -init_environ (struct gdb_environ *e) +gdb_environ::set (const char *var, const char *value) { - extern char **environ; - int i; + /* We have to unset the variable in the vector if it exists. */ + unset (var); - if (environ == NULL) - return; - - for (i = 0; environ[i]; i++) /*EMPTY */ ; - - if (e->allocated < i) - { - e->allocated = std::max (i, e->allocated + 10); - e->vector = (char **) xrealloc ((char *) e->vector, - (e->allocated + 1) * sizeof (char *)); - } - - memcpy (e->vector, environ, (i + 1) * sizeof (char *)); - - while (--i >= 0) - { - int len = strlen (e->vector[i]); - char *newobj = (char *) xmalloc (len + 1); - - memcpy (newobj, e->vector[i], len + 1); - e->vector[i] = newobj; - } + /* Insert the element before the last one, which is always NULL. */ + m_environ_vector.insert (m_environ_vector.end () - 1, + concat (var, "=", value, NULL)); } -/* Return the vector of environment E. - This is used to get something to pass to execve. */ +/* See common/environ.h. */ + +void +gdb_environ::unset (const char *var) +{ + size_t len = strlen (var); + + /* We iterate until '.cend () - 1' because the last element is + always NULL. */ + for (std::vector::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; + } +} + +/* See common/environ.h. */ char ** -environ_vector (struct gdb_environ *e) +gdb_environ::envp () const { - return e->vector; -} - -/* Return the value in environment E of variable VAR. */ - -char * -get_in_environ (const struct gdb_environ *e, const char *var) -{ - int len = strlen (var); - char **vector = e->vector; - char *s; - - for (; (s = *vector) != NULL; vector++) - if (strncmp (s, var, len) == 0 && s[len] == '=') - return &s[len + 1]; - - return 0; -} - -/* Store the value in E of VAR as VALUE. */ - -void -set_in_environ (struct gdb_environ *e, const char *var, const char *value) -{ - int i; - int len = strlen (var); - char **vector = e->vector; - char *s; - - 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; -} - -/* Remove the setting for variable VAR from environment E. */ - -void -unset_in_environ (struct gdb_environ *e, const char *var) -{ - int len = strlen (var); - char **vector = e->vector; - char *s; - - 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; - } - } + return const_cast (&m_environ_vector[0]); } diff --git a/gdb/common/environ.h b/gdb/common/environ.h index 3ace69e30a3..0bbb1913617 100644 --- a/gdb/common/environ.h +++ b/gdb/common/environ.h @@ -17,33 +17,65 @@ #if !defined (ENVIRON_H) #define ENVIRON_H 1 -/* We manipulate environments represented as these structures. */ +#include -struct gdb_environ +/* Class that represents the environment variables as seen by the + inferior. */ + +class gdb_environ +{ +public: + /* Regular constructor and destructor. */ + 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; - }; + /* 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); + } -extern struct gdb_environ *make_environ (void); + ~gdb_environ () + { + clear (); + } -extern void free_environ (struct gdb_environ *); + /* 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); + } -extern void init_environ (struct gdb_environ *); + /* Move assignment. */ + gdb_environ &operator= (gdb_environ &&e); -extern char *get_in_environ (const struct gdb_environ *, const char *); + /* Create a gdb_environ object using the host's environment + variables. */ + static gdb_environ from_host_environ (); -extern void set_in_environ (struct gdb_environ *, const char *, const char *); + /* Clear the environment variables stored in the object. */ + void clear (); -extern void unset_in_environ (struct gdb_environ *, const char *); + /* 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 **environ_vector (struct gdb_environ *); + /* Store VAR=VALUE in the environment. */ + void set (const char *var, const char *value); + + /* Unset VAR in environment. */ + void unset (const char *var); + + /* Return the environment vector represented as a 'char **'. */ + char **envp () const; + +private: + /* A vector containing the environment variables. */ + std::vector m_environ_vector; +}; #endif /* defined (ENVIRON_H) */ diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index c190fce3132..f9261d60d6f 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,15 @@ +2017-06-20 Sergio Durigan Junior + + * 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 * linux-low.c (linux_read_memory, linux_write_memory): Remove diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index c8e8d08cf91..3d7cfe31b5a 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -998,7 +998,7 @@ linux_create_inferior (const char *program, 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); diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c index 35160d618d2..77f570e8518 100644 --- a/gdb/gdbserver/lynx-low.c +++ b/gdb/gdbserver/lynx-low.c @@ -259,7 +259,7 @@ lynx_create_inferior (const char *program, 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); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 1d7a8b0278e..38383510e8b 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -42,7 +42,7 @@ /* 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. */ @@ -257,10 +257,10 @@ get_exec_file (int err) /* See server.h. */ -struct gdb_environ * +gdb_environ * get_environ () { - return our_environ; + return &our_environ; } static int @@ -3698,8 +3698,7 @@ captured_main (int argc, char *argv[]) } /* 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 (); diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 4de4244f090..46b614c5dd1 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -62,6 +62,7 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap); #include "mem-break.h" #include "gdbthread.h" #include "inferiors.h" +#include "environ.h" /* Target-specific functions */ @@ -154,9 +155,8 @@ extern int in_queued_stop_replies (ptid_t ptid); 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; diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c index 0f770a0df1f..636250282ae 100644 --- a/gdb/gdbserver/spu-low.c +++ b/gdb/gdbserver/spu-low.c @@ -289,7 +289,7 @@ spu_create_inferior (const char *program, 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); diff --git a/gdb/infcmd.c b/gdb/infcmd.c index d55163960f2..defa7b0c48c 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -610,7 +610,7 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main) 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. */ @@ -2131,7 +2131,7 @@ environment_info (char *var, int from_tty) { if (var) { - char *val = get_in_environ (current_inferior ()->environment, var); + const char *val = current_inferior ()->environment.get (var); if (val) { @@ -2149,11 +2149,11 @@ environment_info (char *var, int from_tty) } 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"); } } @@ -2215,10 +2215,10 @@ set_environment_command (char *arg, int from_tty) 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); } @@ -2230,13 +2230,10 @@ unset_environment_command (char *var, int from_tty) /* 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). */ @@ -2247,8 +2244,7 @@ static void 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"); } @@ -2261,13 +2257,13 @@ path_command (char *dirname, int from_tty) 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); diff --git a/gdb/inferior.c b/gdb/inferior.c index 0b655f4835c..9fa2dadd857 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -81,7 +81,6 @@ inferior::~inferior () inferior_free_data (inf); xfree (inf->args); xfree (inf->terminal); - free_environ (inf->environment); target_desc_info_free (inf->tdesc_info); xfree (inf->priv); } @@ -89,10 +88,9 @@ inferior::~inferior () 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); } diff --git a/gdb/inferior.h b/gdb/inferior.h index 1c541b7a740..8ada4f8a472 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -30,7 +30,6 @@ struct regcache; struct ui_out; struct terminal_info; struct target_desc_info; -struct gdb_environ; struct continuation; struct inferior; @@ -43,6 +42,9 @@ struct inferior; /* For struct frame_id. */ #include "frame.h" +/* For gdb_environ. */ +#include "environ.h" + #include "progspace.h" #include "registry.h" @@ -363,7 +365,7 @@ class inferior : public refcounted_object /* 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; diff --git a/gdb/mi/mi-cmd-env.c b/gdb/mi/mi-cmd-env.c index 97be139afd3..bf4578cd332 100644 --- a/gdb/mi/mi-cmd-env.c +++ b/gdb/mi/mi-cmd-env.c @@ -167,7 +167,7 @@ mi_cmd_env_path (const char *command, char **argv, int argc) 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) @@ -178,9 +178,9 @@ mi_cmd_env_path (const char *command, char **argv, int argc) 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); } diff --git a/gdb/solib.c b/gdb/solib.c index 491c18a685b..788cf15ad94 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -350,16 +350,15 @@ solib_find_1 (const char *in_pathname, int *fd, int is_solib) /* 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); diff --git a/gdb/unittests/environ-selftests.c b/gdb/unittests/environ-selftests.c new file mode 100644 index 00000000000..ecc395531de --- /dev/null +++ b/gdb/unittests/environ-selftests.c @@ -0,0 +1,151 @@ +/* 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 . */ + +#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); +}