/* Support for Non-ASCII Path Name
   Copyright (C) 1985, 1986, 1992, 1993 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs 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 1, or (at your option)
any later version.

GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* 94.3.9   modified for Mule Ver.1.1 by T.Emami <enami@sys.ptg.sony.co.jp>
	The last arg of encode() should be a pointer to a real Lisp_Object. */
/* 94.6.22  modified for Mule Ver.2.0 by K.Handa <handa@etl.go.jp>
	CONV_BUF_SIZE -> ENCODE_BUF_SIZE/DECODE_BUF_SIZE.
	Macro definition of ITNCODE and AUTOCONV switched. */

#define MCPATH_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>

#ifdef MSDOS  /* 94.8.9  by K.Fujii */
#include <dos.h>
#include <jctype.h>
#endif

/* mcpath.h should be included in config.h */
#include "config.h"

#ifdef MCPATH_ASSERT			/* for debugging */
#include <assert.h>
#endif

#undef NILP

#include "lisp.h"
#include "buffer.h"

#include "charset.h"
#include "coding.h"

Lisp_Object Qpathname_coding_system = 0;

/* this function should be lisp function. */
DEFUN ("set-pathname-coding-system",
  Fset_pathname_coding_system, Sset_pathname_coding_system,
  1, 1, 0,
  "Set the pathname-coding-system to CODING_SYSTEM.")
  (coding_system)
     register Lisp_Object coding_system;
{
  Fcheck_code (coding_system);
  Fset (Qpathname_coding_system, coding_system);
}

static void mcpath_encode_code (cp)
     coding_type *cp;
{
  Lisp_Object coding_system;

  coding_system = Fsymbol_value (Qpathname_coding_system);

  encode_code (coding_system, cp);
  CODE_CNTL (cp) |= CC_END;	/* 94.6.22 by K.Handa */
}

static int encode_path_1 (src, srcsize, dst, dstsize)
     unsigned char *src, *dst;
     unsigned int srcsize, dstsize;
{
  coding_type code;

  mcpath_encode_code (&code);
  if (CODE_TYPE (&code) > AUTOCONV)
    {
      unsigned char *buf;

					/* get_conversion_buffer () is not */
					/* re-entrant. */
      buf = (unsigned char *) alloca (ENCODE_BUF_SIZE (srcsize, &code));
      if (buf)
	{
	  int len;
	  Lisp_Object dummy = Qnil; /* 94.3.9 by T.Enami */

	  len = encode (&code, src, buf, srcsize, &dummy);
	  if (!CODE_CHAR (&code) && len <= dstsize)
	    {
	      bcopy (buf, dst, len);
	      return len;
	    }
	}
    }
  return -1;				/* use original */
}

static unsigned char *decode_path_1 (src, dst, dstsize)
     unsigned char *src, *dst;
     unsigned int dstsize;
{
  coding_type code;

  mcpath_encode_code (&code);
  if (CODE_TYPE (&code) > AUTOCONV)
    {
      int len;
      unsigned char *buf;

      len = strlen (src) + 1;		/* + 1 for '\0' */

					/* get_conversion_buffer () is not */
					/* re-entrant. */
      buf = (unsigned char *) alloca (DECODE_BUF_SIZE (len, &code));
      if (buf)
	{
	  CODE_CNTL (&code) |= CC_END;
	  len = decode (&code, src, buf, len);
	  if (!CODE_CHAR (&code) && len <= dstsize)
	    {
	      bcopy (buf, dst, len);	/* len should include '\0' */
	      return dst;
	    }
	}
    }
  return src;
}

static unsigned char *decode_path (path, ext_path)
     unsigned char *path, ext_path[MC_MAXPATHLEN];
{
  return
    (Qpathname_coding_system
     ? decode_path_1 (path, ext_path, MC_MAXPATHLEN)
     : path);				/* in case of before initialization */
}

/* patch 94.8.9  by K.Fujii */
unsigned char *encode_path (path, encode_buffer, size)
     unsigned char *path;
     unsigned char *encode_buffer;
     unsigned int size;
{
  int len;

				/* encode up to '\0', the end of string. */
  len = encode_path_1 (path, strlen (path) + 1, encode_buffer, size);
  if (len > 0)
    path = encode_buffer;
#ifdef MSDOS
  /* convert the MSDOS style path delimiter to the UNIX style.  Note
     that now the code is *internal*, so we can simply compare each
     character with '\\'.  And this operation will alter the contents
     of Lisp Object, PATH. */
  {
    unsigned char *p = path;

    while (*p)
      {
	if (*p == '\\')
	  *p = '/';
	p++;
      }
  }
#endif /* MSDOS */
  return path;
}
/* end of patch */

