view src/os_mac.c @ 442:c36697964844

updated for version 7.0115
author vimboss
date Sun, 24 Jul 2005 21:16:51 +0000
parents 3da34f87c760
children d0d15b184c56
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * os_mac.c -- code for the MacOS
 *
 * This file is mainly based on os_unix.c.
 */

#include "vim.h"

#if defined(__MRC__) || defined(__SC__) /* for Apple MPW Compilers */

#include "StandardFile.h"

/*
 * Implements the corresponding unix function
 */
    int
stat(
    char *p,
    struct stat *p_st)
{
    /*
       TODO: Use functions which fill the FileParam struct (Files.h)
	     and copy these contents to our self-defined stat struct
     */
    return 0;
}
#endif

/*
 * change the current working directory
 */
    int
mch_chdir(char *p_name)
{
#if defined(__MRC__) || defined(__SC__) /* for Apple MPW Compilers */
    /* TODO */
    return FAIL;
#else
    return chdir(p_name);
#endif
}


/*
 * Recursively build up a list of files in "gap" matching the first wildcard
 * in `path'.  Called by mch_expandpath().
 * "path" has backslashes before chars that are not to be expanded.
 */
    int
mac_expandpath(
    garray_T	*gap,
    char_u	*path,
    int		flags,		/* EW_* flags */
    short	start_at,
    short	as_full)
{
    /*
     * TODO:
     *		+Get Volumes (when looking for files in current dir)
     *		+Make it work when working dir not on select volume
     *		+Cleanup
     */
    short	index = 1;
    OSErr	gErr;
    char_u	dirname[256];
    char_u	cfilename[256];
    long	dirID;
    char_u	*new_name;
    CInfoPBRec	gMyCPB;
    HParamBlockRec gMyHPBlock;
    FSSpec	usedDir;

    char_u	*buf;
    char_u	*p, *s, *e, dany;
    int		start_len, c;
    char_u	*pat;
    regmatch_T	regmatch;

    start_len = gap->ga_len;
    buf = alloc(STRLEN(path) + BASENAMELEN + 5);/* make room for file name */
    if (buf == NULL)
	return 0;

/*
 * Find the first part in the path name that contains a wildcard.
 * Copy it into buf, including the preceding characters.
 */
    p = buf;
    s = buf;
    e = NULL;
#if 1
    STRNCPY(buf, path, start_at);
    p += start_at;
    path += start_at;
#endif

    while (*path)
    {
	if (*path == ':')
	{
	    if (e)
		break;
	    else
		s = p + 1;
	}
	/* should use  WILCARDLIST but what about ` */
	/*	    if (vim_strchr((char_u *)"*?[{~$", *path) != NULL)*/
	else if (vim_strchr((char_u *)WILDCHAR_LIST, *path) != NULL)
	    e = p;
#ifdef FEAT_MBYTE
	if (has_mbyte)
	{
	    int len = (*mb_ptr2len_check)(path);

	    STRNCPY(p, path, len);
	    p += len;
	    path += len;
	}
	else
#endif
	    *p++ = *path++;
    }
    e = p;

    /* now we have one wildcard component between s and e */
    *e = NUL;

#if 1
    dany = *s;
    *s = NUL;
    backslash_halve(buf);
    *s = dany;
#endif

    /* convert the file pattern to a regexp pattern */
    pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
    if (pat == NULL)
    {
	vim_free(buf);
	return 0;
    }

    /* compile the regexp into a program */
    regmatch.rm_ic = FALSE;			/* Don't ever ignore case */
    regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
    vim_free(pat);

    if (regmatch.regprog == NULL)
    {
	vim_free(buf);
	return 0;
    }

    /* open the directory for scanning */
    c = *s;
    *s = NUL;

    if (*buf == NUL)
    {
	as_full = TRUE;
#if 0
	(void) mch_dirname (&dirname[1], 254);
	dirname[0] = STRLEN(&dirname[1]);
#endif
    }
    else
    {
	if (*buf == ':')  /* relative path */
	{
	    (void)mch_dirname(&dirname[1], 254);
	    new_name = concat_fnames(&dirname[1], buf+1, TRUE);
	    STRCPY(&dirname[1], new_name);
	    dirname[0] = STRLEN(new_name);
	    vim_free(new_name);
	}
	else
	{
	    STRCPY(&dirname[1], buf);
	    backslash_halve(&dirname[1]);
	    dirname[0] = STRLEN(buf);
	}
    }
    *s = c;

    FSMakeFSSpec (0, 0, dirname, &usedDir);

    gMyCPB.dirInfo.ioNamePtr    = dirname;
    gMyCPB.dirInfo.ioVRefNum    = usedDir.vRefNum;
    gMyCPB.dirInfo.ioFDirIndex  = 0;
    gMyCPB.dirInfo.ioDrDirID    = 0;

    gErr = PBGetCatInfo(&gMyCPB, false);

    gMyCPB.dirInfo.ioCompletion = NULL;
    dirID = gMyCPB.dirInfo.ioDrDirID;
    do
    {
	gMyCPB.hFileInfo.ioFDirIndex = index;
	gMyCPB.hFileInfo.ioDirID     = dirID;

	gErr = PBGetCatInfo(&gMyCPB,false);

	if (gErr == noErr)
	{
	    vim_strncpy(cfilename, &dirname[1], dirname[0]);
	    if (vim_regexec(&regmatch, cfilename, (colnr_T)0))
	    {
		if (s[-1] != ':')
		{
		    /* TODO: need to copy with cleaned name */
		    STRCPY(s+1, cfilename);
		    s[0] = ':';
		}
		else
		{   /* TODO: need to copy with cleeaned name */
		    STRCPY(s, cfilename);
		}
		start_at = STRLEN(buf);
		STRCAT(buf, path);
		if (mch_has_exp_wildcard(path))	/* handle more wildcards */
		    (void)mac_expandpath(gap, buf, flags, start_at, FALSE);
		else
		{
#ifdef DONT_ADD_PATHSEP_TO_DIR
		    if ((gMyCPB.hFileInfo.ioFlAttrib & ioDirMask) !=0 )
			STRCAT(buf, PATHSEPSTR);
#endif
		    addfile(gap, buf, flags);
		}
	    }
	    if ((gMyCPB.hFileInfo.ioFlAttrib & ioDirMask) !=0 )
	    {
	    }
	    else
	    {
	    }
	}
	index++;
    }
    while (gErr == noErr);

    if (as_full)
    {
	index = 1;
	do
	{
	    gMyHPBlock.volumeParam.ioNamePtr = (char_u *) dirname;
	    gMyHPBlock.volumeParam.ioVRefNum =0;
	    gMyHPBlock.volumeParam.ioVolIndex = index;

	    gErr = PBHGetVInfo (&gMyHPBlock,false);
	    if (gErr == noErr)
	    {
		vim_strncpy(cfilename, &dirname[1], dirname[0]);
		if (vim_regexec(&regmatch, cfilename, (colnr_T)0))
		{
		    STRCPY(s, cfilename);
		    STRCAT(buf, path);
		    if (mch_has_exp_wildcard(path)) /* handle more wildcards */
			(void)mac_expandpath(gap, s, flags, 0, FALSE);
		    else
		    {
#ifdef DONT_ADD_PATHSEP_TO_DIR
/*			if ((gMyCPB.hFileInfo.ioFlAttrib & ioDirMask) !=0 )
*/			    STRCAT(buf, PATHSEPSTR);
#endif
			addfile(gap, s, flags);
		    }
#if 0
		    STRCAT(cfilename, PATHSEPSTR);
		    addfile (gap, cfilename, flags);
#endif
		}
	    }
	    index++;
	}
	while (gErr == noErr);
    }

    vim_free(regmatch.regprog);

    return gap->ga_len - start_len;
}


