unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#7617: 24.0.50; `expand-file-name': removal of slashes
@ 2010-12-11 21:53 Drew Adams
  2010-12-12 15:58 ` Eli Zaretskii
  2010-12-12 21:04 ` Andreas Schwab
  0 siblings, 2 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-11 21:53 UTC (permalink / raw)
  To: 7617

[-- Attachment #1: Type: text/plain, Size: 3600 bytes --]

emacs -Q
 
(expand-file-name "share/" "~/today//usr/") ->
c:/today/usr/share/.

(On my Windows laptop, `~' is just `c:/'.)

IOW, multiple consecutive slashes are collapsed to one.  This is not the
behavior I want.  This behavior started in Emacs 21; in Emacs 20 no such
removal of slashes occurs.

I would consider this loss of slashes a bug.  But I see that comments in
the current C code indicate that it is intentional (but not _why_).  I'm
not great at reading C code, but I see these comments in the Emacs 24 code:
 
/* We want to replace multiple `/' in a row with a single
   slash.  */
else if (p > nm
         && IS_DIRECTORY_SEP (p[0])
         && IS_DIRECTORY_SEP (p[1]))
 
And later on:
 
/* Now canonicalize by removing `//', `/.' and `/foo/..' if they
   appear.  */
...
/* Collapse multiple `/' in a row.  */
 
In the Emacs 20 C code the equivalent comment says only this:
 
/* Now canonicalize by removing /. and /foo/.. if they appear.  */
 
I don't have C source for Emacs 21, but the behavior of 21 indicates that
that is when the change was made, presumably intentionally.  I searched
the change logs but found no mention of this change or why it was made.
 
Given that the change was presumably intentional (but why?), you might
not be disposed to consider this a bug.  I imagine you might argue that
the second arg to `expand-file-name' in this case is not a valid
directory name, so all bets are off wrt the behavior - or something like
that.  Or perhaps you will argue that `expand*' is supposed to give you
a canonicalized file name, and a name such as "c:/today//usr/share/"
cannot be said to be canonicalized.
 
IOW, I imagine there can be arguments that defend the current (Emacs
21+) behavior.
 
But shouldn't `expand-file-name' do the right thing if the second arg is
in fact `file-directory-p'?

For a user on GNU/Linux with $HOME = /home/toto":
(file-directory-p "~/today//usr") -> nil, but
(file-directory-p "~//usr/") --> t, and we have the same problem:
(expand-file-name "foo" "~//usr/") -> "/home/toto/usr/foo"
 
Surely the behavior here is buggy, no?  The result should be
"/home/toto//usr/foo", I would think.

It seems like any special treatment of double slashes should only
be an interactive treatment (e.g. ignore or remove everything up
to the second consecutive slash).  _Why should_ `expand-file-name'
collapse multiple consecutive slashes into a single slash?
 
I would start with that questiom.  What is the rationale for that
Emacs 21 change wrt Emacs 20.
 
In case it helps, I've attached the Emacs 20 code for `expand-file-name'.

Finally, I need the Emacs 20 behavior for this for some of my code.  If you
decide not to change the behavior for this in Emacs, can you suggest a good way
(in Lisp) to get the behavior I want - i.e., the Emacs 20 behavior?

I can try to work around this by substituting something for multiple consecutive
slashes, so that `expand*' won't collapse them, and then substituting back again
to get the slashes, but that is a truly gross and fragile hack.  I'm open to
better suggestions.  Thanks.

Oh, and another thing.  This behavior of `expand-file-name' is not documented.
If the decision is to keep this behavior, then the doc should let users know
that the function does this.  See the doc string - it describes everything the
function does (including `.' removal etc.) - except this.

In GNU Emacs 24.0.50.1 (i386-mingw-nt5.1.2600)
 of 2010-12-06 on 3249CTO
Windowing system distributor `Microsoft Corp.', version 5.1.2600
configured using `configure --with-gcc (4.4) --no-opt --cflags
-Ic:/imagesupport/include'
 

[-- Attachment #2: emacs-20-dot-7-expand-file-name.c --]
[-- Type: application/octet-stream, Size: 18800 bytes --]

DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
  "Convert filename NAME to absolute, and canonicalize it.\n\
Second arg DEFAULT-DIRECTORY is directory to start with if NAME is relative\n\
 (does not start with slash); if DEFAULT-DIRECTORY is nil or missing,\n\
the current buffer's value of default-directory is used.\n\
File name components that are `.' are removed, and \n\
so are file name components followed by `..', along with the `..' itself;\n\
note that these simplifications are done without checking the resulting\n\
file names in the file system.\n\
An initial `~/' expands to your home directory.\n\
An initial `~USER/' expands to USER's home directory.\n\
See also the function `substitute-in-file-name'.")
  (name, default_directory)
     Lisp_Object name, default_directory;
{
  unsigned char *nm;

  register unsigned char *newdir, *p, *o;
  int tlen;
  unsigned char *target;
  struct passwd *pw;
#ifdef VMS
  unsigned char * colon = 0;
  unsigned char * close = 0;
  unsigned char * slash = 0;
  unsigned char * brack = 0;
  int lbrack = 0, rbrack = 0;
  int dots = 0;
#endif /* VMS */
#ifdef DOS_NT
  int drive = 0;
  int collapse_newdir = 1;
  int is_escaped = 0;
#endif /* DOS_NT */
  int length;
  Lisp_Object handler;

  CHECK_STRING (name, 0);

  /* If the file name has special constructs in it,
     call the corresponding file handler.  */
  handler = Ffind_file_name_handler (name, Qexpand_file_name);
  if (!NILP (handler))
    return call3 (handler, Qexpand_file_name, name, default_directory);

  /* Use the buffer's default-directory if DEFAULT_DIRECTORY is omitted.  */
  if (NILP (default_directory))
    default_directory = current_buffer->directory;
  if (! STRINGP (default_directory))
    default_directory = build_string ("/");

  if (!NILP (default_directory))
    {
      handler = Ffind_file_name_handler (default_directory, Qexpand_file_name);
      if (!NILP (handler))
	return call3 (handler, Qexpand_file_name, name, default_directory);
    }

  o = XSTRING (default_directory)->data;

  /* Make sure DEFAULT_DIRECTORY is properly expanded.
     It would be better to do this down below where we actually use
     default_directory.  Unfortunately, calling Fexpand_file_name recursively
     could invoke GC, and the strings might be relocated.  This would
     be annoying because we have pointers into strings lying around
     that would need adjusting, and people would add new pointers to
     the code and forget to adjust them, resulting in intermittent bugs.
     Putting this call here avoids all that crud.

     The EQ test avoids infinite recursion.  */
  if (! NILP (default_directory) && !EQ (default_directory, name)
      /* Save time in some common cases - as long as default_directory
	 is not relative, it can be canonicalized with name below (if it
	 is needed at all) without requiring it to be expanded now.  */
#ifdef DOS_NT
      /* Detect MSDOS file names with drive specifiers.  */
      && ! (IS_DRIVE (o[0]) && IS_DEVICE_SEP (o[1]) && IS_DIRECTORY_SEP (o[2]))
#ifdef WINDOWSNT
      /* Detect Windows file names in UNC format.  */
      && ! (IS_DIRECTORY_SEP (o[0]) && IS_DIRECTORY_SEP (o[1]))
#endif
#else /* not DOS_NT */
      /* Detect Unix absolute file names (/... alone is not absolute on
	 DOS or Windows).  */
      && ! (IS_DIRECTORY_SEP (o[0]))
#endif /* not DOS_NT */
      )
    {
      struct gcpro gcpro1;

      GCPRO1 (name);
      default_directory = Fexpand_file_name (default_directory, Qnil);
      UNGCPRO;
    }

#ifdef VMS
  /* Filenames on VMS are always upper case.  */
  name = Fupcase (name);
#endif
#ifdef FILE_SYSTEM_CASE
  name = FILE_SYSTEM_CASE (name);
#endif

  nm = XSTRING (name)->data;

#ifdef DOS_NT
  /* We will force directory separators to be either all \ or /, so make
     a local copy to modify, even if there ends up being no change. */
  nm = strcpy (alloca (strlen (nm) + 1), nm);

  /* Note if special escape prefix is present, but remove for now.  */
  if (nm[0] == '/' && nm[1] == ':')
    {
      is_escaped = 1;
      nm += 2;
    }

  /* Find and remove drive specifier if present; this makes nm absolute
     even if the rest of the name appears to be relative.  Only look for
     drive specifier at the beginning.  */
  if (IS_DRIVE (nm[0]) && IS_DEVICE_SEP (nm[1]))
    {
      drive = nm[0];
      nm += 2;
    }

#ifdef WINDOWSNT
  /* If we see "c://somedir", we want to strip the first slash after the
     colon when stripping the drive letter.  Otherwise, this expands to
     "//somedir".  */
  if (drive && IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1]))
    nm++;
#endif /* WINDOWSNT */
#endif /* DOS_NT */

#ifdef WINDOWSNT
  /* Discard any previous drive specifier if nm is now in UNC format. */
  if (IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1]))
    {
      drive = 0;
    }