/* patch 94.9.4  by K.Fujii */
DEFUN ("encode-path", Fencode_path, Sencode_path, 1, 1, 0,
       "encode the PATH from external code to internal code.\n\
If the system type is MSDOS, path delimter of PATH will be altered.")
  (path)
     Lisp_Object path;
{
  unsigned char encode_buffer[MC_MAXPATHLEN];
  unsigned char *tmp;

  CHECK_STRING (path, 0);

  tmp = encode_path (XSTRING (path)->data,
		     encode_buffer, sizeof (encode_buffer));
  if (XSTRING (path)->data != tmp)
    path = build_string (tmp);
  return path;
}
/* end of patch */

int mc_creat (path, mode)
     unsigned char *path;
     int mode;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return creat (decode_path (path, buffer), mode);
}

#if defined (INTERRUPTABLE_OPEN) || defined (INTERRUPTIBLE_OPEN)
#define open sys_open			/* call open in sysdep.c */
#endif
int mc_open (path, flag, mode)
     unsigned char *path;
     int flag, mode;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return open (decode_path (path, buffer), flag, mode);
}

int mc_access (path, mode)
     unsigned char *path;
     int mode;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return access (decode_path (path, buffer), mode);
}

int mc_chmod (path, mode)
     unsigned char *path;
     int mode;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return chmod (decode_path (path, buffer), mode);
}

/* if system does not have symbolic links, it does not have lstat.
   In that case, use ordinary stat instead.  */

#ifdef S_IFLNK
int mc_lstat (path, st_addr)
     unsigned char *path;
     struct stat *st_addr;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return lstat (decode_path (path, buffer), st_addr);
}

int mc_readlink (path, buf, size)
     unsigned char *path, *buf;
     int size;
{
  unsigned char buffer[MC_MAXPATHLEN], buffer2[MAXPATHLEN];
  int nread;

  /* decode given `path' into buffer, and get the link contents into
     buffer2. */
  nread = readlink (decode_path (path, buffer), buffer2, MAXPATHLEN);
  if (nread > 0)
    {
      int len;
      unsigned char *result;

      len = encode_path_1 (buffer2, nread, buffer, sizeof (buffer));

      /* if the conversion success and the result fits to `buf', use
         it. */
      if (0 <= len && len <= size)
	{
	  nread = len;
	  result = buffer;
	}
      else			/* use original one. */
	result = buffer2;
	
      bcopy (result, buf, nread);
    }
  return nread;
}
#endif

#ifndef nec_ews_svr4				/* hir, 1993.10.22 */
int mc_stat (path, st_addr)
#else
int mc_xstat (v, path, st_addr)
     int v;
#endif
     unsigned char *path;
     struct stat *st_addr;
{
  unsigned char buffer[MC_MAXPATHLEN];
#ifndef nec_ews_svr4		/* hir, 1993.10.22 */
  return stat (decode_path (path, buffer), st_addr);
#else
  return _xstat (v, decode_path (path, buffer), st_addr);
#endif
}

int mc_unlink (path)
     unsigned char *path;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return unlink (decode_path (path, buffer));
}

#ifdef HAVE_RENAME
int mc_rename (path, newpath)
     unsigned char *path, *newpath;
{
  unsigned char buffer[MC_MAXPATHLEN], buffer2[MC_MAXPATHLEN];
  return rename (decode_path (path, buffer), decode_path (newpath, buffer2));
}
#endif

int mc_link (path, newpath)
     unsigned char *path, *newpath;
{
  unsigned char buffer[MC_MAXPATHLEN], buffer2[MC_MAXPATHLEN];
  return link (decode_path (path, buffer), decode_path (newpath, buffer2));
}

#ifndef MSDOS  /* 94.8.9  by K.Fujii */
int mc_symlink (path, newpath)
     unsigned char *path, *newpath;
{
  unsigned char buffer[MC_MAXPATHLEN], buffer2[MC_MAXPATHLEN];
  return symlink (decode_path (path, buffer), decode_path (newpath, buffer2));
}
#endif

