/* 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.
 */

#include "vim.h"

/*
 * os_riscos.c
 *
 * Thomas Leonard <tal197@ecs.soton.ac.uk>
 */

const char *__dynamic_da_name = "Vim heap"; /* Enable and name our dynamic area */
int ro_line_mode = TRUE;  /* For Ex mode we much echo chars to the screen ourselves */
int windowed;		/* Flag - are we running inside a text window? */
int WinLeft, WinTop;	/* We might be started inside a text window */
int ScrollTop;		/* Make cursor movements relative to ScrollTop. */

int old_escape_state = -1;
int old_cursor_state = -1;

#define rgb(r,g,b) ((b<<24) + (g<<16) + (r<<8))
#define NORMAL_FG 0x00000000
#define NORMAL_BG 0xffffffff

/* Convert a DOS colour number to an RGB palette entry.
 * Mappings from X11 rgb/txt file.
 */
    static int
map_colour(dos)
    int dos;		/* Standard DOS colour number. */
{
    switch (dos)
    {
	case 0: return 0;			/* Black */
	case 1: return rgb(0,0,139);		/* DarkBlue */
	case 2: return rgb(0,100,0);		/* DarkGreen */
	case 3: return rgb(0,139,139);		/* DarkCyan */
	case 4: return rgb(139,0,0);		/* DarkRed */
	case 5: return rgb(139,0,139);		/* DarkMagenta */
	case 6: return rgb(165,42,42);		/* Brown, DarkYellow */
	case 7: return rgb(211,211,211);	/* LightGray, LightGrey, Gray, Grey */
	case 8: return rgb(169,169,169);	/* DarkGray, DarkGrey */
	case 9: return rgb(173,216,230);	/* Blue, LightBlue */
	case 10: return rgb(144,238,144);	/* Green, LightGreen */
	case 11: return rgb(224,255,255);	/* Cyan, LightCyan */
	case 12: return rgb(255,0,0);		/* Red, LightRed */
	case 13: return rgb(255,0,255);		/* Magenta, LightMagenta */
	case 14: return rgb(255,255,0);		/* Yellow, LightYellow */
	case 15: return rgb(255,255,255);	/* White */
    }
    return rgb(100,100,100);
}

    static void
text_fg(fg)
    int fg;		/* Foregound colour in the form &BBGGRR00 */
{
    xswi(ColourTrans_SetTextColour, fg, 0, 0, 0);
}

    static void
text_bg(bg)
    int		bg;	/* Backgound colour in the form &BBGGRR00 */
{
    xswi(ColourTrans_SetTextColour, bg, 0, 0, 1 << 7);
}

#define OUT_NORMAL 0
#define OUT_NUMBER 1		/* Reading in a number */

    void
