3004 lines
		
	
	
		
			77 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3004 lines
		
	
	
		
			77 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* path.cc: path support.
 | 
						||
 | 
						||
   Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
 | 
						||
 | 
						||
This file is part of Cygwin.
 | 
						||
 | 
						||
This software is a copyrighted work licensed under the terms of the
 | 
						||
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						||
details. */
 | 
						||
 | 
						||
/* This module's job is to
 | 
						||
   - convert between POSIX and Win32 style filenames,
 | 
						||
   - support the `mount' functionality,
 | 
						||
   - support symlinks for files and directories
 | 
						||
 | 
						||
   Pathnames are handled as follows:
 | 
						||
 | 
						||
   - / is equivalent to \
 | 
						||
   - Paths beginning with // (or \\) are not translated (i.e. looked
 | 
						||
     up in the mount table) and are assumed to be UNC path names.
 | 
						||
   - Paths containing a : are not translated (paths like
 | 
						||
   /foo/bar/baz:qux: don't make much sense but having the rule written
 | 
						||
   this way allows one to use strchr).
 | 
						||
 | 
						||
   The goal in the above set of rules is to allow both POSIX and Win32
 | 
						||
   flavors of pathnames without either interfering.  The rules are
 | 
						||
   intended to be as close to a superset of both as possible.
 | 
						||
 | 
						||
   A possible future enhancement would be to allow people to
 | 
						||
   disable/enable the mount table handling to support pure Win32
 | 
						||
   pathnames.  Hopefully this won't be needed.  The suggested way to
 | 
						||
   do this would be an environment variable because
 | 
						||
   a) we need something that is inherited from parent to child,
 | 
						||
   b) environment variables can be passed from the DOS shell to a
 | 
						||
   cygwin app,
 | 
						||
   c) it allows disabling the feature on an app by app basis within
 | 
						||
   the same session (whereas playing about with the registry wouldn't
 | 
						||
   -- without getting too complicated).  Example:
 | 
						||
   CYGWIN=pathrules[=@]{win32,posix}.  If CYGWIN=pathrules=win32,
 | 
						||
   mount table handling is disabled.  [The intent is to have CYGWIN be
 | 
						||
   a catchall for tweaking various cygwin.dll features].
 | 
						||
 | 
						||
   Note that you can have more than one path to a file.  The mount
 | 
						||
   table is always prefered when translating Win32 paths to POSIX
 | 
						||
   paths.  Win32 paths in mount table entries may be UNC paths or
 | 
						||
   standard Win32 paths starting with <drive-letter>:
 | 
						||
 | 
						||
   Text vs Binary issues are not considered here in path style
 | 
						||
   decisions.
 | 
						||
 | 
						||
   / and \ are treated as equivalent.  One or the other is prefered in
 | 
						||
   certain situations (e.g. / is preferred in result of getcwd, \ is
 | 
						||
   preferred in arguments to Win32 api calls), but this code will
 | 
						||
   translate as necessary.
 | 
						||
 | 
						||
   Apps wishing to translate to/from pure Win32 and POSIX-like
 | 
						||
   pathnames can use cygwin_foo.
 | 
						||
 | 
						||
   Removing mounted filesystem support would simplify things greatly,
 | 
						||
   but having it gives us a mechanism of treating disk that lives on a
 | 
						||
   UNIX machine as having UNIX semantics [it allows one to edit a text
 | 
						||
   file on that disk and not have cr's magically appear and perhaps
 | 
						||
   break apps running on UNIX boxes].  It also useful to be able to
 | 
						||
   layout a hierarchy without changing the underlying directories.
 | 
						||
 | 
						||
   The semantics of mounting file systems is not intended to precisely
 | 
						||
   follow normal UNIX systems.
 | 
						||
 | 
						||
   Each DOS drive is defined to have a current directory.  Supporting
 | 
						||
   this would complicate things so for now things are defined so that
 | 
						||
   c: means c:\.
 | 
						||
*/
 | 
						||
 | 
						||
#include "winsup.h"
 | 
						||
#include <stdio.h>
 | 
						||
#include <stdlib.h>
 | 
						||
#include <sys/mount.h>
 | 
						||
#include <mntent.h>
 | 
						||
#include <fcntl.h>
 | 
						||
#include <unistd.h>
 | 
						||
#include <errno.h>
 | 
						||
#include <ctype.h>
 | 
						||
#include <winioctl.h>
 | 
						||
#include "cygerrno.h"
 | 
						||
#include "fhandler.h"
 | 
						||
#include "path.h"
 | 
						||
#include "thread.h"
 | 
						||
#include "sync.h"
 | 
						||
#include "sigproc.h"
 | 
						||
#include "pinfo.h"
 | 
						||
 | 
						||
static int normalize_win32_path (const char *cwd, const char *src, char *dst);
 | 
						||
static char *getcwd_inner (char *buf, size_t ulen, int posix_p, int with_chroot);
 | 
						||
static void slashify (const char *src, char *dst, int trailing_slash_p);
 | 
						||
static void backslashify (const char *src, char *dst, int trailing_slash_p);
 | 
						||
static int path_prefix_p_ (const char *path1, const char *path2, int len1);
 | 
						||
static int get_cwd_win32 ();
 | 
						||
 | 
						||
static NO_COPY const char escape_char = '^';
 | 
						||
 | 
						||
struct symlink_info
 | 
						||
{
 | 
						||
  char buf[3 + MAX_PATH * 3];
 | 
						||
  char *known_suffix;
 | 
						||
  char *ext_here;
 | 
						||
  char *contents;
 | 
						||
  unsigned pflags;
 | 
						||
  DWORD fileattr;
 | 
						||
  int is_symlink;
 | 
						||
  int error;
 | 
						||
  symlink_info (): known_suffix (NULL), contents (buf + MAX_PATH + 1) {}
 | 
						||
  int check (const char *path, const suffix_info *suffixes);
 | 
						||
};
 | 
						||
 | 
						||
/********************** Path Helper Functions *************************/
 | 
						||
 | 
						||
#define path_prefix_p(p1, p2, l1) \
 | 
						||
       ((tolower(*(p1))==tolower(*(p2))) && \
 | 
						||
       path_prefix_p_(p1, p2, l1))
 | 
						||
 | 
						||
#define SYMLINKATTR(x) \
 | 
						||
  (((x) & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) == \
 | 
						||
   FILE_ATTRIBUTE_SYSTEM)
 | 
						||
 | 
						||
/* Return non-zero if PATH1 is a prefix of PATH2.
 | 
						||
   Both are assumed to be of the same path style and / vs \ usage.
 | 
						||
   Neither may be "".
 | 
						||
   LEN1 = strlen (PATH1).  It's passed because often it's already known.
 | 
						||
 | 
						||
   Examples:
 | 
						||
   /foo/ is a prefix of /foo  <-- may seem odd, but desired
 | 
						||
   /foo is a prefix of /foo/
 | 
						||
   / is a prefix of /foo/bar
 | 
						||
   / is not a prefix of foo/bar
 | 
						||
   foo/ is a prefix foo/bar
 | 
						||
   /foo is not a prefix of /foobar
 | 
						||
*/
 | 
						||
 | 
						||
/* Determine if path prefix matches current cygdrive */
 | 
						||
#define iscygdrive(path) \
 | 
						||
  (path_prefix_p (cygwin_shared->mount.cygdrive, (path), cygwin_shared->mount.cygdrive_len))
 | 
						||
 | 
						||
#define iscygdrive_device(path) \
 | 
						||
  (iscygdrive(path) && isalpha(path[cygwin_shared->mount.cygdrive_len]) && \
 | 
						||
   (isdirsep(path[cygwin_shared->mount.cygdrive_len + 1]) || \
 | 
						||
    !path[cygwin_shared->mount.cygdrive_len + 1]))
 | 
						||
 | 
						||
/******************** Directory-related Support **************************/
 | 
						||
 | 
						||
/* Cache getcwd value.  FIXME: We need a lock for these in order to
 | 
						||
   support multiple threads.  */
 | 
						||
 | 
						||
#ifdef _MT_SAFE
 | 
						||
#define cwd_win32  _reent_winsup()->_cwd_win32
 | 
						||
#define cwd_posix _reent_winsup()->_cwd_posix
 | 
						||
#define cwd_hash _reent_winsup()->_cwd_hash
 | 
						||
#else
 | 
						||
static char *cwd_win32;
 | 
						||
static char *cwd_posix;
 | 
						||
static unsigned long cwd_hash;
 | 
						||
#endif
 | 
						||
 | 
						||
#define ischrootpath(path) \
 | 
						||
	(myself->rootlen && \
 | 
						||
	 strncasematch (myself->root, path, myself->rootlen) && \
 | 
						||
	 (path[myself->rootlen] == '/' || path[myself->rootlen] == '\0'))
 | 
						||
 | 
						||
static int
 | 
						||