#endif

  /* If nm is absolute, look for /./ or /../ sequences; if none are
     found, we can probably return right away.  We will avoid allocating
     a new string if name is already fully expanded.  */
  if (
      IS_DIRECTORY_SEP (nm[0])
#ifdef MSDOS
      && drive && !is_escaped
#endif
#ifdef WINDOWSNT
      && (drive || IS_DIRECTORY_SEP (nm[1])) && !is_escaped
#endif
#ifdef VMS
      || index (nm, ':')
#endif /* VMS */
      )
    {
      /* If it turns out that the filename we want to return is just a
	 suffix of FILENAME, we don't need to go through and edit
	 things; we just need to construct a new string using data
	 starting at the middle of FILENAME.  If we set lose to a
	 non-zero value, that means we've discovered that we can't do
	 that cool trick.  */
      int lose = 0;

      p = nm;
      while (*p)
	{
	  /* Since we know the name is absolute, we can assume that each
	     element starts with a "/".  */

	  /* "." and ".." are hairy.  */
	  if (IS_DIRECTORY_SEP (p[0])
	      && p[1] == '.'
	      && (IS_DIRECTORY_SEP (p[2])
		  || p[2] == 0
		  || (p[2] == '.' && (IS_DIRECTORY_SEP (p[3])
				      || p[3] == 0))))
	    lose = 1;
#ifdef VMS
	  if (p[0] == '\\')
	    lose = 1;
	  if (p[0] == '/') {
	    /* if dev:[dir]/, move nm to / */
	    if (!slash && p > nm && (brack || colon)) {
	      nm = (brack ? brack + 1 : colon + 1);
	      lbrack = rbrack = 0;
	      brack = 0;
	      colon = 0;
	    }
	    slash = p;
	  }
	  if (p[0] == '-')
#ifndef VMS4_4
	    /* VMS pre V4.4,convert '-'s in filenames. */
	    if (lbrack == rbrack)
	      {
		if (dots < 2)   /* this is to allow negative version numbers */
		  p[0] = '_';
	      }
	    else
#endif /* VMS4_4 */
	      if (lbrack > rbrack &&
		  ((p[-1] == '.' || p[-1] == '[' || p[-1] == '<') &&
		   (p[1] == '.' || p[1] == ']' || p[1] == '>')))
		lose = 1;
#ifndef VMS4_4
	      else
		p[0] = '_';
#endif /* VMS4_4 */
	  /* count open brackets, reset close bracket pointer */
	  if (p[0] == '[' || p[0] == '<')
	    lbrack++, brack = 0;
	  /* count close brackets, set close bracket pointer */
	  if (p[0] == ']' || p[0] == '>')
	    rbrack++, brack = p;
	  /* detect ][ or >< */
	  if ((p[0] == ']' || p[0] == '>') && (p[1] == '[' || p[1] == '<'))
	    lose = 1;
	  if ((p[0] == ':' || p[0] == ']' || p[0] == '>') && p[1] == '~')
	    nm = p + 1, lose = 1;
	  if (p[0] == ':' && (colon || slash))
	    /* if dev1:[dir]dev2:, move nm to dev2: */
	    if (brack)
	      {
		nm = brack + 1;
		brack = 0;
	      }
	    /* if /name/dev:, move nm to dev: */
	    else if (slash)
	      nm = slash + 1;
	    /* if node::dev:, move colon following dev */
	    else if (colon && colon[-1] == ':')
	      colon = p;
	    /* if dev1:dev2:, move nm to dev2: */
	    else if (colon && colon[-1] != ':')
	      {
		nm = colon + 1;
		colon = 0;
	      }
	  if (p[0] == ':' && !colon)
	    {
	      if (p[1] == ':')
		p++;
	      colon = p;
	    }
	  if (lbrack == rbrack)
	    if (p[0] == ';')
	      dots = 2;
	    else if (p[0] == '.')
	      dots++;
#endif /* VMS */
	  p++;
	}
      if (!lose)
	{
#ifdef VMS
	  if (index (nm, '/'))
	    return build_string (sys_translate_unix (nm));
#endif /* VMS */
#ifdef DOS_NT
	  /* Make sure directories are all separated with / or \ as
	     desired, but avoid allocation of a new string when not
	     required. */
	  CORRECT_DIR_SEPS (nm);
#ifdef WINDOWSNT
	  if (IS_DIRECTORY_SEP (nm[1]))
	    {
	      if (strcmp (nm, XSTRING (name)->data) != 0)
		name = build_string (nm);
	    }
	  else
#endif
	  /* drive must be set, so this is okay */
	  if (strcmp (nm - 2, XSTRING (name)->data) != 0)
	    {
	      name = make_string (nm - 2, p - nm + 2);
	      XSTRING (name)->data[0] = DRIVE_LETTER (drive);
	      XSTRING (name)->data[1] = ':';
	    }
	  return name;
#else /* not DOS_NT */
	  if (nm == XSTRING (name)->data)
	    return name;
	  return build_string (nm);
#endif /* not DOS_NT */
	}
    }

  /* At this point, nm might or might not be an absolute file name.  We
     need to expand ~ or ~user if present, otherwise prefix nm with
     default_directory if nm is not absolute, and finally collapse /./
     and /foo/../ sequences.

     We set newdir to be the appropriate prefix if one is needed:
       - the relevant user directory if nm starts with ~ or ~user
       - the specified drive's working dir (DOS/NT only) if nm does not
         start with /
       - the value of default_directory.

     Note that these prefixes are not guaranteed to be absolute (except
     for the working dir of a drive).  Therefore, to ensure we always
     return an absolute name, if the final prefix is not absolute we
     append it to the current working directory.  */

  newdir = 0;

  if (nm[0] == '~')		/* prefix ~ */
    {
      if (IS_DIRECTORY_SEP (nm[1])
#ifdef VMS
	  || nm[1] == ':'
#endif /* VMS */
	  || nm[1] == 0)	/* ~ by itself */
	{
	  if (!(newdir = (unsigned char *) egetenv ("HOME")))
	    newdir = (unsigned char *) "";
	  nm++;
#ifdef DOS_NT
	  collapse_newdir = 0;
#endif
#ifdef VMS
	  nm++;			/* Don't leave the slash in nm.  */
#endif /* VMS */
	}
      else			/* ~user/filename */
	{
	  for (p = nm; *p && (!IS_DIRECTORY_SEP (*p)
#ifdef VMS
			      && *p != ':'
#endif /* VMS */
			      ); p++);
	  o = (unsigned char *) alloca (p - nm + 1);
	  bcopy ((char *) nm, o, p - nm);
	  o [p - nm] = 0;

	  pw = (struct passwd *) getpwnam (o + 1);
	  if (pw)
	    {
	      newdir = (unsigned char *) pw -> pw_dir;
#ifdef VMS
	      nm = p + 1;	/* skip the terminator */
#else
	      nm = p;
#ifdef DOS_NT
	      collapse_newdir = 0;
#endif
#endif /* VMS */
	    }

	  /* If we don't find a user of that name, leave the name
	     unchanged; don't move nm forward to p.  */
	}
    }

