/*
 * Copyright © 2014-2018 Red Hat, Inc
 * Copyright © 2020-2021 Collabora Ltd.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

#include "environ.h"

/*
 * PvEnviron:
 *
 * Each environment variable we deal with in pressure-vessel has
 * the following possible values:
 *
 * - Set to a value (empty or non-empty)
 * - Forced to be unset
 * - Inherited from the execution environment of bwrap(1)
 *
 * We represent this as follows:
 *
 * - Set to a value: values[VAR] = VAL
 * - Forced to be unset: values[VAR] = NULL
 * - Inherited from the execution environment of bwrap(1): VAR not in `values`
 */
struct _PvEnviron
{
  /* (element-type filename filename) */
  GHashTable *values;
};

PvEnviron *
pv_environ_new (void)
{
  g_autoptr(PvEnviron) self = g_slice_new0 (PvEnviron);

  self->values = g_hash_table_new_full (g_str_hash, g_str_equal,
                                        g_free, g_free);
  return g_steal_pointer (&self);
}

void
pv_environ_free (PvEnviron *self)
{
  g_return_if_fail (self != NULL);

  g_clear_pointer (&self->values, g_hash_table_unref);
  g_slice_free (PvEnviron, self);
}

/*
 * Set @var to @val, which may be %NULL to unset it.
 */
void
pv_environ_setenv (PvEnviron *self,
                   const char *var,
                   const char *val)
{
  g_return_if_fail (self != NULL);
  g_return_if_fail (var != NULL);
  /* val may be NULL, to unset it */

  g_hash_table_replace (self->values, g_strdup (var), g_strdup (val));
}

/*
 * Set @var to whatever value it happens to have inherited.
 */
void
pv_environ_inherit_env (PvEnviron *self,
                        const char *var)
{
  g_return_if_fail (self != NULL);
  g_return_if_fail (var != NULL);

  g_hash_table_remove (self->values, var);
}

static int
generic_strcmp (gconstpointer a,
                gconstpointer b)
{
  return strcmp (a, b);
}

/*
 * Returns: (transfer container): The variables that are set or forced
 *  to be unset, but not the variables that are inherited
 */
GList *
pv_environ_get_vars (PvEnviron *self)
{
  g_return_val_if_fail (self != NULL, NULL);

  return g_list_sort (g_hash_table_get_keys (self->values), generic_strcmp);
}

/*
 * Returns: (nullable): The value of @var, or %NULL if @var is either
 *  unset or inherited
 */
const char *
pv_environ_getenv (PvEnviron *self,
                   const char *var)
{
  g_return_val_if_fail (self != NULL, NULL);

  return g_hash_table_lookup (self->values, var);
}