#ifdef USE_UNIXFILENAME
    static int
pstrcmp(a, b)
    const void *a, *b;
{
    return (pathcmp(*(char **)a, *(char **)b, -1));
}

    static int
unix_expandpath(gap, path, wildoff, flags)
    garray_T	*gap;
    char_u	*path;
    int		wildoff;
    int		flags;		/* EW_* flags */
{
    char_u	*buf;
    char_u	*path_end;
    char_u	*p, *s, *e;
    int		start_len, c;
    char_u	*pat;
    DIR		*dirp;
    regmatch_T	regmatch;
    struct dirent *dp;
    int		starts_with_dot;
    int		matches;
    int		len;

    start_len = gap->ga_len;
    buf = alloc(STRLEN(path) + BASENAMELEN + 5);/* make room for file name */
    if (buf == NULL)
	return 0;

/*
 * Find the first part in the path name that contains a wildcard.
 * Copy it into buf, including the preceding characters.
 */
    p = buf;
    s = buf;
    e = NULL;
    path_end = path;
    while (*path_end)
    {
	/* May ignore a wildcard that has a backslash before it */
	if (path_end >= path + wildoff && rem_backslash(path_end))
	    *p++ = *path_end++;
	else if (*path_end == '/')
	{
	    if (e != NULL)
		break;
	    else
		s = p + 1;
	}
	else if (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL)
	    e = p;
#ifdef FEAT_MBYTE
	if (has_mbyte)
	{
	    len = (*mb_ptr2len_check)(path_end);
	    STRNCPY(p, path_end, len);
	    p += len;
	    path_end += len;
	}
	else
#endif
	    *p++ = *path_end++;
    }
    e = p;
    *e = NUL;

    /* now we have one wildcard component between s and e */
    /* Remove backslashes between "wildoff" and the start of the wildcard
     * component. */
    for (p = buf + wildoff; p < s; ++p)
	if (rem_backslash(p))
	{
	    STRCPY(p, p + 1);
	    --e;
	    --s;
	}

    /* convert the file pattern to a regexp pattern */
    starts_with_dot = (*s == '.');
    pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
    if (pat == NULL)
    {
	vim_free(buf);
	return 0;
    }

    /* compile the regexp into a program */
#ifdef MACOS_X
    /* We want to behave like Terminal.app */
    regmatch.rm_ic = TRUE;
#else
    regmatch.rm_ic = FALSE;		/* Don't ever ignore case */
#endif
    regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
    vim_free(pat);

    if (regmatch.regprog == NULL)
    {
	vim_free(buf);
	return 0;
    }

    /* open the directory for scanning */
    c = *s;
    *s = NUL;
    dirp = opendir(*buf == NUL ? "." : (char *)buf);
    *s = c;

    /* Find all matching entries */
    if (dirp != NULL)
    {
	for (;;)
	{
	    dp = readdir(dirp);
	    if (dp == NULL)
		break;
	    if ((dp->d_name[0] != '.' || starts_with_dot)
		    && vim_regexec(&regmatch, (char_u *)dp->d_name, (colnr_T)0))
	    {
		STRCPY(s, dp->d_name);
		len = STRLEN(buf);
		STRCPY(buf + len, path_end);
		if (mch_has_exp_wildcard(path_end)) /* handle more wildcards */
		{
		    /* need to expand another component of the path */
		    /* remove backslashes for the remaining components only */
		    (void)unix_expandpath(gap, buf, len + 1, flags);
		}
		else
		{
		    /* no more wildcards, check if there is a match */
		    /* remove backslashes for the remaining components only */
		    if (*path_end)
			backslash_halve(buf + len + 1);
		    if (mch_getperm(buf) >= 0)	/* add existing file */
			addfile(gap, buf, flags);
		}
	    }
	}

	closedir(dirp);
    }

    vim_free(buf);
    vim_free(regmatch.regprog);

    matches = gap->ga_len - start_len;
    if (matches)
	qsort(((char_u **)gap->ga_data) + start_len, matches,
						   sizeof(char_u *), pstrcmp);
    return matches;
}
#endif