#ifdef DOS_NT
  /* On DOS and Windows, nm is absolute if a drive name was specified;
     use the drive's current directory as the prefix if needed.  */
  if (!newdir && drive)
    {
      /* Get default directory if needed to make nm absolute. */
      if (!IS_DIRECTORY_SEP (nm[0]))
	{
	  newdir = alloca (MAXPATHLEN + 1);
	  if (!getdefdir (toupper (drive) - 'A' + 1, newdir))
	    newdir = NULL;
	}
      if (!newdir)
	{
	  /* Either nm starts with /, or drive isn't mounted. */
	  newdir = alloca (4);
	  newdir[0] = DRIVE_LETTER (drive);
	  newdir[1] = ':';
	  newdir[2] = '/';
	  newdir[3] = 0;
	}
    }
#endif /* DOS_NT */

  /* Finally, if no prefix has been specified and nm is not absolute,
     then it must be expanded relative to default_directory. */

  if (1
#ifndef DOS_NT
      /* /... alone is not absolute on DOS and Windows. */
      && !IS_DIRECTORY_SEP (nm[0])
#endif
#ifdef WINDOWSNT
      && !(IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1]))
#endif
#ifdef VMS
      && !index (nm, ':')
#endif
      && !newdir)
    {
      newdir = XSTRING (default_directory)->data;
#ifdef DOS_NT
      /* Note if special escape prefix is present, but remove for now.  */
      if (newdir[0] == '/' && newdir[1] == ':')
	{
	  is_escaped = 1;
	  newdir += 2;
	}
#endif
    }

#ifdef DOS_NT
  if (newdir)
    {
      /* First ensure newdir is an absolute name. */
      if (
	  /* Detect MSDOS file names with drive specifiers.  */
	  ! (IS_DRIVE (newdir[0])
	     && IS_DEVICE_SEP (newdir[1]) && IS_DIRECTORY_SEP (newdir[2]))
#ifdef WINDOWSNT
	  /* Detect Windows file names in UNC format.  */
	  && ! (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1]))
#endif
	  )
	{
	  /* Effectively, let newdir be (expand-file-name newdir cwd).
	     Because of the admonition against calling expand-file-name
	     when we have pointers into lisp strings, we accomplish this
	     indirectly by prepending newdir to nm if necessary, and using
	     cwd (or the wd of newdir's drive) as the new newdir. */

	  if (IS_DRIVE (newdir[0]) && newdir[1] == ':')
	    {
	      drive = newdir[0];
	      newdir += 2;
	    }
	  if (!IS_DIRECTORY_SEP (nm[0]))
	    {
	      char * tmp = alloca (strlen (newdir) + strlen (nm) + 2);
	      file_name_as_directory (tmp, newdir);
	      strcat (tmp, nm);
	      nm = tmp;
	    }
	  newdir = alloca (MAXPATHLEN + 1);
	  if (drive)
	    {
	      if (!getdefdir (toupper (drive) - 'A' + 1, newdir))
		newdir = "/";
	    }
	  else
	    getwd (newdir);
	}

      /* Strip off drive name from prefix, if present. */
      if (IS_DRIVE (newdir[0]) && newdir[1] == ':')
	{
	  drive = newdir[0];
	  newdir += 2;
	}

      /* Keep only a prefix from newdir if nm starts with slash
         (//server/share for UNC, nothing otherwise).  */
      if (IS_DIRECTORY_SEP (nm[0]) && collapse_newdir)
	{
#ifdef WINDOWSNT
	  if (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1]))
	    {
	      newdir = strcpy (alloca (strlen (newdir) + 1), newdir);
	      p = newdir + 2;
	      while (*p && !IS_DIRECTORY_SEP (*p)) p++;
	      p++;
	      while (*p && !IS_DIRECTORY_SEP (*p)) p++;
	      *p = 0;
	    }
	  else
#endif
	    newdir = "";
	}
    }
#endif /* DOS_NT */

  if (newdir)
    {
      /* Get rid of any slash at the end of newdir, unless newdir is
	 just / or // (an incomplete UNC name).  */
      length = strlen (newdir);
      if (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1])
#ifdef WINDOWSNT
	  && !(length == 2 && IS_DIRECTORY_SEP (newdir[0]))
#endif
	  )
	{
	  unsigned char *temp = (unsigned char *) alloca (length);
	  bcopy (newdir, temp, length - 1);
	  temp[length - 1] = 0;
	  newdir = temp;
	}
      tlen = length + 1;
    }
  else
    tlen = 0;

  /* Now concatenate the directory and name to new space in the stack frame */
  tlen += strlen (nm) + 1;
#ifdef DOS_NT
  /* Reserve space for drive specifier and escape prefix, since either
     or both may need to be inserted.  (The Microsoft x86 compiler
     produces incorrect code if the following two lines are combined.)  */
  target = (unsigned char *) alloca (tlen + 4);
  target += 4;
#else  /* not DOS_NT */
  target = (unsigned char *) alloca (tlen);
#endif /* not DOS_NT */
  *target = 0;

  if (newdir)
    {
#ifndef VMS
      if (nm[0] == 0 || IS_DIRECTORY_SEP (nm[0]))
	{
#ifdef DOS_NT
	  /* If newdir is effectively "C:/", then the drive letter will have
	     been stripped and newdir will be "/".  Concatenating with an
	     absolute directory in nm produces "//", which will then be
	     incorrectly treated as a network share.  Ignore newdir in
	     this case (keeping the drive letter).  */
	  if (!(drive && nm[0] && IS_DIRECTORY_SEP (newdir[0]) 
		&& newdir[1] == '\0'))
#endif
	    strcpy (target, newdir);
	}
      else
#endif
	file_name_as_directory (target, newdir);
    }

  strcat (target, nm);
#ifdef VMS
  if (index (target, '/'))
    strcpy (target, sys_translate_unix (target));
#endif /* VMS */

  /* ASSERT (IS_DIRECTORY_SEP (target[0])) if not VMS */

  /* Now canonicalize by removing /. and /foo/.. if they appear.  */

  p = target;
  o = target;

  while (*p)
    {
#ifdef VMS
      if (*p != ']' && *p != '>' && *p != '-')
	{
	  if (*p == '\\')
	    p++;
	  *o++ = *p++;
	}
      else if ((p[0] == ']' || p[0] == '>') && p[0] == p[1] + 2)
	/* brackets are offset from each other by 2 */
	{
	  p += 2;
	  if (*p != '.' && *p != '-' && o[-1] != '.')
	    /* convert [foo][bar] to [bar] */
	    while (o[-1] != '[' && o[-1] != '<')
	      o--;
	  else if (*p == '-' && *o != '.')
	    *--p = '.';
	}
      else if (p[0] == '-' && o[-1] == '.' &&
	       (p[1] == '.' || p[1] == ']' || p[1] == '>'))
	/* flush .foo.- ; leave - if stopped by '[' or '<' */
	{
	  do
	    o--;
	  while (o[-1] != '.' && o[-1] != '[' && o[-1] != '<');
	  if (p[1] == '.')      /* foo.-.bar ==> bar.  */
	    p += 2;
	  else if (o[-1] == '.') /* '.foo.-]' ==> ']' */
	    p++, o--;
	  /* else [foo.-] ==> [-] */
	}
      else
	{
#ifndef VMS4_4
	  if (*p == '-' &&
	      o[-1] != '[' && o[-1] != '<' && o[-1] != '.' &&
	      p[1] != ']' && p[1] != '>' && p[1] != '.')
	    *p = '_';
#endif /* VMS4_4 */
	  *o++ = *p++;
	}
#else /* not VMS */
      if (!IS_DIRECTORY_SEP (*p))
	{
	  *o++ = *p++;
	}
      else if (IS_DIRECTORY_SEP (p[0])
	       && p[1] == '.'
	       && (IS_DIRECTORY_SEP (p[2])
		   || p[2] == 0))
	{
	  /* If "/." is the entire filename, keep the "/".  Otherwise,
	     just delete the whole "/.".  */
	  if (o == target && p[2] == '\0')
	    *o++ = *p;
	  p += 2;
	}
      else if (IS_DIRECTORY_SEP (p[0]) && p[1] == '.' && p[2] == '.'
	       /* `/../' is the "superroot" on certain file systems.  */
	       && o != target
	       && (IS_DIRECTORY_SEP (p[3]) || p[3] == 0))
	{
	  while (o != target && (--o) && !IS_DIRECTORY_SEP (*o))
	    ;
	  /* Keep initial / only if this is the whole name.  */
	  if (o == target && IS_ANY_SEP (*o) && p[3] == 0)
	    ++o;
	  p += 3;
	}
      else
	{
	  *o++ = *p++;
	}
#endif /* not VMS */
    }

