pspp-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Bug #33255 (Windows cannot open files with non-ascii names)


From: Ben Pfaff
Subject: Re: Bug #33255 (Windows cannot open files with non-ascii names)
Date: Fri, 9 Oct 2015 08:06:46 -0700
User-agent: Mutt/1.5.23 (2014-03-12)

On Fri, Oct 09, 2015 at 08:02:43AM -0700, Ben Pfaff wrote:
> On Fri, Oct 09, 2015 at 07:29:12AM +0200, John Darrington wrote:
> > On Thu, Oct 08, 2015 at 09:29:35PM -0700, Ben Pfaff wrote:
> >      On Thu, Oct 08, 2015 at 06:47:05PM +0200, John Darrington wrote:
> >      > In addition to the changes I've submitted for review, I think the 
> > resolution to this 
> >      > problem involves work in src/data/make-file.c
> >      > 
> >      > The issue is, this module contains a lot of calls to posix file 
> > functions, such as 
> >      > open, rename, unlink etc.
> >      > 
> >      > These work in w32 so long as there are no non-ascii characters in 
> > the filenames.
> >      > To get other characters to work, we have to do two things:
> >      > 
> >      > 1. Convert the filenames to UTF-16LE
> >      > AND
> >      > 2. replace the function calls by their WideChar equivalents.
> >      > 
> >      > Step 2  however has caveats:
> >      > 
> >      > open --> _wopen is no problem.
> >      > unlink --> _wunlink is no problem.
> >      > rename --> _wrename will not work, because windows rename refuses to 
> > overwrite an existing file.  We will 
> >      >      have to use the native Windows function MoveFileW
> >      > 
> >      > Not sure about stat.
> >      > 
> >      > 
> >      > It may end up being easier to have a platform specific 
> > implementation of make-file.c
> >      > 
> >      > What do you think?
> >      
> >      Can this be fixed in Gnulib somehow?
> > 
> > It probably can be.  The question one has to ask is does it make sense to 
> > do so:
> > 
> > The awkward issue is the _wrename function.   The POSIX rename silently 
> > overwrites
> > the target.  The Windows rename (and presumably the corresponding _wrename) 
> > however 
> > fails with an error if the target exists.
> > 
> > Now some years ago, we (you actually!) contributed a replacement rename to 
> > Gnulib 
> > such that it behaves per POSIX on Windows.  I think it is perfectly 
> > possible to 
> > write a replacement _wrename for w32 which behaves in a more POSIX like way,
> > however then it will not behave how Microsoft's documentation says.
> > 
> > Perhaps it might make sense to have a "widechar" version of rename in 
> > Gnulib, but
> > call it something other than _wrename.
> 
> I guess I was assuming that we'd provide a replacement rename(), or I
> guess enhance the existing rename() replacement, so that it accepted
> UTF-8 file names.  It could be implemented in terms of MoveFileW or
> whatever is appropriate.

Actually, maybe the solution is to use g_rename().  The implementation
looks correct to me:

/**
 * g_rename:
 * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
 * @newfilename: a pathname in the GLib file name encoding
 *
 * A wrapper for the POSIX rename() function. The rename() function 
 * renames a file, moving it between directories if required.
 * 
 * See your C library manual for more details about how rename() works
 * on your system. It is not possible in general on Windows to rename
 * a file that is open to some process.
 *
 * Returns: 0 if the renaming succeeded, -1 if an error occurred
 * 
 * Since: 2.6
 */
int
g_rename (const gchar *oldfilename,
          const gchar *newfilename)
{
#ifdef G_OS_WIN32
  wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
  wchar_t *wnewfilename;
  int retval;
  int save_errno = 0;

  if (woldfilename == NULL)
    {
      errno = EINVAL;
      return -1;
    }

  wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);

  if (wnewfilename == NULL)
    {
      g_free (woldfilename);
      errno = EINVAL;
      return -1;
    }

  if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
    retval = 0;
  else
    {
      retval = -1;
      switch (GetLastError ())
        {
#define CASE(a,b) case ERROR_##a: save_errno = b; break
          CASE (FILE_NOT_FOUND, ENOENT);
          CASE (PATH_NOT_FOUND, ENOENT);
          CASE (ACCESS_DENIED, EACCES);
          CASE (NOT_SAME_DEVICE, EXDEV);
          CASE (LOCK_VIOLATION, EACCES);
          CASE (SHARING_VIOLATION, EACCES);
          CASE (FILE_EXISTS, EEXIST);
          CASE (ALREADY_EXISTS, EEXIST);
#undef CASE
        default: save_errno = EIO;
        }
    }

  g_free (woldfilename);
  g_free (wnewfilename);
    
  errno = save_errno;
  return retval;
#else
  return rename (oldfilename, newfilename);
#endif
}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]