path_prefix_p_ (const char *path1, const char *path2, int len1)
 | 
						||
{
 | 
						||
  /* Handle case where PATH1 has trailing '/' and when it doesn't.  */
 | 
						||
  if (len1 > 0 && SLASH_P (path1[len1 - 1]))
 | 
						||
    len1--;
 | 
						||
 | 
						||
  if (len1 == 0)
 | 
						||
    return SLASH_P (path2[0]) && !SLASH_P (path2[1]);
 | 
						||
 | 
						||
  if (!strncasematch (path1, path2, len1))
 | 
						||
    return 0;
 | 
						||
 | 
						||
  return SLASH_P (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';
 | 
						||
}
 | 
						||
 | 
						||
/* Convert an arbitrary path SRC to a pure Win32 path, suitable for
 | 
						||
   passing to Win32 API routines.
 | 
						||
 | 
						||
   If an error occurs, `error' is set to the errno value.
 | 
						||
   Otherwise it is set to 0.
 | 
						||
 | 
						||
   follow_mode values:
 | 
						||
	SYMLINK_FOLLOW	    - convert to PATH symlink points to
 | 
						||
	SYMLINK_NOFOLLOW    - convert to PATH of symlink itself
 | 
						||
	SYMLINK_IGNORE	    - do not check PATH for symlinks
 | 
						||
	SYMLINK_CONTENTS    - just return symlink contents
 | 
						||
*/
 | 
						||
 | 
						||
void
 | 
						||
path_conv::check (const char *src, unsigned opt,
 | 
						||
		  const suffix_info *suffixes)
 | 
						||
{
 | 
						||
  /* This array is used when expanding symlinks.  It is MAX_PATH * 2
 | 
						||
     in length so that we can hold the expanded symlink plus a
 | 
						||
     trailer.  */
 | 
						||
  char path_buf[MAX_PATH];
 | 
						||
  char path_copy[MAX_PATH];
 | 
						||
  char tmp_buf[MAX_PATH];
 | 
						||
  symlink_info sym;
 | 
						||
 | 
						||
  char *rel_path, *full_path;
 | 
						||
 | 
						||
  if (!(opt & PC_NULLEMPTY))
 | 
						||
    error = 0;
 | 
						||
  else if ((error = check_null_empty_path (src)))
 | 
						||
    return;
 | 
						||
 | 
						||
  if (opt & PC_FULL)
 | 
						||
    rel_path = path_buf, full_path = this->path;
 | 
						||
  else
 | 
						||
    rel_path = this->path, full_path = path_buf;
 | 
						||
 | 
						||
  /* This loop handles symlink expansion.  */
 | 
						||
  int loop = 0;
 | 
						||
  path_flags = 0;
 | 
						||
  known_suffix = NULL;
 | 
						||
  fileattr = (DWORD) -1;
 | 
						||
  for (;;)
 | 
						||
    {
 | 
						||
      MALLOC_CHECK;
 | 
						||
      /* Must look up path in mount table, etc.  */
 | 
						||
      error = cygwin_shared->mount.conv_to_win32_path (src, rel_path,
 | 
						||
						       full_path,
 | 
						||
						       devn, unit, &path_flags);
 | 
						||
      MALLOC_CHECK;
 | 
						||
      if (error)
 | 
						||
	return;
 | 
						||
      if (devn != FH_BAD)
 | 
						||
	{
 | 
						||
	  fileattr = 0;
 | 
						||
	  return;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Eat trailing slashes */
 | 
						||
      char *tail = strchr (full_path, '\0');
 | 
						||
      /* If path is only a drivename, Windows interprets it as
 | 
						||
	 the current working directory on this drive instead of
 | 
						||
	 the root dir which is what we want. So we need
 | 
						||
	 the trailing backslash in this case. */
 | 
						||
      while (tail > full_path + 3 && (*--tail == '\\'))
 | 
						||
	*tail = '\0';
 | 
						||
      if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
 | 
						||
	strcat (full_path, "\\");
 | 
						||
 | 
						||
      if (opt & PC_SYM_IGNORE)
 | 
						||
	{
 | 
						||
	  fileattr = GetFileAttributesA (path);
 | 
						||
	  goto out;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Make a copy of the path that we can munge up */
 | 
						||
      strcpy (path_copy, full_path);
 | 
						||
 | 
						||
      tail = path_copy + 1 + (tail - full_path);   // Point to end of copy
 | 
						||
 | 
						||
      /* Scan path_copy from right to left looking either for a symlink
 | 
						||
	 or an actual existing file.  If an existing file is found, just
 | 
						||
	 return.  If a symlink is found exit the for loop.
 | 
						||
	 Also: be careful to preserve the errno returned from
 | 
						||
	 symlink.check as the caller may need it. */
 | 
						||
      /* FIXME: Do we have to worry about multiple \'s here? */
 | 
						||
      int component = 0;		// Number of translated components
 | 
						||
      sym.contents[0] = '\0';
 | 
						||
 | 
						||
      for (;;)
 | 
						||
	{
 | 
						||
	  const suffix_info *suff;
 | 
						||
 | 
						||
	  /* Don't allow symlink.check to set anything in the path_conv
 | 
						||
	     class if we're working on an inner component of the path */
 | 
						||
	  if (component)
 | 
						||
	    {
 | 
						||
	      suff = NULL;
 | 
						||
	      sym.pflags = 0;
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    {
 | 
						||
	      suff = suffixes;
 | 
						||
	      sym.pflags = path_flags;
 | 
						||
	    }
 | 
						||
 | 
						||
	  int len = sym.check (path_copy, suff);
 | 
						||
 | 
						||
	  if (!component)
 | 
						||
	    path_flags = sym.pflags;
 | 
						||
 | 
						||
	  /* If symlink.check found an existing non-symlink file, then
 | 
						||
	     it sets the appropriate flag.  It also sets any suffix found
 | 
						||
	     into `ext_here'. */
 | 
						||
	  if (!sym.is_symlink && sym.fileattr != (DWORD) -1)
 | 
						||
	    {
 | 
						||
	      error = sym.error;
 | 
						||
	      if (component == 0)
 | 
						||
		{
 | 
						||
		  fileattr = sym.fileattr;
 | 
						||
		  goto fillin;
 | 
						||
		}
 | 
						||
	      goto out;	// file found
 | 
						||
	    }
 | 
						||
	  /* Found a symlink if len > 0.  If component == 0, then the
 | 
						||
	     src path itself was a symlink.  If !follow_mode then
 | 
						||
	     we're done.  Otherwise we have to insert the path found
 | 
						||
	     into the full path that we are building and perform all of
 | 
						||
	     these operations again on the newly derived path. */
 | 
						||
	  else if (len > 0)
 | 
						||
	    {
 | 
						||
	      if (component == 0 && !(opt & PC_SYM_FOLLOW))
 | 
						||
		{
 | 
						||
		  set_symlink (); // last component of path is a symlink.
 | 
						||
		  fileattr = sym.fileattr;
 | 
						||
		  if (opt & PC_SYM_CONTENTS)
 | 
						||
		      strcpy (path, sym.contents);
 | 
						||
		  goto fillin;
 | 
						||
		}
 | 
						||
	      break;
 | 
						||
	    }
 | 
						||
 | 
						||
	  /* No existing file found. */
 | 
						||
 | 
						||
	  if (!(tail = strrchr (path_copy, '\\')) ||
 | 
						||
	      (tail > path_copy && tail[-1] == ':'))
 | 
						||
	    goto out;	// all done
 | 
						||
 | 
						||
	  /* Haven't found a valid pathname component yet.
 | 
						||
	     Pinch off the tail and try again. */
 | 
						||
	  *tail = '\0';
 | 
						||
	  component++;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Arrive here if above loop detected a symlink. */
 | 
						||
      if (++loop > MAX_LINK_DEPTH)
 | 
						||
	{
 | 
						||
	  error = ELOOP;   // Eep.
 | 
						||
	  return;
 | 
						||
	}
 | 
						||
      MALLOC_CHECK;
 | 
						||
 | 
						||
      tail = full_path + (tail - path_copy);
 | 
						||
      int taillen = strlen (tail);
 | 
						||
      int buflen = strlen (sym.contents);
 | 
						||
      if (buflen + taillen > MAX_PATH)
 | 
						||
	  {
 | 
						||
	    error = ENAMETOOLONG;
 | 
						||
	    strcpy (path, "::ENAMETOOLONG::");
 | 
						||
	    return;
 | 
						||
	  }
 | 
						||
 | 
						||
      /* Copy tail of full_path to discovered symlink. */
 | 
						||
      char *p;
 | 
						||
      for (p = sym.contents + buflen; *tail; tail++)
 | 
						||
	*p++ = *tail == '\\' ? '/' : *tail;
 | 
						||
      *p = '\0';
 | 
						||
 | 
						||
      /* If symlink referred to an absolute path, then we
 | 
						||
	 just use sym.contents and loop.  Otherwise tack the head of
 | 
						||
	 path_copy before sym.contents and translate it back from a
 | 
						||
	 Win32-style path to a POSIX-style one. */
 | 
						||
      if (isabspath (sym.contents))
 | 
						||
	src = sym.contents;
 | 
						||
      else if (!(tail = strrchr (path_copy, '\\')))
 | 
						||
	system_printf ("problem parsing %s - '%s'", src, full_path);
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  int headlen = 1 + tail - path_copy;
 | 
						||
	  p = sym.contents - headlen;
 | 
						||
	  memcpy (p, path_copy, headlen);
 | 
						||
	  MALLOC_CHECK;
 | 
						||
	  error = cygwin_shared->mount.conv_to_posix_path (p, tmp_buf, 1);
 | 
						||
	  MALLOC_CHECK;
 | 
						||
	  if (error)
 | 
						||
	    return;
 | 
						||
	  src = tmp_buf;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
fillin:
 | 
						||
  if (sym.known_suffix)
 | 
						||
    known_suffix = this->path + (sym.known_suffix - path_copy);
 | 
						||
  else if (sym.ext_here && !(opt & PC_SYM_CONTENTS))
 | 
						||
    {
 | 
						||
      known_suffix = strchr (this->path, '\0');
 | 
						||
      strcpy (known_suffix, sym.ext_here);
 | 
						||
    }
 | 
						||
 | 
						||
out:
 | 
						||
  DWORD serial, volflags;
 | 
						||
 | 
						||
  strcpy (tmp_buf, full_path);
 | 
						||
  if (!rootdir (tmp_buf) ||
 | 
						||
      !GetVolumeInformation (tmp_buf, NULL, 0, &serial, NULL, &volflags, NULL, 0))
 | 
						||
    {
 | 
						||
      debug_printf ("GetVolumeInformation(%s) = ERR, full_path(%s), set_has_acls(FALSE)",
 | 
						||
		    tmp_buf, full_path, GetLastError ());
 | 
						||
      set_has_acls (FALSE);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      debug_printf ("GetVolumeInformation(%s) = OK, full_path(%s), set_has_acls(%d)",
 | 
						||
		    tmp_buf, full_path, volflags & FS_PERSISTENT_ACLS);
 | 
						||
      set_has_acls (volflags & FS_PERSISTENT_ACLS);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
#define deveq(s) (strcasematch (name, (s)))
 | 
						||
#define deveqn(s, n) (strncasematch (name, (s), (n)))
 | 
						||
 | 
						||
static __inline int
 | 
						||
digits (const char *name)
 | 
						||
{
 | 
						||
  char *p;
 | 
						||
  int n = strtol(name, &p, 10);
 | 
						||
 | 
						||
  return p > name && !*p ? n : -1;
 | 
						||
}
 | 
						||
 | 
						||
const char *windows_device_names[] =
 | 
						||
{
 | 
						||
  NULL,
 | 
						||
  "\\dev\\console",
 | 
						||
  "conin",
 | 
						||
  "conout",
 | 
						||
  "\\dev\\ttym",
 | 
						||
  "\\dev\\tty%d",
 | 
						||
  "\\dev\\ptym",
 | 
						||
  "\\\\.\\com%d",
 | 
						||
  "\\dev\\pipe",
 | 
						||
  "\\dev\\piper",
 | 
						||
  "\\dev\\pipew",
 | 
						||
  "\\dev\\socket",
 | 
						||
  "\\dev\\windows",
 | 
						||
 | 
						||
  NULL, NULL, NULL,
 | 
						||
 | 
						||
  "\\dev\\disk",
 | 
						||
  "\\dev\\fd%d",
 | 
						||
  "\\dev\\st%d",
 | 
						||
  "nul",
 | 
						||
  "\\dev\\zero",
 | 
						||
  "\\dev\\%srandom",
 | 
						||
};
 | 
						||
 | 
						||
static int
 | 
						||
get_raw_device_number (const char *uxname, const char *w32path, int &unit)
 | 
						||
{
 | 
						||
  DWORD devn = FH_BAD;
 | 
						||
 | 
						||
  if (strncasematch (w32path, "\\\\.\\tape", 8))
 | 
						||
    {
 | 
						||
      devn = FH_TAPE;
 | 
						||
      unit = digits (w32path + 8);
 | 
						||
      // norewind tape devices have leading n in name
 | 
						||
      if (strncasematch (uxname, "/dev/n", 6))
 | 
						||
	unit += 128;
 | 
						||
    }
 | 
						||
  else if (isdrive (w32path + 4))
 | 
						||
    {
 | 
						||
      devn = FH_FLOPPY;
 | 
						||
      unit = tolower (w32path[4]) - 'a';
 | 
						||
    }
 | 
						||
  else if (strncasematch (w32path, "\\\\.\\physicaldrive", 17))
 | 
						||
    {
 | 
						||
      devn = FH_FLOPPY;
 | 
						||
      unit = digits (w32path + 17) + 128;
 | 
						||
    }
 | 
						||
  return devn;
 | 
						||
}
 | 
						||
 | 
						||
int __stdcall
 | 
						||
get_device_number (const char *name, int &unit, BOOL from_conv)
 | 
						||
{
 | 
						||
  DWORD devn = FH_BAD;
 | 
						||
  unit = 0;
 | 
						||
 | 
						||
  if ((*name == '/' && deveqn ("/dev/", 5)) ||
 | 
						||
      (*name == '\\' && deveqn ("\\dev\\", 5)))
 | 
						||
    {
 | 
						||
      name += 5;
 | 
						||
      if (deveq ("tty"))
 | 
						||
	{
 | 
						||
	  if (tty_attached (myself))
 | 
						||
	    {
 | 
						||
	      unit = myself->ctty;
 | 
						||
	      devn = FH_TTYS;
 | 
						||
	    }
 | 
						||
	  else if (myself->ctty > 0)
 | 
						||
	    devn = FH_CONSOLE;
 | 
						||
	}
 | 
						||
      else if (deveqn ("tty", 3) && (unit = digits (name + 3)) >= 0)
 | 
						||
	devn = FH_TTYS;
 | 
						||
      else if (deveq ("ttym"))
 | 
						||
	devn = FH_TTYM;
 | 
						||
      else if (deveq ("ptmx"))
 | 
						||
	devn = FH_PTYM;
 | 
						||
      else if (deveq ("windows"))
 | 
						||
	devn = FH_WINDOWS;
 | 
						||
      else if (deveq ("conin"))
 | 
						||
	devn = FH_CONIN;
 | 
						||
      else if (deveq ("conout"))
 | 
						||
	devn = FH_CONOUT;
 | 
						||
      else if (deveq ("null"))
 | 
						||
	devn = FH_NULL;
 | 
						||
      else if (deveq ("zero"))
 | 
						||
	devn = FH_ZERO;
 | 
						||
      else if (deveq ("random") || deveq ("urandom"))
 | 
						||
	{
 | 
						||
	  devn = FH_RANDOM;
 | 
						||
	  unit = 8 + (deveqn ("u", 1) ? 1 : 0); /* Keep unit Linux conformant */
 | 
						||
	}
 | 
						||
      else if (deveqn ("com", 3) && (unit = digits (name + 3)) >= 0)
 | 
						||
	devn = FH_SERIAL;
 | 
						||
      else if (deveq ("pipe") || deveq ("piper") || deveq ("pipew"))
 | 
						||
	devn = FH_PIPE;
 | 
						||
      else if (deveq ("tcp") || deveq ("udp") || deveq ("streamsocket")
 | 
						||
	       || deveq ("dgsocket"))
 | 
						||
	devn = FH_SOCKET;
 | 
						||
      else if (! from_conv)
 | 
						||
	devn = get_raw_device_number (name - 5,
 | 
						||
				      path_conv (name - 5,
 | 
						||
						 PC_SYM_IGNORE).get_win32 (),
 | 
						||
				      unit);
 | 
						||
    }
 | 
						||
  else if (deveqn ("com", 3) && (unit = digits (name + 3)) >= 0)
 | 
						||
    devn = FH_SERIAL;
 | 
						||
 | 
						||
  return devn;
 | 
						||
}
 | 
						||
 | 
						||
/* Return TRUE if src_path is a Win32 device name, filling out the device
 | 
						||
   name in win32_path */
 | 
						||
 | 
						||
static BOOL
 | 
						||
win32_device_name (const char *src_path, char *win32_path,
 | 
						||
		   DWORD &devn, int &unit)
 | 
						||
{
 | 
						||
  const char *devfmt;
 | 
						||
 | 
						||
  devn = get_device_number (src_path, unit, TRUE);
 | 
						||
 | 
						||
  if (devn == FH_BAD)
 | 
						||
    return FALSE;
 | 
						||
 | 
						||
  if ((devfmt = windows_device_names[FHDEVN (devn)]) == NULL)
 | 
						||
    return FALSE;
 | 
						||
  if (devn == FH_RANDOM)
 | 
						||
    __small_sprintf (win32_path, devfmt, unit == 8 ? "" : "u");
 | 
						||
  else
 | 
						||
    __small_sprintf (win32_path, devfmt, unit);
 | 
						||
  return TRUE;
 | 
						||
}
 | 
						||
 | 
						||
/* Normalize a POSIX path.
 | 
						||
   \'s are converted to /'s in the process.
 | 
						||
   All duplicate /'s, except for 2 leading /'s, are deleted.
 | 
						||
   The result is 0 for success, or an errno error value.  */
 | 
						||
 | 
						||
#define isslash(c) ((c) == '/')
 | 
						||
 | 
						||
static int
 | 
						||
normalize_posix_path (const char *cwd, const char *src, char *dst)
 | 
						||
{
 | 
						||
  const char *src_start = src;
 | 
						||
  char *dst_start = dst;
 | 
						||
 | 
						||
  syscall_printf ("cwd %s, src %s", cwd, src);
 | 
						||
  if (isdrive (src) || strpbrk (src, "\\:"))
 | 
						||
    {
 | 
						||
      cygwin_conv_to_full_posix_path (src, dst);
 | 
						||
      return 0;
 | 
						||
    }
 | 
						||
  if (!isslash (src[0]))
 | 
						||
    {
 | 
						||
      if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
 | 
						||
	{
 | 
						||
	  debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
 | 
						||
	  return ENAMETOOLONG;
 | 
						||
	}
 | 
						||
      strcpy (dst, cwd);
 | 
						||
      dst = strchr (dst, '\0');
 | 
						||
      if (*src == '.')
 | 
						||
	{
 | 
						||
	  if (dst == dst_start + 1)
 | 
						||
	    dst--;
 | 
						||
	  goto sawdot;
 | 
						||
	}
 | 
						||
      if (dst > dst_start && !isslash (dst[-1]))
 | 
						||
	*dst++ = '/';
 | 
						||
    }
 | 
						||
  /* Two leading /'s?  If so, preserve them.  */
 | 
						||
  else if (isslash (src[1]))
 | 
						||
    {
 | 
						||
      if (myself->rootlen)
 | 
						||
	{
 | 
						||
	  debug_printf ("ENOENT = normalize_posix_path (%s)", src);
 | 
						||
	  return ENOENT;
 | 
						||
	}
 | 
						||
      *dst++ = '/';
 | 
						||
      *dst++ = '/';
 | 
						||
      src += 2;
 | 
						||
      if (isslash (*src))
 | 
						||
	{ /* Starts with three or more slashes - reset. */
 | 
						||
	  dst = dst_start;
 | 
						||
	  *dst++ = '/';
 | 
						||
	  src = src_start + 1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  /* Exactly one leading slash. Absolute path. Check for chroot. */
 | 
						||
  else if (myself->rootlen)
 | 
						||
    {
 | 
						||
      strcpy (dst, myself->root);
 | 
						||
      dst += myself->rootlen;
 | 
						||
    }
 | 
						||
 | 
						||
  while (*src)
 | 
						||
    {
 | 
						||
      /* Strip runs of /'s.  */
 | 
						||
      if (!isslash (*src))
 | 
						||
	*dst++ = *src++;
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  while (*++src)
 | 
						||
	    {
 | 
						||
	      while (isslash (*src))
 | 
						||
		src++;
 | 
						||
 | 
						||
	      if (*src != '.')
 | 
						||
		break;
 | 
						||
 | 
						||
	    sawdot:
 | 
						||
	      if (src[1] != '.')
 | 
						||
		{
 | 
						||
		  if (!src[1] || !isslash (src[1]))
 | 
						||
		    break;
 | 
						||
		}
 | 
						||
	      else
 | 
						||
		{
 | 
						||
		  if (src[2] && !isslash (src[2]))
 | 
						||
		    break;
 | 
						||
		  if (!ischrootpath (dst_start) ||
 | 
						||
		      dst - dst_start != (int) myself->rootlen)
 | 
						||
		    while (dst > dst_start && !isslash (*--dst))
 | 
						||
		      continue;
 | 
						||
		  src++;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
 | 
						||
	  *dst++ = '/';
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  *dst = '\0';
 | 
						||
  if (--dst > dst_start && isslash (*dst))
 | 
						||
    *dst = '\0';
 | 
						||
 | 
						||
  debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Normalize a Win32 path.
 | 
						||
   /'s are converted to \'s in the process.
 | 
						||
   All duplicate \'s, except for 2 leading \'s, are deleted.
 | 
						||
 | 
						||
   The result is 0 for success, or an errno error value.
 | 
						||
   FIXME: A lot of this should be mergeable with the POSIX critter.  */
 | 
						||
static int
 | 
						||
normalize_win32_path (const char *cwd, const char *src, char *dst)
 | 
						||
{
 | 
						||
  const char *src_start = src;
 | 
						||
  char *dst_start = dst;
 | 
						||
  char *dst_root_start = dst;
 | 
						||
 | 
						||
  if (!SLASH_P (src[0]) && strchr (src, ':') == NULL)
 | 
						||
    {
 | 
						||
      if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
 | 
						||
	{
 | 
						||
	  debug_printf ("ENAMETOOLONG = normalize_win32_path (%s)", src);
 | 
						||
	  return ENAMETOOLONG;
 | 
						||
	}
 | 
						||
      strcpy (dst, cwd);
 | 
						||
      dst += strlen (dst);
 | 
						||
      if (!*cwd || !SLASH_P (dst[-1]))
 | 
						||
	*dst++ = '\\';
 | 
						||
    }
 | 
						||
  /* Two leading \'s?  If so, preserve them.  */
 | 
						||
  else if (SLASH_P (src[0]) && SLASH_P (src[1]))
 | 
						||
    {
 | 
						||
      if (myself->rootlen)
 | 
						||
	{
 | 
						||
	  debug_printf ("ENOENT = normalize_win32_path (%s)", src);
 | 
						||
	  return ENOENT;
 | 
						||
	}
 | 
						||
      *dst++ = '\\';
 | 
						||
      ++src;
 | 
						||
    }
 | 
						||
  /* If absolute path, care for chroot. */
 | 
						||
  else if (SLASH_P (src[0]) && !SLASH_P (src[1]) && myself->rootlen)
 | 
						||
    {
 | 
						||
      strcpy (dst, myself->root);
 | 
						||
      char *c;
 | 
						||
      while ((c = strchr (dst, '/')) != NULL)
 | 
						||
	*c = '\\';
 | 
						||
      dst += myself->rootlen;
 | 
						||
      dst_root_start = dst;
 | 
						||
      *dst++ = '\\';
 | 
						||
    }
 | 
						||
 | 
						||
  while (*src)
 | 
						||
    {
 | 
						||
      /* Strip duplicate /'s.  */
 | 
						||
      if (SLASH_P (src[0]) && SLASH_P (src[1]))
 | 
						||
	src++;
 | 
						||
      /* Ignore "./".  */
 | 
						||
      else if (src[0] == '.' && SLASH_P (src[1])
 | 
						||
	       && (src == src_start || SLASH_P (src[-1])))
 | 
						||
	{
 | 
						||
	  src += 2;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Backup if "..".  */
 | 
						||
      else if (src[0] == '.' && src[1] == '.'
 | 
						||
	       /* dst must be greater than dst_start */
 | 
						||
	       && dst[-1] == '\\'
 | 
						||
	       && (SLASH_P (src[2]) || src[2] == 0))
 | 
						||
	{
 | 
						||
	  /* Back up over /, but not if it's the first one.  */
 | 
						||
	  if (dst > dst_root_start + 1)
 | 
						||
	    dst--;
 | 
						||
	  /* Now back up to the next /.  */
 | 
						||
	  while (dst > dst_root_start + 1 && dst[-1] != '\\' && dst[-2] != ':')
 | 
						||
	    dst--;
 | 
						||
	  src += 2;
 | 
						||
	  if (SLASH_P (*src))
 | 
						||
	    src++;
 | 
						||
	}
 | 
						||
      /* Otherwise, add char to result.  */
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  if (*src == '/')
 | 
						||
	    *dst++ = '\\';
 | 
						||
	  else
 | 
						||
	    *dst++ = *src;
 | 
						||
	  ++src;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  *dst = 0;
 | 
						||
  debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/* Various utilities.  */
 | 
						||
 | 
						||
/* slashify: Convert all back slashes in src path to forward slashes
 | 
						||
   in dst path.  Add a trailing slash to dst when trailing_slash_p arg
 | 
						||
   is set to 1. */
 | 
						||
 | 
						||
static void
 | 
						||
slashify (const char *src, char *dst, int trailing_slash_p)
 | 
						||
{
 | 
						||
  const char *start = src;
 | 
						||
 | 
						||
  while (*src)
 | 
						||
    {
 | 
						||
      if (*src == '\\')
 | 
						||
	*dst++ = '/';
 | 
						||
      else
 | 
						||
	*dst++ = *src;
 | 
						||
      ++src;
 | 
						||
    }
 | 
						||
  if (trailing_slash_p
 | 
						||
      && src > start
 | 
						||
      && !isdirsep (src[-1]))
 | 
						||
    *dst++ = '/';
 | 
						||
  *dst++ = 0;
 | 
						||
}
 | 
						||
 | 
						||
/* backslashify: Convert all forward slashes in src path to back slashes
 | 
						||
   in dst path.  Add a trailing slash to dst when trailing_slash_p arg
 | 
						||
   is set to 1. */
 | 
						||
 | 
						||
static void
 | 
						||
backslashify (const char *src, char *dst, int trailing_slash_p)
 | 
						||
{
 | 
						||
  const char *start = src;
 | 
						||
 | 
						||
  while (*src)
 | 
						||
    {
 | 
						||
      if (*src == '/')
 | 
						||
	*dst++ = '\\';
 | 
						||
      else
 | 
						||
	*dst++ = *src;
 | 
						||
      ++src;
 | 
						||
    }
 | 
						||
  if (trailing_slash_p
 | 
						||
      && src > start
 | 
						||
      && !isdirsep (src[-1]))
 | 
						||
    *dst++ = '\\';
 | 
						||
  *dst++ = 0;
 | 
						||
}
 | 
						||
 | 
						||
/* nofinalslash: Remove trailing / and \ from SRC (except for the
 | 
						||
   first one).  It is ok for src == dst.  */
 | 
						||
 | 
						||
void __stdcall
 | 
						||
nofinalslash (const char *src, char *dst)
 | 
						||
{
 | 
						||
  int len = strlen (src);
 | 
						||
  if (src != dst)
 | 
						||
    memcpy (dst, src, len + 1);
 | 
						||
  while (len > 1 && SLASH_P (dst[--len]))
 | 
						||
    dst[len] = '\0';
 | 
						||
}
 | 
						||
 | 
						||
/* slash_drive_prefix_p: Return non-zero if PATH begins with
 | 
						||
   //<letter>.  */
 | 
						||
 | 
						||
static int
 | 
						||
slash_drive_prefix_p (const char *path)
 | 
						||
{
 | 
						||
  return (isdirsep(path[0])
 | 
						||
	  && isdirsep(path[1])
 | 
						||
	  && isalpha (path[2])
 | 
						||
	  && (path[3] == 0 || path[3] == '/'));
 | 
						||
}
 | 
						||
 | 
						||
/* slash_unc_prefix_p: Return non-zero if PATH begins with //UNC/SHARE */
 | 
						||
 | 
						||
int __stdcall
 | 
						||
slash_unc_prefix_p (const char *path)
 | 
						||
{
 | 
						||
  char *p = NULL;
 | 
						||
  int ret = (isdirsep (path[0])
 | 
						||
	     && isdirsep (path[1])
 | 
						||
	     && isalpha (path[2])
 | 
						||
	     && path[3] != 0
 | 
						||
	     && !isdirsep (path[3])
 | 
						||
	     && ((p = strchr(&path[3], '/')) != NULL));
 | 
						||
  if (!ret || p == NULL)
 | 
						||
    return ret;
 | 
						||
  return ret && isalnum (p[1]);
 | 
						||
}
 | 
						||
 | 
						||
/* conv_path_list: Convert a list of path names to/from Win32/POSIX.
 | 
						||
 | 
						||
   SRC is not a const char * because we temporarily modify it to ease
 | 
						||
   the implementation.
 | 
						||
 | 
						||
   I believe Win32 always has '.' in $PATH.   POSIX obviously doesn't.
 | 
						||
   We certainly don't want to handle that here, but it is something for
 | 
						||
   the caller to think about.  */
 | 
						||
 | 
						||
static void
 | 
						||
conv_path_list (const char *src, char *dst, int to_posix_p)
 | 
						||
{
 | 
						||
  char *s;
 | 
						||
  char *d = dst;
 | 
						||
  char src_delim = to_posix_p ? ';' : ':';
 | 
						||
  char dst_delim = to_posix_p ? ':' : ';';
 | 
						||
  int (*conv_fn) (const char *, char *) = (to_posix_p
 | 
						||
					   ? cygwin_conv_to_posix_path
 | 
						||
					   : cygwin_conv_to_win32_path);
 | 
						||
 | 
						||
  do
 | 
						||
    {
 | 
						||
      s = strchr (src, src_delim);
 | 
						||
      if (s)
 | 
						||
	{
 | 
						||
	  *s = 0;
 | 
						||
	  (*conv_fn) (src[0] != 0 ? src : ".", d);
 | 
						||
	  d += strlen (d);
 | 
						||
	  *d++ = dst_delim;
 | 
						||
	  *s = src_delim;
 | 
						||
	  src = s + 1;
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  /* Last one.  */
 | 
						||
	  (*conv_fn) (src[0] != 0 ? src : ".", d);
 | 
						||
	}
 | 
						||
    }
 | 
						||
  while (s != NULL);
 | 
						||
}
 | 
						||
 | 
						||
/************************* mount_info class ****************************/
 | 
						||
 | 
						||
/* init: Initialize the mount table.  */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::init ()
 | 
						||
{
 | 
						||
  nmounts = 0;
 | 
						||
  had_to_create_mount_areas = 0;
 | 
						||
 | 
						||
  /* Fetch the mount table and cygdrive-related information from
 | 
						||
     the registry.  */
 | 
						||
  from_registry ();
 | 
						||
}
 | 
						||
 | 
						||
/* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
 | 
						||
   the result in win32_path.
 | 
						||
 | 
						||
   If win32_path != NULL, the relative path, if possible to keep, is
 | 
						||
   stored in win32_path.  If the relative path isn't possible to keep,
 | 
						||
   the full path is stored.
 | 
						||
 | 
						||
   If full_win32_path != NULL, the full path is stored there.
 | 
						||
 | 
						||
   The result is zero for success, or an errno value.
 | 
						||
 | 
						||
   {,full_}win32_path must have sufficient space (i.e. MAX_PATH bytes).  */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::conv_to_win32_path (const char *src_path, char *win32_path,
 | 
						||
				char *full_win32_path, DWORD &devn, int &unit,
 | 
						||
				unsigned *flags)
 | 
						||
{
 | 
						||
  int src_path_len = strlen (src_path);
 | 
						||
  int trailing_slash_p = (src_path_len > 1
 | 
						||
			  && SLASH_P (src_path[src_path_len - 1]));
 | 
						||
  MALLOC_CHECK;
 | 
						||
  int isrelpath;
 | 
						||
  unsigned dummy_flags;
 | 
						||
 | 
						||
  devn = FH_BAD;
 | 
						||
  unit = 0;
 | 
						||
 | 
						||
  if (!flags)
 | 
						||
    flags = &dummy_flags;
 | 
						||
 | 
						||
  *flags = 0;
 | 
						||
  debug_printf ("conv_to_win32_path (%s)", src_path);
 | 
						||
 | 
						||
  if (src_path_len >= MAX_PATH)
 | 
						||
    {
 | 
						||
      debug_printf ("ENAMETOOLONG = conv_to_win32_path (%s)", src_path);
 | 
						||
      return ENAMETOOLONG;
 | 
						||
    }
 | 
						||
 | 
						||
  int i, rc;
 | 
						||
  char *dst = NULL;
 | 
						||
  mount_item *mi = NULL;	/* initialized to avoid compiler warning */
 | 
						||
  char pathbuf[MAX_PATH];
 | 
						||
 | 
						||
  char cwd[MAX_PATH];
 | 
						||
  getcwd_inner (cwd, MAX_PATH, TRUE, 0); /* FIXME: check rc */
 | 
						||
 | 
						||
  /* Determine where the destination should be placed. */
 | 
						||
  if (full_win32_path != NULL)
 | 
						||
    dst = full_win32_path;
 | 
						||
  else if (win32_path != NULL)
 | 
						||
    dst = win32_path;
 | 
						||
 | 
						||
  if (dst == NULL)
 | 
						||
    goto out;		/* Sanity check. */
 | 
						||
 | 
						||
  /* An MS-DOS spec has either a : or a \.  If this is found, short
 | 
						||
     circuit most of the rest of this function. */
 | 
						||
  if (strpbrk (src_path, ":\\") != NULL)
 | 
						||
    {
 | 
						||
      debug_printf ("%s already win32", src_path);
 | 
						||
      rc = normalize_win32_path (cwd_win32, src_path, dst);
 | 
						||
      if (rc)
 | 
						||
	{
 | 
						||
	  debug_printf ("normalize_win32_path failed, rc %d", rc);
 | 
						||
	  return rc;
 | 
						||
	}
 | 
						||
      isrelpath = !isabspath (src_path);
 | 
						||
      *flags = set_flags_from_win32_path (dst);
 | 
						||
      if (myself->rootlen && dst[0] && dst[1] == ':')
 | 
						||
	{
 | 
						||
	  char posix_path[MAX_PATH + 1];
 | 
						||
 | 
						||
	  rc = cygwin_shared->mount.conv_to_posix_path (dst, posix_path, 0);
 | 
						||
	  if (rc)
 | 
						||
	    {
 | 
						||
	      debug_printf ("conv_to_posix_path failed, rc %d", rc);
 | 
						||
	      return rc;
 | 
						||
	    }
 | 
						||
	  if (!ischrootpath (posix_path))
 | 
						||
	    {
 | 
						||
	      debug_printf ("ischrootpath failed");
 | 
						||
	      return ENOENT;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
      goto fillin;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Normalize the path, taking out ../../ stuff, we need to do this
 | 
						||
     so that we can move from one mounted directory to another with relative
 | 
						||
     stuff.
 | 
						||
 | 
						||
     eg mounting c:/foo /foo
 | 
						||
     d:/bar /bar
 | 
						||
 | 
						||
     cd /bar
 | 
						||
     ls ../foo
 | 
						||
 | 
						||
     should look in c:/foo, not d:/foo.
 | 
						||
 | 
						||
     We do this by first getting an absolute UNIX-style path and then
 | 
						||
     converting it to a DOS-style path, looking up the appropriate drive
 | 
						||
     in the mount table.  */
 | 
						||
 | 
						||
  /* No need to fetch cwd if path is absolute.  */
 | 
						||
  isrelpath = !isslash (*src_path);
 | 
						||
 | 
						||
  rc = normalize_posix_path (cwd, src_path, pathbuf);
 | 
						||
 | 
						||
  if (rc)
 | 
						||
    {
 | 
						||
      debug_printf ("%d = conv_to_win32_path (%s)", rc, src_path);
 | 
						||
      *flags = 0;
 | 
						||
      return rc;
 | 
						||
    }
 | 
						||
 | 
						||
  /* See if this is a cygwin "device" */
 | 
						||
  if (win32_device_name (pathbuf, dst, devn, unit))
 | 
						||
    {
 | 
						||
      *flags = MOUNT_BINARY;	/* FIXME: Is this a sensible default for devices? */
 | 
						||
      goto fillin;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Check if the cygdrive prefix was specified.  If so, just strip
 | 
						||
     off the prefix and transform it into an MS-DOS path. */
 | 
						||
  MALLOC_CHECK;
 | 
						||
  if (iscygdrive_device (pathbuf))
 | 
						||
    {
 | 
						||
      if (!cygdrive_win32_path (pathbuf, dst, trailing_slash_p))
 | 
						||
	return ENOENT;
 | 
						||
      *flags = cygdrive_flags;
 | 
						||
      goto fillin;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Check the mount table for prefix matches. */
 | 
						||
  for (i = 0; i < nmounts; i++)
 | 
						||
    {
 | 
						||
      mi = mount + posix_sorted[i];
 | 
						||
      if (path_prefix_p (mi->posix_path, pathbuf, mi->posix_pathlen))
 | 
						||
	break;
 | 
						||
    }
 | 
						||
 | 
						||
  if (i >= nmounts)
 | 
						||
    {
 | 
						||
      if (slash_drive_prefix_p (pathbuf))
 | 
						||
	slash_drive_to_win32_path (pathbuf, dst, trailing_slash_p);
 | 
						||
      else
 | 
						||
	backslashify (pathbuf, dst, trailing_slash_p);	/* just convert */
 | 
						||
      *flags = 0;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      int n = mi->native_pathlen;
 | 
						||
      memcpy (dst, mi->native_path, n + 1);
 | 
						||
      char *p = pathbuf + mi->posix_pathlen;
 | 
						||
      if (!trailing_slash_p && !*p)
 | 
						||
	{
 | 
						||
	  if (isdrive (dst) && !dst[2])
 | 
						||
	    dst[n++] = '\\';
 | 
						||
	  dst[n] = '\0';
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  /* Do not add trailing \ to UNC device names like \\.\a: */
 | 
						||
	  if (*p != '/' &&  /* FIXME: this test seems wrong. */
 | 
						||
	     (strncmp (mi->native_path, "\\\\.\\", 4) != 0 ||
 | 
						||
	       strncmp (mi->native_path + 4, "UNC\\", 4) == 0))
 | 
						||
	    dst[n++] = '\\';
 | 
						||
	  strcpy (dst + n, p);
 | 
						||
	}
 | 
						||
      backslashify (dst, dst, trailing_slash_p);
 | 
						||
      *flags = mi->flags;
 | 
						||
    }
 | 
						||
 | 
						||
fillin:
 | 
						||
  /* Compute relative path if asked to and able to.  */
 | 
						||
  unsigned cwdlen;
 | 
						||
  cwdlen = 0;	/* avoid a (hopefully) bogus compiler warning */
 | 
						||
  if (win32_path == NULL)
 | 
						||
    /* nothing to do */;
 | 
						||
  else if (isrelpath &&
 | 
						||
	   path_prefix_p (cwd_win32, dst, cwdlen = strlen (cwd_win32)))
 | 
						||
    {
 | 
						||
      size_t n = strlen (dst);
 | 
						||
      if (n < cwdlen)
 | 
						||
	strcpy (win32_path, dst);
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  if (n == cwdlen)
 | 
						||
	    dst += cwdlen;
 | 
						||
	  else
 | 
						||
	    dst += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
 | 
						||
 | 
						||
	  memmove (win32_path, dst, strlen (dst) + 1);
 | 
						||
	  if (!*win32_path)
 | 
						||
	    {
 | 
						||
	      strcpy (win32_path, ".");
 | 
						||
	      if (trailing_slash_p)
 | 
						||
		strcat (win32_path, "\\");
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
  else if (win32_path != dst)
 | 
						||
    strcpy (win32_path, dst);
 | 
						||
 | 
						||
out:
 | 
						||
  MALLOC_CHECK;
 | 
						||
  debug_printf ("%s(rel), %s(abs) %p(flags) = conv_to_win32_path (%s)",
 | 
						||
		win32_path, full_win32_path, *flags,
 | 
						||
		src_path);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Convert PATH (for which slash_drive_prefix_p returns 1) to WIN32 form.  */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::slash_drive_to_win32_path (const char *path, char *buf,
 | 
						||
				       int trailing_slash_p)
 | 
						||
{
 | 
						||
  buf[0] = path[2];
 | 
						||
  buf[1] = ':';
 | 
						||
  if (path[3] == '0')
 | 
						||
    strcpy (buf + 2, "\\");
 | 
						||
  else
 | 
						||
    backslashify (path + 3, buf + 2, trailing_slash_p);
 | 
						||
}
 | 
						||
 | 
						||
/* cygdrive_posix_path: Build POSIX path used as the
 | 
						||
   mount point for cygdrives created when there is no other way to
 | 
						||
   obtain a POSIX path from a Win32 one. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
 | 
						||
{
 | 
						||
  int len = cygdrive_len;
 | 
						||
 | 
						||
  memcpy (dst, cygdrive, len + 1);
 | 
						||
 | 
						||
  /* Now finish the path off with the drive letter to be used.
 | 
						||
     The cygdrive prefix always ends with a trailing slash so
 | 
						||
     the drive letter is added after the path. */
 | 
						||
  dst[len++] = tolower (src[0]);
 | 
						||
  if (!src[2] || (SLASH_P (src[2]) && !src[3]))
 | 
						||
    dst[len++] = '\000';
 | 
						||
  else
 | 
						||
    {
 | 
						||
      dst[len++] = '/';
 | 
						||
      strcpy (dst + len, src + 3);
 | 
						||
    }
 | 
						||
  slashify (dst, dst, trailing_slash_p);
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
mount_info::cygdrive_win32_path (const char *src, char *dst, int trailing_slash_p)
 | 
						||
{
 | 
						||
  const char *p = src + cygdrive_len;
 | 
						||
  if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
 | 
						||
    return 0;
 | 
						||
  dst[0] = *p;
 | 
						||
  dst[1] = ':';
 | 
						||
  strcpy (dst + 2, p + 1);
 | 
						||
  backslashify (dst, dst, trailing_slash_p || !dst[2]);
 | 
						||
  debug_printf ("src '%s', dst '%s'", src, dst);
 | 
						||
  return 1;
 | 
						||
}
 | 
						||
 | 
						||
/* conv_to_posix_path: Ensure src_path is a POSIX path.
 | 
						||
 | 
						||
   The result is zero for success, or an errno value.
 | 
						||
   posix_path must have sufficient space (i.e. MAX_PATH bytes).
 | 
						||
   If keep_rel_p is non-zero, relative paths stay that way.  */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
 | 
						||
				int keep_rel_p)
 | 
						||
{
 | 
						||
  int src_path_len = strlen (src_path);
 | 
						||
  int relative_path_p = !isabspath (src_path);
 | 
						||
  int trailing_slash_p;
 | 
						||
 | 
						||
  if (src_path_len <= 1)
 | 
						||
    trailing_slash_p = 0;
 | 
						||
  else
 | 
						||
    {
 | 
						||
      const char *lastchar = src_path + src_path_len - 1;
 | 
						||
      trailing_slash_p = SLASH_P (*lastchar) && lastchar[-1] != ':';
 | 
						||
    }
 | 
						||
 | 
						||
  debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
 | 
						||
		keep_rel_p ? "keep-rel" : "no-keep-rel",
 | 
						||
		trailing_slash_p ? "add-slash" : "no-add-slash");
 | 
						||
  MALLOC_CHECK;
 | 
						||
 | 
						||
  if (src_path_len >= MAX_PATH)
 | 
						||
    {
 | 
						||
      debug_printf ("ENAMETOOLONG");
 | 
						||
      return ENAMETOOLONG;
 | 
						||
    }
 | 
						||
 | 
						||
  /* FIXME: For now, if the path is relative and it's supposed to stay
 | 
						||
     that way, skip mount table processing. */
 | 
						||
 | 
						||
  if (keep_rel_p && relative_path_p)
 | 
						||
    {
 | 
						||
      slashify (src_path, posix_path, 0);
 | 
						||
      debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
 | 
						||
      return 0;
 | 
						||
    }
 | 
						||
 | 
						||
  char pathbuf[MAX_PATH];
 | 
						||
  char cwd[MAX_PATH];
 | 
						||
 | 
						||
  /* No need to fetch cwd if path is absolute. */
 | 
						||
  if (relative_path_p)
 | 
						||
    getcwd_inner (cwd, MAX_PATH, 0, 0); /* FIXME: check rc */
 | 
						||
  else
 | 
						||
    strcpy (cwd, "/"); /* some innocuous value */
 | 
						||
 | 
						||
  int rc = normalize_win32_path (cwd, src_path, pathbuf);
 | 
						||
  if (rc != 0)
 | 
						||
    {
 | 
						||
      debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
 | 
						||
      return rc;
 | 
						||
    }
 | 
						||
 | 
						||
  int pathbuflen = strlen (pathbuf);
 | 
						||
  for (int i = 0; i < nmounts; ++i)
 | 
						||
    {
 | 
						||
      mount_item &mi = mount[native_sorted[i]];
 | 
						||
      if (! path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen))
 | 
						||
	continue;
 | 
						||
 | 
						||
      /* SRC_PATH is in the mount table. */
 | 
						||
      int nextchar;
 | 
						||
      const char *p = pathbuf + mi.native_pathlen;
 | 
						||
 | 
						||
      if (!*p || !p[1])
 | 
						||
	nextchar = 0;
 | 
						||
      else if (isdirsep (*p))
 | 
						||
	nextchar = -1;
 | 
						||
      else
 | 
						||
	nextchar = 1;
 | 
						||
 | 
						||
      int addslash = nextchar > 0 ? 1 : 0;
 | 
						||
      if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= MAX_PATH)
 | 
						||
	return ENAMETOOLONG;
 | 
						||
      strcpy (posix_path, mi.posix_path);
 | 
						||
      if (addslash)
 | 
						||
	strcat (posix_path, "/");
 | 
						||
      if (nextchar)
 | 
						||
	slashify (p,
 | 
						||
		  posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
 | 
						||
		  trailing_slash_p);
 | 
						||
      goto out;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Not in the database.  This should [theoretically] only happen if either
 | 
						||
     the path begins with //, or / isn't mounted, or the path has a drive
 | 
						||
     letter not covered by the mount table.  If it's a relative path then the
 | 
						||
     caller must want an absolute path (otherwise we would have returned
 | 
						||
     above).  So we always return an absolute path at this point. */
 | 
						||
  if (isdrive (pathbuf))
 | 
						||
    cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* The use of src_path and not pathbuf here is intentional.
 | 
						||
	 We couldn't translate the path, so just ensure no \'s are present. */
 | 
						||
      slashify (src_path, posix_path, trailing_slash_p);
 | 
						||
    }
 | 
						||
 | 
						||
out:
 | 
						||
  debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
 | 
						||
  MALLOC_CHECK;
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Return flags associated with a mount point given the win32 path. */
 | 
						||
 | 
						||
unsigned
 | 
						||
mount_info::set_flags_from_win32_path (const char *p)
 | 
						||
{
 | 
						||
  for (int i = 0; i < nmounts; i++)
 | 
						||
    {
 | 
						||
      mount_item &mi = mount[native_sorted[i]];
 | 
						||
      if (path_prefix_p (mi.native_path, p, mi.native_pathlen))
 | 
						||
	return mi.flags;
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* read_mounts: Given a specific regkey, read mounts from under its
 | 
						||
   key. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::read_mounts (reg_key& r)
 | 
						||
{
 | 
						||
  char posix_path[MAX_PATH];
 | 
						||
  HKEY key = r.get_key ();
 | 
						||
  DWORD i, posix_path_size;
 | 
						||
  int found_cygdrive = FALSE;
 | 
						||
 | 
						||
  /* Loop through subkeys */
 | 
						||
  /* FIXME: we would like to not check MAX_MOUNTS but the heap in the
 | 
						||
     shared area is currently statically allocated so we can't have an
 | 
						||
     arbitrarily large number of mounts. */
 | 
						||
  for (DWORD i = 0; ; i++)
 | 
						||
    {
 | 
						||
      char native_path[MAX_PATH];
 | 
						||
      int mount_flags;
 | 
						||
 | 
						||
      posix_path_size = MAX_PATH;
 | 
						||
      /* FIXME: if maximum posix_path_size is 256, we're going to
 | 
						||
	 run into problems if we ever try to store a mount point that's
 | 
						||
	 over 256 but is under MAX_PATH! */
 | 
						||
      LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
 | 
						||
			  NULL, NULL, NULL);
 | 
						||
 | 
						||
      if (err == ERROR_NO_MORE_ITEMS)
 | 
						||
	break;
 | 
						||
      else if (err != ERROR_SUCCESS)
 | 
						||
	{
 | 
						||
	  debug_printf ("RegEnumKeyEx failed, error %d!\n", err);
 | 
						||
	  break;
 | 
						||
	}
 | 
						||
 | 
						||
      if (iscygdrive (posix_path))
 | 
						||
	{
 | 
						||
	  found_cygdrive = TRUE;
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Get a reg_key based on i. */
 | 
						||
      reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL);
 | 
						||
 | 
						||
      /* Fetch info from the subkey. */
 | 
						||
      subkey.get_string ("native", native_path, sizeof (native_path), "");
 | 
						||
      mount_flags = subkey.get_int ("flags", 0);
 | 
						||
 | 
						||
      /* Add mount_item corresponding to registry mount point. */
 | 
						||
      int res = cygwin_shared->mount.add_item (native_path, posix_path, mount_flags, FALSE);
 | 
						||
      if (res && get_errno () == EMFILE)
 | 
						||
	break; /* The number of entries exceeds MAX_MOUNTS */
 | 
						||
    }
 | 
						||
 | 
						||
  if (!found_cygdrive)
 | 
						||
    return;
 | 
						||
 | 
						||
loop:
 | 
						||
  for (i = 0; ;i++)
 | 
						||
    {
 | 
						||
      posix_path_size = MAX_PATH;
 | 
						||
      LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
 | 
						||
			  NULL, NULL, NULL);
 | 
						||
 | 
						||
      if (err != ERROR_SUCCESS)
 | 
						||
	break;
 | 
						||
 | 
						||
      if (iscygdrive (posix_path))
 | 
						||
	{
 | 
						||
	  /* This shouldn't be in the mount table. */
 | 
						||
	  (void) r.kill (posix_path);
 | 
						||
	  goto loop;
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* from_registry: Build the entire mount table from the registry.  Also,
 | 
						||
   read in cygdrive-related information from its registry location. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::from_registry ()
 | 
						||
{
 | 
						||
  /* Use current mount areas if either user or system mount areas
 | 
						||
     already exist.  Otherwise, import old mounts. */
 | 
						||
 | 
						||
  reg_key r;
 | 
						||
 | 
						||
  /* Retrieve cygdrive-related information. */
 | 
						||
  read_cygdrive_info_from_registry ();
 | 
						||
 | 
						||
  nmounts = 0;
 | 
						||
 | 
						||
  /* First read mounts from user's table. */
 | 
						||
  read_mounts (r);
 | 
						||
 | 
						||
  /* Then read mounts from system-wide mount table. */
 | 
						||
  reg_key r1 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE",
 | 
						||
	      CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
	      CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
	      CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
	      NULL);
 | 
						||
  read_mounts (r1);
 | 
						||
 | 
						||
  /* If we had to create both user and system mount areas, import
 | 
						||
     old mounts. */
 | 
						||
  if (had_to_create_mount_areas == 2)
 | 
						||
    import_v1_mounts ();
 | 
						||
}
 | 
						||
 | 
						||
/* add_reg_mount: Add mount item to registry.  Return zero on success,
 | 
						||
   non-zero on failure. */
 | 
						||
/* FIXME: Need a mutex to avoid collisions with other tasks. */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::add_reg_mount (const char * native_path, const char * posix_path, unsigned mountflags)
 | 
						||
{
 | 
						||
  /* Add the mount to the right registry location, depending on
 | 
						||
     whether MOUNT_SYSTEM is set in the mount flags. */
 | 
						||
  if (!(mountflags & MOUNT_SYSTEM)) /* current_user mount */
 | 
						||
    {
 | 
						||
      /* reg_key for user mounts in HKEY_CURRENT_USER. */
 | 
						||
      reg_key reg_user;
 | 
						||
 | 
						||
      /* Start by deleting existing mount if one exists. */
 | 
						||
      reg_user.kill (posix_path);
 | 
						||
 | 
						||
      /* Create the new mount. */
 | 
						||
      reg_key subkey = reg_key (reg_user.get_key (),
 | 
						||
				KEY_ALL_ACCESS,
 | 
						||
				posix_path, NULL);
 | 
						||
      subkey.set_string ("native", native_path);
 | 
						||
      subkey.set_int ("flags", mountflags);
 | 
						||
    }
 | 
						||
  else /* local_machine mount */
 | 
						||
    {
 | 
						||
      /* reg_key for system mounts in HKEY_LOCAL_MACHINE. */
 | 
						||
      reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
		       CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
		       CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
		       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
		       NULL);
 | 
						||
 | 
						||
      if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
 | 
						||
	{
 | 
						||
	  set_errno (EACCES);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Start by deleting existing mount if one exists. */
 | 
						||
      reg_sys.kill (posix_path);
 | 
						||
 | 
						||
      /* Create the new mount. */
 | 
						||
      reg_key subkey = reg_key (reg_sys.get_key (),
 | 
						||
				KEY_ALL_ACCESS,
 | 
						||
				posix_path, NULL);
 | 
						||
      subkey.set_string ("native", native_path);
 | 
						||
      subkey.set_int ("flags", mountflags);
 | 
						||
    }
 | 
						||
 | 
						||
  return 0; /* Success! */
 | 
						||
}
 | 
						||
 | 
						||
/* del_reg_mount: delete mount item from registry indicated in flags.
 | 
						||
   Return zero on success, non-zero on failure.*/
 | 
						||
/* FIXME: Need a mutex to avoid collisions with other tasks. */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::del_reg_mount (const char * posix_path, unsigned flags)
 | 
						||
{
 | 
						||
  int killres;
 | 
						||
 | 
						||
  if ((flags & MOUNT_SYSTEM) == 0)	/* Delete from user registry */
 | 
						||
    {
 | 
						||
      reg_key reg_user (KEY_ALL_ACCESS,
 | 
						||
			CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
 | 
						||
      killres = reg_user.kill (posix_path);
 | 
						||
    }
 | 
						||
  else					/* Delete from system registry */
 | 
						||
    {
 | 
						||
      reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
		       CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
		       CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
		       CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
		       NULL);
 | 
						||
 | 
						||
      if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
 | 
						||
	{
 | 
						||
	  set_errno (EACCES);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
 | 
						||
      killres = reg_sys.kill (posix_path);
 | 
						||
    }
 | 
						||
 | 
						||
  if (killres != ERROR_SUCCESS)
 | 
						||
    {
 | 
						||
      __seterrno_from_win_error (killres);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  return 0; /* Success! */
 | 
						||
}
 | 
						||
 | 
						||
/* read_cygdrive_info_from_registry: Read the default prefix and flags
 | 
						||
   to use when creating cygdrives from the special user registry
 | 
						||
   location used to store cygdrive information. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::read_cygdrive_info_from_registry ()
 | 
						||
{
 | 
						||
  /* reg_key for user path prefix in HKEY_CURRENT_USER. */
 | 
						||
  reg_key r;
 | 
						||
 | 
						||
  if (r.get_string ("cygdrive prefix", cygdrive, sizeof (cygdrive), "") != 0)
 | 
						||
    {
 | 
						||
      /* Didn't find the user path prefix so check the system path prefix. */
 | 
						||
 | 
						||
      /* reg_key for system path prefix in HKEY_LOCAL_MACHINE.  */
 | 
						||
      reg_key r2 (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
		 CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
		 CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
		 CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
		 NULL);
 | 
						||
 | 
						||
    if (r2.get_string ("cygdrive prefix", cygdrive, sizeof (cygdrive), "") != 0)
 | 
						||
      {
 | 
						||
	/* Didn't find either so write the default to the registry and use it.
 | 
						||
	   NOTE: We are writing and using the user path prefix.  */
 | 
						||
	write_cygdrive_info_to_registry ("/cygdrive", MOUNT_AUTO);
 | 
						||
      }
 | 
						||
    else
 | 
						||
      {
 | 
						||
	/* Fetch system cygdrive_flags from registry; returns MOUNT_AUTO on
 | 
						||
	   error. */
 | 
						||
	cygdrive_flags = r2.get_int ("cygdrive flags", MOUNT_AUTO);
 | 
						||
	slashify (cygdrive, cygdrive, 1);
 | 
						||
	cygdrive_len = strlen(cygdrive);
 | 
						||
      }
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* Fetch user cygdrive_flags from registry; returns MOUNT_AUTO on
 | 
						||
	 error. */
 | 
						||
      cygdrive_flags = r.get_int ("cygdrive flags", MOUNT_AUTO);
 | 
						||
      slashify (cygdrive, cygdrive, 1);
 | 
						||
      cygdrive_len = strlen(cygdrive);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* write_cygdrive_info_to_registry: Write the default prefix and flags
 | 
						||
   to use when creating cygdrives to the special user registry
 | 
						||
   location used to store cygdrive information. */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags)
 | 
						||
{
 | 
						||
  /* Determine whether to modify user or system cygdrive path prefix. */
 | 
						||
  HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
 | 
						||
 | 
						||
  /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
 | 
						||
     HKEY_LOCAL_MACHINE.  */
 | 
						||
  reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
	     CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
	     CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
	     CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
	     NULL);
 | 
						||
 | 
						||
  /* Verify cygdrive prefix starts with a forward slash and if there's
 | 
						||
     another character, it's not a slash. */
 | 
						||
  if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
 | 
						||
      (!isslash (cygdrive_prefix[0])) ||
 | 
						||
      ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
 | 
						||
      {
 | 
						||
	set_errno (EINVAL);
 | 
						||
	return -1;
 | 
						||
      }
 | 
						||
 | 
						||
  char hold_cygdrive_prefix[strlen (cygdrive_prefix) + 1];
 | 
						||
  /* Ensure that there is never a final slash */
 | 
						||
  nofinalslash (cygdrive_prefix, hold_cygdrive_prefix);
 | 
						||
 | 
						||
  r.set_string ("cygdrive prefix", hold_cygdrive_prefix);
 | 
						||
  r.set_int ("cygdrive flags", flags);
 | 
						||
 | 
						||
  /* This also needs to go in the in-memory copy of "cygdrive", but only if
 | 
						||
     appropriate:
 | 
						||
       1. setting user path prefix, or
 | 
						||
       2. overwriting (a previous) system path prefix */
 | 
						||
  if ((flags & MOUNT_SYSTEM) == 0 ||
 | 
						||
      (cygwin_shared->mount.cygdrive_flags & MOUNT_SYSTEM) != 0)
 | 
						||
    {
 | 
						||
      slashify (cygdrive_prefix, cygwin_shared->mount.cygdrive, 1);
 | 
						||
      cygwin_shared->mount.cygdrive_flags = flags;
 | 
						||
      cygwin_shared->mount.cygdrive_len = strlen(cygwin_shared->mount.cygdrive);
 | 
						||
    }
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
mount_info::remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags)
 | 
						||
{
 | 
						||
  /* Determine whether to modify user or system cygdrive path prefix. */
 | 
						||
  HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
 | 
						||
 | 
						||
  /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
 | 
						||
     HKEY_LOCAL_MACHINE.  */
 | 
						||
  reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
	     CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
	     CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
	     CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
	     NULL);
 | 
						||
 | 
						||
  /* Delete cygdrive prefix and flags. */
 | 
						||
  int res = r.killvalue ("cygdrive prefix");
 | 
						||
  int res2 = r.killvalue ("cygdrive flags");
 | 
						||
 | 
						||
  /* Reinitialize the cygdrive path prefix to reflect to removal from the
 | 
						||
     registry. */
 | 
						||
  read_cygdrive_info_from_registry ();
 | 
						||
 | 
						||
  return (res != ERROR_SUCCESS) ? res : res2;
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
mount_info::get_cygdrive_prefixes (char *user, char *system)
 | 
						||
{
 | 
						||
  /* Get the user path prefix from HKEY_CURRENT_USER. */
 | 
						||
  reg_key r;
 | 
						||
  int res = r.get_string ("cygdrive prefix", user, MAX_PATH, "");
 | 
						||
 | 
						||
  /* Get the system path prefix from HKEY_LOCAL_MACHINE. */
 | 
						||
  reg_key r2 (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
 | 
						||
	      CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
 | 
						||
	      CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
 | 
						||
	      CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
 | 
						||
	      NULL);
 | 
						||
  int res2 = r2.get_string ("cygdrive prefix", system, MAX_PATH, "");
 | 
						||
 | 
						||
  return (res != ERROR_SUCCESS) ? res : res2;
 | 
						||
}
 | 
						||
 | 
						||
struct mntent *
 | 
						||
mount_info::getmntent (int x)
 | 
						||
{
 | 
						||
  if (x < 0 || x >= nmounts)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  return mount[native_sorted[x]].getmntent ();
 | 
						||
}
 | 
						||
 | 
						||
static mount_item *mounts_for_sort;
 | 
						||
 | 
						||
/* sort_by_posix_name: qsort callback to sort the mount entries.  Sort
 | 
						||
   user mounts ahead of system mounts to the same POSIX path. */
 | 
						||
/* FIXME: should the user should be able to choose whether to
 | 
						||
   prefer user or system mounts??? */
 | 
						||
static int
 | 
						||
sort_by_posix_name (const void *a, const void *b)
 | 
						||
{
 | 
						||
  mount_item *ap = mounts_for_sort + (*((int*) a));
 | 
						||
  mount_item *bp = mounts_for_sort + (*((int*) b));
 | 
						||
 | 
						||
  /* Base weighting on longest posix path first so that the most
 | 
						||
     obvious path will be chosen. */
 | 
						||
  size_t alen = strlen (ap->posix_path);
 | 
						||
  size_t blen = strlen (bp->posix_path);
 | 
						||
 | 
						||
  int res = blen - alen;
 | 
						||
 | 
						||
  if (res)
 | 
						||
    return res;		/* Path lengths differed */
 | 
						||
 | 
						||
  /* The two paths were the same length, so just determine normal
 | 
						||
     lexical sorted order. */
 | 
						||
  res = strcmp (ap->posix_path, bp->posix_path);
 | 
						||
 | 
						||
  if (res == 0)
 | 
						||
   {
 | 
						||
     /* need to select between user and system mount to same POSIX path */
 | 
						||
     if ((bp->flags & MOUNT_SYSTEM) == 0) /* user mount */
 | 
						||
      return 1;
 | 
						||
     else
 | 
						||
      return -1;
 | 
						||
   }
 | 
						||
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
/* sort_by_native_name: qsort callback to sort the mount entries.  Sort
 | 
						||
   user mounts ahead of system mounts to the same POSIX path. */
 | 
						||
/* FIXME: should the user should be able to choose whether to
 | 
						||
   prefer user or system mounts??? */
 | 
						||
static int
 | 
						||
sort_by_native_name (const void *a, const void *b)
 | 
						||
{
 | 
						||
  mount_item *ap = mounts_for_sort + (*((int*) a));
 | 
						||
  mount_item *bp = mounts_for_sort + (*((int*) b));
 | 
						||
 | 
						||
  /* Base weighting on longest win32 path first so that the most
 | 
						||
     obvious path will be chosen. */
 | 
						||
  size_t alen = strlen (ap->native_path);
 | 
						||
  size_t blen = strlen (bp->native_path);
 | 
						||
 | 
						||
  int res = blen - alen;
 | 
						||
 | 
						||
  if (res)
 | 
						||
    return res;		/* Path lengths differed */
 | 
						||
 | 
						||
  /* The two paths were the same length, so just determine normal
 | 
						||
     lexical sorted order. */
 | 
						||
  res = strcmp (ap->native_path, bp->native_path);
 | 
						||
 | 
						||
  if (res == 0)
 | 
						||
   {
 | 
						||
     /* need to select between user and system mount to same POSIX path */
 | 
						||
     if ((bp->flags & MOUNT_SYSTEM) == 0) /* user mount */
 | 
						||
      return 1;
 | 
						||
     else
 | 
						||
      return -1;
 | 
						||
   }
 | 
						||
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
mount_info::sort ()
 | 
						||
{
 | 
						||
  for (int i = 0; i < nmounts; i++)
 | 
						||
    native_sorted[i] = posix_sorted[i] = i;
 | 
						||
  /* Sort them into reverse length order, otherwise we won't
 | 
						||
     be able to look for /foo in /.  */
 | 
						||
  mounts_for_sort = mount;	/* ouch. */
 | 
						||
  qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
 | 
						||
  qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
 | 
						||
}
 | 
						||
 | 
						||
/* Add an entry to the mount table.
 | 
						||
   Returns 0 on success, -1 on failure and errno is set.
 | 
						||
 | 
						||
   This is where all argument validation is done.  It may not make sense to
 | 
						||
   do this when called internally, but it's cleaner to keep it all here.  */
 | 
						||
 | 
						||
int
 | 
						||
mount_info::add_item (const char *native, const char *posix, unsigned mountflags, int reg_p)
 | 
						||
{
 | 
						||
  /* Something's wrong if either path is NULL or empty, or if it's
 | 
						||
     not a UNC or absolute path. */
 | 
						||
 | 
						||
  if ((native == NULL) || (*native == 0) ||
 | 
						||
      (posix == NULL) || (*posix == 0) ||
 | 
						||
      (!slash_unc_prefix_p (native) && !isabspath (native)))
 | 
						||
    {
 | 
						||
      set_errno (EINVAL);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Make sure both paths do not end in /. */
 | 
						||
  char nativetmp[MAX_PATH];
 | 
						||
  char posixtmp[MAX_PATH];
 | 
						||
 | 
						||
  if (slash_drive_prefix_p (native))
 | 
						||
    slash_drive_to_win32_path (native, nativetmp, 0);
 | 
						||
  else
 | 
						||
    backslashify (native, nativetmp, 0);
 | 
						||
  nofinalslash (nativetmp, nativetmp);
 | 
						||
 | 
						||
  slashify (posix, posixtmp, 0);
 | 
						||
  nofinalslash (posixtmp, posixtmp);
 | 
						||
 | 
						||
  debug_printf ("%s[%s], %s[%s], %p",
 | 
						||
		native, nativetmp, posix, posixtmp, mountflags);
 | 
						||
 | 
						||
  /* Duplicate /'s in path are an error. */
 | 
						||
  for (char *p = posixtmp + 1; *p; ++p)
 | 
						||
    {
 | 
						||
      if (p[-1] == '/' && p[0] == '/')
 | 
						||
	{
 | 
						||
	  set_errno (EINVAL);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  /* Write over an existing mount item with the same POSIX path if
 | 
						||
     it exists and is from the same registry area. */
 | 
						||
  int i;
 | 
						||
  for (i = 0; i < nmounts; i++)
 | 
						||
    {
 | 
						||
      if (strcasematch (mount[i].posix_path, posixtmp) &&
 | 
						||
	  (mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
 | 
						||
	break;
 | 
						||
    }
 | 
						||
 | 
						||
  if (i == nmounts)
 | 
						||
    {
 | 
						||
      if (nmounts < MAX_MOUNTS)
 | 
						||
	i = nmounts++;
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  set_errno (EMFILE);
 | 
						||
	  return -1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  if (reg_p && add_reg_mount (nativetmp, posixtmp, mountflags))
 | 
						||
    return -1;
 | 
						||
 | 
						||
  mount[i].init (nativetmp, posixtmp, mountflags);
 | 
						||
  sort ();
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Delete a mount table entry where path is either a Win32 or POSIX
 | 
						||
   path. Since the mount table is really just a table of aliases,
 | 
						||
   deleting / is ok (although running without a slash mount is
 | 
						||
   strongly discouraged because some programs may run erratically
 | 
						||
   without one).  If MOUNT_SYSTEM is set in flags, remove from system
 | 
						||
   registry, otherwise remove the user registry mount.
 | 
						||
*/
 | 
						||
 | 
						||
int
 | 
						||
mount_info::del_item (const char *path, unsigned flags, int reg_p)
 | 
						||
{
 | 
						||
  char pathtmp[MAX_PATH];
 | 
						||
  int posix_path_p = FALSE;
 | 
						||
 | 
						||
  /* Something's wrong if path is NULL or empty. */
 | 
						||
  if (path == NULL || *path == 0 || !isabspath (path))
 | 
						||
    {
 | 
						||
      set_errno (EINVAL);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  if (slash_drive_prefix_p (path))
 | 
						||
      slash_drive_to_win32_path (path, pathtmp, 0);
 | 
						||
  else if (slash_unc_prefix_p (path) || strpbrk (path, ":\\"))
 | 
						||
      backslashify (path, pathtmp, 0);
 | 
						||
  else
 | 
						||
    {
 | 
						||
      slashify (path, pathtmp, 0);
 | 
						||
      posix_path_p = TRUE;
 | 
						||
    }
 | 
						||
  nofinalslash (pathtmp, pathtmp);
 | 
						||
 | 
						||
  if (reg_p && posix_path_p &&
 | 
						||
      del_reg_mount (pathtmp, flags) &&
 | 
						||
      del_reg_mount (path, flags)) /* for old irregular entries */
 | 
						||
    return -1;
 | 
						||
 | 
						||
  for (int i = 0; i < nmounts; i++)
 | 
						||
    {
 | 
						||
      int ent = native_sorted[i]; /* in the same order as getmntent() */
 | 
						||
      if (((posix_path_p)
 | 
						||
	   ? strcasematch (mount[ent].posix_path, pathtmp)
 | 
						||
	   : strcasematch (mount[ent].native_path, pathtmp)) &&
 | 
						||
	  (mount[ent].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM))
 | 
						||
	{
 | 
						||
	  if (!posix_path_p &&
 | 
						||
	      reg_p && del_reg_mount (mount[ent].posix_path, flags))
 | 
						||
	    return -1;
 | 
						||
 | 
						||
	  nmounts--; /* One less mount table entry */
 | 
						||
	  /* Fill in the hole if not at the end of the table */
 | 
						||
	  if (ent < nmounts)
 | 
						||
	    memmove (mount + ent, mount + ent + 1,
 | 
						||
		     sizeof (mount[ent]) * (nmounts - ent));
 | 
						||
	  sort (); /* Resort the table */
 | 
						||
	  return 0;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  set_errno (EINVAL);
 | 
						||
  return -1;
 | 
						||
}
 | 
						||
 | 
						||
/* read_v1_mounts: Given a reg_key to an old mount table registry area,
 | 
						||
   read in the mounts.  The "which" arg contains zero if we're reading
 | 
						||
   the user area and MOUNT_SYSTEM if we're reading the system area.
 | 
						||
   This way we can store the mounts read in the appropriate place when
 | 
						||
   they are written back to the new registry layout. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::read_v1_mounts (reg_key r, unsigned which)
 | 
						||
{
 | 
						||
  unsigned mountflags = 0;
 | 
						||
 | 
						||
  /* MAX_MOUNTS was 30 when we stopped using the v1 layout */
 | 
						||
  for (int i = 0; i < 30; i++)
 | 
						||
    {
 | 
						||
      char key_name[10];
 | 
						||
      char win32path[MAX_PATH];
 | 
						||
      char unixpath[MAX_PATH];
 | 
						||
 | 
						||
      __small_sprintf (key_name, "%02x", i);
 | 
						||
 | 
						||
      reg_key k (r.get_key (), KEY_ALL_ACCESS, key_name, NULL);
 | 
						||
 | 
						||
      /* The registry names are historical but useful so are left alone.  */
 | 
						||
      k.get_string ("native", win32path, sizeof (win32path), "");
 | 
						||
      k.get_string ("unix", unixpath, sizeof (unixpath), "");
 | 
						||
 | 
						||
      /* Does this entry contain something?  */
 | 
						||
      if (*win32path != 0)
 | 
						||
	{
 | 
						||
	  mountflags = 0;
 | 
						||
 | 
						||
	  if (k.get_int ("fbinary", 0))
 | 
						||
	    mountflags |= MOUNT_BINARY;
 | 
						||
 | 
						||
	  /* Or in zero or MOUNT_SYSTEM depending on which table
 | 
						||
	     we're reading. */
 | 
						||
	  mountflags |= which;
 | 
						||
 | 
						||
	  int res = cygwin_shared->mount.add_item (win32path, unixpath, mountflags, TRUE);
 | 
						||
	  if (res && get_errno () == EMFILE)
 | 
						||
	    break; /* The number of entries exceeds MAX_MOUNTS */
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* import_v1_mounts: If v1 mounts are present, load them and write
 | 
						||
   the new entries to the new registry area. */
 | 
						||
 | 
						||
void
 | 
						||
mount_info::import_v1_mounts ()
 | 
						||
{
 | 
						||
  reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS,
 | 
						||
	     "SOFTWARE",
 | 
						||
	     "Cygnus Solutions",
 | 
						||
	     "CYGWIN.DLL setup",
 | 
						||
	     "b15.0",
 | 
						||
	     "mounts",
 | 
						||
	     NULL);
 | 
						||
 | 
						||
  nmounts = 0;
 | 
						||
 | 
						||
  /* First read mounts from user's table. */
 | 
						||
  read_v1_mounts (r, 0);
 | 
						||
 | 
						||
  /* Then read mounts from system-wide mount table. */
 | 
						||
  reg_key r1 (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS,
 | 
						||
	      "SOFTWARE",
 | 
						||
	      "Cygnus Solutions",
 | 
						||
	      "CYGWIN.DLL setup",
 | 
						||
	      "b15.0",
 | 
						||
	      "mounts",
 | 
						||
	      NULL);
 | 
						||
  read_v1_mounts (r1, MOUNT_SYSTEM);
 | 
						||
}
 | 
						||
 | 
						||
/************************* mount_item class ****************************/
 | 
						||
 | 
						||
struct mntent *
 | 
						||
mount_item::getmntent ()
 | 
						||
{
 | 
						||
#ifdef _MT_SAFE
 | 
						||
  struct mntent &ret=_reent_winsup()->_ret;
 | 
						||
#else
 | 
						||
  static NO_COPY struct mntent ret;
 | 
						||
#endif
 | 
						||
 | 
						||
  /* Pass back pointers to mount_info strings reserved for use by
 | 
						||
     getmntent rather than pointers to strings in the internal mount
 | 
						||
     table because the mount table might change, causing weird effects
 | 
						||
     from the getmntent user's point of view. */
 | 
						||
 | 
						||
  strcpy (cygwin_shared->mount.mnt_fsname, native_path);
 | 
						||
  ret.mnt_fsname = cygwin_shared->mount.mnt_fsname;
 | 
						||
  strcpy (cygwin_shared->mount.mnt_dir, posix_path);
 | 
						||
  ret.mnt_dir = cygwin_shared->mount.mnt_dir;
 | 
						||
 | 
						||
  if (!(flags & MOUNT_SYSTEM))		/* user mount */
 | 
						||
    strcpy (cygwin_shared->mount.mnt_type, (char *) "user");
 | 
						||
  else					/* system mount */
 | 
						||
    strcpy (cygwin_shared->mount.mnt_type, (char *) "system");
 | 
						||
 | 
						||
  if ((flags & MOUNT_AUTO))		/* cygdrive */
 | 
						||
    strcat (cygwin_shared->mount.mnt_type, (char *) ",auto");
 | 
						||
 | 
						||
  ret.mnt_type = cygwin_shared->mount.mnt_type;
 | 
						||
 | 
						||
  /* mnt_opts is a string that details mount params such as
 | 
						||
     binary or textmode, or exec.  We don't print
 | 
						||
     `silent' here; it's a magic internal thing. */
 | 
						||
 | 
						||
  if (! (flags & MOUNT_BINARY))
 | 
						||
    strcpy (cygwin_shared->mount.mnt_opts, (char *) "textmode");
 | 
						||
  else
 | 
						||
    strcpy (cygwin_shared->mount.mnt_opts, (char *) "binmode");
 | 
						||
 | 
						||
  if (flags & MOUNT_CYGWIN_EXEC)
 | 
						||
    strcat (cygwin_shared->mount.mnt_opts, (char *) ",cygexec");
 | 
						||
  else if (flags & MOUNT_EXEC)
 | 
						||
    strcat (cygwin_shared->mount.mnt_opts, (char *) ",exec");
 | 
						||
 | 
						||
 | 
						||
  ret.mnt_opts = cygwin_shared->mount.mnt_opts;
 | 
						||
 | 
						||
  ret.mnt_freq = 1;
 | 
						||
  ret.mnt_passno = 1;
 | 
						||
  return &ret;
 | 
						||
}
 | 
						||
 | 
						||
/* Fill in the fields of a mount table entry.  */
 | 
						||
 | 
						||
void
 | 
						||
mount_item::init (const char *native, const char *posix, unsigned mountflags)
 | 
						||
{
 | 
						||
  strcpy ((char *) native_path, native);
 | 
						||
  strcpy ((char *) posix_path, posix);
 | 
						||
 | 
						||
  native_pathlen = strlen (native_path);
 | 
						||
  posix_pathlen = strlen (posix_path);
 | 
						||
 | 
						||
  flags = mountflags;
 | 
						||
}
 | 
						||
 | 
						||
/********************** Mount System Calls **************************/
 | 
						||
 | 
						||
/* Mount table system calls.
 | 
						||
   Note that these are exported to the application.  */
 | 
						||
 | 
						||
/* mount: Add a mount to the mount table in memory and to the registry
 | 
						||
   that will cause paths under win32_path to be translated to paths
 | 
						||
   under posix_path. */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
mount (const char *win32_path, const char *posix_path, unsigned flags)
 | 
						||
{
 | 
						||
  int res = -1;
 | 
						||
 | 
						||
  if (flags & MOUNT_AUTO) /* normal mount */
 | 
						||
    {
 | 
						||
      /* When flags include MOUNT_AUTO, take this to mean that
 | 
						||
	we actually want to change the cygdrive prefix and flags
 | 
						||
	without actually mounting anything. */
 | 
						||
      res = cygwin_shared->mount.write_cygdrive_info_to_registry (posix_path, flags);
 | 
						||
      win32_path = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      if (iscygdrive (posix_path))
 | 
						||
	{
 | 
						||
	  set_errno (EINVAL);
 | 
						||
	  return res;	/* Don't try to add cygdrive prefix. */
 | 
						||
	}
 | 
						||
 | 
						||
      res = cygwin_shared->mount.add_item (win32_path, posix_path, flags, TRUE);
 | 
						||
    }
 | 
						||
 | 
						||
  syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
/* umount: The standard umount call only has a path parameter.  Since
 | 
						||
   it is not possible for this call to specify whether to remove the
 | 
						||
   mount from the user or global mount registry table, assume the user
 | 
						||
   table. */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
umount (const char *path)
 | 
						||
{
 | 
						||
  return cygwin_umount (path, 0);
 | 
						||
}
 | 
						||
 | 
						||
/* cygwin_umount: This is like umount but takes an additional flags
 | 
						||
   parameter that specifies whether to umount from the user or system-wide
 | 
						||
   registry area. */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_umount (const char *path, unsigned flags)
 | 
						||
{
 | 
						||
  int res = -1;
 | 
						||
 | 
						||
  if (flags & MOUNT_AUTO)
 | 
						||
    {
 | 
						||
      /* When flags include MOUNT_AUTO, take this to mean that we actually want
 | 
						||
	 to remove the cygdrive prefix and flags without actually unmounting
 | 
						||
	 anything. */
 | 
						||
      res = cygwin_shared->mount.remove_cygdrive_info_from_registry (path, flags);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      res = cygwin_shared->mount.del_item (path, flags, TRUE);
 | 
						||
    }
 | 
						||
 | 
						||
  syscall_printf ("%d = cygwin_umount (%s, %d)", res,  path, flags);
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
#ifdef _MT_SAFE
 | 
						||
#define iteration _reent_winsup()->_iteration
 | 
						||
#else
 | 
						||
static int iteration;
 | 
						||
#endif
 | 
						||
 | 
						||
extern "C"
 | 
						||
FILE *
 | 
						||
setmntent (const char *filep, const char *)
 | 
						||
{
 | 
						||
  iteration = 0;
 | 
						||
  return (FILE *) filep;
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
struct mntent *
 | 
						||
getmntent (FILE *)
 | 
						||
{
 | 
						||
  return cygwin_shared->mount.getmntent (iteration++);
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
endmntent (FILE *)
 | 
						||
{
 | 
						||
  return 1;
 | 
						||
}
 | 
						||
 | 
						||
/********************** Symbolic Link Support **************************/
 | 
						||
 | 
						||
/* Create a symlink from FROMPATH to TOPATH. */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
symlink (const char *topath, const char *frompath)
 | 
						||
{
 | 
						||
  HANDLE h;
 | 
						||
  int res = -1;
 | 
						||
 | 
						||
  path_conv win32_path (frompath, PC_SYM_NOFOLLOW);
 | 
						||
  if (win32_path.error)
 | 
						||
    {
 | 
						||
      set_errno (win32_path.error);
 | 
						||
      goto done;
 | 
						||
    }
 | 
						||
 | 
						||
  syscall_printf ("symlink (%s, %s)", topath, win32_path.get_win32 ());
 | 
						||
 | 
						||
  if (topath[0] == 0)
 | 
						||
    {
 | 
						||
      set_errno (EINVAL);
 | 
						||
      goto done;
 | 
						||
    }
 | 
						||
  if (strlen (topath) >= MAX_PATH)
 | 
						||
    {
 | 
						||
      set_errno (ENAMETOOLONG);
 | 
						||
      goto done;
 | 
						||
    }
 | 
						||
 | 
						||
  if (win32_path.is_device () ||
 | 
						||
      win32_path.file_attributes () != (DWORD) -1)
 | 
						||
    {
 | 
						||
      set_errno (EEXIST);
 | 
						||
      goto done;
 | 
						||
    }
 | 
						||
 | 
						||
  h = CreateFileA(win32_path.get_win32 (), GENERIC_WRITE, 0, &sec_none_nih,
 | 
						||
		  CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
 | 
						||
  if (h == INVALID_HANDLE_VALUE)
 | 
						||
      __seterrno ();
 | 
						||
  else
 | 
						||
    {
 | 
						||
      char buf[sizeof (SYMLINK_COOKIE) + MAX_PATH + 10];
 | 
						||
 | 
						||
      __small_sprintf (buf, "%s%s", SYMLINK_COOKIE, topath);
 | 
						||
      DWORD len = strlen (buf) + 1;
 | 
						||
 | 
						||
      /* Note that the terminating nul is written.  */
 | 
						||
      DWORD written;
 | 
						||
      if (!WriteFile (h, buf, len, &written, NULL) || written != len)
 | 
						||
	{
 | 
						||
	  __seterrno ();
 | 
						||
	  CloseHandle (h);
 | 
						||
	  DeleteFileA (win32_path.get_win32 ());
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  CloseHandle (h);
 | 
						||
	  set_file_attribute (win32_path.has_acls (),
 | 
						||
			      win32_path.get_win32 (),
 | 
						||
			      S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO);
 | 
						||
	  SetFileAttributesA (win32_path.get_win32 (), FILE_ATTRIBUTE_SYSTEM);
 | 
						||
	  res = 0;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
done:
 | 
						||
  syscall_printf ("%d = symlink (%s, %s)", res, topath, frompath);
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
static __inline char *
 | 
						||
has_suffix (const char *path, const suffix_info *suffixes)
 | 
						||
{
 | 
						||
  char *ext = strrchr (path, '.');
 | 
						||
  if (ext)
 | 
						||
    for (const suffix_info *ex = suffixes; ex->name != NULL; ex++)
 | 
						||
      if (strcasematch (ext, ex->name))
 | 
						||
	return ext;
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
static __inline__ int
 | 
						||
next_suffix (char *ext_here, const suffix_info *&suffixes)
 | 
						||
{
 | 
						||
  if (!suffixes)
 | 
						||
    return 1;
 | 
						||
 | 
						||
  while (suffixes && suffixes->name)
 | 
						||
    if (!suffixes->addon)
 | 
						||
      suffixes++;
 | 
						||
    else
 | 
						||
      {
 | 
						||
	strcpy (ext_here, suffixes->name);
 | 
						||
	suffixes++;
 | 
						||
	return 1;
 | 
						||
      }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Check if PATH is a symlink.  PATH must be a valid Win32 path name.
 | 
						||
 | 
						||
   If PATH is a symlink, put the value of the symlink--the file to
 | 
						||
   which it points--into BUF.  The value stored in BUF is not
 | 
						||
   necessarily null terminated.  BUFLEN is the length of BUF; only up
 | 
						||
   to BUFLEN characters will be stored in BUF.  BUF may be NULL, in
 | 
						||
   which case nothing will be stored.
 | 
						||
 | 
						||
   Set *SYML if PATH is a symlink.
 | 
						||
 | 
						||
   Set *EXEC if PATH appears to be executable.  This is an efficiency
 | 
						||
   hack because we sometimes have to open the file anyhow.  *EXEC will
 | 
						||
   not be set for every executable file.
 | 
						||
 | 
						||
   Return -1 on error, 0 if PATH is not a symlink, or the length
 | 
						||
   stored into BUF if PATH is a symlink.  */
 | 
						||
 | 
						||
int
 | 
						||
symlink_info::check (const char *in_path, const suffix_info *suffixes)
 | 
						||
{
 | 
						||
  HANDLE h;
 | 
						||
  int res = 0;
 | 
						||
  char extbuf[MAX_PATH + 5];
 | 
						||
  const char *path = in_path;
 | 
						||
 | 
						||
  if (!suffixes)
 | 
						||
    ext_here = NULL;
 | 
						||
  else if ((known_suffix = has_suffix (in_path, suffixes)) != NULL)
 | 
						||
    {
 | 
						||
      suffixes = NULL;
 | 
						||
      ext_here = NULL;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      path = strcpy (extbuf, in_path);
 | 
						||
      ext_here = strchr (path, '\0');
 | 
						||
    }
 | 
						||
 | 
						||
  is_symlink = TRUE;
 | 
						||
 | 
						||
  error = 0;
 | 
						||
  do
 | 
						||
    {
 | 
						||
      if (!next_suffix (ext_here, suffixes))
 | 
						||
	break;
 | 
						||
      error = 0;
 | 
						||
      fileattr = GetFileAttributesA (path);
 | 
						||
      if (fileattr == (DWORD) -1)
 | 
						||
	{
 | 
						||
	  /* The GetFileAttributesA call can fail for reasons that don't
 | 
						||
	     matter, so we just return 0.  For example, getting the
 | 
						||
	     attributes of \\HOST will typically fail.  */
 | 
						||
	  debug_printf ("GetFileAttributesA (%s) failed", path);
 | 
						||
	  error = geterrno_from_win_error (GetLastError (), EACCES);
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Windows allows path\. even when `path' isn't a directory.
 | 
						||
	 Detect this scenario and disallow it, since it is non-UNIX like.
 | 
						||
	 FIXME: This code actually checks for things like foo/ and foo/..
 | 
						||
	 even though those usages have already been (erroneously?) eaten
 | 
						||
	 by cygwin_shared->mount.conv_to_win32_path in path_conv::check. */
 | 
						||
 | 
						||
      char *p = strrchr (path, '\\');
 | 
						||
      if (p && !(fileattr & FILE_ATTRIBUTE_DIRECTORY) &&
 | 
						||
	  (*++p == '\0' || (*p == '.' && (*++p == '\0' || (*p == '.' && p[1] == '\0')))))
 | 
						||
	{
 | 
						||
	  debug_printf ("%s is a non-directory", path);
 | 
						||
	  error = ENOTDIR;
 | 
						||
	  goto file_not_symlink;
 | 
						||
	}
 | 
						||
 | 
						||
      /* A symlink will have the `system' file attribute. */
 | 
						||
      /* Only files can be symlinks (which can be symlinks to directories). */
 | 
						||
      if (!(pflags & PATH_SYMLINK) && !SYMLINKATTR (fileattr))
 | 
						||
	goto file_not_symlink;
 | 
						||
 | 
						||
      /* Open the file.  */
 | 
						||
 | 
						||
      h = CreateFileA (path, GENERIC_READ, FILE_SHARE_READ, &sec_none_nih, OPEN_EXISTING,
 | 
						||
		       FILE_ATTRIBUTE_NORMAL, 0);
 | 
						||
      res = -1;
 | 
						||
      if (h == INVALID_HANDLE_VALUE)
 | 
						||
	goto file_not_symlink;
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
 | 
						||
	  DWORD got;
 | 
						||
 | 
						||
	  if (! ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0))
 | 
						||
	    error = EIO;
 | 
						||
	  else if (got == sizeof (cookie_buf)
 | 
						||
		   && memcmp (cookie_buf, SYMLINK_COOKIE,
 | 
						||
			      sizeof (cookie_buf)) == 0)
 | 
						||
	    {
 | 
						||
	      /* It's a symlink.  */
 | 
						||
	      pflags = PATH_SYMLINK;
 | 
						||
 | 
						||
	      res = ReadFile (h, contents, MAX_PATH + 1, &got, 0);
 | 
						||
	      if (!res)
 | 
						||
		error = EIO;
 | 
						||
	      else
 | 
						||
		{
 | 
						||
		  /* Versions prior to b16 stored several trailing
 | 
						||
		     NULs with the path (to fill the path out to 1024
 | 
						||
		     chars).  Current versions only store one trailing
 | 
						||
		     NUL.  The length returned is the path without
 | 
						||
		     *any* trailing NULs.  We also have to handle (or
 | 
						||
		     at least not die from) corrupted paths.  */
 | 
						||
		  if (memchr (contents, 0, got) != NULL)
 | 
						||
		    res = strlen (contents);
 | 
						||
		  else
 | 
						||
		    res = got;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	  else if (got == sizeof (cookie_buf)
 | 
						||
		   && memcmp (cookie_buf, SOCKET_COOKIE,
 | 
						||
			      sizeof (cookie_buf)) == 0)
 | 
						||
	    {
 | 
						||
	      pflags |= PATH_SOCKET;
 | 
						||
	      goto close_and_return;
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    {
 | 
						||
	      /* Not a symlink, see if executable.  */
 | 
						||
	      if (!(pflags & (PATH_EXEC | PATH_CYGWIN_EXEC)) && got >= 2 &&
 | 
						||
		  ((cookie_buf[0] == '#' && cookie_buf[1] == '!') ||
 | 
						||
		   (cookie_buf[0] == ':' && cookie_buf[1] == '\n')))
 | 
						||
		pflags |= PATH_EXEC;
 | 
						||
	    close_and_return:
 | 
						||
	      CloseHandle (h);
 | 
						||
	      goto file_not_symlink;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
      CloseHandle (h);
 | 
						||
      break;
 | 
						||
    }
 | 
						||
  while (suffixes);
 | 
						||
  goto out;
 | 
						||
 | 
						||
file_not_symlink:
 | 
						||
  is_symlink = FALSE;
 | 
						||
  syscall_printf ("not a symlink");
 | 
						||
  res = 0;
 | 
						||
 | 
						||
out:
 | 
						||
  syscall_printf ("%d = symlink.check (%s, %p) (%p)",
 | 
						||
		  res, path, contents, pflags);
 | 
						||
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
/* readlink system call */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
readlink (const char *path, char *buf, int buflen)
 | 
						||
{
 | 
						||
  extern suffix_info stat_suffixes[];
 | 
						||
  path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
 | 
						||
 | 
						||
  if (pathbuf.error)
 | 
						||
    {
 | 
						||
      set_errno (pathbuf.error);
 | 
						||
      syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  if (!pathbuf.issymlink ())
 | 
						||
    {
 | 
						||
      if (pathbuf.fileattr != (DWORD) -1)
 | 
						||
	set_errno (EINVAL);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  int len = strlen (pathbuf.get_win32 ());
 | 
						||
  if (len > (buflen - 1))
 | 
						||
    {
 | 
						||
      set_errno (ENAMETOOLONG);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
  memcpy (buf, pathbuf.get_win32 (), len);
 | 
						||
  buf[len] = '\0';
 | 
						||
 | 
						||
  /* errno set by symlink.check if error */
 | 
						||
  return len;
 | 
						||
}
 | 
						||
 | 
						||
/* Some programs rely on st_dev/st_ino being unique for each file.
 | 
						||
   Hash the path name and hope for the best.  The hash arg is not
 | 
						||
   always initialized to zero since readdir needs to compute the
 | 
						||
   dirent ino_t based on a combination of the hash of the directory
 | 
						||
   done during the opendir call and the hash or the filename within
 | 
						||
   the directory.  FIXME: Not bullet-proof. */
 | 
						||
/* Cygwin internal */
 | 
						||
 | 
						||
unsigned long __stdcall
 | 
						||
hash_path_name (unsigned long hash, const char *name)
 | 
						||
{
 | 
						||
  if (!*name)
 | 
						||
    return hash;
 | 
						||
 | 
						||
  /* Perform some initial permutations on the pathname if this is
 | 
						||
     not "seeded" */
 | 
						||
  if (!hash)
 | 
						||
    {
 | 
						||
      /* Simplistic handling of drives.  If there is a drive specified,
 | 
						||
	 make sure that the initial letter is upper case.  If there is
 | 
						||
	 no \ after the ':' assume access through the root directory
 | 
						||
	 of that drive.
 | 
						||
	 FIXME:  Should really honor MS-Windows convention of using
 | 
						||
	 the environment to track current directory on various drives. */
 | 
						||
      if (name[1] == ':')
 | 
						||
	{
 | 
						||
	  char *nn, *newname = (char *) alloca (strlen (name) + 2);
 | 
						||
	  nn = strncpy (newname, name, 2);
 | 
						||
	  if (islower (*nn))
 | 
						||
	    *newname = toupper (*nn);
 | 
						||
	  *(nn += 2) = '\0';
 | 
						||
	  name += 2;
 | 
						||
	  if (*name != '\\')
 | 
						||
	    {
 | 
						||
	      *nn = '\\';
 | 
						||
	      *++nn = '\0';
 | 
						||
	    }
 | 
						||
	  strcpy (nn, name);
 | 
						||
	  name = newname;
 | 
						||
	  goto hashit;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Fill out the hashed path name with the current working directory if
 | 
						||
	 this is not an absolute path and there is no pre-specified hash value.
 | 
						||
	 Otherwise the inodes same will differ depending on whether a file is
 | 
						||
	 referenced with an absolute value or relatively. */
 | 
						||
 | 
						||
      if (*name != '\\' && (cwd_win32 == NULL ||
 | 
						||
			    get_cwd_win32 ()))
 | 
						||
	{
 | 
						||
	  hash = cwd_hash;
 | 
						||
	  if (name[0] == '.' && name[1] == '\0')
 | 
						||
	    return hash;
 | 
						||
	  hash = hash_path_name (hash, "\\");
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
hashit:
 | 
						||
  /* Build up hash.  Ignore single trailing slash or \a\b\ != \a\b or
 | 
						||
     \a\b\.  but allow a single \ if that's all there is. */
 | 
						||
  do
 | 
						||
    {
 | 
						||
      hash += *name + (*name << 17);
 | 
						||
      hash ^= hash >> 2;
 | 
						||
    }
 | 
						||
  while (*++name != '\0' &&
 | 
						||
	 !(*name == '\\' && (!name[1] || (name[1] == '.' && !name[2]))));
 | 
						||
  return hash;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
get_cwd_win32 ()
 | 
						||
{
 | 
						||
  DWORD dlen, len;
 | 
						||
 | 
						||
  for (dlen = 256; ; dlen *= 2)
 | 
						||
    {
 | 
						||
      cwd_win32 = (char *) realloc (cwd_win32, dlen + 2);
 | 
						||
      if ((len = GetCurrentDirectoryA (dlen, cwd_win32)) < dlen)
 | 
						||
	break;
 | 
						||
    }
 | 
						||
 | 
						||
  if (len == 0)
 | 
						||
    __seterrno ();
 | 
						||
  else
 | 
						||
    cwd_hash = hash_path_name (0, cwd_win32);
 | 
						||
 | 
						||
  return len;
 | 
						||
}
 | 
						||
 | 
						||
/* getcwd */
 | 
						||
 | 
						||
char *
 | 
						||
getcwd_inner (char *buf, size_t ulen, int posix_p, int with_chroot)
 | 
						||
{
 | 
						||
  char *resbuf = NULL;
 | 
						||
  size_t len = ulen;
 | 
						||
 | 
						||
  if (cwd_win32 == NULL && !get_cwd_win32 ())
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  if (!posix_p)
 | 
						||
    {
 | 
						||
      if (strlen (cwd_win32) >= len)
 | 
						||
	set_errno (ERANGE);
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  strcpy (buf, cwd_win32);
 | 
						||
	  resbuf = buf;
 | 
						||
	}
 | 
						||
 | 
						||
      syscall_printf ("%p (%s) = getcwd_inner (%p, %d, win32) (cached)",
 | 
						||
		      resbuf, resbuf ? resbuf : "", buf, len);
 | 
						||
      return resbuf;
 | 
						||
    }
 | 
						||
  else if (cwd_posix != NULL)
 | 
						||
    {
 | 
						||
      debug_printf("myself->root: %s, cwd_posix: %s", myself->root, cwd_posix);
 | 
						||
      if (strlen (cwd_posix) >= len)
 | 
						||
	set_errno (ERANGE);
 | 
						||
      else if (with_chroot && ischrootpath(cwd_posix))
 | 
						||
	{
 | 
						||
	  strcpy (buf, cwd_posix + myself->rootlen);
 | 
						||
	  if (!buf[0])
 | 
						||
	    strcpy (buf, "/");
 | 
						||
	  resbuf = buf;
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  strcpy (buf, cwd_posix);
 | 
						||
	  resbuf = buf;
 | 
						||
	}
 | 
						||
 | 
						||
      syscall_printf ("%p (%s) = getcwd_inner (%p, %d, posix) (cached)",
 | 
						||
		      resbuf, resbuf ? resbuf : "", buf, len);
 | 
						||
      return resbuf;
 | 
						||
    }
 | 
						||
 | 
						||
  /* posix_p required and cwd_posix == NULL */
 | 
						||
 | 
						||
  char temp[MAX_PATH];
 | 
						||
 | 
						||
  /* Turn from Win32 style to our style.  */
 | 
						||
  cygwin_shared->mount.conv_to_posix_path (cwd_win32, temp, 0);
 | 
						||
 | 
						||
  size_t tlen = strlen (temp);
 | 
						||
 | 
						||
  if (with_chroot && ischrootpath (temp))
 | 
						||
    tlen -= myself->rootlen;
 | 
						||
 | 
						||
  cwd_posix = (char *) realloc (
 | 
						||
				  cwd_posix, tlen + 1);
 | 
						||
  if (cwd_posix != NULL)
 | 
						||
    if (with_chroot && ischrootpath (temp))
 | 
						||
      {
 | 
						||
	strcpy (cwd_posix, temp + myself->rootlen);
 | 
						||
	if (!buf[0])
 | 
						||
	  strcpy (buf, "/");
 | 
						||
      }
 | 
						||
    else
 | 
						||
      strcpy (cwd_posix, temp);
 | 
						||
 | 
						||
  if (tlen >= ulen)
 | 
						||
    {
 | 
						||
      /* len was too small */
 | 
						||
      set_errno (ERANGE);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      strcpy (buf, temp);
 | 
						||
      resbuf = buf;
 | 
						||
    }
 | 
						||
 | 
						||
  syscall_printf ("%p (%s) = getcwd_inner (%p, %d, %s)",
 | 
						||
		  resbuf, resbuf ? resbuf : "",
 | 
						||
		  buf, len, posix_p ? "posix" : "win32");
 | 
						||
  return resbuf;
 | 
						||
}
 | 
						||
 | 
						||
char *
 | 
						||
getcwd (char *buf, size_t ulen)
 | 
						||
{
 | 
						||
  char *res;
 | 
						||
 | 
						||
  if (buf == NULL || ulen == 0)
 | 
						||
    {
 | 
						||
      buf = (char *) alloca (MAX_PATH);
 | 
						||
      res = getcwd_inner (buf, MAX_PATH, 1, 1);
 | 
						||
      res = strdup (buf);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      res = getcwd_inner (buf, ulen, 1, 1);
 | 
						||
    }
 | 
						||
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
/* getwd: standards? */
 | 
						||
extern "C"
 | 
						||
char *
 | 
						||
getwd (char *buf)
 | 
						||
{
 | 
						||
  return getcwd (buf, MAX_PATH);
 | 
						||
}
 | 
						||
 | 
						||
/* chdir: POSIX 5.2.1.1 */
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
chdir (const char *dir)
 | 
						||
{
 | 
						||
  syscall_printf ("dir %s", dir);
 | 
						||
  path_conv path (dir);
 | 
						||
 | 
						||
  if (path.error)
 | 
						||
    {
 | 
						||
      set_errno (path.error);
 | 
						||
      syscall_printf ("-1 = chdir (%s)", dir);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  char *native_dir = path.get_win32 ();
 | 
						||
 | 
						||
  /* Check to see if path translates to something like C:.
 | 
						||
     If it does, append a \ to the native directory specification to
 | 
						||
     defeat the Windows 95 (i.e. MS-DOS) tendency of returning to
 | 
						||
     the last directory visited on the given drive. */
 | 
						||
  if (isdrive (native_dir) && !native_dir[2])
 | 
						||
    {
 | 
						||
      native_dir[2] = '\\';
 | 
						||
      native_dir[3] = '\0';
 | 
						||
    }
 | 
						||
  int res = SetCurrentDirectoryA (native_dir) ? 0 : -1;
 | 
						||
  if (res == -1)
 | 
						||
    __seterrno ();
 | 
						||
  else
 | 
						||
    {
 | 
						||
      /* Store new cache information */
 | 
						||
      free (cwd_win32);
 | 
						||
      cwd_win32 = strdup (path);
 | 
						||
 | 
						||
      char pathbuf[MAX_PATH];
 | 
						||
      (void) normalize_posix_path (cwd_posix, dir, pathbuf);
 | 
						||
      /* Look for trailing path component consisting entirely of dots.  This
 | 
						||
	 is needed only in case of chdir since Windows simply ignores count
 | 
						||
	 of dots > 2 here instead of returning an error code.  Counts of dots
 | 
						||
	 <= 2 are already eliminated by normalize_posix_path. */
 | 
						||
      char *last_slash = strrchr (pathbuf, '/');
 | 
						||
      if (last_slash > pathbuf && strspn (last_slash + 1, ".") == strlen (last_slash + 1))
 | 
						||
	*last_slash = '\0';
 | 
						||
      free (cwd_posix);
 | 
						||
      cwd_posix = strdup (pathbuf);
 | 
						||
    }
 | 
						||
 | 
						||
  syscall_printf ("%d = chdir() cwd_posix '%s' native '%s'", res, cwd_posix, native_dir);
 | 
						||
  return res;
 | 
						||
}
 | 
						||
 | 
						||
/******************** Exported Path Routines *********************/
 | 
						||
 | 
						||
/* Cover functions to the path conversion routines.
 | 
						||
   These are exported to the world as cygwin_foo by cygwin.din.  */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_conv_to_win32_path (const char *path, char *win32_path)
 | 
						||
{
 | 
						||
  path_conv p (path, PC_SYM_FOLLOW);
 | 
						||
  if (p.error)
 | 
						||
    {
 | 
						||
      set_errno (p.error);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  strcpy (win32_path, p.get_win32 ());
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
 | 
						||
{
 | 
						||
  path_conv p (path, PC_SYM_FOLLOW | PC_FULL);
 | 
						||
  if (p.error)
 | 
						||
    {
 | 
						||
      set_errno (p.error);
 | 
						||
      return -1;
 | 
						||
    }
 | 
						||
 | 
						||
  strcpy (win32_path, p.get_win32 ());
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* This is exported to the world as cygwin_foo by cygwin.din.  */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_conv_to_posix_path (const char *path, char *posix_path)
 | 
						||
{
 | 
						||
  if (check_null_empty_path_errno (path))
 | 
						||
    return -1;
 | 
						||
  cygwin_shared->mount.conv_to_posix_path (path, posix_path, 1);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
 | 
						||
{
 | 
						||
  if (check_null_empty_path_errno (path))
 | 
						||
    return -1;
 | 
						||
  cygwin_shared->mount.conv_to_posix_path (path, posix_path, 0);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* The realpath function is supported on some UNIX systems.  */
 | 
						||
 | 
						||
extern "C"
 | 
						||
char *
 | 
						||
realpath (const char *path, char *resolved)
 | 
						||
{
 | 
						||
  int err;
 | 
						||
 | 
						||
  path_conv real_path (path, PC_SYM_FOLLOW | PC_FULL);
 | 
						||
 | 
						||
  if (real_path.error)
 | 
						||
    err = real_path.error;
 | 
						||
  else
 | 
						||
    {
 | 
						||
      err = cygwin_shared->mount.conv_to_posix_path (real_path.get_win32 (), resolved, 0);
 | 
						||
      if (err == 0)
 | 
						||
	return resolved;
 | 
						||
    }
 | 
						||
 | 
						||
  /* FIXME: on error, we are supposed to put the name of the path
 | 
						||
     component which could not be resolved into RESOLVED.  */
 | 
						||
  resolved[0] = '\0';
 | 
						||
 | 
						||
  set_errno (err);
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/* Return non-zero if path is a POSIX path list.
 | 
						||
   This is exported to the world as cygwin_foo by cygwin.din.
 | 
						||
 | 
						||
DOCTOOL-START
 | 
						||
<sect1 id="add-func-cygwin-posix-path-list-p">
 | 
						||
  <para>Rather than use a mode to say what the "proper" path list
 | 
						||
  format is, we allow any, and give apps the tools they need to
 | 
						||
  convert between the two.  If a ';' is present in the path list it's
 | 
						||
  a Win32 path list.  Otherwise, if the first path begins with
 | 
						||
  [letter]: (in which case it can be the only element since if it
 | 
						||
  wasn't a ';' would be present) it's a Win32 path list.  Otherwise,
 | 
						||
  it's a POSIX path list.</para>
 | 
						||
</sect1>
 | 
						||
DOCTOOL-END
 | 
						||
  */
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_posix_path_list_p (const char *path)
 | 
						||
{
 | 
						||
  int posix_p = ! (strchr (path, ';') || isdrive (path));
 | 
						||
  return posix_p;
 | 
						||
}
 | 
						||
 | 
						||
/* These are used for apps that need to convert env vars like PATH back and
 | 
						||
   forth.  The conversion is a two step process.  First, an upper bound on the
 | 
						||
   size of the buffer needed is computed.  Then the conversion is done.  This
 | 
						||
   allows the caller to use alloca if it wants.  */
 | 
						||
 | 
						||
static int
 | 
						||
conv_path_list_buf_size (const char *path_list, int to_posix_p)
 | 
						||
{
 | 
						||
  int i, num_elms, max_mount_path_len, size;
 | 
						||
  const char *p;
 | 
						||
 | 
						||
  /* The theory is that an upper bound is
 | 
						||
     current_size + (num_elms * max_mount_path_len)  */
 | 
						||
 | 
						||
  char delim = to_posix_p ? ';' : ':';
 | 
						||
  p = path_list;
 | 
						||
  for (num_elms = 1; (p = strchr (p, delim)) != NULL; ++num_elms)
 | 
						||
    ++p;
 | 
						||
 | 
						||
  /* 7: strlen ("//c") + slop, a conservative initial value */
 | 
						||
  for (max_mount_path_len = 7, i = 0; i < cygwin_shared->mount.nmounts; ++i)
 | 
						||
    {
 | 
						||
      int mount_len = (to_posix_p
 | 
						||
		       ? cygwin_shared->mount.mount[i].posix_pathlen
 | 
						||
		       : cygwin_shared->mount.mount[i].native_pathlen);
 | 
						||
      if (max_mount_path_len < mount_len)
 | 
						||
	max_mount_path_len = mount_len;
 | 
						||
    }
 | 
						||
 | 
						||
  /* 100: slop */
 | 
						||
  size = strlen (path_list) + (num_elms * max_mount_path_len) + 100;
 | 
						||
  return size;
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
 | 
						||
{
 | 
						||
  return conv_path_list_buf_size (path_list, 1);
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
 | 
						||
{
 | 
						||
  return conv_path_list_buf_size (path_list, 0);
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_win32_to_posix_path_list (const char *win32, char *posix)
 | 
						||
{
 | 
						||
  conv_path_list (win32, posix, 1);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
extern "C"
 | 
						||
int
 | 
						||
cygwin_posix_to_win32_path_list (const char *posix, char *win32)
 | 
						||
{
 | 
						||
  conv_path_list (posix, win32, 0);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* cygwin_split_path: Split a path into directory and file name parts.
 | 
						||
   Buffers DIR and FILE are assumed to be big enough.
 | 
						||
 | 
						||
   Examples (path -> `dir' / `file'):
 | 
						||
   / -> `/' / `'
 | 
						||
   "" -> `.' / `'
 | 
						||
   . -> `.' / `.' (FIXME: should this be `.' / `'?)
 | 
						||
   .. -> `.' / `..' (FIXME: should this be `..' / `'?)
 | 
						||
   foo -> `.' / `foo'
 | 
						||
   foo/bar -> `foo' / `bar'
 | 
						||
   foo/bar/ -> `foo' / `bar'
 | 
						||
   /foo -> `/' / `foo'
 | 
						||
   /foo/bar -> `/foo' / `bar'
 | 
						||
   c: -> `c:/' / `'
 | 
						||
   c:/ -> `c:/' / `'
 | 
						||
   c:foo -> `c:/' / `foo'
 | 
						||
   c:/foo -> `c:/' / `foo'
 | 
						||
 */
 | 
						||
 | 
						||
extern "C"
 | 
						||
void
 | 
						||
cygwin_split_path (const char *path, char *dir, char *file)
 | 
						||
{
 | 
						||
  int dir_started_p = 0;
 | 
						||
 | 
						||
  /* Deal with drives.
 | 
						||
     Remember that c:foo <==> c:/foo.  */
 | 
						||
  if (isdrive (path))
 | 
						||
    {
 | 
						||
      *dir++ = *path++;
 | 
						||
      *dir++ = *path++;
 | 
						||
      *dir++ = '/';
 | 
						||
      if (! *path)
 | 
						||
	{
 | 
						||
	  *dir = 0;
 | 
						||
	  *file = 0;
 | 
						||
	  return;
 | 
						||
	}
 | 
						||
      if (SLASH_P (*path))
 | 
						||
	++path;
 | 
						||
      dir_started_p = 1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Determine if there are trailing slashes and "delete" them if present.
 | 
						||
     We pretend as if they don't exist.  */
 | 
						||
  const char *end = path + strlen (path);
 | 
						||
  /* path + 1: keep leading slash.  */
 | 
						||
  while (end > path + 1 && SLASH_P (end[-1]))
 | 
						||
    --end;
 | 
						||
 | 
						||
  /* At this point, END points to one beyond the last character
 | 
						||
     (with trailing slashes "deleted").  */
 | 
						||
 | 
						||
  /* Point LAST_SLASH at the last slash (duh...).  */
 | 
						||
  const char *last_slash;
 | 
						||
  for (last_slash = end - 1; last_slash >= path; --last_slash)
 | 
						||
    if (SLASH_P (*last_slash))
 | 
						||
      break;
 | 
						||
 | 
						||
  if (last_slash == path)
 | 
						||
    {
 | 
						||
      *dir++ = '/';
 | 
						||
      *dir = 0;
 | 
						||
    }
 | 
						||
  else if (last_slash > path)
 | 
						||
    {
 | 
						||
      memcpy (dir, path, last_slash - path);
 | 
						||
      dir[last_slash - path] = 0;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      if (dir_started_p)
 | 
						||
	; /* nothing to do */
 | 
						||
      else
 | 
						||
	*dir++ = '.';
 | 
						||
      *dir = 0;
 | 
						||
    }
 | 
						||
 | 
						||
  memcpy (file, last_slash + 1, end - last_slash - 1);
 | 
						||
  file[end - last_slash - 1] = 0;
 | 
						||
}
 | 
						||
 | 
						||
/********************** String Helper Functions ************************/
 | 
						||
 | 
						||
#define CHXOR ('a' ^ 'A')
 | 
						||
#define ch_case_eq(ch1, ch2) \
 | 
						||
    ({ \
 | 
						||
      unsigned char x; \
 | 
						||
      !((x = ((unsigned char)ch1 ^ (unsigned char)ch2)) && \
 | 
						||
       (x != CHXOR || !isalpha (ch1))); \
 | 
						||
    })
 | 
						||
 | 
						||
/* Return TRUE if two strings match up to length n */
 | 
						||
int __stdcall
 | 
						||
strncasematch (const char *s1, const char *s2, size_t n)
 | 
						||
{
 | 
						||
  if (s1 == s2)
 | 
						||
    return 1;
 | 
						||
 | 
						||
  n++;
 | 
						||
  while (--n && *s1)
 | 
						||
    {
 | 
						||
      if (!ch_case_eq (*s1, *s2))
 | 
						||
	return 0;
 | 
						||
      s1++; s2++;
 | 
						||
    }
 | 
						||
  return !n || *s2 == '\0';
 | 
						||
}
 | 
						||
 | 
						||
/* Return TRUE if two strings match */
 | 
						||
int __stdcall
 | 
						||
strcasematch (const char *s1, const char *s2)
 | 
						||
{
 | 
						||
  if (s1 == s2)
 | 
						||
    return 1;
 | 
						||
 | 
						||
  while (*s1)
 | 
						||
    {
 | 
						||
      if (!ch_case_eq (*s1, *s2))
 | 
						||
	return 0;
 | 
						||
      s1++; s2++;
 | 
						||
    }
 | 
						||
  return *s2 == '\0';
 | 
						||
}
 | 
						||
 | 
						||
char * __stdcall
 | 
						||
strcasestr (const char *searchee, const char *lookfor)
 | 
						||
{
 | 
						||
  if (*searchee == 0)
 | 
						||
    {
 | 
						||
      if (*lookfor)
 | 
						||
	return NULL;
 | 
						||
      return (char *) searchee;
 | 
						||
    }
 | 
						||
 | 
						||
  while (*searchee)
 | 
						||
    {
 | 
						||
      int i = 0;
 | 
						||
      while (1)
 | 
						||
	{
 | 
						||
	  if (lookfor[i] == 0)
 | 
						||
	    return (char *) searchee;
 | 
						||
 | 
						||
	  if (!ch_case_eq (lookfor[i], searchee[i]))
 | 
						||
	    break;
 | 
						||
	  lookfor++;
 | 
						||
	}
 | 
						||
      searchee++;
 | 
						||
    }
 | 
						||
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
int __stdcall
 | 
						||
check_null_empty_path (const char *name)
 | 
						||
{
 | 
						||
  MEMORY_BASIC_INFORMATION m;
 | 
						||
  if (!name || !VirtualQuery (name, &m, sizeof (m)) || (m.State != MEM_COMMIT))
 | 
						||
    return EFAULT;
 | 
						||
 | 
						||
  if (!*name)
 | 
						||
    return ENOENT;
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 |