#ifdef DOS_NT
  /* At last, set drive name. */
#ifdef WINDOWSNT
  /* Except for network file name.  */
  if (!(IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1])))
#endif /* WINDOWSNT */
    {
      if (!drive) abort ();
      target -= 2;
      target[0] = DRIVE_LETTER (drive);
      target[1] = ':';
    }
  /* Reinsert the escape prefix if required.  */
  if (is_escaped)
    {
      target -= 2;
      target[0] = '/';
      target[1] = ':';
    }
  CORRECT_DIR_SEPS (target);
#endif /* DOS_NT */

  return make_string (target, o - target);
}

^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-11 21:53 bug#7617: 24.0.50; `expand-file-name': removal of slashes Drew Adams
@ 2010-12-12 15:58 ` Eli Zaretskii
  2010-12-12 18:03   ` Drew Adams
  2010-12-12 20:39   ` Eli Zaretskii
  2010-12-12 21:04 ` Andreas Schwab
  1 sibling, 2 replies; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-12 15:58 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Date: Sat, 11 Dec 2010 13:53:38 -0800
> Cc: 
> 
> emacs -Q
>  
> (expand-file-name "share/" "~/today//usr/") ->
> c:/today/usr/share/.
> 
> (On my Windows laptop, `~' is just `c:/'.)
> 
> IOW, multiple consecutive slashes are collapsed to one.

Correct.

> This is not the behavior I want.  This behavior started in Emacs 21;
> in Emacs 20 no such removal of slashes occurs.

Indeed, this change was made in preparation for release of Emacs 21.1:

  2001-03-02  Gerd Moellmann  <gerd@gnu.org>

	  * fileio.c (Fexpand_file_name): Collapse sequences of slashes
	  to a single slash in the middle of file names.

> I would consider this loss of slashes a bug.

Please explain why you consider this a bug.  foo//bar is at best
equivalent to foo/bar, and at worst simply fails various system calls
and naive Lisp code that doesn't expect more than one slash in a row.

> But I see that comments in
> the current C code indicate that it is intentional (but not _why_).

I didn't find any related discussions that would explain the immediate
reasons for the above change.  So I don't know (and certainly don't
remember ;-) why it was made.  But I do think it's the right behavior
for expand-file-name, because other primitives and Lisp code normally
expects a canonicalized file name, so it would make sense to have a
single primitive that produces such canonicalized file names.

> I don't have C source for Emacs 21, but the behavior of 21 indicates that
> that is when the change was made, presumably intentionally.  I searched
> the change logs but found no mention of this change or why it was made.

See above, for the change log, but it is not really helpful in saying
why (nor should it be).  I haven't found anything in NEWS, either.

> Given that the change was presumably intentional (but why?), you might
> not be disposed to consider this a bug.  I imagine you might argue that
> the second arg to `expand-file-name' in this case is not a valid
> directory name, so all bets are off wrt the behavior - or something like
> that.  Or perhaps you will argue that `expand*' is supposed to give you
> a canonicalized file name, and a name such as "c:/today//usr/share/"
> cannot be said to be canonicalized.

Right, on both counts.

> But shouldn't `expand-file-name' do the right thing if the second arg is
> in fact `file-directory-p'?
> 
> For a user on GNU/Linux with $HOME = /home/toto":
> (file-directory-p "~/today//usr") -> nil, but
> (file-directory-p "~//usr/") --> t, and we have the same problem:
> (expand-file-name "foo" "~//usr/") -> "/home/toto/usr/foo"
>  
> Surely the behavior here is buggy, no?

Sorry, I don't see a bug here.  Please explain more.

> The result should be "/home/toto//usr/foo", I would think.

Not clear why.  As I said, "/home/toto//usr/foo" is at best equivalent
to "/home/toto/usr/foo", and at worst will simply fail in another
place.  So what does the former give you that the latter doesn't?

> _Why should_ `expand-file-name' collapse multiple consecutive
> slashes into a single slash?

In order to produce a _canonicalized_ file name.

> Finally, I need the Emacs 20 behavior for this for some of my code.

Can you describe the use-case where the old behavior is needed?

> Oh, and another thing.  This behavior of `expand-file-name' is not documented.

Well, one could argue that "canonicalized" does mean removing
consecutive slashes, but I guess it's easy enough to say that
explicitly in the doc string.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 15:58 ` Eli Zaretskii
@ 2010-12-12 18:03   ` Drew Adams
  2010-12-12 19:33     ` Eli Zaretskii
                       ` (2 more replies)
  2010-12-12 20:39   ` Eli Zaretskii
  1 sibling, 3 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-12 18:03 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> Please explain why you consider this a bug.  foo//bar is at best
> equivalent to foo/bar,

In what way is that _ever_ equivalent?  Please show the equivalence for any of
the OS's that Emacs supports.