mch_write(s, len)
    char_u  *s;
    int	    len;
{
    static int mode = OUT_NORMAL;
    static int x, y;			/* For reading numbers in. */

    if (!term_console)
    {
	/* Maybe we are running Vim remotely - don't interpret chars */
	while (len--)
	{
	    char_u c = *s++;
	    swi(OS_WriteC, c);
	    /* We might need to send a CR too. This shouldn't
	     * hurt if we don't need it, should it?
	     */
	    if (c == 10)
		swi(OS_WriteI + 13);
	}
	return;
    }

    while (len--)
    {
	char_u c = *s++;
	switch (mode)
	{
	    case OUT_NUMBER:
		if (c < '0' || c > '9')
		{
		    mode = OUT_NORMAL;
		}
		else
		{
		    x = (x * 10) + c - '0';
		    continue;
		}
	    /* note: no break here! */

	    case OUT_NORMAL:
		switch (c)
		{
		    case 1:
			/* Number (in decimal) follows. */
			mode = OUT_NUMBER;
			y = x;
			x = 0;
			break;
		    case 2:
			/* Position cursor. */
			swi(OS_WriteI + 31);
			swi(OS_WriteC, x);
			swi(OS_WriteC, y - ScrollTop);
			break;
		    case 3:
			/* Set scroll region. */
			if (x == Rows -1 && y == 0 && !windowed)
			{
			    /* Whole screen - remove text window.
			     * This is MUCH faster.
			     */
			    swi(OS_WriteI + 26);
			}
			else
			{
			    /* Create a text window. */
			    swi(OS_WriteI + 28);
			    swi(OS_WriteC, WinLeft);
			    swi(OS_WriteC, WinTop + x);
			    swi(OS_WriteC, WinLeft + Columns - 1);
			    swi(OS_WriteC, WinTop + y);
			}
			ScrollTop = y;
			break;
		    case 4:
			/* Normal mode. */
			text_fg(NORMAL_FG);
			text_bg(NORMAL_BG);
			break;
		    case 5:
			/* Reverse mode. */
			text_fg(NORMAL_BG);
			text_bg(NORMAL_FG);
			break;
		    case 10:
			swi(OS_NewLine);
			break;
		    case 14:
			/* Cursor invisible. */
			swi(OS_WriteN,
			     "\027\001\000\000\000\000\000\000\000\000",
			     10);
			break;
		    case 15:
			/* Cursor visible. */
			swi(OS_WriteN,
			     "\027\001\002\000\000\000\000\000\000\000",
			     10);
			break;
		    case 16:
			/* Cursor very visible (flash) */
			swi(OS_WriteN,
			     "\027\001\003\000\000\000\000\000\000\000",
			     10);
		    case 17:
			/* Set foreground colour. */
			text_fg(map_colour(x));
			break;
		    case 18:
			/* Set background colour. */
			text_bg(map_colour(x));
			break;
		    case 19:
			/* Scroll text down. */
			swi(OS_WriteN,
			     "\027\007\000\002\000\000\000\000\000\000",
			     10);
			break;
		    default:
			swi(OS_WriteC, c);
		}
		continue;

	    default:
		printf("[output error]");
		mode = OUT_NORMAL;
	}
    }
}

/*
 * mch_inchar(): low level input funcion.
 * Get a characters from the keyboard.
 * Return the number of characters that are available.
 * If wtime == 0 do not wait for characters.
 * If wtime == n wait n msecs for characters.
 * If wtime == -1 wait forever for characters.
 *
 * TODO: call convert_input() for 'fileencoding' to 'encoding' conversion.
 */
    int
mch_inchar(buf, maxlen, wtime, tb_change_cnt)
    char_u  *buf;
    int	    maxlen;
    long    wtime;
    int	    tb_change_cnt;
{
    int got=0;
    unsigned int start_time = clock();

    if (ro_line_mode)
    {
	/* We're probably in Ex mode - get whole lines at a time. */

	static char_u	line_buffer[256];
	static int	remaining_chars = 0;
	static int	buf_pos = 0;

	/* Do we need to fetch another line? */
	if (remaining_chars == 0)
	{
	    int		old_esc_state;
	    swi(OS_Byte, 200, 1, 0xfe);
	    old_esc_state = r1;

	    buf_pos = 0;
	    if (xswi(OS_ReadLine, line_buffer, 255, 0, 255) & (c_flag | v_flag))
	    {
		got_int = TRUE;	    /* ESC pressed */
		r1 = 0;
	    }
	    line_buffer[r1] = 13;
	    remaining_chars = r1 + 1;	/* Count CR as part of input */

	    swi(OS_Byte, 200, old_esc_state, 0);
	}

	/* Can we send the rest of the buffer back in one go? */
	if (remaining_chars <= maxlen)
	{
	    int	    got = remaining_chars;

	    memcpy(buf, line_buffer + buf_pos, got);
	    remaining_chars = 0;
	    return  got;
	}

	/* Send as much as we can */
	memcpy(buf, line_buffer + buf_pos, maxlen);
	buf_pos += maxlen;
	remaining_chars -= maxlen;

	return maxlen;
    }

    if (!term_console)
    {
	/* Use OS_ReadC for all input.
	 * Avoids problems with remote access getting interference from
	 * the keyboard.
	 */
	if (wtime == 0)
	    return 0;	    /* Ignore quick key checks */

	if (xswi(OS_ReadC) & c_flag)
	{
	    got_int = TRUE;	/* ESC pressed - can this happen? */
	    swi(OS_Byte, 124);	/* Clear Escape state */
	    r0 = 0x1b;		/* It *might* not have been Escape! */
	}
	buf[0] = r0;
	return 1;
    }

    /*
     * OK, here's the plan:
     *
     * 1) Wait until wtime expires or we get a key
     * 2) Get keys until the keyboard buffer is empty or buf is full
     */

    while (xswi(OS_Byte,145,0) & c_flag)
    {
	/* Nothing at all in the keyboard buffer.
	 * Has our time expired yet?
	 */
	if ( (wtime != -1) && (clock() - start_time) >= wtime )
	    return 0;		/* Nothing read - giving up */
    }

    /* We've got one char (in r2) - are there any more? */

    while (got < maxlen)
    {
	buf[got++] = r2;

	if (xswi(OS_Byte,145,0) & c_flag)
	    return got;		/* Keyboard buffer empty */
    }
    return got;			/* buf is full */
}

