Example #1
0
File: p-file.c Project: mbk/ren-c
*/	static void Setup_File(REBREQ *file, REBCNT args, REBVAL *path)
/*
**		Convert native action refinements to file modes.
**
***********************************************************************/
{
	REBSER *ser;

	if (args & AM_OPEN_WRITE) SET_FLAG(file->modes, RFM_WRITE);
	if (args & AM_OPEN_READ) SET_FLAG(file->modes, RFM_READ);
	if (args & AM_OPEN_SEEK) SET_FLAG(file->modes, RFM_SEEK);

	if (args & AM_OPEN_NEW) {
		SET_FLAG(file->modes, RFM_NEW);
		if (!(args & AM_OPEN_WRITE)) Trap1(RE_BAD_FILE_MODE, path);
	}

	// Convert file name to OS format, let it GC later.
	if (!(ser = Value_To_OS_Path(path, TRUE)))
		Trap1(RE_BAD_FILE_PATH, path);

	file->special.file.path = cast(REBCHR*, ser->data);

	SET_FLAG(file->modes, RFM_NAME_MEM);

	Secure_Port(SYM_FILE, file, path, ser);
}
Example #2
0
File: p-file.c Project: mbk/ren-c
*/	static REB_R File_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action)
/*
**		Internal port handler for files.
**
***********************************************************************/
{
	REBVAL *spec;
	REBVAL *path;
	REBREQ *file = 0;
	REBCNT args = 0;
	REBCNT len;
	REBOOL opened = FALSE;	// had to be opened (shortcut case)

	//Print("FILE ACTION: %r", Get_Action_Word(action));

	Validate_Port(port, action);

	*D_OUT = *D_ARG(1);

	// Validate PORT fields:
	spec = BLK_SKIP(port, STD_PORT_SPEC);
	if (!IS_OBJECT(spec)) Trap1_DEAD_END(RE_INVALID_SPEC, spec);
	path = Obj_Value(spec, STD_PORT_SPEC_HEAD_REF);
	if (!path) Trap1_DEAD_END(RE_INVALID_SPEC, spec);

	if (IS_URL(path)) path = Obj_Value(spec, STD_PORT_SPEC_HEAD_PATH);
	else if (!IS_FILE(path)) Trap1_DEAD_END(RE_INVALID_SPEC, path);

	// Get or setup internal state data:
	file = (REBREQ*)Use_Port_State(port, RDI_FILE, sizeof(*file));

	switch (action) {

	case A_READ:
		args = Find_Refines(call_, ALL_READ_REFS);

		// Handle the READ %file shortcut case:
		if (!IS_OPEN(file)) {
			REBCNT nargs = AM_OPEN_READ;
			if (args & AM_READ_SEEK) nargs |= AM_OPEN_SEEK;
			Setup_File(file, nargs, path);
			Open_File_Port(port, file, path);
			opened = TRUE;
		}

		if (args & AM_READ_SEEK) Set_Seek(file, D_ARG(ARG_READ_INDEX));
		len = Set_Length(
			file, D_REF(ARG_READ_PART) ? VAL_INT64(D_ARG(ARG_READ_LENGTH)) : -1
		);
		Read_File_Port(D_OUT, port, file, path, args, len);

		if (opened) {
			OS_DO_DEVICE(file, RDC_CLOSE);
			Cleanup_File(file);
		}

		if (file->error) Trap_Port_DEAD_END(RE_READ_ERROR, port, file->error);
		break;

	case A_APPEND:
		if (!(IS_BINARY(D_ARG(2)) || IS_STRING(D_ARG(2)) || IS_BLOCK(D_ARG(2))))
			Trap1_DEAD_END(RE_INVALID_ARG, D_ARG(2));
		file->special.file.index = file->special.file.size;
		SET_FLAG(file->modes, RFM_RESEEK);

	case A_WRITE:
		args = Find_Refines(call_, ALL_WRITE_REFS);
		spec = D_ARG(2); // data (binary, string, or block)

		// Handle the READ %file shortcut case:
		if (!IS_OPEN(file)) {
			REBCNT nargs = AM_OPEN_WRITE;
			if (args & AM_WRITE_SEEK || args & AM_WRITE_APPEND) nargs |= AM_OPEN_SEEK;
			else nargs |= AM_OPEN_NEW;
			Setup_File(file, nargs, path);
			Open_File_Port(port, file, path);
			opened = TRUE;
		}
		else {
			if (!GET_FLAG(file->modes, RFM_WRITE)) Trap1_DEAD_END(RE_READ_ONLY, path);
		}

		// Setup for /append or /seek:
		if (args & AM_WRITE_APPEND) {
			file->special.file.index = -1; // append
			SET_FLAG(file->modes, RFM_RESEEK);
		}
		if (args & AM_WRITE_SEEK) Set_Seek(file, D_ARG(ARG_WRITE_INDEX));

		// Determine length. Clip /PART to size of string if needed.
		len = VAL_LEN(spec);
		if (args & AM_WRITE_PART) {
			REBCNT n = Int32s(D_ARG(ARG_WRITE_LENGTH), 0);
			if (n <= len) len = n;
		}

		Write_File_Port(file, spec, len, args);

		if (opened) {
			OS_DO_DEVICE(file, RDC_CLOSE);
			Cleanup_File(file);
		}

		if (file->error) Trap1_DEAD_END(RE_WRITE_ERROR, path);
		break;

	case A_OPEN:
		args = Find_Refines(call_, ALL_OPEN_REFS);
		// Default file modes if not specified:
		if (!(args & (AM_OPEN_READ | AM_OPEN_WRITE))) args |= (AM_OPEN_READ | AM_OPEN_WRITE);
		Setup_File(file, args, path);
		Open_File_Port(port, file, path); // !!! needs to change file modes to R/O if necessary
		break;

	case A_COPY:
		if (!IS_OPEN(file)) Trap1_DEAD_END(RE_NOT_OPEN, path); //!!!! wrong msg
		len = Set_Length(file, D_REF(2) ? VAL_INT64(D_ARG(3)) : -1);
		Read_File_Port(D_OUT, port, file, path, args, len);
		break;

	case A_OPENQ:
		if (IS_OPEN(file)) return R_TRUE;
		return R_FALSE;

	case A_CLOSE:
		if (IS_OPEN(file)) {
			OS_DO_DEVICE(file, RDC_CLOSE);
			Cleanup_File(file);
		}
		break;

	case A_DELETE:
		if (IS_OPEN(file)) Trap1_DEAD_END(RE_NO_DELETE, path);
		Setup_File(file, 0, path);
		if (OS_DO_DEVICE(file, RDC_DELETE) < 0 ) Trap1_DEAD_END(RE_NO_DELETE, path);
		break;

	case A_RENAME:
		if (IS_OPEN(file)) Trap1_DEAD_END(RE_NO_RENAME, path);
		else {
			REBSER *target;

			Setup_File(file, 0, path);

			// Convert file name to OS format:
			if (!(target = Value_To_OS_Path(D_ARG(2), TRUE)))
				Trap1_DEAD_END(RE_BAD_FILE_PATH, D_ARG(2));
			file->common.data = BIN_DATA(target);
			OS_DO_DEVICE(file, RDC_RENAME);
			Free_Series(target);
			if (file->error) Trap1_DEAD_END(RE_NO_RENAME, path);
		}
		break;

	case A_CREATE:
		// !!! should it leave file open???
		if (!IS_OPEN(file)) {
			Setup_File(file, AM_OPEN_WRITE | AM_OPEN_NEW, path);
			if (OS_DO_DEVICE(file, RDC_CREATE) < 0) Trap_Port_DEAD_END(RE_CANNOT_OPEN, port, file->error);
			OS_DO_DEVICE(file, RDC_CLOSE);
		}
		break;

	case A_QUERY:
		if (!IS_OPEN(file)) {
			Setup_File(file, 0, path);
			if (OS_DO_DEVICE(file, RDC_QUERY) < 0) return R_NONE;
		}
		Ret_Query_File(port, file, D_OUT);
		// !!! free file path?
		break;

	case A_MODIFY:
		Set_Mode_Value(file, Get_Mode_Id(D_ARG(2)), D_ARG(3));
		if (!IS_OPEN(file)) {
			Setup_File(file, 0, path);
			if (OS_DO_DEVICE(file, RDC_MODIFY) < 0) return R_NONE;
		}
		return R_TRUE;
		break;

	case A_INDEXQ:
		SET_INTEGER(D_OUT, file->special.file.index + 1);
		break;

	case A_LENGTHQ:
		SET_INTEGER(D_OUT, file->special.file.size - file->special.file.index); // !clip at zero
		break;

	case A_HEAD:
		file->special.file.index = 0;
		goto seeked;

    case A_TAIL:
		file->special.file.index = file->special.file.size;
		goto seeked;

	case A_NEXT:
		file->special.file.index++;
		goto seeked;

	case A_BACK:
		if (file->special.file.index > 0) file->special.file.index--;
		goto seeked;

	case A_SKIP:
		file->special.file.index += Get_Num_Arg(D_ARG(2));
		goto seeked;

    case A_HEADQ:
		DECIDE(file->special.file.index == 0);

    case A_TAILQ:
		DECIDE(file->special.file.index >= file->special.file.size);

    case A_PASTQ:
		DECIDE(file->special.file.index > file->special.file.size);

	case A_CLEAR:
		// !! check for write enabled?
		SET_FLAG(file->modes, RFM_RESEEK);
		SET_FLAG(file->modes, RFM_TRUNCATE);
		file->length = 0;
		if (OS_DO_DEVICE(file, RDC_WRITE) < 0) Trap1_DEAD_END(RE_WRITE_ERROR, path);
		break;

	/* Not yet implemented:
		A_AT,					// 38
		A_PICK,					// 41
		A_PATH,					// 42
		A_PATH_SET,				// 43
		A_FIND,					// 44
		A_SELECT,				// 45
		A_TAKE,					// 49
		A_INSERT,				// 50
		A_REMOVE,				// 52
		A_CHANGE,				// 53
		A_POKE,					// 54
		A_QUERY,				// 64
		A_FLUSH,				// 65
	*/

	default:
		Trap_Action_DEAD_END(REB_PORT, action);
	}

	return R_OUT;

seeked:
	SET_FLAG(file->modes, RFM_RESEEK);
	return R_ARG1;

is_true:
	return R_TRUE;

is_false:
	return R_FALSE;
}
Example #3
0
File: p-dir.c Project: 51weekend/r3
*/	static int Dir_Actor(REBVAL *ds, REBSER *port, REBCNT action)
/*
**		Internal port handler for file directories.
**
***********************************************************************/
{
	REBVAL *spec;
	REBVAL *path;
	REBVAL *state;
	REBREQ dir;
	REBCNT args = 0;
	REBINT result;
	REBCNT len;
	//REBYTE *flags;

	Validate_Port(port, action);

	*D_RET = *D_ARG(1);
	CLEARS(&dir);

	// Validate and fetch relevant PORT fields:
	spec  = BLK_SKIP(port, STD_PORT_SPEC);
	if (!IS_OBJECT(spec)) Trap1(RE_INVALID_SPEC, spec);
	path = Obj_Value(spec, STD_PORT_SPEC_HEAD_REF);
	if (!path) Trap1(RE_INVALID_SPEC, spec);

	if (IS_URL(path)) path = Obj_Value(spec, STD_PORT_SPEC_HEAD_PATH);
	else if (!IS_FILE(path)) Trap1(RE_INVALID_SPEC, path);
	
	state = BLK_SKIP(port, STD_PORT_STATE); // if block, then port is open.

	//flags = Security_Policy(SYM_FILE, path);

	// Get or setup internal state data:
	dir.port = port;
	dir.device = RDI_FILE;

	switch (action) {

	case A_READ:
		//Trap_Security(flags[POL_READ], POL_READ, path);
		args = Find_Refines(ds, ALL_READ_REFS);
		if (!IS_BLOCK(state)) {		// !!! ignores /SKIP and /PART, for now
			Init_Dir_Path(&dir, path, 1, POL_READ);
			Set_Block(state, Make_Block(7)); // initial guess
			result = Read_Dir(&dir, VAL_SERIES(state));
			///OS_FREE(dir.file.path);
			if (result < 0) Trap_Port(RE_CANNOT_OPEN, port, dir.error);
			*D_RET = *state;
			SET_NONE(state);
		} else {
			len = VAL_BLK_LEN(state);
			// !!? Why does this need to copy the block??
			Set_Block(D_RET, Copy_Block_Values(VAL_SERIES(state), 0, len, TS_STRING));
		}
		break;

	case A_CREATE:
		//Trap_Security(flags[POL_WRITE], POL_WRITE, path);
		if (IS_BLOCK(state)) Trap1(RE_ALREADY_OPEN, path); // already open
create:
		Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too
		result = OS_DO_DEVICE(&dir, RDC_CREATE);
		///OS_FREE(dir.file.path);
		if (result < 0) Trap1(RE_NO_CREATE, path);
		if (action == A_CREATE) return R_ARG2;
		SET_NONE(state);
		break;

	case A_RENAME:
		if (IS_BLOCK(state)) Trap1(RE_ALREADY_OPEN, path); // already open
		else {
			REBSER *target;

			Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too
			// Convert file name to OS format:
			if (!(target = Value_To_OS_Path(D_ARG(2)))) Trap1(RE_BAD_FILE_PATH, D_ARG(2));
			dir.data = BIN_DATA(target);
			OS_DO_DEVICE(&dir, RDC_RENAME);
			Free_Series(target);
			if (dir.error) Trap1(RE_NO_RENAME, path);
		}
		break;

	case A_DELETE:
		//Trap_Security(flags[POL_WRITE], POL_WRITE, path);
		SET_NONE(state);
		Init_Dir_Path(&dir, path, 0, POL_WRITE);
		// !!! add *.r deletion
		// !!! add recursive delete (?)
		result = OS_DO_DEVICE(&dir, RDC_DELETE);
		///OS_FREE(dir.file.path);
		if (result < 0) Trap1(RE_NO_DELETE, path);
		return R_ARG2;

	case A_OPEN:
		// !! If open fails, what if user does a READ w/o checking for error?
		if (IS_BLOCK(state)) Trap1(RE_ALREADY_OPEN, path); // already open
		//Trap_Security(flags[POL_READ], POL_READ, path);
		args = Find_Refines(ds, ALL_OPEN_REFS);
		if (args & AM_OPEN_NEW) goto create;
		//if (args & ~AM_OPEN_READ) Trap1(RE_INVALID_SPEC, path);
		Set_Block(state, Make_Block(7));
		Init_Dir_Path(&dir, path, 1, POL_READ);
		result = Read_Dir(&dir, VAL_SERIES(state));
		///OS_FREE(dir.file.path);
		if (result < 0) Trap_Port(RE_CANNOT_OPEN, port, dir.error);
		break;

	case A_OPENQ:
		if (IS_BLOCK(state)) return R_TRUE;
		return R_FALSE;

	case A_CLOSE:
		SET_NONE(state);
		break;

	case A_QUERY:
		//Trap_Security(flags[POL_READ], POL_READ, path);
		SET_NONE(state);
		Init_Dir_Path(&dir, path, -1, REMOVE_TAIL_SLASH | POL_READ);
		if (OS_DO_DEVICE(&dir, RDC_QUERY) < 0) return R_NONE;
		Ret_Query_File(port, &dir, D_RET);
		///OS_FREE(dir.file.path);
		break;

	//-- Port Series Actions (only called if opened as a port)

	case A_LENGTHQ:
		len = IS_BLOCK(state) ? VAL_BLK_LEN(state) : 0;
		SET_INTEGER(D_RET, len);
		break;

	default:
		Trap_Action(REB_PORT, action);
	}

	return R_RET;
}
Example #4
0
File: p-dir.c Project: 51weekend/r3
*/	static void Init_Dir_Path(REBREQ *dir, REBVAL *path, REBINT wild, REBCNT policy)
/*
**		Convert REBOL dir path to file system path.
**		On Windows, we will also need to append a * if necessary.
**
**	ARGS:
**		Wild:
**			0 - no wild cards, path must end in / else error
**			1 - accept wild cards * and ?, and * if need
**		   -1 - not wild, if path does not end in /, add it
**
***********************************************************************/
{
	REBINT len;
	REBSER *ser;
	//REBYTE *flags;

	SET_FLAG(dir->modes, RFM_DIR);

	// We depend on To_Local_Path giving us 2 extra chars for / and *
	ser = Value_To_OS_Path(path);
	len = ser->tail;
	dir->file.path = (REBCHR*)(ser->data);

	Secure_Port(SYM_FILE, dir, path, ser);

	if (len == 1 && dir->file.path[0] == '.') {
		if (wild > 0) {
			dir->file.path[0] = '*';
			dir->file.path[1] = 0;
		}
	}
	else if (len == 2 && dir->file.path[0] == '.' && dir->file.path[1] == '.') {
		// Insert * if needed:
		if (wild > 0) {
			dir->file.path[len++] = '/';
			dir->file.path[len++] = '*';
			dir->file.path[len] = 0;
		}
	}
	else if (dir->file.path[len-1] == '/' || dir->file.path[len-1] == '\\') {
		if (policy & REMOVE_TAIL_SLASH) {
			dir->file.path[len-1] = 0;
		}
		else {
			// Insert * if needed:
			if (wild > 0) {
				dir->file.path[len++] = '*';
				dir->file.path[len] = 0;
			}
		}
	} else {
		// Path did not end with /, so we better be wild:
		if (wild == 0) {
			///OS_FREE(dir->file.path);
			Trap1(RE_BAD_FILE_PATH, path);
		}
		else if (wild < 0) {
			dir->file.path[len++] = OS_DIR_SEP;
			dir->file.path[len] = 0;
		}
	}
}
Example #5
0
*/	static void Init_Dir_Path(REBREQ *dir, REBVAL *path, REBINT wild, REBCNT policy)
/*
**		Convert REBOL dir path to file system path.
**		On Windows, we will also need to append a * if necessary.
**
**	ARGS:
**		Wild:
**			0 - no wild cards, path must end in / else error
**			1 - accept wild cards * and ?, and * if need
**		   -1 - not wild, if path does not end in /, add it
**
***********************************************************************/
{
	REBINT len;
	REBSER *ser;
	//REBYTE *flags;

	SET_FLAG(dir->modes, RFM_DIR);

	// We depend on To_Local_Path giving us 2 extra chars for / and *
	ser = Value_To_OS_Path(path, TRUE);
	len = ser->tail;
	dir->special.file.path = cast(REBCHR*, ser->data);

	Secure_Port(SYM_FILE, dir, path, ser);

	if (len == 1 && OS_CH_EQUAL(dir->special.file.path[0], '.')) {
		if (wild > 0) {
			dir->special.file.path[0] = OS_MAKE_CH('*');
			dir->special.file.path[1] = OS_MAKE_CH('\0');
		}
	}
	else if (
		len == 2
		&& OS_CH_EQUAL(dir->special.file.path[0], '.')
		&& OS_CH_EQUAL(dir->special.file.path[1], '.')
	) {
		// Insert * if needed:
		if (wild > 0) {
			dir->special.file.path[len++] = OS_MAKE_CH('/');
			dir->special.file.path[len++] = OS_MAKE_CH('*');
			dir->special.file.path[len] = OS_MAKE_CH('\0');
		}
	}
	else if (
		OS_CH_EQUAL(dir->special.file.path[len-1], '/')
		|| OS_CH_EQUAL(dir->special.file.path[len-1], '\\')
	) {
		if (policy & REMOVE_TAIL_SLASH) {
			dir->special.file.path[len-1] = OS_MAKE_CH('\0');
		}
		else {
			// Insert * if needed:
			if (wild > 0) {
				dir->special.file.path[len++] = OS_MAKE_CH('*');
				dir->special.file.path[len] = OS_MAKE_CH('\0');
			}
		}
	} else {
		// Path did not end with /, so we better be wild:
		if (wild == 0) {
			// !!! Comment said `OS_FREE(dir->special.file.path);` (needed?)
			raise Error_1(RE_BAD_FILE_PATH, path);
		}
		else if (wild < 0) {
			dir->special.file.path[len++] = OS_MAKE_CH(OS_DIR_SEP);
			dir->special.file.path[len] = OS_MAKE_CH('\0');
		}
	}
}
Example #6
0
*/	static REB_R Dir_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action)
/*
**		Internal port handler for file directories.
**
***********************************************************************/
{
	REBVAL *spec;
	REBVAL *path;
	REBVAL *state;
	REBREQ dir;
	REBCNT args = 0;
	REBINT result;
	REBCNT len;
	//REBYTE *flags;

	Validate_Port(port, action);

	*D_OUT = *D_ARG(1);
	CLEARS(&dir);

	// Validate and fetch relevant PORT fields:
	spec  = BLK_SKIP(port, STD_PORT_SPEC);
	if (!IS_OBJECT(spec)) raise Error_1(RE_INVALID_SPEC, spec);
	path = Obj_Value(spec, STD_PORT_SPEC_HEAD_REF);
	if (!path) raise Error_1(RE_INVALID_SPEC, spec);

	if (IS_URL(path)) path = Obj_Value(spec, STD_PORT_SPEC_HEAD_PATH);
	else if (!IS_FILE(path)) raise Error_1(RE_INVALID_SPEC, path);

	state = BLK_SKIP(port, STD_PORT_STATE); // if block, then port is open.

	//flags = Security_Policy(SYM_FILE, path);

	// Get or setup internal state data:
	dir.port = port;
	dir.device = RDI_FILE;

	switch (action) {

	case A_READ:
		//Trap_Security(flags[POL_READ], POL_READ, path);
		args = Find_Refines(call_, ALL_READ_REFS);
		if (!IS_BLOCK(state)) {		// !!! ignores /SKIP and /PART, for now
			Init_Dir_Path(&dir, path, 1, POL_READ);
			Val_Init_Block(state, Make_Array(7)); // initial guess
			result = Read_Dir(&dir, VAL_SERIES(state));
			///OS_FREE(dir.file.path);
			if (result < 0)
				raise Error_On_Port(RE_CANNOT_OPEN, port, dir.error);
			*D_OUT = *state;
			SET_NONE(state);
		}
		else {
			// !!! This copies the strings in the block, shallowly.  What is
			// the purpose of doing this?  Why copy at all?
			Val_Init_Block(
				D_OUT,
				Copy_Array_Core_Managed(
					VAL_SERIES(state),
					0,
					VAL_BLK_LEN(state),
					FALSE, // !deep
					TS_STRING
				)
			);
		}
		break;

	case A_CREATE:
		//Trap_Security(flags[POL_WRITE], POL_WRITE, path);
		if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path);
create:
		Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too
		result = OS_DO_DEVICE(&dir, RDC_CREATE);
		///OS_FREE(dir.file.path);
		if (result < 0) raise Error_1(RE_NO_CREATE, path);
		if (action == A_CREATE) {
			// !!! Used to return R_ARG2, but create is single arity.  :-/
			return R_ARG1;
		}
		SET_NONE(state);
		break;

	case A_RENAME:
		if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path);
		else {
			REBSER *target;

			Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too
			// Convert file name to OS format:
			if (!(target = Value_To_OS_Path(D_ARG(2), TRUE)))
				raise Error_1(RE_BAD_FILE_PATH, D_ARG(2));
			dir.common.data = BIN_DATA(target);
			OS_DO_DEVICE(&dir, RDC_RENAME);
			Free_Series(target);
			if (dir.error) raise Error_1(RE_NO_RENAME, path);
		}
		break;

	case A_DELETE:
		//Trap_Security(flags[POL_WRITE], POL_WRITE, path);
		SET_NONE(state);
		Init_Dir_Path(&dir, path, 0, POL_WRITE);
		// !!! add *.r deletion
		// !!! add recursive delete (?)
		result = OS_DO_DEVICE(&dir, RDC_DELETE);
		///OS_FREE(dir.file.path);
		if (result < 0) raise Error_1(RE_NO_DELETE, path);
		// !!! Returned R_ARG2 before, but there is no second argument :-/
		return R_ARG1;

	case A_OPEN:
		// !! If open fails, what if user does a READ w/o checking for error?
		if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path);
		//Trap_Security(flags[POL_READ], POL_READ, path);
		args = Find_Refines(call_, ALL_OPEN_REFS);
		if (args & AM_OPEN_NEW) goto create;
		//if (args & ~AM_OPEN_READ) raise Error_1(RE_INVALID_SPEC, path);
		Val_Init_Block(state, Make_Array(7));
		Init_Dir_Path(&dir, path, 1, POL_READ);
		result = Read_Dir(&dir, VAL_SERIES(state));
		///OS_FREE(dir.file.path);
		if (result < 0) raise Error_On_Port(RE_CANNOT_OPEN, port, dir.error);
		break;

	case A_OPENQ:
		if (IS_BLOCK(state)) return R_TRUE;
		return R_FALSE;

	case A_CLOSE:
		SET_NONE(state);
		break;

	case A_QUERY:
		//Trap_Security(flags[POL_READ], POL_READ, path);
		SET_NONE(state);
		Init_Dir_Path(&dir, path, -1, REMOVE_TAIL_SLASH | POL_READ);
		if (OS_DO_DEVICE(&dir, RDC_QUERY) < 0) return R_NONE;
		Ret_Query_File(port, &dir, D_OUT);
		///OS_FREE(dir.file.path);
		break;

	//-- Port Series Actions (only called if opened as a port)

	case A_LENGTH:
		len = IS_BLOCK(state) ? VAL_BLK_LEN(state) : 0;
		SET_INTEGER(D_OUT, len);
		break;

	default:
		raise Error_Illegal_Action(REB_PORT, action);
	}

	return R_OUT;
}