/*
 * Recursively build up a list of files in "gap" matching the first wildcard
 * in `path'.  Called by expand_wildcards().
 * "pat" has backslashes before chars that are not to be expanded.
 */
    int
mch_expandpath(
    garray_T	*gap,
    char_u	*path,
    int		flags)		/* EW_* flags */
{
#ifdef USE_UNIXFILENAME
    return unix_expandpath(gap, path, 0, flags);
#else
    char_u first = *path;
    short  scan_volume;

    slash_n_colon_adjust(path);

    scan_volume = (first != *path);

    return mac_expandpath(gap, path, flags, 0, scan_volume);
#endif
}

    void
fname_case(name, len)
    char_u	*name;
    int		len;	    /* buffer size, ignored here */
{
    /*
     * TODO: get the real casing for the file
     *       make it called
     *       with USE_FNAME_CASE & USE_LONG_FNAME
     *		    CASE_INSENSITIVE_FILENAME
     *       within setfname, fix_fname, do_ecmd
     */
#ifdef USE_UNIXFILENAME
    OSStatus	status;
    FSRef	refFile;
    UInt32	pathSize = STRLEN(name) + 1;
    char_u	*path;
    Boolean	isDirectory;

    path = alloc(pathSize);
    if (path == NULL)
	return;

    status = FSPathMakeRef((UInt8 *)name, &refFile, &isDirectory);
    if (status)
	return;

    status = FSRefMakePath(&refFile, (UInt8 *)path, pathSize);
    if (status)
	return;

    /* Paranoid: Update the name if only the casing differ.*/
    if (STRICMP(name, path) == 0)
	STRCPY(name, path);
#endif
}
static char_u	*oldtitle = (char_u *) "gVim";

