/* fnxform - use iconv(3) to transform strings to and from "filename" format */

/* Copyright (C) 2009-2020,2022 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <config.h>
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include "bashtypes.h"

#include "stdc.h"
#include "bashintl.h"
#include <xmalloc.h>

#if defined (HAVE_ICONV)
#  include <iconv.h>
#endif

#if defined (HAVE_LOCALE_CHARSET)
extern const char *locale_charset (void);
#else
extern char *get_locale_var (char *);
#endif

#if defined (HAVE_ICONV)
static iconv_t conv_fromfs = (iconv_t)-1;
static iconv_t conv_tofs = (iconv_t)-1;

#define OUTLEN_MAX 4096

static char *outbuf = 0;
static size_t outlen = 0;

static char *curencoding (void);
static void init_tofs (void);
static void init_fromfs (void);

static char *
curencoding (void)
{
  char *loc;
#if defined (HAVE_LOCALE_CHARSET)
  loc = (char *)locale_charset ();
  return loc;
#else
  char *dot, *mod;

  loc = get_locale_var ("LC_CTYPE");
  if (loc == 0 || *loc == 0)
    return "";
  dot = strchr (loc, '.');
  if (dot == 0)
    return loc;
  mod = strchr (dot, '@');
  if (mod)
    *mod = '\0';
  return ++dot;
#endif
}  

static void
init_tofs (void)
{
  char *cur;

  cur = curencoding ();
  conv_tofs = iconv_open ("UTF-8-MAC", cur);
}

static void
init_fromfs (void)
{
  char *cur;

  cur = curencoding ();
  conv_fromfs = iconv_open (cur, "UTF-8-MAC");
}

char *
fnx_tofs (char *string, size_t len)
{
#ifdef MACOSX
  ICONV_CONST char *inbuf;
  char *tempbuf;
  size_t templen;
  
  if (conv_tofs == (iconv_t)-1)
    init_tofs ();
  if (conv_tofs == (iconv_t)-1)
    return string;

  /* Free and reallocate outbuf if it's *too* big */
  if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8)
    {
      free (outbuf);
      outbuf = 0;
      outlen = 0;
    }

  inbuf = string;
  if (outbuf == 0 || outlen < len + 8)
    {
      outlen = len + 8;
      outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1);
    }
  tempbuf = outbuf;
  templen = outlen;

  iconv (conv_tofs, NULL, NULL, NULL, NULL);

  if (iconv (conv_tofs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1)
    return string;

  *tempbuf = '\0';
  return outbuf;
#else
  return string;
#endif
}

char *
fnx_fromfs (char *string, size_t len)
{
#ifdef MACOSX
  ICONV_CONST char *inbuf;
  char *tempbuf;
  size_t templen;

  if (conv_fromfs == (iconv_t)-1)
    init_fromfs ();
  if (conv_fromfs == (iconv_t)-1)
    return string;

  /* Free and reallocate outbuf if it's *too* big */
  if (outlen >= OUTLEN_MAX && len < OUTLEN_MAX - 8)
    {
      free (outbuf);
      outbuf = 0;
      outlen = 0;
    }

  inbuf = string;
  if (outbuf == 0 || outlen < (len + 8))
    {
      outlen = len + 8;
      outbuf = outbuf ? xrealloc (outbuf, outlen + 1) : xmalloc (outlen + 1);
    }
  tempbuf = outbuf;
  templen = outlen;

  iconv (conv_fromfs, NULL, NULL, NULL, NULL);

  if (iconv (conv_fromfs, &inbuf, &len, &tempbuf, &templen) == (size_t)-1)
    return string;

  *tempbuf = '\0';
  return outbuf;
#else
  return string;
#endif
}

#else
char *
fnx_tofs (char *string, size_t len)
{
  return string;
}

char *
fnx_fromfs (char *string, size_t len)
{
  return string;
}
#endif