/*
 * return non-zero if a character is available
 */
    int
mch_char_avail()
{
    if (!term_console)
	return 0;	    /* Can't tell */
    if (xswi(OS_Byte, 152, 0) & c_flag)
	return 0;
    return 1;
}

/* Find out how much free memory we have.
 * I don't know how to work this out exactly but, since we can claim
 * more memory from the OS, let's just report the free pool size.
 * Dynamic area 6 doesn't exist pre 3.6 according to StrongHelp, so
 * we'll use Wimp_SlotSize. If that fails (outside the desktop?)
 * then just return a big number and hope.
 */
    long_u
mch_avail_mem(special)
    int special;
{
    if (xswi(Wimp_SlotSize, -1, -1) & v_flag)
	return 0x7fffffff;
    return r2;
}

    void
mch_delay(msec, ignoreinput)
    long	msec;
    int		ignoreinput;
{
    int		start_time, time_now;
    int		csec = msec / 10;

    swi(OS_ReadMonotonicTime);
    start_time = r0;

    for (;;)
    {
	swi(OS_ReadMonotonicTime);
	time_now = r0;
	if (time_now - start_time > csec)
	    return;
#ifdef FEAT_GUI
	/* In the GUI, allow other programs to run while waiting. */
	if (gui.in_use)
	    gui_mch_wait_for_chars(start_time + csec);
#endif
    }
}

/*
 * If the machine has job control, use it to suspend the program,
 * otherwise fake it by starting a new shell.
 */
    void
mch_suspend()
{
    suspend_shell();
}

    void
mch_init()
{
    /*
     * Read window size first. Calls to mch_get_shellsize() will
     * simply return these values in future so that setting the
     * text window (used for scrolling) won't give strange results.
     */

    int buf[7] = {132, 135, 256, 257, 1, 2, -1};

    /* Command windows are no longer forced open, since if we are
     * in the desktop then we'll use the GUI version.
     * Opening a command window here messes up the GUI version startup
     */
#ifndef FEAT_GUI
    swi(OS_WriteI);
#endif
    swi(OS_ReadVduVariables, buf, buf);
    WinLeft = buf[0];
    WinTop  = buf[1];
    Columns = buf[2];
    Rows    = buf[3] + 1;	/* Seems to be one off (VduVars wrong?) */
    ScrollTop = 0;

    /* Are we running in a textwindow? */
    if (Rows == buf[5] + 1 && Columns == buf[4] + 1)
	windowed = 0;
    else
	windowed = 1;

    /* Choose a nice colour scheme. */
    text_fg(NORMAL_FG);
    text_bg(NORMAL_BG);
}

/*
 * Check_win checks whether we have an interactive stdout.
 */