In interactive use, `...//abc...' is treated as just `/abc...' by Emacs.  The
prefix `.../' is ignored until you hit `RET'.  And then the prefix is dropped
when you enter the file name (`RET') - but only then.  And that's appropriate.
Other than when you actually hit `RET', the prefix should not be removed but
simply ignored.

Ignoring the prefix is a user convenience.  Dropping it when you hit `RET'
guarantees that the caller (e.g. a file-system utility) never sees it.  So much
for treating the `...//abc...' as `/abc...'.

As to collapsing multiple consecutive slashes, which is something completely
different - what is the rationale for that?  You claim that `foo//bar' is
equivalent to `foo/bar'.  Where do you get that?  Unix?  GNU/Linux?  Windows?
VMS?  I don't think so.

If they were equivalent, the Emacs would transform `...//abc...' to just
`.../abc...' when you hit `RET', not transform it to `/abc...'.  Try changing
that behavior and see how many Emacs users give you their opinion about how
equivalent `//' and `/' are. ;-)

> and at worst simply fails various system calls
> and naive Lisp code that doesn't expect more than one slash in a row.

Red herring.  When such a name is actually _entered_ (`RET') the prefix `.../'
is removed.  So there is no way that system calls see such a name.

And arguing on the basis of some supposed "naive Lisp code" is pretty weak.
Which Lisp code currently depends on this collapsing?

And you know, if you introduce _any_ change in behavior, no matter how bad, some
Lisp code will eventually adapt to it and thus expect it.  That is no argument
at all.

> > But I see that comments in
> > the current C code indicate that it is intentional (but not _why_).
> 
> I didn't find any related discussions that would explain the immediate
> reasons for the above change.  So I don't know (and certainly don't
> remember ;-) why it was made.  But I do think it's the right behavior
> for expand-file-name, because other primitives and Lisp code normally
> expects a canonicalized file name, so it would make sense to have a
> single primitive that produces such canonicalized file names.

Then please create a separate primitive the does just that.

Currently, there is no way to maintain the integrity of the user's input if that
input is passed through `expand-file-name' to do what the doc says `expand*'
does.

Currently, using `expand*' on input can easily change perfectly valid input
(valid for Emacs, since it ignores and ultimately removes the prefix `.../')
into input that is invalid (represents no file at all).

IOW, please separate this slash-collapsing from what `expand-file-name' is
actually _documented_ to do - there is nothing about slash collapsing in
`expand*'s contract with its users.

Make a separate function `collapse-slashes' which does (only) that, if you like.
Then the cleaned-up `expand-file-name' can be used alone on user input without
changing valid references to actual files into non-matches.

Or if you prefer, leave `expand*' the way it is (but document the
slash-collapsing), and create another primitive that does only what `expand*' is
documented as doing: everything except the slash-collapsing.  

That new primitive would ignore a `...//' prefix up to the second slash - the
same way Emacs ignores it during input.  That primitive would simply expand the
file name wrt the dir argument, removing `.' etc. as documented now - in sum,
exactly what we _say_ `expand*' does, and no more.

I don't really care which is done: clean up `expand*' or provide a new function
that does what we _say_ `expand*' does (and what it actually did prior to Emacs
21).

Either way I will have a function I can call to get the currently _documented_
`expand*' behavior (and only that).  I will be able to apply it to user input
and know that I haven't suddenly moved the target of the input from one file to
another (probably nonexistent) one.

> > But shouldn't `expand-file-name' do the right thing if the 
> > second arg is in fact `file-directory-p'?
> > 
> > For a user on GNU/Linux with $HOME = /home/toto":
> > (file-directory-p "~/today//usr") -> nil, but
> > (file-directory-p "~//usr/") --> t, and we have the same problem:
> > (expand-file-name "foo" "~//usr/") -> "/home/toto/usr/foo"
> >  
> > Surely the behavior here is buggy, no?
> 
> Sorry, I don't see a bug here.  Please explain more.
> 
> > The result should be "/home/toto//usr/foo", I would think.
> 
> Not clear why.  As I said, "/home/toto//usr/foo" is at best equivalent
> to "/home/toto/usr/foo",

It is not equivalent - neither at best nor at worst - not at all.

In Emacs, file-name input `/home/toto//usr/foo' is interpreted as `/usr/foo'
when you enter it.  That _is_ an equivalence (for Emacs file-name input).  The
former is not valid for the file system, of course, but in Emacs the two names
are _equivalent as input_.

Assuming the file `/usr/foo' exists, `/home/toto//usr/foo' targets it validly
(in Emacs).  But `/home/toto/usr/foo' is not valid: Most likely no such file
exists, and if it did exist it would anyway not be the same file as `/usr/foo'.

That's the point.  Valid file-name input (for Emacs) is in this case changed by
`expand-file-name' into invalid input (invalid in the sense of not representing
the same file).  Not good; a bug.

> and at worst will simply fail in another
> place.  So what does the former give you that the latter doesn't?

See above.  It maintains the integrity of the user's input:
`/home/toto//usr/foo' is, as Emacs file-name input, equivalent to `/usr/foo'.
`/home/toto/usr/foo' is _not_ equivalent.

> > _Why should_ `expand-file-name' collapse multiple consecutive
> > slashes into a single slash?
> 
> In order to produce a _canonicalized_ file name.

"Canonicalization" should not change which file is targeted.  Just because you
call the behavior "canonicalization" does not mean that the transformation is a
good one.  That's a word game.

If the user's input targets file `/usr/foo', true canonicalization should not
change the target to `/home/toto/usr/foo', which is something completely
different.

> > Finally, I need the Emacs 20 behavior for this for some of my code.
> 
> Can you describe the use-case where the old behavior is needed?

No, and it is irrelevant.  I need the behavior of `expand-file-name' minus the
slash-collapsing part.  It does not matter why I need such a function.

If `expand-file-name' were defined in Lisp I would just gather the part of it
that I need into a new function.  But I cannot do that because it is in C.

But why is it in C, BTW?  Is that really necessary?  If you argue that some
parts of it deal with platform issues and so are better handled in C, then I ask
why not leave only those parts in C and move the actual logic of the function -
what it is documented as _doing_, to Lisp?  Expansion of the file name, removal
of `.' etc. could be done in Lisp with if necessary  sub-calls to C primitives
to perform any platform-dependent stuff that cannot be done in Lisp.

When Emacs code is in C it is much more difficult for users to (re)use it.

> > Oh, and another thing.  This behavior of `expand-file-name' 
> > is not documented.
> 
> Well, one could argue that "canonicalized" does mean removing
> consecutive slashes, 

No, one cannot argue that honestly.  You could define any transformation you
like, call it "canonicalization", and then claim that all of its behavior is
documented as soon as you have simply said that it "canonicalizes".  That's
completely disingenuous.

> but I guess it's easy enough to say that explicitly in the doc string.

Please add this missing information to the doc string, if you refuse to see the
misbehavior bug.

And I hope I can get some information about how, in Lisp, to get the `expand*'
behavior minus the target-changing slash-collapsing.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 18:03   ` Drew Adams
@ 2010-12-12 19:33     ` Eli Zaretskii
  2010-12-12 20:21       ` Drew Adams
  2010-12-12 22:35       ` Drew Adams
  2010-12-12 20:15     ` Andreas Schwab
  2010-12-13  0:49     ` Jason Rumney
  2 siblings, 2 replies; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-12 19:33 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: <7617@debbugs.gnu.org>
> Date: Sun, 12 Dec 2010 10:03:08 -0800
> 
> > Please explain why you consider this a bug.  foo//bar is at best
> > equivalent to foo/bar,
> 
> In what way is that _ever_ equivalent?  Please show the equivalence for any of
> the OS's that Emacs supports.

On Unix and GNU/Linux -- always, in the sense that file-related system
calls will treat them the same.  On Windows -- sometimes, depending on
the implementation of the standard C library.

> In interactive use, `...//abc...' is treated as just `/abc...' by Emacs.  The
> prefix `.../' is ignored until you hit `RET'.  And then the prefix is dropped
> when you enter the file name (`RET') - but only then.  And that's appropriate.
> Other than when you actually hit `RET', the prefix should not be removed but
> simply ignored.

That's right, but I was talking about non-interactive uses.  I believe
in interactive use, Emacs still does what you (and I) expect.

> Then please create a separate primitive the does just that.

Feel free to file a separate feature request for that.

> Currently, there is no way to maintain the integrity of the user's input if that
> input is passed through `expand-file-name' to do what the doc says `expand*'
> does.

If all you need is ignore everything before the double slash, there
are easier ways than asking to change a primitive.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 18:03   ` Drew Adams
  2010-12-12 19:33     ` Eli Zaretskii
@ 2010-12-12 20:15     ` Andreas Schwab
  2010-12-12 20:25       ` Drew Adams
  2010-12-13  0:49     ` Jason Rumney
  2 siblings, 1 reply; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 20:15 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

"Drew Adams" <drew.adams@oracle.com> writes:

> In interactive use, `...//abc...' is treated as just `/abc...' by
> Emacs.

This has nothing to do with expand-file-name, this is the function of
substitute-in-file-name.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 19:33     ` Eli Zaretskii
@ 2010-12-12 20:21       ` Drew Adams
  2010-12-12 20:32         ` Eli Zaretskii
                           ` (2 more replies)
  2010-12-12 22:35       ` Drew Adams
  1 sibling, 3 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-12 20:21 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> > In interactive use, `...//abc...' is treated as just 
> > `/abc...' by Emacs.  The prefix `.../' is ignored until
> > you hit `RET'.  And then the prefix is dropped
> > when you enter the file name (`RET') - but only then.
> > And that's appropriate.  Other than when you actually
> > hit `RET', the prefix should not be removed but
> > simply ignored.
> 
> That's right, but I was talking about non-interactive uses.  I believe
> in interactive use, Emacs still does what you (and I) expect.

This is not about existing calls to `expand-file-name' in the vanilla Emacs
sources.

I am using the function `expand*' on user input (one operation among many), to
do just what is advertised for `expand*' - just the kind of canonicalization
that is described, which does not include collapsing consecutive slashes.

`expand*' does not do what is documented for it.  The choices - for this bug
report - are to either fix that behavior or provide a function that does what we
claim `expand*' does.  That is what this bug report is about: providing that
functionality, one way or the other.

> > Then please create a separate primitive the does just that.
> 
> Feel free to file a separate feature request for that.

_This_ bug is about the need for a function that does that.  The doc for
`expand*' describes it as exactly that function.  If you do not want to fix the
`expand*' behavior to reflect its description, then please provide the function
under a different name from `extract*'.  You already have the doc string for it.
;-)
 
I would think that you would want to fix the `extract*' behavior, since it can
lead to changing which file is targeted.  But if you do not want to face that,
then please at least provide some other way to get the behavior that is
documented.

> > Currently, there is no way to maintain the integrity of the 
> > user's input if that input is passed through `expand-file-name'
> > to do what the doc says `expand*' does.
> 
> If all you need is ignore everything before the double slash, there
> are easier ways than asking to change a primitive.

You are just running away, Eli.  (Why am I not surprised?)

Stop inventing.  I did not say that I need only to ignore everything before the
double slash.  If that were all I needed I would just do that in Lisp - no need
for any Emacs primitive for that.

I said (very clearly) that I need to do just what `expand*' is contracted to do
- _everything_ that its doc says it does, and no more.

You are ignoring the fact that collapsing consecutive slashes typically leads to
changing which file is targeted.  I gave a simple example, but you have nothing
to say about it.  That, to me, is a clear bug in the `expand*' behavior.  But if
you want to ignore that misbehavior, that's your privilege.

I hope that you will at least provide the functionality I asked for, if only via
a different function from `extract*'.

Barring that, I hope you will tell me how, in Lisp, to get the effect that
`expand*' is documented to have, that is, the effect that it had before Emacs
21.







^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:15     ` Andreas Schwab
@ 2010-12-12 20:25       ` Drew Adams
  2010-12-12 20:36         ` Andreas Schwab
  0 siblings, 1 reply; 28+ messages in thread
From: Drew Adams @ 2010-12-12 20:25 UTC (permalink / raw)
  To: 'Andreas Schwab'; +Cc: 7617

> > In interactive use, `...//abc...' is treated as just `/abc...' by
> > Emacs.
> 
> This has nothing to do with expand-file-name, this is the function of
> substitute-in-file-name.

I know that.  I never said that `expand-file-name' removed the prefix or that it
should remove the prefix.  In fact I said clearly that it should not.  I do not
want to remove _any_ of the prefix, including its slash.

I am not interested in obtaining the behavior of `substitute-file-name'.  I am
interested in obtaining the documented behavior of `expand-file-name', which
does not include collapsing consecutive slashes.

Can you tell me how to get that behavior?






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:21       ` Drew Adams
@ 2010-12-12 20:32         ` Eli Zaretskii
  2010-12-12 20:36           ` Drew Adams
  2010-12-12 20:42         ` Andreas Schwab
  2010-12-13  3:53         ` Stefan Monnier
  2 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-12 20:32 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: <7617@debbugs.gnu.org>
> Date: Sun, 12 Dec 2010 12:21:31 -0800
> 
> You are just running away, Eli.  (Why am I not surprised?)
> 
> Stop inventing.

Sigh.

> I did not say that I need only to ignore everything before the
> double slash.

You didn't say anything, actually.  I have no idea what is the problem
you are trying to solve, and you refuse to describe it.  Sorry, I'm
not interested in this conversation anymore, not with that attitude of
yours.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:25       ` Drew Adams
@ 2010-12-12 20:36         ` Andreas Schwab
  2010-12-12 20:42           ` Drew Adams
  0 siblings, 1 reply; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 20:36 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

"Drew Adams" <drew.adams@oracle.com> writes:

> Can you tell me how to get that behavior?

Whatfor?  I cannot think of _any_ useful use.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:32         ` Eli Zaretskii
@ 2010-12-12 20:36           ` Drew Adams
  0 siblings, 0 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-12 20:36 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> Sigh.

Sigh, indeed.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 15:58 ` Eli Zaretskii
  2010-12-12 18:03   ` Drew Adams
@ 2010-12-12 20:39   ` Eli Zaretskii
  1 sibling, 0 replies; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-12 20:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 7617

> From: Eli Zaretskii <eliz@gnu.org>
> Date: Sun, 12 Dec 2010 10:58:26 -0500
> Cc: 7617@debbugs.gnu.org
> 
> I guess it's easy enough to say that explicitly in the doc string.

Done.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:21       ` Drew Adams
  2010-12-12 20:32         ` Eli Zaretskii
@ 2010-12-12 20:42         ` Andreas Schwab
  2010-12-13  3:53         ` Stefan Monnier
  2 siblings, 0 replies; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 20:42 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

"Drew Adams" <drew.adams@oracle.com> writes:

> `expand*' does not do what is documented for it.

It canonicalizes the file name.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:36         ` Andreas Schwab
@ 2010-12-12 20:42           ` Drew Adams
  2010-12-12 21:00             ` Andreas Schwab
  0 siblings, 1 reply; 28+ messages in thread
From: Drew Adams @ 2010-12-12 20:42 UTC (permalink / raw)
  To: 'Andreas Schwab'; +Cc: 7617

> > Can you tell me how to get that behavior?
> 
> Whatfor?  I cannot think of _any_ useful use.

The class of useful uses is not limited to those you can think of, your
Excellency.

But I take it that you have nothing to say about how to obtain this behavior in
Lisp.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:42           ` Drew Adams
@ 2010-12-12 21:00             ` Andreas Schwab
  0 siblings, 0 replies; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 21:00 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

So you cannot name any useful use?

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-11 21:53 bug#7617: 24.0.50; `expand-file-name': removal of slashes Drew Adams
  2010-12-12 15:58 ` Eli Zaretskii
@ 2010-12-12 21:04 ` Andreas Schwab
  1 sibling, 0 replies; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 21:04 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

"Drew Adams" <drew.adams@oracle.com> writes:

> The result should be "/home/toto//usr/foo", I would think.

That is not a canonical file name, Your Royal Highness.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 19:33     ` Eli Zaretskii
  2010-12-12 20:21       ` Drew Adams
@ 2010-12-12 22:35       ` Drew Adams
  2010-12-12 23:40         ` Andreas Schwab
  2010-12-13  5:17         ` Eli Zaretskii
  1 sibling, 2 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-12 22:35 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> On Unix and GNU/Linux -- always, in the sense that file-related system
> calls will treat them the same.  On Windows -- sometimes, depending on
> the implementation of the standard C library.

A last attempt to communicate, in hopes that you actually were trying to
understand but I didn't explain myself well enough.

I understand that a file system might treat ...//... the same as .../....  From
a file-system point of view, the two might well be equivalent (the file system
ignores the extra slash).

But Emacs treats ...//... in the minibuffer as a file name that is equivalent to
/... -  as far as user input is concerned, ...//... is equivalent to /....  And
as user input .../... generally targets a very different file than does ...//...
- they are not equivalent.

So the question for `expand-file-name' is what kind of file-name arguments is it
supposed to handle - what are the arguments to `expand-file-name' expected to
be?

If we say that they must be strings acceptable to the file system, then the
current behavior is correct, and the only fix needed for `expand-file-name' is
wrt the doc (mention the slash collapse and say what forms the args must take -
and what happens if they are not of the right form).

If we say that the args can be any strings that Emacs accepts as file names
during input, then the story is different, and the proper fix is the behavior I
suggested (the Emacs 20 behavior).  In that case, `expand-file-name' is more
tolerant, accepting a wider class of strings.

And in that case, the resultant canonicalization does not necessarily produce a
file name acceptable to the file system, even if it produces a name acceptable
as file-name input for Emacs.  (Just as, in the other case, canonicalization of
...//... generally makes the name target the wrong file.)

IOW, the question here is about the purpose of `expand*'.  The behavior changed
from Emacs 20 to 21, with apparently no record of why.

My code that DTRT in Emacs 20 breaks with later versions because of this change,
so I'm looking for a solution.  My own need here remains the same regardless of
what is decided about the intention wrt `expand*'s args.  I need a function that
has the latter behavior (but it need not be named `expand-file-name').

Besides a workable but ugly replace-regexp-in-string hack that saves and
restores consecutive slashes around a call to `expand-file-name', I don't see a
good way to do that.  In this case, consider this bug report a request for such
a function (enhancement request).

And also in this case - i.e., if it is decided that the input args must be
acceptable to the file system, that constraint on the args should be documented.
And we should say what the function does/returns otherwise (e.g., say that it is
undefined - i.e., that it is defined only in the case where the args are
acceptable names for the file system).  If we tell users that the args must be
of a form acceptable by the current file system, then we should also tell them
what happens otherwise (raise an error?...).






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 22:35       ` Drew Adams
@ 2010-12-12 23:40         ` Andreas Schwab
  2010-12-13  5:17         ` Eli Zaretskii
  1 sibling, 0 replies; 28+ messages in thread
From: Andreas Schwab @ 2010-12-12 23:40 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

"Drew Adams" <drew.adams@oracle.com> writes:

> IOW, the question here is about the purpose of `expand*'.

Returning a canonicalized file name, as documented.

> The behavior changed from Emacs 20 to 21, with apparently no record of
> why.

It's an obvious bug fix.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 18:03   ` Drew Adams
  2010-12-12 19:33     ` Eli Zaretskii
  2010-12-12 20:15     ` Andreas Schwab
@ 2010-12-13  0:49     ` Jason Rumney
  2 siblings, 0 replies; 28+ messages in thread
From: Jason Rumney @ 2010-12-13  0:49 UTC (permalink / raw)
  To: bug-gnu-emacs

On 13/12/2010 02:03, Drew Adams wrote:
> In interactive use, `...//abc...' is treated as just `/abc...' by Emacs.

That is a convenient shortcut for interactive use.  For expand-file-name 
it could open security holes where filename input is coming from 
untrusted sources, so code wants to ensure that the files given are 
contained in a safe directory.

>>> Finally, I need the Emacs 20 behavior for this for some of my code.
>>>        
>> Can you describe the use-case where the old behavior is needed?
>>      
> No, and it is irrelevant.

And you have just made this report irrelevant too, as the developers can 
see no valid reason to make the change you are requesting.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 20:21       ` Drew Adams
  2010-12-12 20:32         ` Eli Zaretskii
  2010-12-12 20:42         ` Andreas Schwab
@ 2010-12-13  3:53         ` Stefan Monnier
  2010-12-13  4:32           ` Drew Adams
  2 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2010-12-13  3:53 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> I am using the function `expand*' on user input (one operation among
> many), to do just what is advertised for `expand*'

There-s your problem.  You treat user input in a `read-file-name' as an
actual file name (i.e. suitable to pass to functions like
expand-file-name), whereas it is actually a different beast, closely
related, but different: it has this "..// -> /" and ".../~ -> ~" rules,
as well as env-var expansion (which also implies "$$ -> $" rewrite).
And even these special rules depend on file-name-handlers so, e.g., they
do not apply to URLs but other rules may aaply to other
special filenames.  I.e. the only safe thing to do is to first pass those
strings through substitute-for-file-name.


        Stefan





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13  3:53         ` Stefan Monnier
@ 2010-12-13  4:32           ` Drew Adams
  2010-12-13  5:23             ` Eli Zaretskii
  0 siblings, 1 reply; 28+ messages in thread
From: Drew Adams @ 2010-12-13  4:32 UTC (permalink / raw)
  To: 'Stefan Monnier'; +Cc: 7617

> > I am using the function `expand*' on user input (one operation among
> > many), to do just what is advertised for `expand*'
> 
> There-s your problem.  You treat user input in a `read-file-name'
> as an actual file name (i.e. suitable to pass to functions like
> expand-file-name),

