simplify_filename(char_u *filename)
{
#ifndef AMIGA	    // Amiga doesn't have "..", it uses "/"
    int		components = 0;
    char_u	*p, *tail, *start;
    int		stripping_disabled = FALSE;
    int		relative = TRUE;

    p = filename;
# ifdef BACKSLASH_IN_FILENAME
    if (p[1] == ':')	    // skip "x:"
	p += 2;
# endif

    if (vim_ispathsep(*p))
    {
	relative = FALSE;
	do
	    ++p;
	while (vim_ispathsep(*p));
    }
    start = p;	    // remember start after "c:/" or "/" or "///"
#ifdef UNIX
    // Posix says that "//path" is unchanged but "///path" is "/path".
    if (start > filename + 2)
    {
	STRMOVE(filename + 1, p);
	start = p = filename + 1;
    }
#endif

    do
    {
	// At this point "p" is pointing to the char following a single "/"
	// or "p" is at the "start" of the (absolute or relative) path name.
# ifdef VMS
	// VMS allows device:[path] - don't strip the [ in directory
	if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
	{
	    // :[ or :< composition: vms directory component
	    ++components;
	    p = getnextcomp(p + 1);
	}
	// allow remote calls as host"user passwd"::device:[path]
	else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
	{
	    // ":: composition: vms host/passwd component
	    ++components;
	    p = getnextcomp(p + 2);
	}
	else
# endif
	  if (vim_ispathsep(*p))
	    STRMOVE(p, p + 1);		// remove duplicate "/"
	else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
	{
	    if (p == start && relative)
		p += 1 + (p[1] != NUL);	// keep single "." or leading "./"
	    else
	    {
		// Strip "./" or ".///".  If we are at the end of the file name
		// and there is no trailing path separator, either strip "/." if
		// we are after "start", or strip "." if we are at the beginning
		// of an absolute path name .
		tail = p + 1;
		if (p[1] != NUL)
		    while (vim_ispathsep(*tail))
			MB_PTR_ADV(tail);
		else if (p > start)
		    --p;		// strip preceding path separator
		STRMOVE(p, tail);
	    }
	}
	else if (p[0] == '.' && p[1] == '.' &&
	    (vim_ispathsep(p[2]) || p[2] == NUL))
	{
	    // Skip to after ".." or "../" or "..///".
	    tail = p + 2;
	    while (vim_ispathsep(*tail))
		MB_PTR_ADV(tail);

	    if (components > 0)		// strip one preceding component
	    {
		int		do_strip = FALSE;
		char_u		saved_char;
		stat_T		st;

		// Don't strip for an erroneous file name.
		if (!stripping_disabled)
		{
		    // If the preceding component does not exist in the file
		    // system, we strip it.  On Unix, we don't accept a symbolic
		    // link that refers to a non-existent file.
		    saved_char = p[-1];
		    p[-1] = NUL;
# ifdef UNIX
		    if (mch_lstat((char *)filename, &st) < 0)
# else
			if (mch_stat((char *)filename, &st) < 0)
# endif
			    do_strip = TRUE;
		    p[-1] = saved_char;

		    --p;
		    // Skip back to after previous '/'.
		    while (p > start && !after_pathsep(start, p))
			MB_PTR_BACK(start, p);

		    if (!do_strip)
		    {
			// If the component exists in the file system, check
			// that stripping it won't change the meaning of the
			// file name.  First get information about the
			// unstripped file name.  This may fail if the component
			// to strip is not a searchable directory (but a regular
			// file, for instance), since the trailing "/.." cannot
			// be applied then.  We don't strip it then since we
			// don't want to replace an erroneous file name by
			// a valid one, and we disable stripping of later
			// components.
			saved_char = *tail;
			*tail = NUL;
			if (mch_stat((char *)filename, &st) >= 0)
			    do_strip = TRUE;
			else
			    stripping_disabled = TRUE;
			*tail = saved_char;
# ifdef UNIX
			if (do_strip)
			{
			    stat_T	new_st;

			    // On Unix, the check for the unstripped file name
			    // above works also for a symbolic link pointing to
			    // a searchable directory.  But then the parent of
			    // the directory pointed to by the link must be the
			    // same as the stripped file name.  (The latter
			    // exists in the file system since it is the
			    // component's parent directory.)
			    if (p == start && relative)
				(void)mch_stat(".", &new_st);
			    else
			    {
				saved_char = *p;
				*p = NUL;
				(void)mch_stat((char *)filename, &new_st);
				*p = saved_char;
			    }

			    if (new_st.st_ino != st.st_ino ||
				new_st.st_dev != st.st_dev)
			    {
				do_strip = FALSE;
				// We don't disable stripping of later
				// components since the unstripped path name is
				// still valid.
			    }
			}
# endif
		    }
		}

		if (!do_strip)
		{
		    // Skip the ".." or "../" and reset the counter for the
		    // components that might be stripped later on.
		    p = tail;
		    components = 0;
		}
		else
		{
		    // Strip previous component.  If the result would get empty
		    // and there is no trailing path separator, leave a single
		    // "." instead.  If we are at the end of the file name and
		    // there is no trailing path separator and a preceding
		    // component is left after stripping, strip its trailing
		    // path separator as well.
		    if (p == start && relative && tail[-1] == '.')
		    {
			*p++ = '.';
			*p = NUL;
		    }
		    else
		    {
			if (p > start && tail[-1] == '.')
			    --p;
			STRMOVE(p, tail);	// strip previous component
		    }

		    --components;
		}
	    }
	    else if (p == start && !relative)	// leading "/.." or "/../"
		STRMOVE(p, tail);		// strip ".." or "../"
	    else
	    {
		if (p == start + 2 && p[-2] == '.')	// leading "./../"
		{
		    STRMOVE(p - 2, p);			// strip leading "./"
		    tail -= 2;
		}
		p = tail;		// skip to char after ".." or "../"
	    }
	}
	else
	{
	    ++components;		// simple path component
	    p = getnextcomp(p);
	}
    } while (*p != NUL);
#endif // !AMIGA
}
simplify_filename(char_u *filename)
{
#ifndef AMIGA	     
    int		components = 0;
    char_u	*p, *tail, *start;
    int		stripping_disabled = FALSE;
    int		relative = TRUE;

    p = filename;
# ifdef BACKSLASH_IN_FILENAME
    if (p[1] == ':')	     
	p += 2;
# endif

    if (vim_ispathsep(*p))
    {
	relative = FALSE;
	do
	    ++p;
	while (vim_ispathsep(*p));
    }
    start = p;	     
#ifdef UNIX
     
    if (start > filename + 2)
    {
	STRMOVE(filename + 1, p);
	start = p = filename + 1;
    }
#endif

    do
    {
	 
	 
# ifdef VMS
	 
	if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
	{
	     
	    ++components;
	    p = getnextcomp(p + 1);
	}
	 
	else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
	{
	     
	    ++components;
	    p = getnextcomp(p + 2);
	}
	else
# endif
	  if (vim_ispathsep(*p))
	    STRMOVE(p, p + 1);		 
	else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
	{
	    if (p == start && relative)
		p += 1 + (p[1] != NUL);	 
	    else
	    {
		 
		 
		 
		 
		tail = p + 1;
		if (p[1] != NUL)
		    while (vim_ispathsep(*tail))
			MB_PTR_ADV(tail);
		else if (p > start)
		    --p;		 
		STRMOVE(p, tail);
	    }
	}
	else if (p[0] == '.' && p[1] == '.' &&
	    (vim_ispathsep(p[2]) || p[2] == NUL))
	{
	     
	    tail = p + 2;
	    while (vim_ispathsep(*tail))
		MB_PTR_ADV(tail);

	    if (components > 0)		 
	    {
		int		do_strip = FALSE;
		char_u		saved_char;
		stat_T		st;

		 
		if (!stripping_disabled)
		{
		     
		     
		     
		    saved_char = p[-1];
		    p[-1] = NUL;
# ifdef UNIX
		    if (mch_lstat((char *)filename, &st) < 0)
# else
			if (mch_stat((char *)filename, &st) < 0)
# endif
			    do_strip = TRUE;
		    p[-1] = saved_char;

		    --p;
		     
		    while (p > start && !after_pathsep(start, p))
			MB_PTR_BACK(start, p);

		    if (!do_strip)
		    {
			 
			 
			 
			 
			 
			 
			 
			 
			 
			 
			saved_char = *tail;
			*tail = NUL;
			if (mch_stat((char *)filename, &st) >= 0)
			    do_strip = TRUE;
			else
			    stripping_disabled = TRUE;
			*tail = saved_char;
# ifdef UNIX
			if (do_strip)
			{
			    stat_T	new_st;

			     
			     
			     
			     
			     
			     
			     
			    if (p == start && relative)
				(void)mch_stat(".", &new_st);
			    else
			    {
				saved_char = *p;
				*p = NUL;
				(void)mch_stat((char *)filename, &new_st);
				*p = saved_char;
			    }

			    if (new_st.st_ino != st.st_ino ||
				new_st.st_dev != st.st_dev)
			    {
				do_strip = FALSE;
				 
				 
				 
			    }
			}
# endif
		    }
		}

		if (!do_strip)
		{
		     
		     
		    p = tail;
		    components = 0;
		}
		else
		{
		     
		     
		     
		     
		     
		     
		    if (p == start && relative && tail[-1] == '.')
		    {
			*p++ = '.';
			*p = NUL;
		    }
		    else
		    {
			if (p > start && tail[-1] == '.')
			    --p;
			STRMOVE(p, tail);	 
		    }

		    --components;
		}
	    }
	    else if (p == start && !relative)	 
		STRMOVE(p, tail);		 
	    else
	    {
		if (p == start + 2 && p[-2] == '.')	 
		{
		    STRMOVE(p - 2, p);			 
		    tail -= 2;
		}
		p = tail;		 
	    }
	}
	else
	{
	    ++components;		 
	    p = getnextcomp(p);
	}
    } while (*p != NUL);
#endif  
}