/* ARGSUSED */
    int
mch_check_win(argc, argv)
    int	    argc;
    char    **argv;
{
    return OK;
}

/*
 * Return TRUE if the input comes from a terminal, FALSE otherwise.
 */
    int
mch_input_isatty()
{
    if (xswi(OS_ChangeRedirection, -1, -1) & v_flag)
	return TRUE;		/* Error - TRUE is probably correct though */
    if (r0 == 0)
	return TRUE;
    return FALSE;
}

#ifdef FEAT_TITLE
    int
mch_can_restore_title()
{
    return FALSE;
}

    int
mch_can_restore_icon()
{
    return FALSE;
}


/*
 * Set the window title and icon.
 */
    void
mch_settitle(title, icon)
    char_u *title;
    char_u *icon;
{
    if (title == NULL)
	title = (char_u *) "<untitled>";
#ifdef FEAT_GUI
    if (gui.in_use && strcmp(title, gui.window_title))
    {
	int length;
	length = strlen(title);
	if (length >= gui.window_title_size)
	    length = gui.window_title_size - 1;
	strncpy(gui.window_title, title, length);
	gui.window_title[length] = 0;
	ro_redraw_title(gui.window_handle);
    }
#endif
    return;
}

/*
 * Restore the window/icon title.
 * "which" is one of:
 *  1  only restore title
 *  2  only restore icon
 *  3  restore title and icon
 */
    void
mch_restore_title(which)
    int which;
{
    return;
}
#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;
{
    /* RISC OS doesn't support user names. */
    *s = NUL;
    return FAIL;
}

/*
 * Insert host name in s[len].
 */

    void
mch_get_host_name(s, len)
    char_u  *s;
    int	    len;
{
    if (xswi(OS_ReadVarVal, "Machine$Name", s, len, 0, 3) & v_flag)
    {
	/* Variable does not exist (normal operation) */
	vim_strncpy(s, "(unknown)", len - 1);
    }
}

/*
 * return process ID
 */
    long
mch_get_pid()
{
    if (xswi(Wimp_ReadSysInfo, 5) & v_flag)
	return 0;
    return r0;
}

/*
 * 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 (xswi(OS_FSControl, 37, "@", buf, 0, 0, len) & v_flag)
	return FAIL;
    return OK;
}

/*
 * Get absolute file name 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.
			 * Not used under RISC OS.
			 */
{
    if (xswi(OS_FSControl, 37, fname, buf, 0, 0, len) & v_flag)
	return FAIL;
    return OK;
}

/*
 * Return TRUE if "fname" does not depend on the current directory.
 */
    int
mch_isFullName(fname)
    char_u	*fname;
{
    if (strstr(fname, "::") && strstr(fname,".$."))
	return TRUE;
    return FALSE;
}

/*
 * Get file permissions for 'name'.
 * Returns -1 when it doesn't exist.
 */
    long