"Suitable to pass to functions like expand-file-name" is precisely the question
that you are begging.  That suitability is completely undefined so far.  Part of
this bug report asks that you document (a) just what is acceptable to
`expand-file-name' as "file-name" syntax (for each arg) and (b) what happens if
the arg is a string that is not of acceptable file-name syntax.

For (a) it is no doubt enough to say that it is whatever syntax is acceptable by
the current file system (if that is in fact the answer).  But that should be
stated explicitly, along with a description of (b).

> whereas it is actually a different beast, closely
> related, but different: it has this "..// -> /" and ".../~ -> 
> ~" rules, as well as env-var expansion (which also implies
> "$$ -> $" rewrite).  And even these special rules depend on
> file-name-handlers so, e.g., they do not apply to URLs but other
> rules may aaply to other special filenames.  I.e. the only safe
> thing to do is to first pass those strings through
> substitute-for-file-name.

Please read what I wrote.  I am looking for a function that gives the behavior
that is documented for `expand-file-name'.  That's all.

I do not want the slash-collapsing that also occurs but is (was before now)
undocumented.  And I do not want any of the input-transforming behavior provided
by `read-file-name' or `substitute-in-file-name'.  (I do not see any
`substitute-for-filename' function; I assume you meant `*-in-*'.)

In particular, I do not want ...// prefix removal or env var substitution.  I've
stated several times already that I am not looking for the behavior of
`substitute-in-file-name'.

If you do not regard the undocumented slash-collapsing of `expand-file-name' as
a bug, then I am obviously looking for a different function.  In any case, its
documented behavior (= the Emacs 20 behavior) is exactly what I am looking for.