int mc_chdir (path)
     unsigned char *path;
{
  unsigned char buffer[MC_MAXPATHLEN];

  path = decode_path (path, buffer);

#ifdef MSDOS  /* 94.8.9  by K.Fujii */
  if ((path[0] != 0) && (path[1] == ':'))
    {
      int drive = (tolower (path[0]) - 'a');
      if (getdisk () != drive)
	setdisk (drive);
    }

  /* If path != "/" and path != "a:/" and path ends with slash, remove
     it. */
  {
    int len = strlen (path);

    if (strcmp (path + 1, ":/") && (len > 1) && (path[len - 1] == '/'))
      {
	if (path != buffer)	/* It is not good to modify original path. */
	  {
	    bcopy (path, buffer, len - 1); /* no need to copy last /. */
	    path = buffer;
	  }
	path[len - 1] = 0;
      }
  }
#endif /* MSDOS */

  return chdir (path);
}

#ifdef MSDOS
#ifndef HAVE_GETWD
unsigned char *mc_getcwd (null, size)
     unsigned char *null;		/* in sysdep.c, always 0. */
     size_t size;
{
  unsigned char buffer[MAXPATHLEN];
  unsigned char *path;

  path = (unsigned char *) getcwd ((char *)buffer, MAXPATHLEN);
  if (path)
    {
      /* here, shoule be (path == buffer). */
      path = (unsigned char *) malloc (MC_MAXPATHLEN);	/* MSDOS */
      if (path)
	{
	  int len;
	  int buffer_length = strlen (buffer) + 1;

	  len = encode_path_1 (buffer, buffer_length, path, MC_MAXPATHLEN);
	  if (len < 0)
	    {
	      /* conversion failed.  use value that is returned from system. */
	      bcopy (buffer, path, buffer_length);
	    }
	}
    }
  return path;
}
#else /* HAVE_GETWD */
unsigned char *mc_getwd (path)
     unsigned char path[];
{
  unsigned char *p;
 
  p = getwd (path);
  if (p)
    {
      unsigned char buffer[MC_MAXPATHLEN];
      int len;
      
      len = encode_path_1 (path, strlen (path) + 1, buffer, sizeof buffer);
      if (len > 0)
	{
	  bcopy (buffer, path, len);
	}
    }
  return p;
}
#endif /* HAVE_GETWD */
#endif /* MSDOS */

/* In callproc.c, execvp() is called like this:
 * 	execvp (new_argv[0], new_argv);
 * following implement depends this.
 */
#ifndef NO_MC_EXECVP
void mc_execvp (path, argv)
     unsigned char *path, *argv[];
{
  unsigned char buffer[MC_MAXPATHLEN];
  argv[0] = path = decode_path (path, buffer);
  execvp (path, argv);
}
#endif /* !NO_MC_EXECVP */

DIR *mc_opendir (path)
     unsigned char *path;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return opendir (decode_path (path, buffer));
}

static DIRENTRY mcpath_directory_entry;
DIRENTRY *mc_readdir (d)
     DIR *d;
{
  SYSTEM_DIRENTRY *sp;
  DIRENTRY *dp = &mcpath_directory_entry;

  sp = readdir (d);
  if (!sp) return 0;

#ifndef MSDOS
  dp->d_ino = sp->d_ino;
#endif /* MSDOS */
  {				/* copy d_name with conversion. */
    int len;

    len = encode_path_1 (sp->d_name, NAMLEN (sp),
			 dp->d_name, sizeof (dp->d_name) - 1);
    if (len < 0)
      {
	len = NAMLEN (sp);
#ifdef MCPATH_ASSERT
	assert (len < sizeof (dp->d_name));
#endif
	bcopy (sp->d_name, dp->d_name, len);
      }
    dp->d_name[len] = 0;
  }
  return dp;
}

#ifdef HAVE_MKDIR					/* hir, 1994.8.12 */
int mc_mkdir (path, mode)
     unsigned char *path;
     int mode;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return mkdir (decode_path (path, buffer), mode);
}
#endif

#ifdef HAVE_RMDIR					/* hir, 1994.8.12 */
int mc_rmdir (path)
     unsigned char *path;
{
  unsigned char buffer[MC_MAXPATHLEN];
  return rmdir (decode_path (path, buffer));
}
#endif

syms_of_mcpath ()
{
  Qpathname_coding_system = intern ("pathname-coding-system");
  Fset_pathname_coding_system (Qnil);

  defsubr (&Sset_pathname_coding_system);
/* patch 94.9.4  by K.Fujii */
  defsubr (&Sencode_path);
/* end of patch */
}