/*
 * check for an "interrupt signal": CTRL-break or CTRL-C
 */
    void
mch_breakcheck()
{
    /*
     * TODO: Scan event for a CTRL-C or COMMAND-. and do: got_int=TRUE;
     *       or only go proccess event?
     *       or do nothing
     */
    EventRecord theEvent;

    if (EventAvail(keyDownMask, &theEvent))
	if ((theEvent.message & charCodeMask) == Ctrl_C && ctrl_c_interrupts)
	    got_int = TRUE;
#if 0
    short	i = 0;
    Boolean     found = false;
    EventRecord theEvent;

    while ((i < 10) && (!found))
    {
	found = EventAvail (keyDownMask, &theEvent);
	if (found)
	{
	  if ((theEvent.modifiers & controlKey) != 0)
	    found = false;
	  if ((theEvent.what == keyDown))
	    found = false;
	  if ((theEvent.message & charCodeMask) == Ctrl_C)
	    {
		found = false;
		got_int = TRUE;
	    }
	}
	i++;
    }
#endif

}

/*
 * Return amount of memory currently available.
 */
    long_u
mch_avail_mem(special)
    int     special;
{
    /*
     * TODO: Use MaxBlock, FreeMeM, PurgeSpace, MaxBlockSys FAQ-266
     *       figure out what the special is for
     *
     * FreeMem  ->   returns all avail memory is application heap
     * MaxBlock ->   returns the biggest contigeous block in application heap
     * PurgeSpace ->
     */
    return MaxBlock();
}

    void
mch_delay(msec, ignoreinput)
    long	msec;
    int		ignoreinput;
{
#if (defined(__MWERKS__) && __MWERKS__ >= 0x2000) \
	|| defined(__MRC__) || defined(__SC__)
    unsigned
#endif
    long   finalTick;

    if (ignoreinput)
	Delay (60*msec/1000, &finalTick);
    else
	/* even thougth we should call gui stuff from here
	   it the simplest way to be safe */
	gui_mch_wait_for_chars(msec);
}

    void
mch_init()
{
    /*
     *  TODO: Verify if needed, or override later.
     */
    Columns = 80;
    Rows = 24;
}

/*
 * Check_win checks whether we have an interactive stdout.
 */
    int
mch_check_win(argc, argv)
    int		argc;
    char	**argv;
{
    /*
     * TODO: Maybe to be remove through NO_CONSOLE
     */
    return OK;
}

/*
 * Return TRUE if the input comes from a terminal, FALSE otherwise.
 */
    int
mch_input_isatty()
{
    /*
     * TODO: Maybe to be remove through NO_CONSOLE
     */
    return OK;
}

#ifdef FEAT_TITLE
/*
 * Set the window title and icon.
 * (The icon is not taken care of).
 */
    void
mch_settitle(title, icon)
    char_u *title;
    char_u *icon;
{
    gui_mch_settitle(title, icon);
}

/*
 * Restore the window/icon title.
 * which is one of:
 *	1  Just restore title
 *      2  Just restore icon
 *	3  Restore title and icon
 * but don't care about the icon.
 */
    void
mch_restore_title(which)
    int which;
{
    mch_settitle((which & 1) ? oldtitle : NULL, NULL);
}
#endif

/*
 * Insert user name in s[len].
 * Return OK if a name found.
 */
    int
mch_get_user_name(s, len)
    char_u	*s;
    int		len;
{
#if !(defined(__MRC__) || defined(__SC__)) /* No solution yet */
    /*
     * TODO: clean up and try getlogin ()
     */
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
    struct passwd	*pw;
#endif
    uid_t		uid;

    uid = getuid();
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
    if ((pw = getpwuid(uid)) != NULL
	    && pw->pw_name != NULL && *(pw->pw_name) != NUL)
    {
	vim_strncpy(s, pw->pw_name, len - 1);
	return OK;
    }
#endif
    sprintf((char *)s, "%d", (int)uid);		/* assumes s is long enough */
#endif
    return FAIL;				/* a number is not a name */
}