Do you know a good way to get that behavior in Lisp?  I am getting it now by
calling `expand-file-name' within an envelope that restores any consecutive
slashes that get removed, but I'm looking for something less ugly.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-12 22:35       ` Drew Adams
  2010-12-12 23:40         ` Andreas Schwab
@ 2010-12-13  5:17         ` Eli Zaretskii
  2010-12-13 14:51           ` Drew Adams
  1 sibling, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-13  5:17 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: <7617@debbugs.gnu.org>
> Date: Sun, 12 Dec 2010 14:35:20 -0800
> 
> So the question for `expand-file-name' is what kind of file-name
> arguments is it supposed to handle - what are the arguments to
> `expand-file-name' expected to be?

For predictable behavior, the argument must have semantics of a valid
file name for the underlying filesystem.  In particular, a series of
slashes is interpreted as the filesystem would interpret them.

> Besides a workable but ugly replace-regexp-in-string hack that saves
> and restores consecutive slashes around a call to
> `expand-file-name', I don't see a good way to do that.

If you seek suggestions for how to solve your problem in a non-ugly
way, please tell more about the problem.  It's clear that one
_solution_ to that problem is to have the old behavior of
expand-file-name.  But that behavior is gone and will not come back.
To suggest other solutions, we need to understand the problem.

> And also in this case - i.e., if it is decided that the input args
> must be acceptable to the file system, that constraint on the args
> should be documented.

I think that saying "file names" is enough for a doc string--after
all, every programmer knows what is a valid file name.  The ELisp
manual says a few more words about that.  Putting this kind of detail
into a doc string of every file-oriented function would be excessive.

> And we should say what the function
> does/returns otherwise (e.g., say that it is undefined - i.e., that
> it is defined only in the case where the args are acceptable names
> for the file system).  If we tell users that the args must be of a
> form acceptable by the current file system, then we should also tell
> them what happens otherwise (raise an error?...).

If the argument doesn't have a valid file-name semantics, all bets are
off.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13  4:32           ` Drew Adams
@ 2010-12-13  5:23             ` Eli Zaretskii
  2010-12-13 14:51               ` Drew Adams
  0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-13  5:23 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: "'Eli Zaretskii'" <eliz@gnu.org>, <7617@debbugs.gnu.org>
> Date: Sun, 12 Dec 2010 20:32:12 -0800
> 
> Please read what I wrote.

I'm sure he did.  Stefan is telling you that perhaps you run the
string through expand-file-name too early, before massaging it in some
way that gives a valid file name.  Or maybe that you should run
expand-file-name only on parts of the file name, the parts that _are_
valid file names.

> I am looking for a function that gives the behavior that is
> documented for `expand-file-name'.

`concat' comes to mind.  And if that's not what you want, please
consider describing the problem you are trying to solve.

> Do you know a good way to get that behavior in Lisp?

Call expand-file-name on parts of a file name, then `concat' them
together (with any number of slashes that you want)?





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13  5:17         ` Eli Zaretskii
@ 2010-12-13 14:51           ` Drew Adams
  0 siblings, 0 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-13 14:51 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> > Besides a workable but ugly replace-regexp-in-string hack that saves
