This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
remove duplicate environment variables from environ
[perl5.git] / perl.c
diff --git a/perl.c b/perl.c
index 4a324c6..5c71fd0 100644 (file)
--- a/perl.c
+++ b/perl.c
@@ -4329,23 +4329,70 @@ S_init_postdump_symbols(pTHX_ int argc, char **argv, char **env)
        }
        if (env) {
          char *s, *old_var;
+          STRLEN nlen;
          SV *sv;
+          HV *dups = newHV();
+
          for (; *env; env++) {
            old_var = *env;
 
            if (!(s = strchr(old_var,'=')) || s == old_var)
                continue;
+            nlen = s - old_var;
 
 #if defined(MSDOS) && !defined(DJGPP)
            *s = '\0';
            (void)strupr(old_var);
            *s = '=';
 #endif
-           sv = newSVpv(s+1, 0);
-           (void)hv_store(hv, old_var, s - old_var, sv, 0);
+            if (hv_exists(hv, old_var, nlen)) {
+                const char *name = savepvn(old_var, nlen);
+
+                /* make sure we use the same value as getenv(), otherwise code that
+                   uses getenv() (like setlocale()) might see a different value to %ENV
+                 */
+                sv = newSVpv(PerlEnv_getenv(name), 0);
+
+                /* keep a count of the dups of this name so we can de-dup environ later */
+                if (hv_exists(dups, name, nlen))
+                    ++SvIVX(*hv_fetch(dups, name, nlen, 0));
+                else
+                    (void)hv_store(dups, name, nlen, newSViv(1), 0);
+
+                Safefree(name);
+            }
+            else {
+                sv = newSVpv(s+1, 0);
+            }
+           (void)hv_store(hv, old_var, nlen, sv, 0);
            if (env_is_not_environ)
                mg_set(sv);
          }
+          if (HvKEYS(dups)) {
+              /* environ has some duplicate definitions, remove them */
+              HE *entry;
+              hv_iterinit(dups);
+              while ((entry = hv_iternext_flags(dups, 0))) {
+                  STRLEN nlen;
+                  const char *name = HePV(entry, nlen);
+                  IV count = SvIV(HeVAL(entry));
+                  IV i;
+                  SV **valp = hv_fetch(hv, name, nlen, 0);
+
+                  assert(valp);
+
+                  /* try to remove any duplicate names, depending on the
+                   * implementation used in my_setenv() the iteration might
+                   * not be necessary, but let's be safe.
+                   */
+                  for (i = 0; i < count; ++i)
+                      my_setenv(name, 0);
+
+                  /* and set it back to the value we set $ENV{name} to */
+                  my_setenv(name, SvPV_nolen(*valp));
+              }
+          }
+          SvREFCNT_dec_NN(dups);
       }
 #endif /* USE_ENVIRON_ARRAY */
 #endif /* !PERL_MICRO */