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

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