> > and restores consecutive slashes around a call to
> > `expand-file-name', I don't see a good way to do that.
> 
> If you seek suggestions for how to solve your problem in a non-ugly
> way, please tell more about the problem.  It's clear that one
> _solution_ to that problem is to have the old behavior of
> expand-file-name.  But that behavior is gone and will not come back.
> To suggest other solutions, we need to understand the problem.

The behavior is no longer provided by `expand*'.
But that behavior is what I want to achieve, in Lisp.
Achieving that behavior is the problem.
I understand that `expand*' is no longer the solution.

> > And we should say what the function
> > does/returns otherwise (e.g., say that it is undefined - i.e., that
> > it is defined only in the case where the args are acceptable names
> > for the file system).  If we tell users that the args must be of a
> > form acceptable by the current file system, then we should also tell
> > them what happens otherwise (raise an error?...).
> 
> If the argument doesn't have a valid file-name semantics, all bets are
> off.

Then that's what we should tell users: If either arg is not a file name
acceptable to the file system then the behavior is undefined (aka we have no
idea what will happen).  This is not obvious - since we demand valid file names
and produce a canonical name, one might well wonder what happens otherwise,
whether we raise an error, for example.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13  5:23             ` Eli Zaretskii
@ 2010-12-13 14:51               ` Drew Adams
  2010-12-13 15:17                 ` Eli Zaretskii
  0 siblings, 1 reply; 28+ messages in thread
From: Drew Adams @ 2010-12-13 14:51 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> perhaps you run the string through expand-file-name too early,
> before massaging it in some way that gives a valid file name.

I do not want to change the strings into valid file names.
That should be clear by now.

> Or maybe that you should run expand-file-name only on parts
> of the file name, the parts that _are_ valid file names.

That's one possibility, I suppose.  But I'm not really eager to analyze string
inputs to try to discover pieces that might be valid file names for various
operating systems.

And it's not clear what that would mean in terms of the expansion I'm after
(which is exactly the expansion of `expand*' minus the slash collapsing).

But I recognize what you suggest as a possibility.  Likewise, removing any
ignorable prefix temporarily, expanding, and reapplying the prefix.

Such approaches seem messier than what I'm doing now, which at least leverages
`expand*' (for its OS knowledge and expansion function), and then undoes the
slash collapsing.

> Call expand-file-name on parts of a file name, then `concat' them
> together (with any number of slashes that you want)?

See above.  I appreciate your trying to help, however.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13 14:51               ` Drew Adams
@ 2010-12-13 15:17                 ` Eli Zaretskii
  2010-12-13 15:47                   ` Drew Adams
  0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-13 15:17 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: <monnier@iro.umontreal.ca>, <7617@debbugs.gnu.org>
> Date: Mon, 13 Dec 2010 06:51:42 -0800
> 
> And it's not clear what that would mean in terms of the expansion I'm after
> (which is exactly the expansion of `expand*' minus the slash collapsing).

But after the expansion, wouldn't you remove everything up to and
including the double slash?  If so, why do you need the expansion, if
most of it will be thrown away?  And if you don't remove the part up
to the double slash, then why do you need to keep the double slash?





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13 15:17                 ` Eli Zaretskii
@ 2010-12-13 15:47                   ` Drew Adams
  2010-12-13 16:17                     ` Eli Zaretskii
  2010-12-13 20:40                     ` Stefan Monnier
  0 siblings, 2 replies; 28+ messages in thread
From: Drew Adams @ 2010-12-13 15:47 UTC (permalink / raw)
  To: 'Eli Zaretskii'; +Cc: 7617

> > And it's not clear what that would mean in terms of the 
> > expansion I'm after (which is exactly the expansion of
> > `expand*' minus the slash collapsing).
> 
> But after the expansion, wouldn't you remove everything up to and
> including the double slash?

No, not at all.  If I wanted to remove the prefix I would use
`substitute-in-file-name'.

> If so, why do you need the expansion, if
> most of it will be thrown away?  And if you don't remove the part up
> to the double slash, then why do you need to keep the double slash?

This is raw user input.  I want to keep the prefix, if any, as it is (modulo ~
and any other changes `expand*' makes except for slash collapsing).  I don't
ever remove the prefix.  Emacs (e.g. file-name reading) handles
(ignores/removes) the prefix when the user finally hits RET.

E.g., I want this behavior, with ~ = /home/toto":
(expand-file-name "foo" "~//usr/") -> "/home/toto//usr/foo"

Likewise, if double slashes appear in the first arg I want them preserved.  As I
said, "exactly the expansion of `expand*' minus the slash collapsing".

It's OK.  I can go with what I have now.  And I can keep looking or experiment
with other approaches such as temporarily removing the prefix (if any) and
sewing it back on.

I get the message that I haven't missed some obviously better solution.  And I
get the message that the `expand*' behavior will not be changed (including,
e.g., adding an optional arg that inhibits slash collapsing).

Thanks for adding mention of slash collapsing to the doc string.  I hope you
will also consider mentioning that a valid file name is expected and anything
else results in undefined behavior.  Feel free to close the bug now.






^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13 15:47                   ` Drew Adams
@ 2010-12-13 16:17                     ` Eli Zaretskii
  2010-12-13 20:40                     ` Stefan Monnier
  1 sibling, 0 replies; 28+ messages in thread
From: Eli Zaretskii @ 2010-12-13 16:17 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617-done

> From: "Drew Adams" <drew.adams@oracle.com>
> Cc: <monnier@iro.umontreal.ca>, <7617@debbugs.gnu.org>
> Date: Mon, 13 Dec 2010 07:47:49 -0800
> 
> This is raw user input.  I want to keep the prefix, if any, as it is (modulo ~
> and any other changes `expand*' makes except for slash collapsing).  I don't
> ever remove the prefix.  Emacs (e.g. file-name reading) handles
> (ignores/removes) the prefix when the user finally hits RET.

But then why do you need to expand it? why not keep it as the user
typed it, ~/ and whatnot?  You can then expand it when the user hits
RET, like Emacs does normally.

> I get the message that I haven't missed some obviously better solution.

I'm still not in a position to judge, since I don't know enough about
what you are trying to accomplish.

> Thanks for adding mention of slash collapsing to the doc string.  I hope you
> will also consider mentioning that a valid file name is expected and anything
> else results in undefined behavior.  Feel free to close the bug now.

Done, on both counts.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* bug#7617: 24.0.50; `expand-file-name': removal of slashes
  2010-12-13 15:47                   ` Drew Adams
  2010-12-13 16:17                     ` Eli Zaretskii
@ 2010-12-13 20:40                     ` Stefan Monnier
  1 sibling, 0 replies; 28+ messages in thread
From: Stefan Monnier @ 2010-12-13 20:40 UTC (permalink / raw)
  To: Drew Adams; +Cc: 7617

> E.g., I want this behavior, with ~ = /home/toto":
> (expand-file-name "foo" "~//usr/") -> "/home/toto//usr/foo"

I can't imagine why.  If you want a more constructive answer, you're
going to have to give us more context.


        Stefan





^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2010-12-13 20:40 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-11 21:53 bug#7617: 24.0.50; `expand-file-name': removal of slashes Drew Adams
2010-12-12 15:58 ` Eli Zaretskii
2010-12-12 18:03   ` Drew Adams
2010-12-12 19:33     ` Eli Zaretskii
2010-12-12 20:21       ` Drew Adams
2010-12-12 20:32         ` Eli Zaretskii
2010-12-12 20:36           ` Drew Adams
2010-12-12 20:42         ` Andreas Schwab
2010-12-13  3:53         ` Stefan Monnier
2010-12-13  4:32           ` Drew Adams
2010-12-13  5:23             ` Eli Zaretskii
2010-12-13 14:51               ` Drew Adams
2010-12-13 15:17                 ` Eli Zaretskii
2010-12-13 15:47                   ` Drew Adams
2010-12-13 16:17                     ` Eli Zaretskii
2010-12-13 20:40                     ` Stefan Monnier
2010-12-12 22:35       ` Drew Adams
2010-12-12 23:40         ` Andreas Schwab
2010-12-13  5:17         ` Eli Zaretskii
2010-12-13 14:51           ` Drew Adams
2010-12-12 20:15     ` Andreas Schwab
2010-12-12 20:25       ` Drew Adams
2010-12-12 20:36         ` Andreas Schwab
2010-12-12 20:42           ` Drew Adams
2010-12-12 21:00             ` Andreas Schwab
2010-12-13  0:49     ` Jason Rumney
2010-12-12 20:39   ` Eli Zaretskii
2010-12-12 21:04 ` Andreas Schwab

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).