/****************************************************************************
 * apps/fsutils/ipcfg/ipcfg.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <debug.h>

#include "fsutils/ipcfg.h"
#include "ipcfg.h"

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: ipcfg_allocpath
 *
 * Description:
 *   Allocate memory for and construct the full path to the IPv4
 *   Configuration file.
 *
 * Input Parameters:
 *   netdev - The network device.  For examplel "eth0"
 *
 * Returned Value:
 *   A pointer to the allocated path is returned.  NULL is returned only on
 *  on a failure to allocate.
 *
 ****************************************************************************/

static inline FAR char *ipcfg_allocpath(FAR const char *netdev)
{
#ifndef CONFIG_IPCFG_CHARDEV
  FAR char *path = NULL;
  int ret;

  /* Create the full path to the ipcfg file for the network device.
   * the path of CONFIG_IPCFG_PATH returns to a directory containing
   * multiple files of the form ipcfg-<dev> where <dev> is the network
   * device name.  For example, ipcfg-eth0.
   */

  ret = asprintf(&path, CONFIG_IPCFG_PATH "/ipcfg-%s", netdev);
  if (ret < 0 || path == NULL)
    {
      /* Assume that asprintf failed to allocate memory */

      ferr("ERROR: Failed to create path to ipcfg file: %d\n", ret);
      return NULL;
    }

  return path;

#else
  /* In this case CONFIG_IPCFG_PATH is the full path to a character device
   * that describes the configuration of a single network device.
   *
   * It is dup'ed to simplify typing (const vs non-const) and to make the
   * free operation unconditional.
   */

  return strdup(CONFIG_IPCFG_PATH);
#endif
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: ipcfg_read
 *
 * Description:
 *   Read and parse the IP configuration file for the specified network
 *   device.
 *
 * Input Parameters:
 *   netdev - The network device.  For examplel "eth0"
 *   ipcfg  - Pointer to a user provided location to receive the IP
 *            configuration.  Refers to either struct ipv4cfg_s or
 *            ipv6cfg_s, depending on the value of af.
 *   af     - Identifies the address family whose IP configuration is
 *            requested.  May be either AF_INET or AF_INET6.
 *
 * Returned Value:
 *   Zero is returned on success; a negated errno value is returned on any
 *   failure.
 *
 ****************************************************************************/

int ipcfg_read(FAR const char *netdev, FAR void *ipcfg, sa_family_t af)
{
#ifdef CONFIG_IPCFG_BINARY
  FAR char *path;
  int ret;

  DEBUGASSERT(netdev != NULL && ipcfg != NULL);

  /* Create the full path to the ipcfg file for the network device */

  path = ipcfg_allocpath(netdev);
  if (path == NULL)
    {
      /* Assume failure to allocate memory */

      ferr("ERROR: Failed to create path to ipcfg file\n");
      return -ENOMEM;
    }

#ifdef CONFIG_NET_IPv4
  if (af == AF_INET)
    {
      ret = ipcfg_read_binary_ipv4(path, (FAR struct ipv4cfg_s *)ipcfg);
    }
  else
#endif
#ifdef CONFIG_NET_IPv6
  if (af == AF_INET6)
    {
      ret = ipcfg_read_binary_ipv6(path, (FAR struct ipv6cfg_s *)ipcfg);
    }
  else
#endif
    {
      ferr("ERROR: Unsupported address family: %d\n", af);
      ret = -EAFNOSUPPORT;
    }

  free(path);
  return ret;

#else
  FAR char *path;
  int ret;

  DEBUGASSERT(netdev != NULL && ipcfg != NULL);

  /* Create the full path to the ipcfg file for the network device */

  path = ipcfg_allocpath(netdev);
  if (path == NULL)
    {
      /* Assume failure to allocate memory */

      ferr("ERROR: Failed to create path to ipcfg file\n");
      return -ENOMEM;
    }

#ifdef CONFIG_NET_IPv4
  if (af == AF_INET)
    {
      ret = ipcfg_read_text_ipv4(path, netdev,
                                 (FAR struct ipv4cfg_s *)ipcfg);
    }
  else
#endif
#ifdef CONFIG_NET_IPv6
  if (af == AF_INET6)
    {
      ret = ipcfg_read_text_ipv6(path, netdev,
                                 (FAR struct ipv6cfg_s *)ipcfg);
    }
  else
#endif
    {
      ferr("ERROR: Unsupported address family: %d\n", af);
      ret = -EAFNOSUPPORT;
    }

  free(path);
  return ret;
#endif
}

/****************************************************************************
 * Name: ipcfg_write
 *
 * Description:
 *   Write the IP configuration file for the specified network device.
 *
 * Input Parameters:
 *   netdev - The network device.  For examplel "eth0"
 *   ipcfg  - The IP configuration to be written.  Refers to either struct
 *            ipv4cfg_s or ipv6cfg_s, depending on the value of af.
 *   af     - Identifies the address family whose IP configuration is
 *            to be written.  May be either AF_INET or AF_INET6.
 *
 * Returned Value:
 *   Zero is returned on success; a negated errno value is returned on any
 *   failure.
 *
 ****************************************************************************/

#ifdef CONFIG_IPCFG_WRITABLE
int ipcfg_write(FAR const char *netdev, FAR const void *ipcfg,
                sa_family_t af)
{
#ifdef CONFIG_IPCFG_BINARY
  FAR char *path;
  int ret;

  DEBUGASSERT(netdev != NULL && ipcfg != NULL);

  /* Create the full path to the ipcfg file for the network device */

  path = ipcfg_allocpath(netdev);
  if (path == NULL)
    {
      /* Assume failure to allocate memory */

      ferr("ERROR: Failed to create path to ipcfg file\n");
      return -ENOMEM;
    }

#ifdef CONFIG_NET_IPv4
  if (af == AF_INET)
    {
      ret = ipcfg_write_binary_ipv4(path,
                                    (FAR const struct ipv4cfg_s *)ipcfg);
    }
  else
#endif
#ifdef CONFIG_NET_IPv6
  if (af == AF_INET6)
    {
      ret = ipcfg_write_binary_ipv6(path,
                                    (FAR const struct ipv6cfg_s *)ipcfg);
    }
  else
#endif
    {
      ferr("ERROR: Unsupported address family: %d\n", af);
      ret = -EAFNOSUPPORT;
    }

  free(path);
  return ret;

#else
  FAR char *path;
  int ret;

  DEBUGASSERT(netdev != NULL && ipcfg != NULL);

  /* Create the full path to the ipcfg file for the network device */

  path = ipcfg_allocpath(netdev);
  if (path == NULL)
    {
      /* Assume failure to allocate memory */

      ferr("ERROR: Failed to create path to ipcfg file\n");
      return -ENOMEM;
    }

#ifdef CONFIG_NET_IPv4
  if (af == AF_INET)
    {
      ret = ipcfg_write_text_ipv4(path, netdev,
                                  (FAR const struct ipv4cfg_s *)ipcfg);
    }
  else
#endif
#ifdef CONFIG_NET_IPv6
  if (af == AF_INET6)
    {
      ret = ipcfg_write_text_ipv6(path, netdev,
                                  (FAR const struct ipv6cfg_s *)ipcfg);
    }
  else
#endif
    {
      ferr("ERROR: Unsupported address family: %d\n", af);
      ret = -EAFNOSUPPORT;
    }

  free(path);
  return ret;
#endif
}
#endif