1/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
2   This file based on setenv.c in the GNU C Library.
3
4   The GNU C Library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Library General Public License as
6   published by the Free Software Foundation; either version 2 of the
7   License, or (at your option) any later version.
8
9   The GNU C Library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Library General Public License for more details.
13
14   You should have received a copy of the GNU Library General Public
15   License along with the GNU C Library; see the file COPYING.LIB.  If not,
16   write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
17   Boston, MA 02110-1301, USA.  */
18
19
20/*
21
22@deftypefn Supplemental int setenv (const char *@var{name}, @
23  const char *@var{value}, int @var{overwrite})
24@deftypefnx Supplemental void unsetenv (const char *@var{name})
25
26@code{setenv} adds @var{name} to the environment with value
27@var{value}.  If the name was already present in the environment,
28the new value will be stored only if @var{overwrite} is nonzero.
29The companion @code{unsetenv} function removes @var{name} from the
30environment.  This implementation is not safe for multithreaded code.
31
32@end deftypefn
33
34*/
35
36#if HAVE_CONFIG_H
37# include <config.h>
38#endif
39
40#define setenv libiberty_setenv
41#define unsetenv libiberty_unsetenv
42
43#include "ansidecl.h"
44#include <sys/types.h> /* For `size_t' */
45#include <stdio.h>     /* For `NULL' */
46
47#include <errno.h>
48#if !defined(errno) && !defined(HAVE_ERRNO_DECL)
49extern int errno;
50#endif
51#define __set_errno(ev) ((errno) = (ev))
52
53#if HAVE_STDLIB_H
54# include <stdlib.h>
55#endif
56#if HAVE_STRING_H
57# include <string.h>
58#endif
59#if HAVE_UNISTD_H
60# include <unistd.h>
61#endif
62
63#define __environ	environ
64#include "environ.h"
65
66#undef setenv
67#undef unsetenv
68
69/* LOCK and UNLOCK are defined as no-ops.  This makes the libiberty
70 * implementation MT-Unsafe. */
71#define LOCK
72#define UNLOCK
73
74/* Below this point, it's verbatim code from the glibc-2.0 implementation */
75
76/* If this variable is not a null pointer we allocated the current
77   environment.  */
78static char **last_environ;
79
80
81int
82setenv (const char *name, const char *value, int replace)
83{
84  register char **ep = 0;
85  register size_t size;
86  const size_t namelen = strlen (name);
87  const size_t vallen = strlen (value) + 1;
88
89  LOCK;
90
91  size = 0;
92  if (__environ != NULL)
93    {
94      for (ep = __environ; *ep != NULL; ++ep)
95	if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
96	  break;
97	else
98	  ++size;
99    }
100
101  if (__environ == NULL || *ep == NULL)
102    {
103      char **new_environ;
104      if (__environ == last_environ && __environ != NULL)
105	/* We allocated this space; we can extend it.  */
106	new_environ = (char **) realloc (last_environ,
107					 (size + 2) * sizeof (char *));
108      else
109	new_environ = (char **) malloc ((size + 2) * sizeof (char *));
110
111      if (new_environ == NULL)
112	{
113	  UNLOCK;
114	  return -1;
115	}
116
117      new_environ[size] = (char *) malloc (namelen + 1 + vallen);
118      if (new_environ[size] == NULL)
119	{
120	  free ((char *) new_environ);
121	  __set_errno (ENOMEM);
122	  UNLOCK;
123	  return -1;
124	}
125
126      if (__environ != last_environ)
127	memcpy ((char *) new_environ, (char *) __environ,
128		size * sizeof (char *));
129
130      memcpy (new_environ[size], name, namelen);
131      new_environ[size][namelen] = '=';
132      memcpy (&new_environ[size][namelen + 1], value, vallen);
133
134      new_environ[size + 1] = NULL;
135
136      last_environ = __environ = new_environ;
137    }
138  else if (replace)
139    {
140      size_t len = strlen (*ep);
141      if (len + 1 < namelen + 1 + vallen)
142	{
143	  /* The existing string is too short; malloc a new one.  */
144	  char *new_string = (char *) malloc (namelen + 1 + vallen);
145	  if (new_string == NULL)
146	    {
147	      UNLOCK;
148	      return -1;
149	    }
150	  *ep = new_string;
151	}
152      memcpy (*ep, name, namelen);
153      (*ep)[namelen] = '=';
154      memcpy (&(*ep)[namelen + 1], value, vallen);
155    }
156
157  UNLOCK;
158
159  return 0;
160}
161
162void
163unsetenv (const char *name)
164{
165  const size_t len = strlen (name);
166  char **ep;
167
168  LOCK;
169
170  for (ep = __environ; *ep; ++ep)
171    if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
172      {
173	/* Found it.  Remove this pointer by moving later ones back.  */
174	char **dp = ep;
175	do
176	  dp[0] = dp[1];
177	while (*dp++);
178	/* Continue the loop in case NAME appears again.  */
179      }
180
181  UNLOCK;
182}
183