/*
 * Copy host name into s[len].
 */
    void
mch_get_host_name(s, len)
    char_u	*s;
    int		len;
{
#if defined(__MRC__) || defined(__SC__) || defined(__APPLE_CC__)
    vim_strncpy(s, "Mac", len - 1); /* TODO: use Gestalt information */
#else
    struct utsname vutsname;

    if (uname(&vutsname) < 0)
	*s = NUL;
    else
	vim_strncpy(s, vutsname.nodename, len - 1);
#endif
}

/*
 * return process ID
 */
    long
mch_get_pid()
{
    return (long)getpid();
}

/*
 * Get name of current directory into buffer 'buf' of length 'len' bytes.
 * Return OK for success, FAIL for failure.
 */
    int
mch_dirname(buf, len)
    char_u	*buf;
    int		len;
{
#if defined(__MRC__) || defined(__SC__)
    return FAIL; /* No solution yet */
#else
    /* The last : is already put by getcwd */
    if (getcwd((char *)buf, len) == NULL)
	{
	    STRCPY(buf, strerror(errno));
	    return FAIL;
	}
# ifndef USE_UNIXFILENAME
    else if (*buf != NUL && buf[STRLEN(buf) - 1] == ':')
	buf[STRLEN(buf) - 1] = NUL;	/* remove trailing ':' */
# endif
    return OK;
#endif
}

    void
slash_to_colon(p)
    char_u	*p;
{
    for ( ; *p; ++p)
	if (*p == '/')
	    *p = ':';
}

    char_u *
slash_to_colon_save (p)
    char_u  *p;
{
    char_u	*res;

    res = vim_strsave(p);
    if (res == NULL)
	return p;
    slash_to_colon(res);
    return res;
}

    void
slash_n_colon_adjust (buf)
    char_u *buf;
{
    /*
     * TODO: Make it faster
     */
#ifndef USE_UNIXFILENAME
    char_u  temp[MAXPATHL];
    char_u  *first_colon = vim_strchr(buf, ':');
    char_u  *first_slash = vim_strchr(buf, '/');
    int     full = TRUE;
    char_u  *scanning;
    char_u  *filling;
    char_u  last_copied = NUL;

    if (*buf == NUL)
	return ;

    if ((first_colon == NULL) && (first_slash == NULL))
	full = FALSE;
    if ((first_slash == NULL) && (first_colon != NULL))
	full = TRUE;
    if ((first_colon == NULL) && (first_slash != NULL))
	full =	FALSE;
    if ((first_slash < first_colon) && (first_slash != NULL))
	full = FALSE;
    if ((first_colon < first_slash) && (first_colon != NULL))
	full = TRUE;
    if (first_slash == buf)
	full = TRUE;
    if (first_colon == buf)
	full = FALSE;

    scanning = buf;
    filling  = temp;

    while (*scanning != NUL)
    {
	if (*scanning == '/')
	{
	    if ((scanning[1] != '/') && (scanning[-1] != ':'))
	    {
		*filling++ = ':';
		scanning++;
	    }
	    else
		scanning++;
	}
	else if (*scanning == '.')
	{
	    if ((scanning[1] == NUL) || scanning[1] == '/')
	    {
		if (scanning[1] == NUL)
		    scanning += 1;
		else
		    scanning += 2;
	    }
	    else if (scanning[1] == '.')
	    {
		if ((scanning[2] == NUL) || scanning[2] == '/')
		{
		    *filling++ = ':';
		    if (scanning[2] == NUL)
			scanning +=2;
		    else
			scanning += 3;
		}
		else
		{
		    *filling++ = *scanning++;
		}
	    }
	    else
	    {
		*filling++ = *scanning++;
	    }

	}
	else
	{
	    *filling++ = *scanning++;
	}

    }

    *filling = 0;
    filling = temp;

    if (!full)
    {
	if (buf[0] != ':')
	{
	    buf[0] = ':';
	    buf[1] = NUL;
	}
	else
	    buf[0] = NUL;
    }
    else
    {
	buf[0] = NUL;
	if (filling[0] == ':')
	    filling++;
    }

    STRCAT (buf, filling);
#endif
}

/*
 * Get absolute filename into buffer 'buf' of length 'len' bytes.
 *
 * return FAIL for failure, OK for success
 */
    int