mch_getperm(name)
    char_u *name;
{
    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;
{
    return (chmod((char *)name, (mode_t)perm) == 0 ? OK : FAIL);
}

/*
 * Set hidden flag for "name".
 */
/* ARGSUSED */
    void
mch_hide(name)
    char_u	*name;
{
    /* can't hide a file */
}

/*
 * 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;
{
    if (xswi(OS_File, 17, name) & v_flag)
	return FALSE;
    if (r0 == 2 || r0 == 3)
	return TRUE;		/* Count image files as directories. */
    return FALSE;
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Return 1 if "name" can be executed, 0 if not.
 * Return -1 if unknown. Requires which to work.
 */
    int
mch_can_exe(name)
    char_u	*name;
{
    char_u	*buf;
    char_u	*p;
    int		retval;

    buf = alloc((unsigned)STRLEN(name) + 7);
    if (buf == NULL)
	return -1;
    sprintf((char *)buf, "which %s", name);
    p = get_cmd_output(buf, NULL, SHELL_SILENT);
    vim_free(buf);
    if (p == NULL)
	return -1;
    /* result can be: "name: Command not found" */
    retval = (*p != NUL && strstr((char *)p, "not found") == NULL);
    vim_free(p);
    return retval;
}
#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()
{
    /* Turn off all the horrible filename munging in UnixLib. */
    int __riscosify_control = __RISCOSIFY_NO_PROCESS;
}

    void
mch_exit(r)
    int r;
{
    settmode(TMODE_COOK);
    exiting = TRUE;
    out_flush();
    ml_close_all(TRUE);		/* remove all memfiles */

#ifdef FEAT_GUI
    if (gui.in_use)
	gui_exit(r);
#endif
    swi(OS_NewLine);
    if (old_escape_state != -1)
	swi(OS_Byte, 229, old_escape_state, 0);
    if (old_cursor_state != -1)
	swi(OS_Byte, 4, old_cursor_state);
    exit(r);
}

    void
mch_settmode(tmode)
    int		tmode;	    /* TMODE_RAW or TMODE_COOK */
{
    if (tmode == TMODE_COOK)
    {
	ro_line_mode = TRUE;
	return;
    }

    ro_line_mode = FALSE;

    if (term_console)
    {
	/* Block cursor. */
	swi(OS_WriteN,
		"\027\000\012\000\000\000\000\000\000\000",
		10);

	/* Disable the standard cursor key actions. */
	swi(OS_Byte, 4, 1);
	if (old_cursor_state == -1)
	    old_cursor_state = r1;
    }

    /* Stop Escape from quitting Vim! */
    swi(OS_Byte, 229, 1, 0);
    if (old_escape_state == -1)
	old_escape_state = r1;
}

/*
 * set mouse clicks on or off (only works for xterms)
 */
    void
mch_setmouse(on)
    int	    on;
{
}

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

/*
 * Try to get the current window size.
 * Return OK when size could be determined, FAIL otherwise.
 * Simply return results stored by mch_init() if we are the
 * machine's console. If not, we don't know how big the screen is.
 */
    int
mch_get_shellsize()
{
    /* if size changed: screenalloc will allocate new screen buffers */
    return term_console ? OK : FAIL;
}

/*
 * Can't change the size.
 * Assume the user knows what he's doing and use the new values.
 */
    void
mch_set_shellsize()
{
    /* Assume the user knows what he's doing and use the new values. */
}

/*
 * Rows and/or Columns has changed.
 */
    void
mch_new_shellsize()
{
    /* Nothing to do. */
}

    int
mch_call_shell(cmd, options)
    char_u	*cmd;
    int		options;	/* SHELL_*, see vim.h */
{
    int		retval;
    int		tmode = cur_tmode;

    if (cmd == NULL)
	cmd = (char_u *) "GOS";

#ifdef FEAT_GUI
    if (gui.in_use)
	return gui_mch_call_shell(cmd, options);
#endif
    if (options & SHELL_COOKED)
	settmode(TMODE_COOK);		/* set to normal mode */
    MSG_PUTS("\n");

   /* I don't even want to think about what UnixLib must
    * be doing to allow this to work...
    */
    retval = system(cmd);
    if (retval && !(options & SHELL_SILENT))
	EMSG(strerror(EOPSYS));		/* Doesn't seem to set errno? */

    swi(OS_Byte, 229, 1, 0);		/* Re-disable escape */
    if (tmode == TMODE_RAW)
	settmode(TMODE_RAW);		/* set to raw mode */
    return retval ? FAIL : OK;
}

/*
 * Check for Escape being pressed right now.
 * [ different if !term_console? ]
 */
    void
mch_breakcheck()
{
    if (xswi(OS_Byte, 121, 0xf0) & v_flag)
	return;
    if (r1 == 0xff)
    {
	got_int = TRUE;
	swi(OS_Byte, 15, 1);	/* Flush input buffer */
    }
}

/*
 * Recursively expand one path component into all matching files and/or
 * directories.
 * "path" has backslashes before chars that are not to be expanded.
 * Return the number of matches found.
 */
    int
mch_expandpath(gap, path, flags)
    garray_T	*gap;	/* Grow array for results. */
    char_u	*path;
    int		flags;	/* EW_* flags */
{
    int		got;	/* Number of matches. */
    char_u	*pattern;

   /* Plan:
    *
    * 1) Get first part of path - no wildcards
    * 2) Get next path element (wildcarded)
    * 3) Get rest of path
    *
    * If (3) is nothing then only the leaf is wildcarded - add to gap
    * Otherwise call recursively for each path in (2), passing (3)
    *
    * This is just the header function.
    */

    /* We must be able to modifiy path, so make a copy */
    pattern = vim_strsave(path);
    if (pattern == NULL)
	return 0;
    got = expand_section(gap, (char_u *)"", pattern, flags);
    vim_free(pattern);
    return got;
}

/*
 * expand_section(gap, "$.Dir1.Dir2", "ABBA*.myleaf##")
 *
 * calls expand_section(gap, "$.Dir1.Dir2.ABBA_Gold", "myleaf##")
 *   and expand_section(gap, "$.Dir1.Dir2.ABBA_Live", "myleaf##")
 *
 * If rest is just a leaf then all matches are added to gap.
 *
 * Returns number of items added to gap.
 */
    int
expand_section(gap, root, rest, flags)
    garray_T	*gap;
    char_u	*root;	/* Non-wildcarded path to search */
    char_u	*rest;	/* Wildcarded remainder of path */
    int		flags;	/* Add dirs/files/missing objects. */
{
    static char_u buf[MAXPATHL];	/* Temporary buffer. */
    char_u dir[MAXPATHL];
    int start_element = -1;		/* Start of wildcarded element */
    char_u c;
    int i;
    int got, dir_pos;
    int buflen;			/* Chars used in buf[] */
    int colon = 0;		/* Dir ends in ':' */

    buflen = strlen(root);
    STRNCPY(buf, root, buflen);	/* Copy root into buffer. */

   /*
    * Find end of nonwildcarded section.
    * Count ':' as a path sep since Vim:Bug* is a valid pathname.
    */

    for (i = 0; c = rest[i]; i++)
    {
	if (c == PATHSEP)
	{
	    start_element = i;
	    colon = 0;
	}
	if (c == ':')
	{
	    start_element = i + 1;
	    colon = 1;
	}
	if (c == '#' || c == '*')
	    break;
    }
    if (c == 0)
	start_element = i;

   /*
    * start_element +> terminator for non-wildcarded section.
    * Transfer this bit into buf.
    */
    if (buflen + start_element + 4 >= MAXPATHL)
       return 0;			/* Buffer full */
    if (start_element >= 0)
    {
	if (*root && !colon)
	    buf[buflen++] = PATHSEP;
	strncpy(buf + buflen, rest, start_element);
	buflen += start_element;
    }
    buf[buflen] = 0;

   /*
    * Did we reach the end of the string without hitting any wildcards?
    */
    if (c == 0)
    {
	/* Yes - add combined path to grow array and return. */
	addfile(gap, buf, flags);
	return 1;
    }

    if (start_element < 0 || !colon)
	start_element++;
    rest += start_element;

   /*
    * rest does contain wildcards if we get here.
    *
    * Now : have we reached the leaf names part yet?
    * If so, add all matches (files and dirs) to gap.
    * If not, get next path element and scan all matching directories.
    */

    start_element = -1;
    for (i = 0; rest[i]; i++)
    {
	if (rest[i] == '.')
	{
	    start_element = i;
	    rest[i] = 0;		/* Break string here. */
	    break;
	}
    }

    /* If start_element is -1 then we are matching leaf names */

    r3 = 0;			/* Number of objs read. */
    dir_pos = 0;		/* Position through directory. */
    got = 0;			/* Files added so far. */
    while (dir_pos != -1)
    {
	buf[buflen] = 0;
	if (xswi(OS_GBPB, 9,
		buf,				/* Directory to scan. */
		buf + buflen + (1 - colon),	/* Buffer for result. */
		1,			/* Number of objects to read. */
		dir_pos,		/* Search position. */
		MAXPATHL - 2 - buflen,	/* Size of result buffer. */
		rest)			/* Wildcarded leafname. */
			& v_flag)
	{
	    EMSG(r0 + 4);
	    r4 = -1;
	}
	dir_pos = r4;		/* r4 corrupted by addfile() */
	if (r3 > 0)
	{
	    char_u *path = buf;
	    if (buflen == 0)
		path++;			/* Don't do '.File' */
	    else if (!colon)
		buf[buflen] = '.';		/* Join path and leaf */

	   /* Path -> full path of object found */
	    if (start_element == -1)
	    {
		addfile(gap, path, flags);
		got++;
	    }
	    else
	    {
	       /* Scan into subdirectories and images; ignore files */
		swi(OS_File, 17, path);
		if (r0 == 2 || r0 == 3)
		    got += expand_section(gap,
						path,
						rest + start_element + 1,
						flags);
	    }
	}
    }

    /* Restore the dot if we removed it. */
    if (start_element >= 0)
	rest[start_element] = '.';
    return got;
}

/*
 * mch_expand_wildcards() - this code does wild-card pattern matching using
 * the shell. It isn't used under RISC OS.
 *
 * return OK for success, FAIL for error (you may lose some memory) and put
 * an error message in *file.
 *
 * num_pat is number of input patterns
 * pat is array of pointers to input patterns
 * num_file is pointer to number of matched file names
 * file is pointer to array of pointers to matched file names
 */
    int
mch_expand_wildcards(num_pat, pat, num_file, file, flags)
    int		    num_pat;
    char_u	  **pat;
    int		   *num_file;
    char_u	 ***file;
    int		    flags;		/* EW_* flags */
{
    /* This doesn't get called unless SPECIAL_WILDCHAR is defined. */
    return FAIL;
}

/*
 * Return TRUE if "p" contains wildcards which can be expanded by
 * mch_expandpath().
 */
    int
mch_has_exp_wildcard(p)
    char_u	*p;
{
    if (vim_strpbrk((char_u *)"*#", p))
	return TRUE;
    return FALSE;
}

/* Return TRUE if "p" contains wildcards. */
    int
mch_has_wildcard(p)
    char_u	*p;
{
    if (vim_strpbrk((char_u *)"*#`", p))
	return TRUE;
    return FALSE;
}

    int			/* see Unix unlink(2) */
mch_remove(file)
    char_u *file;	/* Name of file to delete. */
{
    if (xswi(OS_FSControl, 27, file, 0, 0) & v_flag)
	return EXIT_FAILURE;
    return EXIT_SUCCESS;
}

/* Try to make existing scripts work without modification.
 * Return a pointer to the new string (freed by caller), or NULL
 *
 * Two main cases:
 * - Absolute : $VIM/syntax/help.vim
 * - Relative : Adfs::4.$.!Vim.Resources.Syntax/help.vim
 */
    char_u *
mch_munge_fname(fname)
    char_u *fname;
{
    char_u c;
    int len;
    char_u *retval;

    retval = fname = vim_strsave(fname);
    if (fname == NULL)
	return NULL;

    if (strncmp(fname, "$VIM/", 5) == 0)
    {
	strncpy(fname, "Vim:", 4);
	for (fname += 5; c = *fname; fname++)
	{
	    if (c == '.')
		break;
	    if (c == '/')
		fname[-1] = '.';
	    else
		fname[-1] = c;
	}
	fname[-1] = '\0';
    }
    else
    {
	/* Check to see if the file exists without modification. */
	if (xswi(OS_File, 17, fname) & v_flag)
	    r0 == 0;		/* Invalid filename? */
	if (r0)
	    return retval;

	len = strlen(fname);
	if (strcmp(fname + len - 4, ".vim") == 0)
	{
	    fname[len - 4] = '\0';
	    for (; c = *fname; fname++)
	    {
		if (c == '/')
		    *fname = '.';
	    }
	}
    }
    return retval;
}

/* QuickFix reads munged names from the error file.
 * Correct them.
 */
    int
ro_buflist_add(old_name)
    char_u  *old_name;	/* Name of file found by quickfix */
{
    char_u  *fname;
    char_u  *leaf;	/* Pointer to start of leaf in old_name */
    char_u  *ptr;
    char_u  c;
    int	    retval;

    if (old_name == NULL)
	return buflist_add(NULL, 0);

    /* Copy the name so we can mess around with it. */
    fname = vim_strsave(old_name);
    if (fname == NULL)
	/* Out of memory - can't modify name */
	return buflist_add(old_name, 0);

    /* Change `dir/main.c' into `dir.c.main' */
    leaf = fname;
    for (ptr = fname; c = *ptr; ptr++)
    {
	if (c == '/')
	{
	    leaf = ptr + 1;
	    *ptr = '.';
	}
	else if (c == '.')
	    break;
    }
    if (c == '.')
    {
	/* Change `main.c' into `c.main'
	 *	  |    |
	 *      leaf  ptr
	 */
	ptr += old_name - fname;
	*ptr = '\0';
	sprintf(leaf,
		"%s.%s",
		ptr + 1,
		leaf - fname + old_name);
    }

    retval = buflist_add(fname, 0);
    free(fname);
    return retval;
}

/* Change the current directory.
 * Strip trailing dots to make it easier to use with filename completion.
 * Return 0 for success, -1 for failure.
 */
    int
mch_chdir(dir)
    char_u  *dir;
{
    int	    length;
    int	    retval;
    char_u  *new_dir;

    length = strlen(dir);
    if (dir[length - 1] != '.')
	return chdir(dir);	    /* No trailing dots - nothing to do. */
    new_dir = vim_strsave(dir);
    if (new_dir == NULL)
	return chdir(dir);	    /* Can't allocate memory. */

    while (new_dir[--length] == '.')
	new_dir[length] = '\0';

    retval = chdir(new_dir);
    vim_free(new_dir);
    return retval;
}

/* Examine the named file, and set the 'osfiletype' option
 * (in curbuf) to the file's type.
 */
    void
mch_read_filetype(file)
    char_u  *file;
{
    int	    type;
    char_u  type_string[9];
    int	    i;

    if (xswi(OS_File, 23, file) & v_flag)
	type = 0xfff;		/* Default to Text */
    else
	type = r6;

    /* Type is the numerical value - see if we have a textual equivalent */
    swi(OS_FSControl, 18, 0, type);
    ((int *) type_string)[0] = r2;
    ((int *) type_string)[1] = r3;
    type_string[8] = 0;
    for (i = 0; type_string[i] > ' '; i++)
	;
    type_string[i] = 0;

    set_string_option_direct("osfiletype", -1, type_string, OPT_FREE);
    return;
}

    void
mch_set_filetype(file, type)
    char_u  *file;
    char_u  *type;
{
    if (xswi(OS_FSControl, 31, type) & v_flag)
    {
	EMSG(_("E366: Invalid 'osfiletype' option - using Text"));
	r2 = 0xfff;
    }

    swi(OS_File, 18, file, r2);
}

/* Return TRUE if the file's type matches 'type'
 * RISC OS types always start with '&'
 */
    int
mch_check_filetype(fname, type)
    char_u  *fname;
    char_u  *type;
{
    int	    value;
    char    *end;

    if (*type != '&')
	return FALSE;

    value = strtol(type + 1, &end, 16);
    if (*end)
	return FALSE;		/* Invalid type (report error?) */

    if (xswi(OS_File, 23, fname) & v_flag)
	return FALSE;		/* Invalid filename? */

    return (r0 && r6 == value);
}