mch_FullName(fname, buf, len, force)
    char_u  *fname, *buf;
    int     len;
    int     force;	    /* also expand when already absolute path name */
{
    /*
     * TODO: Find what TODO
     */
    int		l;
    char_u	olddir[MAXPATHL];
    char_u	newdir[MAXPATHL];
    char_u	*p;
    char_u	c;
    int		retval = OK;

    if (force || !mch_isFullName(fname))
    {
	/*
	 * Forced or not an absolute path.
	 * If the file name has a path, change to that directory for a moment,
	 * and then do the getwd() (and get back to where we were).
	 * This will get the correct path name with "../" things.
	 */
	if ((p = vim_strrchr(fname, ':')) != NULL)
	{
	    p++;
	    if (mch_dirname(olddir, MAXPATHL) == FAIL)
	    {
		p = NULL;		/* can't get current dir: don't chdir */
		retval = FAIL;
	    }
	    else
	    {
		c = *p;
		*p = NUL;
		if (mch_chdir((char *)fname))
		    retval = FAIL;
		else
		    fname = p; /* + 1;*/
		*p = c;
	    }
	}
	if (mch_dirname(buf, len) == FAIL)
	{
	    retval = FAIL;
	    *newdir = NUL;
	}
	l = STRLEN(buf);
	if (STRCMP(fname, ".") != 0)
	{
#ifdef USE_UNIXFILENAME
	    if (l > 0 && buf[l - 1] != '/' && *fname != NUL)
		STRCAT(buf, "/");
#else
	    if (l > 0 && buf[l - 1] != ':' && *fname != NUL)
		STRCAT(buf, ":");
#endif
	}
	if (p != NULL)
	    mch_chdir((char *)olddir);
	if (STRCMP(fname, ".") != 0)
	    STRCAT(buf, fname);
    }
    else
    {
	vim_strncpy(buf, fname, len - 1);
	slash_n_colon_adjust(buf);
    }

    return retval;
}

/*
 * Return TRUE if "fname" does not depend on the current directory.
 */
	int
mch_isFullName(fname)
	char_u		*fname;
{
#ifdef USE_UNIXFILENAME
    return ((fname[0] == '/') || (fname[0] == '~'));
#else
    /*
     * TODO: Make sure fname is always of mac still
     *       i.e: passed throught slash_n_colon_adjust
     */
	char_u	*first_colon = vim_strchr(fname, ':');
	char_u	*first_slash = vim_strchr(fname, '/');

	if (first_colon == fname)
	  return FALSE;
	if (first_slash == fname)
	  return TRUE;
	if ((first_colon < first_slash) && (first_colon != NULL))
	  return TRUE;
	if ((first_slash < first_colon) && (first_slash != NULL))
	  return FALSE;
	if ((first_colon == NULL) && (first_slash != NULL))
	  return FALSE;
	if ((first_slash == NULL) && (first_colon != NULL))
	  return TRUE;
	if ((first_colon == NULL) && (first_slash == NULL))
	  return FALSE;
	return TRUE;
#endif
}

/*
 * Replace all slashes by colons.
 */
    void
slash_adjust(p)
    char_u  *p;
{
#ifndef USE_UNIXFILENAME
    /*
     * TODO: keep escaped '/'
     */

    while (*p)
    {
	if (*p == '/')
	    *p = ':';
	mb_ptr_adv(p);
    }
#endif
}

/*
 * Get file permissions for 'name'.
 * Returns -1 when it doesn't exist.
 */
    long
mch_getperm(name)
    char_u *name;
{
    /*
     * TODO: Maybe use AppleShare info??
     *       Use locked for non writable
     */

    struct stat statb;

    if (stat((char *)name, &statb))
	return -1;
    return statb.st_mode;
}

/*
 * set file permission for 'name' to 'perm'
 *
 * return FAIL for failure, OK otherwise
 */
    int
mch_setperm(name, perm)
    char_u	*name;
    long	perm;
{
    /*
     * TODO: Maybe use AppleShare info??
     *       Use locked for non writable
     */
    return (OK);
}

/*
 * Set hidden flag for "name".
 */
    void
mch_hide(name)
    char_u  *name;
{
    /*
     * TODO: Hide the file throught FileManager FAQ 8-34
     *
     *  *name is mac style start with : for relative
     */
}


/*
 * return TRUE if "name" is a directory
 * return FALSE if "name" is not a directory
 * return FALSE for error
 */
    int
mch_isdir(name)
    char_u  *name;
{
    /*
     * TODO: Find out by FileManager calls ...
     */
    struct stat statb;

#if defined(TARGET_API_MAC_CARBON) && TARGET_API_MAC_CARBON
    /* For some reason the name is sometimes empty,
     * (such as for a not yet named file). An empty
     * filename is interpreted by the MacOS version
     * of stat (at least under Codewarrior) as the
     * current directory.
     */
    /* AK 20020413
     * This is required for Carbon but breaks the
     * explorer plugin in Classic
     */
    if (name[0] == NULL)
	return FALSE;
#endif

    if (stat((char *)name, &statb))
	return FALSE;
#if defined(__MRC__) || defined(__SC__)
    return FALSE;   /* definitely TODO */
#else
    return ((statb.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
#endif
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Return 1 if "name" can be executed, 0 if not.
 * Return -1 if unknown.
 */
    int
mch_can_exe(name)
    char_u	*name;
{
    /* TODO */
    return -1;
}
#endif

/*
 * Check what "name" is:
 * NODE_NORMAL: file or directory (or doesn't exist)
 * NODE_WRITABLE: writable device, socket, fifo, etc.
 * NODE_OTHER: non-writable things
 */
    int
mch_nodetype(name)
    char_u	*name;
{
    /* TODO */
    return NODE_NORMAL;
}

    void
mch_early_init()
{
}

    void
mch_exit(r)
    int     r;
{
    display_errors();

    ml_close_all(TRUE);		/* remove all memfiles */
    exit(r);
}


    void
mch_settmode(tmode)
    int     tmode;
{
    /*
     * TODO: remove the needs of it.
     */
}

#ifdef FEAT_MOUSE
/*
 * set mouse clicks on or off (only works for xterms)
 */
    void
mch_setmouse(on)
    int     on;
{
    /*
     * TODO: remove the needs of it.
     */
}
#endif

/*
 * set screen mode, always fails.
 */
    int
mch_screenmode(arg)
    char_u	 *arg;
{
    EMSG(_(e_screenmode));
    return FAIL;
}

    int
mch_call_shell(cmd, options)
    char_u	*cmd;
    int		options;	/* SHELL_*, see vim.h */
{
    /*
     * TODO: find a shell or pseudo-shell to call
     *       for some simple useful command
     */

    return (-1);
}

/*
 * Return TRUE if "p" contains a wildcard that can be expanded by
 * mch_expandpath().
 */
    int
mch_has_exp_wildcard(p)
    char_u	*p;
{
    for ( ; *p; mb_ptr_adv(p))
    {
	if (*p == '\\' && p[1] != NUL)
	    ++p;
	else if (vim_strchr((char_u *)WILDCHAR_LIST, *p) != NULL)
	    return TRUE;
    }
    return FALSE;
}

    int
mch_has_wildcard(p)
    char_u	*p;
{
#ifdef USE_UNIXFILENAME
    if (*p == '~' && p[1] != NUL)
	return TRUE;
#endif
    return mch_has_exp_wildcard(p);
}


/*
 * This procedure duplicate a file, it is used in order to keep
 * the footprint of the previous file, when some info can be easily
 * restored with set_perm().
 *
 * Return -1 for failure, 0 for success.
 */
    int
mch_copy_file(from, to)
    char_u *from;
    char_u *to;
{
    char_u  from_str[256];
    char_u  to_str[256];
    char_u  to_name[256];

    HParamBlockRec paramBlock;
    char_u  *char_ptr;
    int	    len;

    /*
     * Convert C string to Pascal string
     */
     char_ptr = from;
     len = 1;
     for (; (*char_ptr != 0) && (len < 255); len++, char_ptr++)
	from_str[len] = *char_ptr;
     from_str[0] = len-1;

     char_ptr = to;
     len = 1;
     for (; (*char_ptr != 0) && (len < 255); len++, char_ptr++)
	to_str[len] = *char_ptr;
     to_str[0] = len-1;

    paramBlock.copyParam.ioCompletion = NULL;
    paramBlock.copyParam.ioNamePtr    = from_str;
 /* paramBlock.copyParam.ioVRefnum    =  overided by ioFilename; */
 /* paramBlock.copyParam.ioDirI       =  overided by ioFilename; */

    paramBlock.copyParam.ioNewName    = to_str;
    paramBlock.copyParam.ioCopyName   = to_name;     /* NIL */
 /* paramBlock.copyParam.ioDstVRefNum = overided by ioNewName;   */
 /* paramBlock.copyParam.ioNewDirID   = overided by ioNewName;   */



    /*
     * First delete the "to" file, this is required on some systems to make
     * the rename() work, on other systems it makes sure that we don't have
     * two files when the rename() fails.
     */
    mch_remove(to);

    /*
     * First try a normal rename, return if it works.
     */
    (void) PBHCopyFile(&paramBlock, false);
    return 0;

}


    int
mch_copy_file_attribute(from, to)
    char_u *from;
    char_u *to;
{
    FSSpec	frFSSpec;
    FSSpec	toFSSpec;
    FInfo	fndrInfo;
    Str255	name;
    ResType	type;
    ResType	sink;
    Handle	resource;
    short	idxTypes;
    short	nbTypes;
    short	idxResources;
    short	nbResources;
    short	ID;
    short	frRFid;
    short	toRFid;
    short	attrs_orig;
    short	attrs_copy;
    short	temp;

    /* TODO: Handle error */
    (void)GetFSSpecFromPath(from, &frFSSpec);
    (void)GetFSSpecFromPath(to  , &toFSSpec);

    /* Copy resource fork */
    temp = 0;

#if 1
     frRFid = FSpOpenResFile (&frFSSpec, fsCurPerm);

     if (frRFid != -1)
     {
	 FSpCreateResFile(&toFSSpec, 'TEXT', UNKNOWN_CREATOR, 0);
	 toRFid = FSpOpenResFile(&toFSSpec, fsRdWrPerm);

	 UseResFile(frRFid);

	 nbTypes = Count1Types();

	 for (idxTypes = 1; idxTypes <= nbTypes; idxTypes++)
	 {
	     Get1IndType(&type, idxTypes);
	     nbResources = Count1Resources(type);

	     for (idxResources = 1; idxResources <= nbResources; idxResources++)
	     {
		 attrs_orig = 0; /* in case GetRes fails */
		 attrs_copy = 0; /* in case GetRes fails */
		 resource = Get1IndResource(type, idxResources);
		 GetResInfo(resource, &ID, &sink, name);
		 HLock(resource);
		 attrs_orig = GetResAttrs(resource);
		 DetachResource(resource);


		 UseResFile(toRFid);
		 AddResource(resource, type, ID, name);
		 attrs_copy = GetResAttrs(resource);
		 attrs_copy = (attrs_copy & 0x2) | (attrs_orig & 0xFD);
		 SetResAttrs(resource, attrs_copy);
		 WriteResource(resource);
		 UpdateResFile(toRFid);

		 temp = GetResAttrs(resource);

		 /*SetResAttrs (resource, 0);*/
		 HUnlock(resource);
		 ReleaseResource(resource);
		 UseResFile(frRFid);
	     }
	 }
	 CloseResFile(toRFid);
	 CloseResFile(frRFid);
     }
#endif
    /* Copy Finder Info */
    (void)FSpGetFInfo(&frFSSpec, &fndrInfo);
    (void)FSpSetFInfo(&toFSSpec, &fndrInfo);

    return (temp == attrs_copy);
}

    int
mch_has_resource_fork (file)
    char_u *file;
{
    FSSpec  fileFSSpec;
    short   fileRFid;

    /* TODO: Handle error */
    (void)GetFSSpecFromPath(file, &fileFSSpec);
    fileRFid = FSpOpenResFile(&fileFSSpec, fsCurPerm);
    if (fileRFid != -1)
	CloseResFile(fileRFid);

    return (fileRFid != -1);
}

    int
mch_get_shellsize(void)
{
    /* never used */
    return OK;
}

    void
mch_set_shellsize(void)
{
    /* never used */
}

/*
 * Rows and/or Columns has changed.
 */
    void
mch_new_shellsize(void)
{
    /* never used */
}

/*
 * Those function were set as #define before, but in order
 * to allow an easier us of os_unix.c for the MacOS X port,
 * they are change to procedure. Thec ompile whould optimize
 * them out.
 */

    int
mch_can_restore_title()
{
    return TRUE;
}

    int
mch_can_restore_icon()
{
    return TRUE;
}

/*
 * If the machine has job control, use it to suspend the program,
 * otherwise fake it by starting a new shell.
 */
    void
mch_suspend()
{
    /* TODO: get calle in #ifndef NO_CONSOLE */
    gui_mch_iconify();
};