diff --git a/info.txt b/info.txt new file mode 100644 index 0000000..9ac15b6 --- /dev/null +++ b/info.txt @@ -0,0 +1,8 @@ +REBOL 3 Host Build Distribution + + Version: 2.100.96.0.0 + Built: 2-Dec-2009/15:37:52-8:00 + +Test build only for early alpha-test developers. Do not distribute. + +Documentation: http://www.rebol.net/wiki/Host-Builds diff --git a/makefile b/makefile new file mode 100644 index 0000000..0353919 --- /dev/null +++ b/makefile @@ -0,0 +1,84 @@ +# REBOL 3.0 Host Makefile - Build A96 + +C= gcc +CFLAGS= -c -O1 -DUNICODE -fpack-struct -I src/include/ +LIBS= r3lib.lib -L c:/mingw/lib/ -lm -lwsock32 -lcomdlg32 +#-lc -lm -lstdc++ + +S= src +O= obj +I= src/include + +# Included from reb-host.h: +INCS = \ + $I/reb-config.h\ + $I/reb-c.h\ + $I/reb-defs.h\ + $I/reb-args.h\ + $I/reb-device.h\ + $I/reb-file.h\ + $I/reb-event.h\ + $I/reb-evtypes.h\ + $I/reb-net.h\ + $I/reb-filereq.h\ + +OBJS = \ + $O/host-main.o\ + $O/host-args.o\ + $O/host-device.o\ + $O/host-stdio.o\ + $O/dev-net.o\ + $O/dev-dns.o\ + $O/host-init.o\ + $O/host-lib.o\ + $O/dev-stdio.o\ + $O/dev-event.o\ + $O/dev-file.o\ + $O/dev-clipboard.o\ + +# Link the files +core: $(OBJS) + $C -o core.exe $(OBJS) $(LIBS) + strip core.exe + +clean: + del /q obj\* + make core + +# Compile: +$O/host-main.o: $S/os/host-main.c $(INCS) $I/reb-host.h $I/host-lib.h $I/rebol-lib.h $I/host-init.h + $C $(CFLAGS) -o $O/host-main.o $S/os/host-main.c + +$O/host-args.o: $S/os/host-args.c $(INCS) $I/reb-config.h $I/reb-c.h $I/reb-args.h + $C $(CFLAGS) -o $O/host-args.o $S/os/host-args.c + +$O/host-device.o: $S/os/host-device.c $(INCS) $I/reb-host.h $I/host-lib.h $I/rebol-lib.h + $C $(CFLAGS) -o $O/host-device.o $S/os/host-device.c + +$O/host-stdio.o: $S/os/host-stdio.c $(INCS) $I/reb-host.h $I/host-lib.h + $C $(CFLAGS) -o $O/host-stdio.o $S/os/host-stdio.c + +$O/dev-net.o: $S/os/dev-net.c $(INCS) $I/reb-host.h $I/host-lib.h $I/sys-net.h + $C $(CFLAGS) -o $O/dev-net.o $S/os/dev-net.c + +$O/dev-dns.o: $S/os/dev-dns.c $(INCS) $I/reb-host.h $I/host-lib.h $I/sys-net.h + $C $(CFLAGS) -o $O/dev-dns.o $S/os/dev-dns.c + +$O/host-init.o: $S/os/host-init.c $(INCS) + $C $(CFLAGS) -o $O/host-init.o $S/os/host-init.c + +$O/host-lib.o: $S/os/win32/host-lib.c $(INCS) $I/reb-host.h $I/host-lib.h + $C $(CFLAGS) -o $O/host-lib.o $S/os/win32/host-lib.c + +$O/dev-stdio.o: $S/os/win32/dev-stdio.c $(INCS) $I/reb-host.h $I/host-lib.h + $C $(CFLAGS) -o $O/dev-stdio.o $S/os/win32/dev-stdio.c + +$O/dev-event.o: $S/os/win32/dev-event.c $(INCS) $I/reb-host.h $I/host-lib.h + $C $(CFLAGS) -o $O/dev-event.o $S/os/win32/dev-event.c + +$O/dev-file.o: $S/os/win32/dev-file.c $(INCS) $I/reb-host.h $I/host-lib.h + $C $(CFLAGS) -o $O/dev-file.o $S/os/win32/dev-file.c + +$O/dev-clipboard.o: $S/os/win32/dev-clipboard.c $(INCS) $I/reb-host.h $I/host-lib.h $I/sys-net.h + $C $(CFLAGS) -o $O/dev-clipboard.o $S/os/win32/dev-clipboard.c + diff --git a/r3lib.dll b/r3lib.dll new file mode 100644 index 0000000..8c29f79 Binary files /dev/null and b/r3lib.dll differ diff --git a/r3lib.exp b/r3lib.exp new file mode 100644 index 0000000..56b7fd9 Binary files /dev/null and b/r3lib.exp differ diff --git a/r3lib.lib b/r3lib.lib new file mode 100644 index 0000000..b257634 Binary files /dev/null and b/r3lib.lib differ diff --git a/src/include/host-init.h b/src/include/host-init.h new file mode 100644 index 0000000..03ca649 --- /dev/null +++ b/src/include/host-init.h @@ -0,0 +1,17 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Host custom init header +** Build: A96 +** Date: 2-Dec-2009/15:37:15-8:00 +** File: host-init.h +** +***********************************************************************/ + +#define REB_INIT_SIZE 6532 +extern REBYTE Reb_Init_Code[REB_INIT_SIZE]; diff --git a/src/include/host-lib.h b/src/include/host-lib.h new file mode 100644 index 0000000..1db8745 --- /dev/null +++ b/src/include/host-lib.h @@ -0,0 +1,171 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Host Access Library +** Build: A96 +** Date: 2-Dec-2009/15:37:16-8:00 +** File: host-lib.h +** +** AUTO-GENERATED FILE - Do not modify. (From: make-os-ext.r) +** +***********************************************************************/ + +#define HOST_LIB_VER 96 +#define HOST_LIB_SUM 5782 +#define HOST_LIB_SIZE 30 + + +typedef struct REBOL_Host_Lib { + int size; + unsigned int ver_sum; + void *(*os_make)(size_t size); + void (*os_free)(void *mem); + void (*os_exit)(int code); + void (*os_crash)(const REBYTE *title, const REBYTE *content); + REBCHR *(*os_form_error)(int errnum, REBCHR *str, int len); + BOOL (*os_get_boot_path)(REBCHR *name); + REBCHR *(*os_get_locale)(int what); + REBCHR *(*os_get_env)(REBCHR *var, int mode); + int (*os_set_env)(REBCHR *expr, int mode); + REBCHR *(*os_list_env)(void); + void (*os_get_time)(REBOL_DAT *dat); + i64 (*os_delta_time)(i64 base, int flags); + int (*os_get_current_dir)(REBCHR **path); + BOOL (*os_set_current_dir)(REBCHR *path); + void (*os_file_time)(REBREQ *file, REBOL_DAT *dat); + void *(*os_open_library)(REBCHR *path, REBCNT *error); + void (*os_close_library)(void *dll); + void *(*os_find_function)(void *dll, char* funcname); + REBINT (*os_create_thread)(CFUNC init, void *arg, REBCNT stack_size); + void (*os_delete_thread)(void); + void (*os_task_ready)(REBINT tid); + int (*os_create_process)(REBCHR *call, int reserved); + int (*os_browse)(REBCHR *url, int reserved); + BOOL (*os_request_file)(REBRFR *fr); + int (*os_call_device)(REBINT device, REBCNT command); + int (*os_do_device)(REBREQ *req, REBCNT command); + REBREQ *(*os_make_devreq)(int device); + int (*os_abort_device)(REBREQ *req); + int (*os_poll_devices)(void); + REBINT (*os_wait)(REBCNT millisec, REBCNT res); +} REBOL_HOST_LIB; + +//** Included by HOST ********************************************* + +#ifndef AS_LIB + +extern void *OS_Make(size_t size); // host-lib.c +extern void OS_Free(void *mem); // host-lib.c +extern void OS_Exit(int code); // host-lib.c +extern void OS_Crash(const REBYTE *title, const REBYTE *content); // host-lib.c +extern REBCHR *OS_Form_Error(int errnum, REBCHR *str, int len); // host-lib.c +extern BOOL OS_Get_Boot_Path(REBCHR *name); // host-lib.c +extern REBCHR *OS_Get_Locale(int what); // host-lib.c +extern REBCHR *OS_Get_Env(REBCHR *var, int mode); // host-lib.c +extern int OS_Set_Env(REBCHR *expr, int mode); // host-lib.c +extern REBCHR *OS_List_Env(void); // host-lib.c +extern void OS_Get_Time(REBOL_DAT *dat); // host-lib.c +extern i64 OS_Delta_Time(i64 base, int flags); // host-lib.c +extern int OS_Get_Current_Dir(REBCHR **path); // host-lib.c +extern BOOL OS_Set_Current_Dir(REBCHR *path); // host-lib.c +extern void OS_File_Time(REBREQ *file, REBOL_DAT *dat); // host-lib.c +extern void *OS_Open_Library(REBCHR *path, REBCNT *error); // host-lib.c +extern void OS_Close_Library(void *dll); // host-lib.c +extern void *OS_Find_Function(void *dll, char* funcname); // host-lib.c +extern REBINT OS_Create_Thread(CFUNC init, void *arg, REBCNT stack_size); // host-lib.c +extern void OS_Delete_Thread(void); // host-lib.c +extern void OS_Task_Ready(REBINT tid); // host-lib.c +extern int OS_Create_Process(REBCHR *call, int reserved); // host-lib.c +extern int OS_Browse(REBCHR *url, int reserved); // host-lib.c +extern BOOL OS_Request_File(REBRFR *fr); // host-lib.c +extern int OS_Call_Device(REBINT device, REBCNT command); // ../host-device.c +extern int OS_Do_Device(REBREQ *req, REBCNT command); // ../host-device.c +extern REBREQ *OS_Make_Devreq(int device); // ../host-device.c +extern int OS_Abort_Device(REBREQ *req); // ../host-device.c +extern int OS_Poll_Devices(void); // ../host-device.c +extern REBINT OS_Wait(REBCNT millisec, REBCNT res); // ../host-device.c + +#ifdef OS_LIB_TABLE + +REBOL_HOST_LIB *Host_Lib; + +REBOL_HOST_LIB Host_Lib_Init = { // Host library function vector table. + HOST_LIB_SIZE, + (HOST_LIB_VER << 16) + HOST_LIB_SUM, + OS_Make, + OS_Free, + OS_Exit, + OS_Crash, + OS_Form_Error, + OS_Get_Boot_Path, + OS_Get_Locale, + OS_Get_Env, + OS_Set_Env, + OS_List_Env, + OS_Get_Time, + OS_Delta_Time, + OS_Get_Current_Dir, + OS_Set_Current_Dir, + OS_File_Time, + OS_Open_Library, + OS_Close_Library, + OS_Find_Function, + OS_Create_Thread, + OS_Delete_Thread, + OS_Task_Ready, + OS_Create_Process, + OS_Browse, + OS_Request_File, + OS_Call_Device, + OS_Do_Device, + OS_Make_Devreq, + OS_Abort_Device, + OS_Poll_Devices, + OS_Wait, +}; + +#endif //OS_LIB_TABLE + +#else //AS_LIB + +//** Included by REBOL ******************************************** + +extern REBOL_HOST_LIB *Host_Lib; + +#define OS_MAKE(a) Host_Lib->os_make(a) +#define OS_FREE(a) Host_Lib->os_free(a) +#define OS_EXIT(a) Host_Lib->os_exit(a) +#define OS_CRASH(a,b) Host_Lib->os_crash(a,b) +#define OS_FORM_ERROR(a,b,c) Host_Lib->os_form_error(a,b,c) +#define OS_GET_BOOT_PATH(a) Host_Lib->os_get_boot_path(a) +#define OS_GET_LOCALE(a) Host_Lib->os_get_locale(a) +#define OS_GET_ENV(a,b) Host_Lib->os_get_env(a,b) +#define OS_SET_ENV(a,b) Host_Lib->os_set_env(a,b) +#define OS_LIST_ENV() Host_Lib->os_list_env() +#define OS_GET_TIME(a) Host_Lib->os_get_time(a) +#define OS_DELTA_TIME(a,b) Host_Lib->os_delta_time(a,b) +#define OS_GET_CURRENT_DIR(a) Host_Lib->os_get_current_dir(a) +#define OS_SET_CURRENT_DIR(a) Host_Lib->os_set_current_dir(a) +#define OS_FILE_TIME(a,b) Host_Lib->os_file_time(a,b) +#define OS_OPEN_LIBRARY(a,b) Host_Lib->os_open_library(a,b) +#define OS_CLOSE_LIBRARY(a) Host_Lib->os_close_library(a) +#define OS_FIND_FUNCTION(a,b) Host_Lib->os_find_function(a,b) +#define OS_CREATE_THREAD(a,b,c) Host_Lib->os_create_thread(a,b,c) +#define OS_DELETE_THREAD() Host_Lib->os_delete_thread() +#define OS_TASK_READY(a) Host_Lib->os_task_ready(a) +#define OS_CREATE_PROCESS(a,b) Host_Lib->os_create_process(a,b) +#define OS_BROWSE(a,b) Host_Lib->os_browse(a,b) +#define OS_REQUEST_FILE(a) Host_Lib->os_request_file(a) +#define OS_CALL_DEVICE(a,b) Host_Lib->os_call_device(a,b) +#define OS_DO_DEVICE(a,b) Host_Lib->os_do_device(a,b) +#define OS_MAKE_DEVREQ(a) Host_Lib->os_make_devreq(a) +#define OS_ABORT_DEVICE(a) Host_Lib->os_abort_device(a) +#define OS_POLL_DEVICES() Host_Lib->os_poll_devices() +#define OS_WAIT(a,b) Host_Lib->os_wait(a,b) + +#endif //AS_LIB diff --git a/src/include/reb-args.h b/src/include/reb-args.h new file mode 100644 index 0000000..3e82683 --- /dev/null +++ b/src/include/reb-args.h @@ -0,0 +1,79 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Program startup arguments +** Date: 1-Dec-2009 +** File: reb-args.h +** +************************************************************************ +** +** Note: arg struct is used by R3 lib, so must not be modified. +** +***********************************************************************/ + +// REBOL startup option structure: +typedef struct rebol_args { + REBCNT options; + REBCHR *script; + REBCHR *args; + REBCHR *do_arg; + REBCHR *version; + REBCHR *debug; + REBCHR *import; + REBCHR *secure; + REBCHR *exe_path; + REBCHR *home_dir; +} REBARGS; + +// REBOL arg option flags: +// Must stay matched to system/catalog/boot-flags. +enum arg_opts { + ROF_EXT, + + ROF_SCRIPT, + ROF_ARGS, + ROF_DO, + ROF_IMPORT, + ROF_VERSION, + ROF_DEBUG, + ROF_SECURE, + + ROF_HELP, + ROF_VERS, + ROF_QUIET, + ROF_VERBOSE, + ROF_NO_BOOT, + ROF_SECURE_MIN, + ROF_SECURE_MAX, + ROF_TRACE, + ROF_HALT, + ROF_CGI, + ROF_IGNORE, +}; + +#define RO_EXT (1< (b)) ? (a) : (b)) +#endif + +// Memory related functions: +#define MAKE_MEM(n) malloc(n) +#define MAKE_NEW(s) malloc(sizeof(s)) +#define FREE_MEM(m) free(m) +#define CLEAR(m, s) memset((void*)(m), 0, s); +#define CLEARS(m) memset((void*)(m), 0, sizeof(*m)); +#define COPY_MEM(t,f,l) memcpy((void*)(t), (void*)(f), l) +#define MOVE_MEM(t,f,l) memmove((void*)(t), (void*)(f), l) + +// Byte string functions: +#define COPY_BYTES(t,f,l) strncpy((char*)t, (char*)f, l) +#define APPEND_BYTES(t,f,l) strncat((char*)t, (char*)f, l) +#define LEN_BYTES(s) strlen((char*)s) +#define CMP_BYTES(s,t) strcmp((char*)s, (char*)t) +#define BYTES(s) (REBYTE*)(s) + +// OS has wide char string interfaces: +#ifdef OS_WIDE_CHAR +#define OS_WIDE TRUE +#define TXT(s) (L##s) +#define COPY_STR(t,f,l) wcsncpy(t, f, l) +#define JOIN_STR(d,s,l) wcsncat(d,s,l) +#define FIND_STR(d,s) wcsstr(d,s) +#define FIND_CHR(d,s) wcschr(d,s) +#define LEN_STR(s) wcslen(s) +#define TO_OS_STR(s1,s2,l) mbstowcs(s1,s2,l) +#define FROM_OS_STR(s1,s2,l) wcstombs(s1,s2,l) +#else +// OS has UTF-8 byte string interfaces: +#define OS_WIDE FALSE +#define TXT(s) (s) +#define COPY_STR(t,f,l) strncpy(t, f, l) +#define JOIN_STR(d,s,l) strncat(d,s,l) +#define FIND_STR(d,s) strstr(d,s) +#define FIND_CHR(d,s) strchr(d,s) +#define LEN_STR(s) strlen(s) +#define TO_OS_STR(s1,s2,l) strncpy(s1,s2,l) +#define FROM_OS_STR(s1,s2,l) strncpy(s1,s2,l) +#endif + +#define MAKE_STR(n) (REBCHR*)(malloc((n) * sizeof(REBCHR))) // OS chars! diff --git a/src/include/reb-config.h b/src/include/reb-config.h new file mode 100644 index 0000000..823f3f3 --- /dev/null +++ b/src/include/reb-config.h @@ -0,0 +1,133 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: General build configuration +** Date: 1-Dec-2009 +** File: reb-config.h +** +** Note: This is the first file included. +** +***********************************************************************/ + +#include "reb-to.h" // build target (#define TO_osname) + +//* Common ************************************************************* + +#define INT_64_MODE // 64 bit integer datatype +#define THREADED // enable threads + + +//* MS Windows 32 ****************************************************** + +#ifdef TO_WIN32 // Win32/Intel + +#define FULL_DEF // fully defined +#define WIN32_LEAN_AND_MEAN // trim down the Win32 headers +#define ENDIAN_LITTLE // uses little endian byte order +#define OS_WIDE_CHAR // OS uses WIDE_CHAR API +#define OS_CRLF TRUE // uses CRLF as line terminator +#define OS_DIR_SEP '\\' // file path separator (Thanks Bill.) +#define HAS_ASYNC_DNS // supports it +#define ATOI // supports it +#define ATOI64 // supports it +#define ITOA64 // supports it +#define HAS_ECVT // supports ecvt() +#define FINITE _finite // name used for it +#define INLINE __inline // name used for it + +#ifdef THREADED +#define THREAD __declspec(thread) +#endif + +// Used when we build REBOL as a DLL: +#define API_EXPORT __declspec(dllexport) +#define API_IMPORT __declspec(dllimport) + +// Use non-standard int64 declarations: +#if (defined(_MSC_VER) && (_MSC_VER <= 1200)) +#define ODD_INT_64 +#else +#define HAS_LONG_DOUBLE +#endif + +// Disable various warnings +#pragma warning(disable : 4201) // nameless unions +#pragma warning(disable : 4100) // unreferenced formal parameter +#pragma warning(disable : 4127) // conditional expression is constant +//#pragma warning(disable : 4057) +//#pragma warning(disable : 4701) + +#else + +//* Non Windows ******************************************************** + +#define MIN_OS // not all devices are working +#define NO_GRAPHICS // no graphics yet +#define FINITE finite +#define INLINE +#define API_EXPORT __attribute__((visibility("default"))) +#define API_IMPORT +#endif + +#ifdef TO_LINUX // Linux/Intel +#define ENDIAN_LITTLE +#define HAS_ECVT +#define HAS_LONG_DOUBLE +#endif + +#ifdef TO_LINUX_PPC // Linux/PPC +#define ENDIAN_BIG +#define HAS_ECVT +#define HAS_LONG_DOUBLE +#endif + +#ifdef TO_OSXI // OSX/Intel +#define ENDIAN_LITTLE +#define HAS_ECVT +#define HAS_LONG_DOUBLE +#endif + +#ifdef TO_OSX // OSX/PPC +#define ENDIAN_BIG +#define HAS_ECVT +#define HAS_LL_CONSTS +#define OLD_COMPILER +#endif + +#ifdef TO_OBSD // OpenBSD +#define COPY_STR(d,s,m) strlcpy(d,s,m) +#define JOIN_STR(d,s,m) strlcat(d,s,m) +#endif + +//* Defaults *********************************************************** + +#ifdef HAS_LONG_DOUBLE +#define HAS_LL_CONSTS +#endif + +#ifndef THREAD +#define THREAD +#endif + +#ifndef OS_DIR_SEP +#define OS_DIR_SEP '/' // rest of the world uses it +#endif + +#ifndef OS_CRLF +#define OS_CRLF FALSE +#endif + +#ifdef REBOL_ONLY +#define REBOL_API // REBOL stand-alone test version +#else +#ifdef REBOL_EXPORTS +#define REBOL_API API_EXPORT // for REBOL library build +#else +#define REBOL_API API_IMPORT // for host files when DLL used +#endif +#endif // REBOL_ONLY diff --git a/src/include/reb-defs.h b/src/include/reb-defs.h new file mode 100644 index 0000000..049aee1 --- /dev/null +++ b/src/include/reb-defs.h @@ -0,0 +1,48 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Miscellaneous structures and definitions +** Date: 1-Dec-2009 +** File: reb_defs.h +** +** Note: This file is used by internal and external C code. It +** should not depend on many other header files prior to it. +** +***********************************************************************/ + +#ifndef FULL_DEFS +typedef void REBSER; +typedef void REBGOB; +#endif + +// X/Y coordinate pair: +typedef struct rebol_pair { + int x; + int y; +} REBPAR; + +// Standard date and time: +typedef struct rebol_dat { + int year; + int month; + int day; + int time; + int nano; + int zone; +} REBOL_DAT; // not same as REBDAT + +// OS metrics: +typedef struct rebol_met { + int len; // # entries in this table + REBPAR screen_size; + REBPAR title_size; + REBPAR border_size; + REBPAR border_fixed; + REBPAR work_origin; + REBPAR work_size; +} REBOL_OS_METRICS; diff --git a/src/include/reb-device.h b/src/include/reb-device.h new file mode 100644 index 0000000..7a2f588 --- /dev/null +++ b/src/include/reb-device.h @@ -0,0 +1,172 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: External REBOL Devices (OS Independent) +** Date: 1-Dec-2009 +** File: host-device.h +** +** Critical: all struct alignment must be 4 bytes (see compile options) +** +***********************************************************************/ + +// REBOL Device Identifiers: +// Critical: Must be in same order as Device table in host-device.c +enum { + RDI_SYSTEM, + RDI_STDIO, + RDI_CONSOLE, + RDI_FILE, + RDI_EVENT, + RDI_NET, + RDI_DNS, + RDI_CLIPBOARD, + RDI_MAX, + RDI_LIMIT = 32 +}; + + +// REBOL Device Commands: +enum { + RDC_INIT, // init device driver resources + RDC_QUIT, // cleanup device driver resources + + RDC_OPEN, // open device unit (port) + RDC_CLOSE, // close device unit + + RDC_READ, // read from unit + RDC_WRITE, // write to unit + + RDC_POLL, // check for activity + RDC_CONNECT, // connect (in or out) + + RDC_QUERY, // query unit info + RDC_MODIFY, // set modes (also get modes) + + RDC_CREATE, // create unit target + RDC_DELETE, // delete unit target + RDC_RENAME, + RDC_LOOKUP, + RDC_MAX, + + RDC_CUSTOM=32 // start of custom commands +}; + +// Device Request (Command) Return Codes: +#define DR_PEND 1 // request is still pending +#define DR_DONE 0 // request is complete w/o errors +#define DR_ERROR -1 // request had an error + +// REBOL Device Flags and Options (bitnums): +enum { + // Status flags: + RDF_INIT, // Device is initialized + RDF_OPEN, // Global open (for devs that cannot multi-open) + // Options: + RDO_MUST_INIT = 16, // Do not allow auto init (manual init required) + RDO_AUTO_POLL, // Poll device, even if no requests (e.g. interrupts) +}; + +// REBOL Request Flags (bitnums): +enum { + RRF_OPEN, // Port is open + RRF_DONE, // Request is done (used when extern proc changes it) + RRF_FLUSH, // Flush WRITE +// RRF_PREWAKE, // C-callback before awake happens (to update port object) + RRF_PENDING, // Request is attached to pending list + RRF_ALLOC, // Request is allocated, not a temp on stack + RRF_WIDE, // Wide char IO +}; + +// REBOL Device Errors: +enum { + RDE_NONE, + RDE_NO_DEVICE, // command did not provide device + RDE_NO_COMMAND, // command past end + RDE_NO_INIT, // device has not been inited +}; + +enum { + RDM_NULL, // Null device +}; + +// Forward references: +typedef struct rebol_device REBDEV; +typedef struct rebol_devreq REBREQ; + +// Commands: +typedef int (*DEV_CMD)(REBREQ *req); +#define DEVICE_CMD static int // Used to define + +// Device structure: +struct rebol_device { + char *title; // title of device + u32 version; // version, revision, release + u32 date; // year, month, day, hour + DEV_CMD *commands; // command dispatch table + u32 max_command; // keep commands in bounds + REBREQ *pending; // pending requests + u32 flags; // state: open, signal + i32 req_size; // size of request struct +}; + +// Inializer (keep ordered same as above) +#define DEFINE_DEV(w,t,v,c,m,s) REBDEV w = {t, v, 0, c, m, 0, 0, s} + +// Request structure: // Allowed to be extended by some devices +struct rebol_devreq { + u32 clen; // size of extended structure + + // Linkages: + u32 device; // device id (dev table) + REBREQ *next; // linked list (pending or done lists) + void *port; // link back to REBOL port object + union { + void *handle; // OS object + int socket; // OS identifier + int id; + }; + + // Command info: + i32 command; // command code + u32 error; // error code + u32 modes; // special modes, types or attributes + u16 flags; // request flags + u16 state; // device process flags + i32 timeout; // request timeout +// int (*prewake)(void *); // callback before awake + + // Common fields: + union { + REBYTE *data; // data to transfer + REBREQ *sock; // temp link to related socket + }; + u32 length; // length to transfer + u32 actual; // length actually transferred + + // Special fields for common IO uses: + union { + struct { + REBCHR *path; // file string (in OS local format) + i64 size; // file size + i64 index; // file index position + I64 time; // file modification time (struct) + } file; + struct { + u32 local_ip; // local address used + u32 local_port; // local port used + u32 remote_ip; // remote address + u32 remote_port; // remote port + void *host_info; // for DNS usage + } net; + }; +}; + +// Simple macros for common OPEN? test (for some but not all ports): +#define SET_OPEN(r) SET_FLAG(((REBREQ*)(r))->flags, RRF_OPEN) +#define SET_CLOSED(r) CLR_FLAG(((REBREQ*)(r))->flags, RRF_OPEN) +#define IS_OPEN(r) GET_FLAG(((REBREQ*)(r))->flags, RRF_OPEN) diff --git a/src/include/reb-event.h b/src/include/reb-event.h new file mode 100644 index 0000000..e4c9b54 --- /dev/null +++ b/src/include/reb-event.h @@ -0,0 +1,42 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: REBOL event definitions +** Date: 1-Dec-2009 +** File: reb-event.h +** +***********************************************************************/ + +// Note: size must be 12 bytes! + +typedef struct rebol_event { + u8 type; // event id (mouse-move, mouse-button, etc) + u8 flags; // special flags + u8 win; // window id + u8 info; // other info + u32 data; // an x/y position or keycode (raw/decoded) + union { + REBREQ *req; // request (for device events) + void *ser; // port or object + }; +} REBEVT; + +// Special event flags: + +enum { + EVF_NO_REQ, // request field not used (e.g. GUI) + EVF_DOUBLE, // double click detected + EVF_CONTROL, + EVF_SHIFT, + EVF_IS_PORT, + EVF_IS_OBJ, + EVF_COPIED, // event data has been copied +}; + +// Special messages +#define WM_DNS (WM_USER+100) diff --git a/src/include/reb-evtypes.h b/src/include/reb-evtypes.h new file mode 100644 index 0000000..08f0340 --- /dev/null +++ b/src/include/reb-evtypes.h @@ -0,0 +1,86 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Event Types +** Build: A96 +** Date: 2-Dec-2009/15:37:16-8:00 +** File: reb-evtypes.h +** +** AUTO-GENERATED FILE - Do not modify. (From: make-boot.r) +** +***********************************************************************/ + +enum event_types { + EVT_IGNORE, + EVT_INTERRUPT, + EVT_DEVICE, + EVT_CUSTOM, + EVT_ERROR, + EVT_INIT, + EVT_OPEN, + EVT_CLOSE, + EVT_CONNECT, + EVT_ACCEPT, + EVT_READ, + EVT_WRITE, + EVT_WROTE, + EVT_LOOKUP, + EVT_READY, + EVT_DONE, + EVT_TIME, + EVT_SHOW, + EVT_HIDE, + EVT_OFFSET, + EVT_RESIZE, + EVT_ACTIVE, + EVT_INACTIVE, + EVT_MINIMIZE, + EVT_MAXIMIZE, + EVT_RESTORE, + EVT_MOVE, + EVT_DOWN, + EVT_UP, + EVT_ALT_DOWN, + EVT_ALT_UP, + EVT_AUX_DOWN, + EVT_AUX_UP, + EVT_KEY, + EVT_KEY_UP, + EVT_SCROLL_LINE, + EVT_SCROLL_PAGE, + EVT_DROP_FILE, + EVT_MAX +}; + +enum event_keys { + EVK_NONE, + EVK_PAGE_UP, + EVK_PAGE_DOWN, + EVK_END, + EVK_HOME, + EVK_LEFT, + EVK_UP, + EVK_RIGHT, + EVK_DOWN, + EVK_INSERT, + EVK_DELETE, + EVK_F1, + EVK_F2, + EVK_F3, + EVK_F4, + EVK_F5, + EVK_F6, + EVK_F7, + EVK_F8, + EVK_F9, + EVK_F10, + EVK_F11, + EVK_F12, + EVK_MAX +}; + diff --git a/src/include/reb-file.h b/src/include/reb-file.h new file mode 100644 index 0000000..6f4da91 --- /dev/null +++ b/src/include/reb-file.h @@ -0,0 +1,42 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Special file device definitions +** Date: 1-Dec-2009 +** File: reb-file.h +** +***********************************************************************/ + +// RFM - REBOL File Modes +enum { + RFM_READ = 0, + RFM_WRITE, + RFM_APPEND, + RFM_SEEK, + RFM_NEW, + RFM_READONLY, + RFM_TRUNCATE, + RFM_RESEEK, // file index has moved, reseek + RFM_NAME_MEM, // converted name allocated in mem + RFM_DIR = 16, +}; + +// RFE - REBOL File Error +enum { + RFE_BAD_PATH = 1, + RFE_NO_MODES, // No file modes specified + RFE_OPEN_FAIL, // File open failed + RFE_BAD_SEEK, // Seek not supported for this file + RFE_NO_HANDLE, // File struct has no handle + RFE_NO_SEEK, // Seek action failed + RFE_BAD_READ, // Read failed (general) + RFE_BAD_WRITE, // Write failed (general) + RFE_DISK_FULL, // No space on target volume +}; + +#define MAX_FILE_NAME 1022 diff --git a/src/include/reb-filereq.h b/src/include/reb-filereq.h new file mode 100644 index 0000000..a239ca9 --- /dev/null +++ b/src/include/reb-filereq.h @@ -0,0 +1,33 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: File requestor definitions +** Date: 1-Dec-2009 +** File: reb-filereq.h +** +***********************************************************************/ + +#define MAX_FILE_REQ_BUF (16*1024) + +typedef struct Reb_File_Requestor { + REBCNT flags; // multi, load/save, unicode + REBCHR *title; // title of requestor + REBCHR *button; // button name + REBCHR *dir; // dir path + REBCHR *files; // buffer to hold results + REBCHR *filter; // buffer to hold results + REBINT len; // length of buffer +} REBRFR; + +// File Request Flags: +enum { + FRF_MULTI, + FRF_SAVE, + FRF_KEEP, +}; + diff --git a/src/include/reb-host.h b/src/include/reb-host.h new file mode 100644 index 0000000..52df722 --- /dev/null +++ b/src/include/reb-host.h @@ -0,0 +1,27 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Include files for hosting +** Date: 1-Dec-2009 +** File: reb-host.h +** +***********************************************************************/ + +#include "reb-config.h" + +#include "reb-c.h" +#include "reb-defs.h" +#include "reb-args.h" +#include "reb-device.h" +#include "reb-file.h" +#include "reb-event.h" +#include "reb-evtypes.h" +#include "reb-net.h" +#include "reb-filereq.h" + + diff --git a/src/include/reb-net.h b/src/include/reb-net.h new file mode 100644 index 0000000..6755858 --- /dev/null +++ b/src/include/reb-net.h @@ -0,0 +1,34 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Network device definitions +** Date: 1-Dec-2009 +** File: reb-net.h +** +***********************************************************************/ + +// REBOL Socket types: +enum socket_types { + RST_UDP, // TCP or UDP + RST_LISTEN = 8, // LISTEN + RST_REVERSE, // DNS reverse +}; + +// REBOL Socket Modes (state flags) +enum { + RSM_OPEN = 0, // socket is allocated + RSM_ATTEMPT, // attempting connection + RSM_CONNECT, // connection is open + RSM_BIND, // socket is bound to port + RSM_LISTEN, // socket is listening (TCP) + RSM_SEND, // sending + RSM_RECEIVE, // receiving + RSM_ACCEPT, // an inbound connection +}; + +#define IPA(a,b,c,d) (a<<24 | b<<16 | c<<8 | d) diff --git a/src/include/reb-to.h b/src/include/reb-to.h new file mode 100644 index 0000000..a3ad08c --- /dev/null +++ b/src/include/reb-to.h @@ -0,0 +1 @@ +#define TO_WIN32 diff --git a/src/include/rebol-lib.h b/src/include/rebol-lib.h new file mode 100644 index 0000000..6531533 --- /dev/null +++ b/src/include/rebol-lib.h @@ -0,0 +1,27 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: REBOL Interface Library +** Build: A96 +** Date: 2-Dec-2009/15:36:34-8:00 +** File: rebol-lib.h +** +** AUTO-GENERATED FILE - Do not modify. (From: make-headers.r) +** +***********************************************************************/ + +extern REBOL_API void Reb_Version(REBYTE vers[]); // a-lib.c +extern REBOL_API REBINT Reb_Init(REBARGS *rargs, REBOL_HOST_LIB *lib); // a-lib.c +extern REBOL_API REBINT Reb_Start(REBINT reserved); // a-lib.c +extern REBOL_API void Reb_Reset(); // a-lib.c +extern REBOL_API void Reb_Escape(REBINT reserved); // a-lib.c +extern REBOL_API REBFLG Reb_Do_String(REBYTE *text); // a-lib.c +extern REBOL_API REBINT Reb_Do_Binary(REBYTE *bin, REBINT length, REBCNT flags, REBCNT key); // a-lib.c +extern REBOL_API void Reb_Print(REBYTE *fmt, ...); // a-lib.c +extern REBOL_API void Reb_Print_TOS(REBCNT flags, REBYTE *marker); // a-lib.c +extern REBOL_API REBINT Reb_Event(REBEVT *evt); // a-lib.c diff --git a/src/include/sys-net.h b/src/include/sys-net.h new file mode 100644 index 0000000..f49e03d --- /dev/null +++ b/src/include/sys-net.h @@ -0,0 +1,74 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: System network definitions +** Date: 1-Dec-2009 +** File: sys-net.h +** +***********************************************************************/ + +//----- Windows - "Network standards? What network standards?" -Bill G. +#ifdef TO_WIN32 + +#include + +#define GET_ERROR WSAGetLastError() +#define IOCTL ioctlsocket +#define CLOSE_SOCKET closesocket + +#define NE_ISCONN WSAEISCONN +#define NE_WOULDBLOCK WSAEWOULDBLOCK +#define NE_INPROGRESS WSAEINPROGRESS +#define NE_ALREADY WSAEALREADY +#define NE_NOTCONN WSAENOTCONN +#define NE_INVALID WSAEINVAL + +//----- BSD - The network standard the rest of the world uses +#else + +#include +#include +#include +#include +#include + +#define GET_ERROR errno +#define IOCTL ioctl +#define CLOSE_SOCKET close +#define SOCKET unsigned int + +#define NE_ISCONN EISCONN +#define NE_WOULDBLOCK EAGAIN // see include/asm/errno.h +#define NE_INPROGRESS EINPROGRESS +#define NE_ALREADY EALREADY +#define NE_NOTCONN ENOTCONN +#define NE_INVALID EINVAL + +// Null Win32 functions: +#define WSADATA int + +// FreeBSD mystery define: +#ifndef u_int32_t +#define u_int32_t long +#endif + +#ifndef HOSTENT +typedef struct hostent HOSTENT; +#endif + +#ifndef MAXGETHOSTSTRUCT +#define MAXGETHOSTSTRUCT ((sizeof(struct hostent)+15) & ~15) +#endif + +#endif // BSD + +typedef struct sockaddr_in SOCKAI; // Internet extensions + +#define BAD_SOCKET (~0) +#define MAX_TRANSFER 32000 // Max send/recv buffer size +#define MAX_HOST_NAME 256 // Max length of host name diff --git a/src/os/dev-dns.c b/src/os/dev-dns.c new file mode 100644 index 0000000..2f2afd1 --- /dev/null +++ b/src/os/dev-dns.c @@ -0,0 +1,205 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: DNS access +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-dns.c +** Author: Carl Sassenrath +** Purpose: Calls local DNS services for domain name lookup. +** +** Notes: +** See MS WSAAsyncGetHost* details regarding multiple requests. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include + +#include "reb-host.h" +#include "host-lib.h" +#include "sys-net.h" + +int Init_Net(REBREQ *); // Share same init +int Quit_Net(REBREQ *); + +void Signal_Device(REBREQ *req, REBINT type); + +#ifdef HAS_ASYNC_DNS +// Async DNS requires a window handle to signal completion (WSAASync) +extern HWND Event_Handle; +#endif + +/*********************************************************************** +** +*/ DEVICE_CMD Open_DNS(REBREQ *sock) +/* +***********************************************************************/ +{ + SET_OPEN(sock); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Close_DNS(REBREQ *sock) +/* +** Note: valid even if not open. +** +***********************************************************************/ +{ + // Terminate a pending request: +#ifdef HAS_ASYNC_DNS + if (GET_FLAG(sock->flags, RRF_PENDING)) { + CLR_FLAG(sock->flags, RRF_PENDING); + if (sock->handle) WSACancelAsyncRequest(sock->handle); + } +#endif + if (sock->net.host_info) OS_Free(sock->net.host_info); + sock->net.host_info = 0; + sock->handle = 0; + SET_CLOSED(sock); + return DR_DONE; // Removes it from device's pending list (if needed) +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Read_DNS(REBREQ *sock) +/* +** Initiate the GetHost request and return immediately. +** Note the temporary results buffer (must be freed later). +** +***********************************************************************/ +{ + void *host; +#ifdef HAS_ASYNC_DNS + HANDLE handle; +#else + HOSTENT *he; +#endif + + host = OS_Make(MAXGETHOSTSTRUCT); // be sure to free it + +#ifdef HAS_ASYNC_DNS + if (!GET_FLAG(sock->modes, RST_REVERSE)) // hostname lookup + handle = WSAAsyncGetHostByName(Event_Handle, WM_DNS, sock->data, host, MAXGETHOSTSTRUCT); + else + handle = WSAAsyncGetHostByAddr(Event_Handle, WM_DNS, (char*)&(sock->net.remote_ip), 4, AF_INET, host, MAXGETHOSTSTRUCT); + + if (handle != 0) { + sock->net.host_info = host; + sock->handle = handle; + return DR_PEND; // keep it on pending list + } +#else + // Use old-style blocking DNS (mainly for testing purposes): + if (GET_FLAG(sock->modes, RST_REVERSE)) { + he = gethostbyaddr((char*)&sock->net.remote_ip, 4, AF_INET); + if (he) { + sock->net.host_info = host; //??? + sock->data = he->h_name; + SET_FLAG(sock->flags, RRF_DONE); + return DR_DONE; + } + } + else { + he = gethostbyname(sock->data); + if (he) { + sock->net.host_info = host; // ?? who deallocs? + COPY_MEM((char*)&(sock->net.remote_ip), (char *)(*he->h_addr_list), 4); //he->h_length); + SET_FLAG(sock->flags, RRF_DONE); + return DR_DONE; + } + } +#endif + + OS_Free(host); + sock->net.host_info = 0; + + sock->error = GET_ERROR; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; // Remove it from pending list +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Poll_DNS(REBREQ *dr) +/* +** Check for completed DNS requests. These are marked with +** RRF_DONE by the windows message event handler (dev-event.c). +** Completed requests are removed from the pending queue and +** event is signalled (for awake dispatch). +** +***********************************************************************/ +{ + REBDEV *dev = (REBDEV*)dr; // to keep compiler happy + REBREQ **prior = &dev->pending; + REBREQ *req; + BOOL change = FALSE; + HOSTENT *host; + + // Scan the pending request list: + for (req = *prior; req; req = *prior) { + + // If done or error, remove command from list: + if (GET_FLAG(req->flags, RRF_DONE)) { // req->error may be set + *prior = req->next; + req->next = 0; + CLR_FLAG(req->flags, RRF_PENDING); + + if (!req->error) { // success! + host = (HOSTENT*)req->net.host_info; + if (GET_FLAG(req->modes, RST_REVERSE)) + req->data = host->h_name; + else + COPY_MEM((char*)&(req->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); + Signal_Device(req, EVT_READ); + } + else + Signal_Device(req, EVT_ERROR); + change = TRUE; + } + else prior = &req->next; + } + + return change; +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = +{ + Init_Net, // Shared init - called only once + Quit_Net, // Shared + Open_DNS, + Close_DNS, + Read_DNS, + 0, // write + Poll_DNS, +}; + +DEFINE_DEV(Dev_DNS, "DNS", 1, Dev_Cmds, RDC_MAX, 0); diff --git a/src/os/dev-net.c b/src/os/dev-net.c new file mode 100644 index 0000000..969ed44 --- /dev/null +++ b/src/os/dev-net.c @@ -0,0 +1,610 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: TCP/IP network access +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-net.c +** Author: Carl Sassenrath +** Purpose: Supports TCP and UDP (but not raw socket modes.) +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include + +#include "reb-host.h" +#include "host-lib.h" +#include "sys-net.h" + +#if (0) +#define WATCH1(s,a) printf(s, a) +#define WATCH2(s,a,b) printf(s, a, b) +#define WATCH4(s,a,b,c,d) printf(s, a, b, c, d) +#else +#define WATCH1(s,a) +#define WATCH2(s,a,b) +#define WATCH4(s,a,b,c,d) +#endif + +void Signal_Device(REBREQ *req, REBINT type); +DEVICE_CMD Listen_Socket(REBREQ *sock); + +#ifdef TO_WIN32 +extern HWND Event_Handle; // For WSAAsync API +#endif + + +/*********************************************************************** +** +** Local Functions +** +***********************************************************************/ + +static void Set_Addr(SOCKAI *sa, long ip, int port) +{ + // Set the IP address and port number in a socket_addr struct. + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = ip; //htonl(ip); NOTE: REBOL stays in network byte order + sa->sin_port = htons((unsigned short)port); +} + +static void Get_Local_IP(REBREQ *sock) +{ + // Get the local IP address and port number. + // This code should be fast and never fail. + SOCKAI sa; + int len = sizeof(sa); + + getsockname(sock->socket, (struct sockaddr *)&sa, &len); + sock->net.local_ip = sa.sin_addr.s_addr; //htonl(ip); NOTE: REBOL stays in network byte order + sock->net.local_port = ntohs(sa.sin_port); +} + +static BOOL Nonblocking_Mode(SOCKET sock) +{ + // Set non-blocking mode. Return TRUE if no error. +#ifdef FIONBIO + long mode = 1; + return !IOCTL(sock, FIONBIO, &mode); +#else + int flags; + flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + //else flags &= ~O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + return TRUE; +#endif +} + + +/*********************************************************************** +** +*/ int Init_Net(REBREQ *dr) +/* +** Intialize networking libraries and related interfaces. +** This function will be called prior to any socket functions. +** +***********************************************************************/ +{ + REBDEV *dev = (REBDEV*)dr; // just to keep compiler happy +#ifdef TO_WIN32 + WSADATA wsaData; + // Initialize Windows Socket API with given VERSION. + // It is ok to call twice, as long as WSACleanup twice. + if (WSAStartup(0x0101, &wsaData)) return DR_ERROR; +#endif + SET_FLAG(dev->flags, RDF_INIT); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ int Quit_Net(REBREQ *dr) +/* +** Close and cleanup networking libraries and related interfaces. +** +***********************************************************************/ +{ + REBDEV *dev = (REBDEV*)dr; // just to keep compiler happy +#ifdef TO_WIN32 + if (GET_FLAG(dev->flags, RDF_INIT)) WSACleanup(); +#endif + CLR_FLAG(dev->flags, RDF_INIT); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ int Host_Address(char *hostname, char *hostaddr) +/* +** Simple lookup of a host address. +** The hostaddr must be at least 16 bytes in size (IPv6). +** This is a synchronous function and blocks during access. +** +** On success, returns length of address. +** On failure, returns 0. +** +** Current version is IPv4 only. +** +***********************************************************************/ +{ + struct hostent *he; + + if (!(he = gethostbyname(hostname))) return DR_DONE; + + COPY_MEM(hostaddr, (char *)(*he->h_addr_list), he->h_length); + + return he->h_length; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Open_Socket(REBREQ *sock) +/* +** Setup a socket with the specified protocol and bind it to +** the related transport service. +** +** Returns 0 on success. +** On failure, error code is OS local. +** +** Note: This is an intialization procedure and no actual +** connection is made at this time. The IP address and port +** number are not needed, only the type of service required. +** +** After usage: +** Close_Socket() - to free OS allocations +** +***********************************************************************/ +{ + int type; + int protocol; + long result; + + sock->error = 0; + sock->state = 0; // clear all flags + + // Setup for correct type and protocol: + if (GET_FLAG(sock->modes, RST_UDP)) { + type = SOCK_DGRAM; + protocol = IPPROTO_UDP; + } + else { // TCP is default + type = SOCK_STREAM; + protocol = IPPROTO_TCP; + } + + // Bind to the transport service, return socket handle or error: + result = (int)socket(AF_INET, type, protocol); + + // Failed, get error code (os local): + if (result == BAD_SOCKET) { + sock->error = GET_ERROR; + return DR_ERROR; + } + + sock->socket = result; + SET_FLAG(sock->state, RSM_OPEN); + + // Set socket to non-blocking async mode: + if (!Nonblocking_Mode(sock->socket)) { + sock->error = GET_ERROR; + return DR_ERROR; + } + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Close_Socket(REBREQ *sock) +/* +** Close a socket. +** +** Returns 0 on success. +** On failure, error code is OS local. +** +***********************************************************************/ +{ + sock->error = 0; + + if (GET_FLAG(sock->state, RSM_OPEN)) { + + sock->state = 0; // clear: RSM_OPEN, RSM_CONNECT + + // If DNS pending, abort it: + if (sock->net.host_info) { // indicates DNS phase active +#ifdef HAS_ASYNC_DNS + if (sock->handle) WSACancelAsyncRequest(sock->handle); +#endif + OS_Free(sock->net.host_info); + sock->socket = sock->length; // Restore TCP socket (see Lookup) + } + + if (CLOSE_SOCKET(sock->socket)) { + sock->error = GET_ERROR; + return DR_ERROR; + } + } + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Lookup_Socket(REBREQ *sock) +/* +** Initiate the GetHost request and return immediately. +** This is very similar to the DNS device. +** The request will pend until the main event handler gets WM_DNS. +** Note the temporary results buffer (must be freed later). +** Note we use the sock->handle for the DNS handle. During use, +** we store the TCP socket in the length field. +** +***********************************************************************/ +{ +#ifdef TO_WIN32 + HANDLE handle; +#endif + HOSTENT *host; + +#ifdef HAS_ASYNC_DNS + // Check if we are polling for completion: + if (host = (HOSTENT*)(sock->net.host_info)) { + // The windows main event handler will change this when it gets WM_DNS event: + if (!GET_FLAG(sock->flags, RRF_DONE)) return DR_PEND; // still waiting + CLR_FLAG(sock->flags, RRF_DONE); + if (!sock->error) { // Success! + host = (HOSTENT*)sock->net.host_info; + COPY_MEM((char*)&(sock->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); + Signal_Device(sock, EVT_LOOKUP); + } + else + Signal_Device(sock, EVT_ERROR); + OS_Free(host); // free what we allocated earlier + sock->socket = sock->length; // Restore TCP socket saved below + sock->net.host_info = 0; + return DR_DONE; + } + + // Else, make the lookup request: + host = OS_Make(MAXGETHOSTSTRUCT); // be sure to free it + handle = WSAAsyncGetHostByName(Event_Handle, WM_DNS, sock->data, (char*)host, MAXGETHOSTSTRUCT); + if (handle != 0) { + sock->net.host_info = host; + sock->length = sock->socket; // save TCP socket temporarily + sock->handle = handle; + return DR_PEND; // keep it on pending list + } + OS_Free(host); +#else + // Use old-style blocking DNS (mainly for testing purposes): + host = gethostbyname(sock->data); + sock->net.host_info = 0; // no allocated data + + if (host) { + COPY_MEM((char*)&(sock->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); + CLR_FLAG(sock->flags, RRF_DONE); + Signal_Device(sock, EVT_LOOKUP); + return DR_DONE; + } +#endif + + sock->error = GET_ERROR; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; // Remove it from pending list +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Connect_Socket(REBREQ *sock) +/* +** Connect a socket to a service. +** Only required for connection-based protocols (e.g. not UDP). +** The IP address must already be resolved before calling. +** +** This function is asynchronous. It will return immediately. +** You can call this function again to check the pending connection. +** +** The function will return: +** =0: connection succeeded (or already is connected) +** >0: in-progress, still trying +** <0: error occurred, no longer trying +** +** Before usage: +** Open_Socket() -- to allocate the socket +** +***********************************************************************/ +{ + int result; + SOCKAI sa; + + if (GET_FLAG(sock->modes, RST_LISTEN)) + return Listen_Socket(sock); + + if (GET_FLAG(sock->state, RSM_CONNECT)) return DR_DONE; // already connected + + Set_Addr(&sa, sock->net.remote_ip, sock->net.remote_port); + result = connect(sock->socket, (struct sockaddr *)&sa, sizeof(sa)); + + if (result != 0) result = GET_ERROR; + + WATCH2("connect() error: %d - %s\n", result, strerror(result)); + + switch (result) { + + case 0: // no error + case NE_ISCONN: + // Connected, set state: + CLR_FLAG(sock->state, RSM_ATTEMPT); + SET_FLAG(sock->state, RSM_CONNECT); + Get_Local_IP(sock); + Signal_Device(sock, EVT_CONNECT); + return DR_DONE; // done + +#ifdef TO_WIN32 + case NE_INVALID: // Corrects for Microsoft bug +#endif + case NE_WOULDBLOCK: + case NE_INPROGRESS: + case NE_ALREADY: + // Still trying: + SET_FLAG(sock->state, RSM_ATTEMPT); + return DR_PEND; + + default: + // An error happened: + CLR_FLAG(sock->state, RSM_ATTEMPT); + sock->error = result; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; + } +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Transfer_Socket(REBREQ *sock) +/* +** Write or read a socket (for connection-based protocols). +** +** This function is asynchronous. It will return immediately. +** You can call this function again to check the pending connection. +** +** The mode is RSM_RECEIVE or RSM_SEND. +** +** The function will return: +** =0: succeeded +** >0: in-progress, still trying +** <0: error occurred, no longer trying +** +** Before usage: +** Open_Socket() +** Connect_Socket() +** Verify that RSM_CONNECT is true +** Setup the sock->data and sock->length +** +** Note that the mode flag is cleared by the caller, not here. +** +***********************************************************************/ +{ + int result; + long len; + int mode = (sock->command == RDC_READ ? RSM_RECEIVE : RSM_SEND); + + if (!GET_FLAG(sock->state, RSM_CONNECT)) { + sock->error = -18; + return DR_ERROR; + } + + SET_FLAG(sock->state, mode); + + // Limit size of transfer: + len = MIN(sock->length, MAX_TRANSFER); + + if (mode == RSM_SEND) { + // If host is no longer connected: + result = send(sock->socket, sock->data, len, 0); + WATCH2("send() len: %d actual: %d\n", len, result); + + if (result >= 0) { + sock->data += result; + sock->actual += result; + if (sock->actual >= sock->length) { + Signal_Device(sock, EVT_WROTE); + return DR_DONE; + } + return DR_PEND; + } + // if (result < 0) ... + } + else { + result = recv(sock->socket, sock->data, len, 0); + WATCH2("recv() len: %d result: %d\n", len, result); + + if (result > 0) { + sock->actual = result; + Signal_Device(sock, EVT_READ); + return DR_DONE; + } + if (result == 0) { // The socket gracefully closed. + sock->actual = 0; + CLR_FLAG(sock->state, RSM_CONNECT); // But, keep RRF_OPEN true + Signal_Device(sock, EVT_CLOSE); + return DR_DONE; + } + // if (result < 0) ... + } + + // Check error code: + result = GET_ERROR; + WATCH2("get error: %d %s\n", result, strerror(result)); + if (result == NE_WOULDBLOCK) return DR_PEND; // still waiting + + WATCH4("ERROR: recv(%d %x) len: %d error: %d\n", sock->socket, sock->data, len, result); + // A nasty error happened: + sock->error = result; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Listen_Socket(REBREQ *sock) +/* +** Setup a server (listening) socket (TCP or UDP). +** +** Before usage: +** Open_Socket(); +** Set local_port to desired port number. +** +** Use this instead of Connect_Socket(). +** +***********************************************************************/ +{ + int result; + int len = 1; + SOCKAI sa; + + // Setup socket address range and port: + Set_Addr(&sa, INADDR_ANY, sock->net.local_port); + + // Allow listen socket reuse: + result = setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (char*)(&len), sizeof(len)); + if (result) { +lserr: + sock->error = GET_ERROR; + return DR_ERROR; + } + + // Bind the socket to our local address: + result = bind(sock->socket, (struct sockaddr *)&sa, sizeof(sa)); + if (result) goto lserr; + + SET_FLAG(sock->state, RSM_BIND); + + // For TCP connections, setup listen queue: + if (!GET_FLAG(sock->modes, RST_UDP)) { + result = listen(sock->socket, SOMAXCONN); + if (result) goto lserr; + SET_FLAG(sock->state, RSM_LISTEN); + } + + Get_Local_IP(sock); + sock->command = RDC_CREATE; // the command done on wakeup + + return DR_PEND; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Accept_Socket(REBREQ *sock) +/* +** Accept an inbound connection on a TCP listen socket. +** +** The function will return: +** =0: succeeded +** >0: in-progress, still trying +** <0: error occurred, no longer trying +** +** Before usage: +** Open_Socket(); +** Set local_port to desired port number. +** Listen_Socket(); +** +***********************************************************************/ +{ + SOCKAI sa; + REBREQ *news; + int len = sizeof(sa); + int result; + extern void Attach_Request(REBREQ **prior, REBREQ *req); + + // Accept a new socket, if there is one: + result = accept(sock->socket, (struct sockaddr *)&sa, &len); + + if (result == BAD_SOCKET) { + result = GET_ERROR; + if (result == NE_WOULDBLOCK) return DR_PEND; + sock->error = result; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; + } + + // To report the new socket, the code here creates a temporary + // request and copies the listen request to it. Then, it stores + // the new values for IP and ports and links this request to the + // original via the sock->data. + news = MAKE_NEW(*news); // Be sure to deallocate it + CLEARS(news); +// *news = *sock; + news->device = sock->device; + + SET_OPEN(news); + SET_FLAG(news->state, RSM_OPEN); + SET_FLAG(news->state, RSM_CONNECT); + + news->socket = result; + news->net.remote_ip = sa.sin_addr.s_addr; //htonl(ip); NOTE: REBOL stays in network byte order + news->net.remote_port = ntohs(sa.sin_port); + Get_Local_IP(news); + + //Nonblocking_Mode(news->socket); ???Needed? + + Attach_Request((REBREQ**)&sock->data, news); + Signal_Device(sock, EVT_ACCEPT); + + // Even though we signalled, we keep the listen pending to + // accept additional connections. + return DR_PEND; +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = { + Init_Net, + Quit_Net, + Open_Socket, + Close_Socket, + Transfer_Socket, // Read + Transfer_Socket, // Write + 0, // poll + Connect_Socket, + 0, // query + 0, // modify + Accept_Socket, // Create + 0, // delete + 0, // rename + Lookup_Socket +}; + +DEFINE_DEV(Dev_Net, "TCP/IP Network", 1, Dev_Cmds, RDC_MAX, sizeof(REBREQ)); diff --git a/src/os/host-args.c b/src/os/host-args.c new file mode 100644 index 0000000..78e3aee --- /dev/null +++ b/src/os/host-args.c @@ -0,0 +1,267 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Command line argument processing +** Build: A95 +** Date: 27-Nov-2009 +** File: host-args.c +** Author: Carl Sassenrath +** Caution: OS independent +** Purpose: +** Parses command line arguments and options, storing them +** in a structure to be used by the REBOL library. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include + +#include "reb-config.h" +#include "reb-c.h" +#include "reb-args.h" + +#define ARG_BUF_SIZE 1024 + +extern int OS_Get_Current_Dir(REBCHR **lp); + +// REBOL Option --Words: + +const struct {const char *word; const int flag;} arg_words[] = { + // Keep in Alpha order! + {"args", RO_ARGS | RO_EXT}, + {"cgi", RO_CGI | RO_QUIET}, + {"debug", RO_DEBUG | RO_EXT}, + {"do", RO_DO | RO_EXT}, + {"halt", RO_HALT}, + {"help", RO_HELP}, + {"import", RO_IMPORT | RO_EXT}, + {"no-boot", RO_NO_BOOT}, + {"quiet", RO_QUIET}, + {"script", RO_SCRIPT | RO_EXT}, + {"secure", RO_SECURE | RO_EXT}, + {"trace", RO_TRACE}, + {"verbose", RO_VERBOSE}, + {"version", RO_VERSION | RO_EXT}, + {"", 0}, +}; + +// REBOL Option -Characters (in alpha sorted order): + +const struct arg_chr {const char cflg; const int flag;} arg_chars[] = { + {'?', RO_HELP}, + {'V', RO_VERS}, + {'c', RO_CGI | RO_QUIET}, + {'h', RO_HALT}, + {'q', RO_QUIET}, + {'s', RO_SECURE_MIN}, + {'t', RO_TRACE}, + {'v', RO_VERS}, + {'\0', 0}, +}; + +// REBOL Option +Characters: + +const struct arg_chr arg_chars2[] = { + {'s', RO_SECURE_MAX}, + {'\0', 0}, +}; + + +/*********************************************************************** +** +*/ static int find_option_word(REBCHR *word) +/* +** Scan options, return flag bits, else zero. +** +***********************************************************************/ +{ + int n; + int i; + char buf[16]; + + // Some shells will pass us the line terminator. Ignore it. + if (word[0] == '\r' || word[0] == '\n') return RO_IGNORE; + + FROM_OS_STR(buf, word, 15); + + for (i = 0; arg_words[i].flag; i++) { + n = strncmp(buf, arg_words[i].word, 15); // correct (bytes) + if (n < 0) break; + if (n == 0) return arg_words[i].flag; + } + return 0; +} + + +/*********************************************************************** +** +*/ static int find_option_char(REBCHR chr, const struct arg_chr list[]) +/* +** Scan option char flags, return flag bits, else zero. +** +***********************************************************************/ +{ + int i; + + // Some shells will pass us the line terminator. Ignore it. + if (chr == '\r' || chr == '\n') return RO_IGNORE; + + for (i = 0; list[i].flag; i++) { + if (chr < list[i].cflg) break; + if (chr == list[i].cflg) return list[i].flag; + } + return 0; +} + + +/*********************************************************************** +** +*/ static int Get_Ext_Arg(int flag, REBARGS *rargs, REBCHR *arg) +/* +** Get extended argument field. +** +***********************************************************************/ +{ + if (arg[1] == (REBCHR)'-') return flag; + + flag &= ~RO_EXT; + + switch (flag) { + + case RO_VERSION: + rargs->version = arg; + break; + + case RO_SCRIPT: + rargs->script = arg; + break; + + case RO_ARGS: + rargs->args = arg; + break; + + case RO_DO: + rargs->do_arg = arg; + break; + + case RO_DEBUG: + rargs->debug = arg; + break; + + case RO_SECURE: + rargs->secure = arg; + break; + + case RO_IMPORT: + rargs->import = arg; + break; + } + + return flag; +} + + +/*********************************************************************** +** +*/ void Parse_Args(int argc, REBCHR **argv, REBARGS *rargs) +/* +** Parse REBOL's command line arguments, setting options +** and values in the provided args structure. +** +***********************************************************************/ +{ + REBCHR *arg; + REBCHR *args = 0; // holds trailing args + int flag; + int i; + + CLEARS(rargs); + + // First arg is path to execuable (on most systems): + if (argc > 0) rargs->exe_path = *argv; + + OS_Get_Current_Dir(&rargs->home_dir); + + // Parse each argument: + for (i = 1; i < argc ; i++) { + arg = argv[i]; + if (arg == 0) continue; // shell bug + if (*arg == '-') { + if (arg[1] == '-') { + // --option words + flag = find_option_word(arg+2); + if (flag & RO_EXT) { + flag = Get_Ext_Arg(flag, rargs, (i+1 >= argc) ? 0 : argv[i+1]); + if ((flag & RO_EXT) == 0) i++; // used it + else flag &= ~RO_EXT; + } + if (!flag) flag = RO_HELP; + rargs->options |= flag; + } + else { + // -x option chars + while (*++arg) { + flag = find_option_char(*arg, arg_chars); + if (flag & RO_EXT) { + flag = Get_Ext_Arg(flag, rargs, (i+1 >= argc) ? 0 : argv[i+1]); + if ((flag & RO_EXT) == 0) i++; // used it + else flag &= ~RO_EXT; + } + if (!flag) flag = RO_HELP; + rargs->options |= flag; + } + } + } + else if (*arg == '+') { + // +x option chars + while (*++arg) { + flag = find_option_char(*arg, arg_chars2); + if (flag & RO_EXT) { + flag = Get_Ext_Arg(flag, rargs, (i+1 >= argc) ? 0 : argv[i+1]); + if ((flag & RO_EXT) == 0) i++; // used it + else flag &= ~RO_EXT; + } + if (!flag) flag = RO_HELP; + rargs->options |= flag; + } + } + else { + // script filename + if (!rargs->script) + rargs->script = arg; + else { + int len; + if (!args) { + args = MAKE_STR(ARG_BUF_SIZE); + args[0] = 0; + } + len = ARG_BUF_SIZE - LEN_STR(args) - 2; // space remaining + JOIN_STR(args, arg, len); + JOIN_STR(args, TXT(" "), 1); + } + } + } + + if (args) { + args[LEN_STR(args)-1] = 0; // remove trailing space + Get_Ext_Arg(RO_ARGS, rargs, args); + } +} + + + diff --git a/src/os/host-device.c b/src/os/host-device.c new file mode 100644 index 0000000..b35ff52 --- /dev/null +++ b/src/os/host-device.c @@ -0,0 +1,449 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device management and command dispatch +** Build: A95 +** Date: 27-Nov-2009 +** File: host-device.c +** Author: Carl Sassenrath +** Caution: OS independent +** Purpose: +** This module implements a device management system for +** REBOL devices and tracking their I/O requests. +** It is intentionally kept very simple (makes debugging easy!) +** +** Special note: +** This module is parsed for function declarations used to +** build prototypes, tables, and other definitions. To change +** function arguments requires a rebuild of the REBOL library. +** +** Design comments: +** 1. Not a lot of devices are needed (dozens, not hundreds). +** 2. Devices are referenced by integer (index into device table). +** 3. A single device can support multiple requests. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include + +#include "reb-host.h" + +#include "host-lib.h" +#include "rebol-lib.h" + + +/*********************************************************************** +** +** REBOL Device Table +** +** The table most be in same order as the RDI_ enums. +** Table is in polling priority order. +** +***********************************************************************/ + +extern REBDEV Dev_StdIO; +extern REBDEV Dev_File; +extern REBDEV Dev_Event; +extern REBDEV Dev_Net; +extern REBDEV Dev_DNS; +#ifndef MIN_OS +extern REBDEV Dev_Clipboard; +#endif + +REBDEV *Devices[RDI_LIMIT] = +{ + 0, + &Dev_StdIO, + 0, + &Dev_File, +#ifdef TO_WIN32 // temporary? + &Dev_Event, +#else + 0, +#endif + &Dev_Net, + &Dev_DNS, +#ifndef MIN_OS + &Dev_Clipboard, +#endif + 0, +}; + + +static int Poll_Default(REBDEV *dev) +{ + // The default polling function for devices. + // Retries pending requests. Return TRUE if status changed. + REBREQ **prior = &dev->pending; + REBREQ *req; + BOOL change = FALSE; + int result; + + for (req = *prior; req; req = *prior) { + + // Call command again: + if (req->command < RDC_MAX) + result = dev->commands[req->command](req); + else { + result = -1; // invalid command, remove it + req->error = ((REBCNT)-1); + } + + // If done or error, remove command from list: + if (result <= 0) { + *prior = req->next; + req->next = 0; + CLR_FLAG(req->flags, RRF_PENDING); + change = TRUE; + } + else prior = &req->next; + } + + return change; +} + + +/*********************************************************************** +** +*/ void Attach_Request(REBREQ **node, REBREQ *req) +/* +** Attach a request to a device's pending or accept list. +** Node is a pointer to the head pointer of the req list. +** +***********************************************************************/ +{ + REBREQ *r; + +#ifdef special_debug + if (req->device == 5) + Debug_Fmt("Attach: %x %x %x %x", req, req->device, req->port, req->next); +#endif + + // See if its there, and get last req: + for (r = *node; r; r = *node) { + if (r == req) return; // already in list + node = &r->next; + } + + // Link the new request to end: + *node = req; + req->next = 0; + SET_FLAG(req->flags, RRF_PENDING); +} + + +/*********************************************************************** +** +*/ void Detach_Request(REBREQ **node, REBREQ *req) +/* +** Detach a request to a device's pending or accept list. +** If it is not in list, then no harm done. +** +***********************************************************************/ +{ + REBREQ *r; + +#ifdef special_debug + if (req->device == 5) + Debug_Fmt("Detach= n: %x r: %x p: %x %x", *node, req, req->port, &req->next); +#endif + + // See if its there, and get last req: + for (r = *node; r; r = *node) { +#ifdef special_debug + if (req->device == 5) + Debug_Fmt("Detach: r: %x n: %x", r, r->next); +#endif + if (r == req) { + *node = req->next; + req->next = 0; + CLR_FLAG(req->flags, RRF_PENDING); + return; + } + node = &r->next; + } +} + + +/*********************************************************************** +** +*/ void Done_Device(int handle, int error) +/* +** Given a handle mark the related request as done. +** (Used by DNS device). +** +***********************************************************************/ +{ + REBINT d; + REBDEV *dev; + REBREQ **prior; + REBREQ *req; + + for (d = RDI_NET; d <= RDI_DNS; d++) { + dev = Devices[d]; + prior = &dev->pending; + // Scan the pending requests, mark the one we got: + for (req = *prior; req; req = *prior) { + if ((int)(req->handle) == handle) { + req->error = error; // zero when no error + SET_FLAG(req->flags, RRF_DONE); + return; + } + prior = &req->next; + } + } +} + + +/*********************************************************************** +** +*/ void Signal_Device(REBREQ *req, REBINT type) +/* +** Generate a device event to awake a port on REBOL. +** +***********************************************************************/ +{ + REBEVT evt; + + CLEARS(&evt); + + evt.type = (REBYTE)type; + evt.req = req; + if (type == EVT_ERROR) evt.data = req->error; + + Reb_Event(&evt); // (returns 0 if queue is full, ignored) +} + + +/*********************************************************************** +** +*/ int OS_Call_Device(REBINT device, REBCNT command) +/* +** Shortcut for non-request calls to device. +** +** Init - Initialize any device-related resources (e.g. libs). +** Quit - Cleanup any device-related resources. +** Make - Create and initialize a request for a device. +** Free - Free a device request structure. +** Poll - Poll device for activity. +** +***********************************************************************/ +{ + REBDEV *dev; + + // Validate device: + if (device >= RDI_MAX || !(dev = Devices[device])) + return -1; + + // Validate command: + if (command > dev->max_command || dev->commands[command] == 0) + return -2; + + // Do command, return result: + return dev->commands[command]((REBREQ*)dev); +} + + +/*********************************************************************** +** +*/ int OS_Do_Device(REBREQ *req, REBCNT command) +/* +** Tell a device to perform a command. Non-blocking in many +** cases and will attach the request for polling. +** +** Returns: +** =0: for command success +** >0: for command still pending +** <0: for command error +** +***********************************************************************/ +{ + REBDEV *dev; + REBINT result; + + req->error = 0; // A94 - be sure its cleared + + // Validate device: + if (req->device >= RDI_MAX || !(dev = Devices[req->device])) { + req->error = RDE_NO_DEVICE; + return -1; + } + + // Confirm device is initialized. If not, return an error or init + // it if auto init option is set. + if (!GET_FLAG(dev->flags, RDF_INIT)) { + if (GET_FLAG(dev->flags, RDO_MUST_INIT)) { + req->error = RDE_NO_INIT; + return -1; + } + if (!dev->commands[RDC_INIT] || !dev->commands[RDC_INIT]((REBREQ*)dev)) + SET_FLAG(dev->flags, RDF_INIT); + } + + // Validate command: + if (command > dev->max_command || dev->commands[command] == 0) { + req->error = RDE_NO_COMMAND; + return -1; + } + + // Do the command: + req->command = command; + result = dev->commands[command](req); + + // If request is pending, attach it to device for polling: + if (result > 0) Attach_Request(&dev->pending, req); + else if (dev->pending) { + Detach_Request(&dev->pending, req); // often a no-op + if (result == DR_ERROR && GET_FLAG(req->flags, RRF_ALLOC)) { // not on stack + Signal_Device(req, EVT_ERROR); + } + } + + return result; +} + + +/*********************************************************************** +** +*/ REBREQ *OS_Make_Devreq(int device) +/* +***********************************************************************/ +{ + REBDEV *dev; + REBREQ *req; + int size; + + // Validate device: + if (device >= RDI_MAX || !(dev = Devices[device])) + return 0; + + size = dev->req_size ? dev->req_size : sizeof(REBREQ); + req = OS_Make(size); + CLEARS(req); + SET_FLAG(req->flags, RRF_ALLOC); + req->clen = size; + req->device = device; + + return req; +} + + +/*********************************************************************** +** +*/ int OS_Abort_Device(REBREQ *req) +/* +** Ask device to abort prior request. +** +***********************************************************************/ +{ + REBDEV *dev; + + if ((dev = Devices[req->device]) != 0) Detach_Request(&dev->pending, req); + return 0; +} + + +/*********************************************************************** +** +*/ int OS_Poll_Devices(void) +/* +** Poll devices for activity. +** +** Returns count of devices that changed status. +** +** Devices with pending lists will be called to see if +** there is a change in status of those requests. If so, +** those devices are allowed to change the state of those +** requests or call-back into special REBOL functions +** (e.g. Add_Event for GUI) to invoke special actions. +** +***********************************************************************/ +{ + int d; + int cnt = 0; + REBDEV *dev; + //int cc = 0; + + //printf("Polling Devices\n"); + + // Check each device: + for (d = 0; d < RDI_MAX; d++) { + dev = Devices[d]; + if (dev && (dev->pending || GET_FLAG(dev->flags, RDO_AUTO_POLL))) { + // If there is a custom polling function, use it: + if (dev->commands[RDC_POLL]) { + if (dev->commands[RDC_POLL]((REBREQ*)dev)) cnt++; + } + else { + if (Poll_Default(dev)) cnt++; + } + } + //if (cc != cnt) {printf("dev=%s ", dev->title); cc = cnt;} + } + + return cnt; +} + + +/*********************************************************************** +** +*/ REBINT OS_Wait(REBCNT millisec, REBCNT res) +/* +** Check if devices need attention, and if not, then wait. +** The wait can be interrupted by a GUI event, otherwise +** the timeout will wake it. +** +** Res specifies resolution. (No wait if less than this.) +** +** Returns: +** -1: Devices have changed state. +** 0: past given millsecs +** 1: wait in timer +** +** The time it takes for the devices to be scanned is +** subtracted from the timer value. +** +***********************************************************************/ +{ + REBREQ req; // OK: QUERY below does not store it + REBCNT delta; + i64 base; + + //printf("OS_Wait %d\n", millisec); + + base = OS_Delta_Time(0, 0); // start timing + + // Setup for timing: + CLEARS(&req); + req.device = RDI_EVENT; + + // Let any pending device I/O have a chance to run: + if (OS_Poll_Devices()) return -1; + + // Nothing, so wait for period of time + delta = (REBCNT)OS_Delta_Time(base, 0)/1000 + res; + if (delta >= millisec) return 0; + millisec -= delta; // account for time lost above + req.length = millisec; + + //printf("Wait: %d ms\n", millisec); + OS_Do_Device(&req, RDC_QUERY); // wait for timer or other event + + return 1; // layer above should check delta again +} diff --git a/src/os/host-init.c b/src/os/host-init.c new file mode 100644 index 0000000..99ea1f5 --- /dev/null +++ b/src/os/host-init.c @@ -0,0 +1,672 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Host custom init code +** Build: A96 +** Date: 2-Dec-2009/15:37:15-8:00 +** File: host-init.h +** +** AUTO-GENERATED FILE - Do not modify. (From: make-host-init.r) +** +***********************************************************************/ + +const unsigned char Reb_Init_Code[6532] = { + 120, 156, 237, 60, 107, 115, 219, 198, 181, 247, + 179, 126, 197, 154, 153, 142, 237, 76, 192, 151, + 36, 91, 70, 235, 106, 28, 219, 105, 50, 227, + 76, 51, 174, 219, 126, 224, 40, 51, 16, 176, + 36, 81, 129, 0, 2, 128, 162, 152, 56, 255, + 253, 158, 115, 246, 129, 221, 197, 2, 164, 100, + 245, 182, 31, 46, 19, 219, 228, 62, 206, 158, + 247, 57, 123, 118, 129, 63, 178, 183, 69, 185, + 175, 210, 213, 186, 97, 31, 223, 127, 251, 215, + 15, 236, 19, 143, 215, 121, 145, 21, 171, 148, + 215, 108, 30, 188, 227, 113, 48, 159, 78, 95, + 77, 102, 231, 225, 233, 203, 112, 118, 30, 92, + 132, 211, 233, 201, 73, 189, 175, 27, 190, 153, + 36, 105, 148, 241, 184, 169, 39, 73, 21, 237, + 66, 22, 23, 121, 195, 239, 26, 182, 56, 97, + 240, 105, 246, 37, 15, 234, 146, 199, 33, 91, + 92, 103, 69, 124, 243, 228, 138, 81, 71, 148, + 55, 105, 16, 101, 105, 84, 67, 15, 46, 21, + 235, 158, 10, 7, 211, 87, 252, 148, 81, 90, + 61, 145, 127, 39, 60, 78, 55, 81, 102, 124, + 217, 21, 85, 242, 132, 233, 193, 118, 59, 53, + 107, 168, 85, 1, 216, 45, 154, 109, 153, 113, + 9, 79, 118, 93, 23, 119, 208, 225, 91, 71, + 14, 136, 211, 42, 206, 184, 30, 211, 193, 66, + 13, 203, 210, 210, 1, 100, 17, 22, 111, 171, + 91, 132, 242, 181, 181, 58, 95, 46, 129, 123, + 206, 60, 139, 85, 60, 3, 192, 53, 183, 135, + 200, 190, 101, 154, 101, 65, 201, 243, 150, 50, + 192, 104, 197, 157, 149, 105, 84, 181, 37, 26, + 136, 51, 178, 125, 21, 109, 54, 17, 180, 57, + 132, 172, 170, 40, 145, 64, 5, 127, 197, 223, + 38, 73, 30, 81, 12, 124, 177, 200, 73, 243, + 91, 94, 53, 193, 38, 106, 170, 20, 25, 175, + 154, 17, 111, 248, 41, 241, 151, 212, 152, 235, + 167, 160, 88, 43, 94, 13, 126, 177, 121, 75, + 176, 2, 160, 190, 225, 149, 67, 140, 67, 114, + 150, 230, 93, 209, 96, 99, 16, 71, 165, 195, + 53, 106, 254, 87, 145, 230, 190, 246, 50, 106, + 96, 177, 92, 43, 181, 34, 228, 107, 223, 138, + 193, 46, 77, 154, 181, 33, 0, 102, 194, 211, + 28, 178, 216, 119, 80, 216, 101, 145, 237, 87, + 69, 222, 161, 166, 220, 214, 107, 23, 88, 197, + 107, 222, 21, 69, 85, 52, 81, 195, 187, 122, + 81, 199, 81, 102, 54, 187, 36, 213, 235, 168, + 228, 238, 18, 245, 13, 223, 121, 64, 149, 146, + 227, 90, 114, 66, 44, 54, 202, 232, 72, 180, + 220, 122, 237, 163, 169, 162, 188, 94, 22, 213, + 198, 68, 173, 207, 97, 88, 224, 113, 98, 38, + 72, 181, 219, 211, 40, 95, 101, 142, 197, 201, + 191, 37, 231, 237, 127, 58, 158, 160, 32, 123, + 53, 12, 191, 35, 142, 181, 100, 128, 51, 117, + 83, 120, 124, 196, 47, 18, 66, 167, 173, 59, + 244, 214, 15, 22, 44, 26, 220, 116, 40, 241, + 41, 242, 52, 150, 223, 161, 121, 83, 228, 73, + 216, 42, 101, 84, 181, 93, 160, 71, 237, 172, + 237, 181, 158, 149, 23, 121, 240, 43, 175, 10, + 249, 147, 223, 242, 60, 40, 18, 5, 229, 26, + 228, 197, 21, 20, 4, 200, 235, 70, 117, 165, + 214, 18, 0, 209, 128, 186, 138, 182, 117, 13, + 172, 15, 181, 114, 70, 155, 18, 197, 32, 198, + 110, 27, 5, 165, 254, 101, 11, 64, 213, 176, + 98, 155, 39, 92, 173, 189, 73, 27, 189, 52, + 125, 15, 174, 1, 187, 204, 28, 171, 224, 25, + 237, 203, 244, 78, 67, 32, 225, 37, 154, 208, + 106, 163, 57, 80, 241, 146, 71, 141, 254, 177, + 204, 200, 113, 11, 198, 69, 213, 74, 33, 84, + 239, 56, 47, 229, 247, 91, 24, 82, 84, 45, + 235, 129, 113, 252, 4, 100, 226, 198, 78, 161, + 233, 253, 177, 179, 6, 157, 204, 87, 74, 223, + 116, 224, 202, 18, 55, 122, 166, 13, 4, 213, + 216, 109, 69, 14, 85, 82, 51, 236, 200, 0, + 43, 66, 91, 113, 253, 47, 64, 67, 123, 138, + 168, 138, 58, 141, 117, 250, 171, 105, 175, 173, + 201, 39, 20, 87, 45, 227, 112, 7, 197, 85, + 145, 101, 142, 254, 38, 85, 81, 118, 225, 245, + 167, 5, 57, 223, 73, 2, 148, 85, 129, 10, + 116, 113, 143, 121, 46, 156, 189, 114, 179, 124, + 217, 24, 158, 13, 179, 156, 246, 231, 181, 148, + 74, 42, 255, 221, 42, 41, 29, 18, 151, 10, + 218, 253, 2, 83, 244, 160, 81, 168, 152, 38, + 254, 81, 61, 89, 185, 142, 54, 219, 204, 232, + 118, 88, 129, 144, 154, 182, 123, 32, 122, 93, + 103, 219, 74, 15, 84, 140, 128, 4, 174, 74, + 151, 251, 78, 72, 117, 150, 17, 227, 126, 237, + 132, 94, 221, 157, 223, 22, 217, 173, 209, 45, + 124, 111, 235, 81, 181, 7, 183, 147, 29, 224, + 76, 21, 213, 77, 47, 121, 177, 148, 191, 232, + 236, 102, 54, 73, 10, 60, 174, 120, 30, 115, + 151, 129, 54, 130, 124, 115, 93, 212, 181, 75, + 61, 8, 133, 231, 201, 16, 252, 101, 122, 28, + 111, 151, 34, 171, 51, 225, 24, 105, 18, 112, + 207, 93, 196, 138, 12, 198, 208, 148, 231, 205, + 177, 99, 45, 189, 24, 28, 186, 87, 65, 217, + 34, 127, 93, 223, 246, 200, 83, 100, 95, 238, + 248, 27, 222, 209, 19, 101, 63, 91, 202, 16, + 253, 50, 220, 80, 214, 224, 211, 110, 32, 160, + 73, 203, 108, 223, 35, 59, 23, 144, 118, 166, + 62, 62, 235, 116, 196, 143, 132, 118, 65, 93, + 81, 187, 225, 217, 74, 175, 96, 94, 37, 146, + 41, 11, 243, 38, 53, 216, 105, 167, 11, 208, + 19, 220, 166, 124, 215, 157, 146, 15, 232, 121, + 177, 217, 128, 228, 217, 111, 63, 7, 39, 63, + 7, 180, 71, 248, 57, 248, 57, 88, 92, 157, + 252, 46, 6, 20, 57, 178, 233, 209, 98, 165, + 114, 89, 215, 89, 20, 223, 132, 108, 58, 134, + 255, 216, 73, 92, 96, 8, 122, 113, 54, 166, + 255, 217, 9, 106, 78, 200, 102, 243, 139, 177, + 252, 195, 78, 74, 190, 35, 207, 57, 123, 57, + 29, 203, 63, 224, 248, 82, 176, 125, 108, 124, + 53, 31, 203, 63, 208, 152, 35, 191, 231, 103, + 211, 177, 252, 195, 78, 118, 235, 20, 69, 52, + 63, 63, 31, 203, 63, 136, 193, 150, 11, 4, + 232, 231, 170, 226, 200, 109, 250, 69, 40, 237, + 35, 253, 147, 6, 84, 24, 117, 169, 19, 187, + 247, 176, 237, 161, 101, 36, 72, 104, 66, 254, + 230, 77, 164, 6, 209, 164, 60, 186, 221, 139, + 69, 136, 136, 140, 71, 75, 252, 137, 84, 193, + 140, 134, 35, 217, 211, 150, 200, 77, 84, 21, + 152, 27, 83, 63, 142, 40, 178, 20, 157, 155, + 226, 4, 180, 148, 219, 138, 24, 41, 134, 208, + 172, 2, 18, 197, 149, 36, 111, 118, 14, 141, + 52, 17, 246, 187, 16, 64, 94, 206, 199, 240, + 255, 236, 5, 208, 11, 59, 76, 4, 125, 250, + 106, 252, 226, 21, 112, 10, 185, 14, 62, 12, + 230, 189, 124, 49, 158, 191, 24, 19, 59, 121, + 158, 3, 1, 179, 23, 211, 241, 197, 124, 124, + 6, 4, 196, 85, 186, 169, 17, 165, 249, 28, + 72, 154, 142, 95, 192, 176, 219, 180, 200, 48, + 182, 1, 224, 233, 248, 213, 20, 65, 167, 40, + 204, 217, 203, 139, 241, 233, 25, 252, 15, 88, + 166, 249, 141, 68, 8, 68, 10, 219, 115, 96, + 48, 165, 3, 196, 175, 233, 249, 24, 165, 210, + 68, 4, 23, 208, 187, 56, 27, 207, 78, 81, + 38, 60, 85, 132, 204, 145, 228, 87, 128, 119, + 122, 91, 84, 123, 67, 118, 56, 19, 53, 15, + 231, 158, 75, 25, 159, 66, 219, 205, 58, 186, + 73, 17, 9, 32, 14, 255, 204, 95, 160, 204, + 174, 49, 126, 64, 235, 217, 92, 112, 121, 70, + 218, 64, 121, 210, 252, 12, 87, 193, 118, 224, + 69, 4, 57, 91, 200, 206, 144, 121, 240, 7, + 225, 65, 202, 78, 202, 62, 29, 159, 17, 227, + 119, 17, 233, 223, 5, 14, 1, 64, 103, 160, + 105, 101, 84, 70, 123, 41, 112, 104, 63, 125, + 9, 44, 188, 65, 197, 21, 52, 11, 21, 216, + 144, 249, 9, 176, 47, 0, 1, 129, 22, 41, + 223, 233, 197, 248, 252, 2, 193, 1, 229, 81, + 13, 155, 57, 129, 44, 77, 21, 127, 164, 162, + 73, 242, 17, 189, 249, 84, 199, 251, 26, 24, + 152, 68, 85, 50, 17, 105, 146, 29, 237, 243, + 104, 3, 43, 140, 34, 204, 238, 70, 210, 165, + 52, 123, 101, 130, 102, 210, 52, 155, 27, 161, + 86, 155, 36, 217, 254, 114, 89, 163, 160, 231, + 119, 115, 181, 47, 138, 48, 224, 77, 239, 166, + 182, 115, 115, 18, 17, 141, 152, 72, 213, 108, + 196, 32, 156, 175, 112, 115, 170, 129, 130, 210, + 219, 13, 41, 164, 131, 72, 144, 94, 166, 137, + 174, 107, 20, 142, 248, 181, 171, 162, 242, 50, + 132, 109, 208, 86, 145, 33, 19, 56, 61, 30, + 50, 180, 21, 0, 124, 138, 217, 149, 76, 116, + 85, 83, 83, 148, 136, 168, 240, 147, 203, 109, + 30, 75, 172, 70, 239, 82, 216, 244, 65, 188, + 98, 17, 219, 1, 2, 197, 142, 225, 152, 177, + 100, 157, 108, 90, 172, 138, 107, 157, 98, 232, + 204, 110, 244, 79, 209, 11, 157, 223, 176, 127, + 252, 240, 142, 45, 129, 75, 223, 0, 161, 244, + 3, 128, 22, 219, 70, 76, 146, 208, 38, 69, + 217, 164, 69, 94, 51, 248, 183, 110, 55, 164, + 10, 142, 234, 197, 12, 203, 158, 151, 23, 193, + 46, 74, 193, 91, 127, 228, 205, 182, 202, 33, + 106, 109, 56, 100, 125, 13, 207, 246, 99, 246, + 174, 0, 57, 52, 140, 6, 0, 255, 89, 89, + 21, 49, 175, 107, 218, 254, 52, 245, 88, 250, + 243, 73, 84, 7, 105, 205, 70, 31, 120, 116, + 203, 21, 93, 81, 205, 210, 90, 67, 128, 172, + 16, 184, 0, 178, 195, 192, 0, 68, 41, 38, + 76, 0, 149, 40, 67, 118, 131, 171, 100, 205, + 166, 100, 119, 123, 96, 166, 96, 96, 186, 164, + 185, 162, 51, 100, 82, 19, 144, 135, 19, 209, + 22, 0, 36, 182, 168, 4, 226, 168, 48, 50, + 2, 33, 15, 66, 80, 130, 27, 14, 127, 149, + 79, 32, 86, 36, 219, 152, 35, 169, 160, 123, + 130, 67, 237, 2, 130, 252, 5, 182, 42, 102, + 8, 77, 184, 210, 131, 4, 125, 98, 8, 125, + 119, 7, 0, 30, 151, 90, 160, 212, 136, 159, + 109, 158, 33, 179, 218, 105, 70, 31, 233, 224, + 6, 242, 44, 57, 203, 237, 152, 40, 67, 209, + 26, 168, 62, 98, 130, 36, 143, 148, 103, 33, + 140, 14, 103, 225, 183, 43, 123, 124, 84, 66, + 204, 79, 212, 58, 48, 72, 247, 26, 3, 145, + 200, 124, 47, 73, 36, 171, 21, 104, 99, 133, + 243, 202, 65, 59, 205, 107, 72, 168, 20, 192, + 22, 13, 185, 80, 92, 148, 123, 103, 70, 235, + 24, 196, 36, 66, 147, 117, 134, 24, 36, 91, + 125, 87, 172, 132, 96, 224, 129, 185, 16, 5, + 88, 3, 211, 238, 16, 233, 130, 90, 194, 174, + 28, 216, 100, 13, 151, 45, 16, 31, 123, 76, + 73, 174, 193, 16, 50, 94, 57, 232, 136, 214, + 64, 24, 134, 7, 85, 225, 59, 159, 162, 242, + 6, 9, 95, 70, 144, 45, 118, 145, 45, 171, + 20, 92, 89, 3, 238, 254, 124, 218, 237, 149, + 11, 43, 31, 67, 75, 185, 178, 49, 32, 129, + 161, 45, 70, 180, 30, 141, 12, 71, 194, 106, + 39, 184, 103, 147, 95, 5, 195, 61, 92, 195, + 15, 168, 68, 189, 75, 155, 120, 109, 206, 243, + 175, 134, 31, 42, 34, 176, 133, 97, 23, 190, + 15, 164, 221, 18, 243, 9, 126, 125, 205, 56, + 100, 243, 37, 191, 242, 78, 232, 35, 14, 63, + 219, 220, 230, 120, 205, 179, 101, 255, 178, 219, + 28, 25, 33, 9, 241, 89, 156, 249, 249, 101, + 155, 54, 61, 248, 120, 155, 235, 117, 113, 12, + 104, 10, 104, 29, 144, 39, 254, 95, 226, 91, + 235, 96, 164, 146, 118, 92, 140, 114, 6, 34, + 34, 76, 174, 33, 3, 94, 81, 201, 71, 187, + 97, 109, 213, 168, 219, 29, 51, 104, 87, 16, + 161, 103, 96, 9, 105, 222, 134, 219, 177, 112, + 79, 34, 204, 83, 125, 12, 232, 88, 190, 97, + 97, 178, 21, 253, 184, 179, 224, 100, 153, 69, + 43, 220, 229, 66, 202, 132, 78, 205, 68, 87, + 142, 16, 37, 164, 150, 194, 38, 109, 50, 110, + 118, 178, 17, 29, 239, 132, 32, 127, 234, 75, + 70, 6, 189, 56, 67, 24, 128, 65, 9, 116, + 224, 142, 233, 178, 167, 27, 63, 70, 79, 200, + 120, 218, 172, 121, 101, 141, 126, 205, 158, 138, + 146, 12, 248, 101, 138, 82, 194, 219, 5, 150, + 239, 155, 176, 57, 104, 55, 100, 112, 119, 240, + 199, 16, 123, 135, 11, 106, 33, 99, 5, 87, + 112, 212, 179, 203, 113, 65, 53, 9, 127, 133, + 70, 143, 51, 154, 88, 171, 71, 75, 70, 183, + 61, 206, 104, 237, 240, 108, 139, 51, 251, 116, + 209, 141, 220, 164, 84, 130, 215, 42, 172, 47, + 164, 234, 200, 159, 162, 91, 239, 73, 97, 164, + 169, 53, 24, 137, 178, 204, 224, 57, 166, 0, + 102, 112, 102, 51, 128, 156, 241, 124, 213, 172, + 47, 37, 72, 201, 145, 118, 78, 82, 72, 44, + 187, 90, 131, 169, 154, 112, 7, 118, 178, 246, + 22, 253, 215, 129, 84, 77, 166, 103, 66, 253, + 69, 237, 4, 173, 218, 200, 179, 48, 77, 195, + 44, 237, 47, 127, 253, 118, 204, 158, 34, 33, + 144, 243, 35, 65, 99, 145, 35, 227, 175, 44, + 170, 27, 95, 250, 163, 243, 158, 3, 9, 143, + 200, 172, 163, 218, 244, 198, 174, 245, 42, 163, + 109, 237, 107, 34, 146, 175, 142, 134, 161, 190, + 18, 195, 73, 18, 49, 108, 39, 43, 37, 40, + 126, 151, 154, 227, 41, 19, 116, 224, 35, 45, + 114, 184, 101, 161, 21, 199, 178, 62, 91, 166, + 174, 216, 59, 82, 167, 157, 59, 238, 84, 116, + 120, 243, 109, 58, 158, 130, 240, 241, 155, 44, + 215, 234, 56, 57, 197, 233, 150, 94, 218, 82, + 125, 147, 36, 40, 83, 165, 194, 77, 193, 192, + 94, 89, 27, 12, 36, 143, 149, 160, 181, 170, + 91, 199, 45, 74, 74, 251, 154, 144, 212, 114, + 210, 24, 147, 79, 52, 137, 208, 128, 4, 181, + 114, 162, 45, 81, 90, 63, 40, 139, 170, 17, + 240, 181, 49, 213, 150, 49, 225, 158, 49, 130, + 32, 188, 0, 63, 3, 140, 192, 49, 87, 26, + 162, 237, 185, 228, 252, 137, 98, 15, 251, 51, + 53, 181, 191, 189, 121, 28, 194, 181, 241, 85, + 159, 107, 88, 249, 102, 34, 211, 107, 12, 236, + 157, 240, 212, 2, 84, 38, 174, 16, 147, 0, + 77, 157, 80, 77, 100, 128, 3, 50, 251, 72, + 186, 99, 138, 109, 89, 21, 155, 35, 5, 167, + 118, 80, 90, 72, 150, 38, 30, 199, 126, 139, + 25, 104, 4, 173, 146, 37, 18, 227, 75, 11, + 101, 185, 119, 146, 251, 34, 61, 95, 224, 130, + 167, 154, 241, 58, 205, 87, 64, 208, 42, 133, + 1, 164, 210, 106, 239, 132, 223, 53, 174, 74, + 214, 196, 193, 35, 145, 237, 104, 192, 132, 236, + 228, 53, 65, 214, 155, 35, 82, 27, 83, 24, + 106, 123, 173, 61, 165, 77, 208, 63, 193, 209, + 146, 238, 41, 179, 149, 59, 62, 166, 40, 221, + 173, 129, 16, 244, 27, 162, 31, 164, 5, 90, + 36, 14, 146, 198, 191, 107, 130, 200, 95, 251, + 9, 193, 197, 211, 60, 109, 168, 190, 24, 136, + 49, 182, 30, 252, 0, 189, 41, 108, 178, 33, + 92, 162, 236, 255, 129, 178, 175, 183, 215, 182, + 224, 165, 109, 242, 210, 220, 53, 250, 87, 132, + 196, 179, 117, 104, 120, 94, 85, 148, 24, 156, + 234, 120, 205, 201, 197, 200, 132, 90, 153, 172, + 7, 2, 196, 251, 82, 77, 151, 130, 48, 220, + 149, 18, 73, 40, 183, 64, 87, 122, 169, 73, + 180, 3, 15, 97, 37, 238, 10, 241, 181, 105, + 68, 235, 80, 230, 145, 253, 94, 129, 184, 186, + 78, 51, 55, 21, 39, 23, 46, 32, 163, 159, + 110, 162, 52, 187, 4, 224, 70, 102, 97, 143, + 23, 123, 2, 182, 158, 204, 116, 124, 23, 179, + 237, 93, 77, 200, 114, 164, 109, 237, 203, 80, + 196, 26, 254, 32, 37, 84, 141, 100, 188, 33, + 222, 111, 138, 100, 171, 145, 166, 100, 44, 148, + 217, 25, 59, 101, 223, 127, 250, 244, 19, 214, + 22, 154, 2, 82, 83, 38, 4, 34, 229, 123, + 11, 148, 167, 5, 149, 77, 103, 170, 134, 36, + 66, 194, 186, 105, 90, 161, 215, 251, 60, 14, + 240, 120, 69, 176, 152, 150, 188, 46, 146, 189, + 246, 221, 88, 80, 55, 121, 32, 189, 43, 49, + 154, 58, 49, 135, 4, 117, 160, 153, 109, 235, + 132, 84, 26, 172, 125, 207, 107, 131, 244, 90, + 212, 231, 141, 217, 118, 151, 18, 120, 8, 246, + 156, 4, 132, 28, 181, 180, 195, 146, 66, 224, + 103, 90, 175, 152, 42, 224, 65, 84, 198, 185, + 160, 70, 96, 161, 21, 255, 101, 203, 107, 129, + 87, 119, 87, 138, 173, 151, 194, 212, 22, 18, + 233, 34, 207, 193, 243, 0, 223, 36, 138, 37, + 143, 33, 59, 222, 112, 216, 35, 0, 19, 144, + 115, 1, 175, 42, 176, 238, 209, 39, 209, 58, + 50, 192, 34, 94, 82, 135, 137, 27, 86, 145, + 192, 74, 205, 136, 94, 142, 231, 24, 18, 247, + 52, 95, 22, 147, 53, 224, 141, 250, 250, 244, + 45, 90, 6, 152, 206, 39, 220, 57, 218, 147, + 58, 163, 39, 253, 131, 33, 113, 129, 92, 231, + 208, 148, 238, 86, 113, 132, 102, 57, 25, 129, + 243, 168, 182, 108, 244, 71, 22, 175, 17, 80, + 243, 250, 239, 159, 190, 11, 46, 70, 125, 27, + 47, 7, 144, 96, 6, 228, 13, 234, 60, 26, + 27, 124, 214, 160, 229, 39, 52, 134, 45, 196, + 134, 216, 17, 153, 158, 173, 243, 36, 75, 63, + 108, 23, 33, 254, 129, 44, 68, 57, 57, 20, + 153, 137, 160, 216, 159, 79, 84, 61, 161, 119, + 159, 46, 245, 129, 73, 141, 234, 112, 202, 80, + 177, 214, 255, 116, 119, 177, 75, 112, 71, 246, + 22, 214, 217, 17, 39, 152, 226, 118, 193, 91, + 169, 131, 103, 154, 44, 29, 220, 123, 158, 80, + 225, 238, 60, 106, 183, 92, 169, 144, 140, 24, + 223, 29, 238, 29, 103, 22, 181, 109, 26, 5, + 252, 35, 245, 199, 230, 153, 21, 130, 201, 12, + 251, 35, 3, 201, 128, 198, 208, 55, 225, 22, + 132, 23, 169, 208, 25, 25, 23, 6, 69, 104, + 242, 72, 78, 79, 151, 190, 74, 132, 148, 142, + 31, 211, 195, 92, 103, 38, 202, 130, 1, 98, + 135, 222, 228, 146, 133, 237, 80, 129, 202, 194, + 246, 119, 78, 183, 33, 48, 53, 194, 24, 207, + 238, 173, 200, 168, 192, 30, 129, 11, 104, 148, + 133, 11, 147, 97, 11, 156, 28, 10, 31, 42, + 249, 163, 81, 243, 148, 113, 32, 226, 196, 55, + 96, 3, 117, 89, 64, 82, 220, 14, 29, 210, + 190, 29, 196, 43, 159, 214, 246, 35, 35, 102, + 28, 198, 198, 136, 3, 146, 4, 240, 60, 129, + 114, 171, 157, 225, 154, 198, 7, 88, 108, 86, + 20, 55, 219, 210, 140, 125, 52, 197, 53, 80, + 233, 63, 60, 85, 221, 14, 170, 251, 46, 22, + 253, 28, 81, 128, 93, 158, 60, 196, 91, 128, + 244, 66, 85, 177, 52, 35, 169, 191, 134, 216, + 231, 10, 143, 192, 89, 251, 245, 94, 140, 123, + 48, 87, 159, 164, 64, 121, 42, 143, 235, 202, + 183, 31, 39, 203, 59, 33, 106, 129, 25, 201, + 255, 198, 43, 72, 150, 100, 14, 206, 218, 20, + 96, 212, 95, 233, 236, 39, 82, 192, 252, 18, + 34, 21, 89, 88, 23, 28, 160, 73, 22, 208, + 168, 136, 39, 239, 17, 92, 250, 98, 125, 44, + 99, 189, 40, 252, 248, 70, 136, 75, 145, 188, + 10, 120, 30, 23, 184, 52, 228, 80, 163, 120, + 189, 205, 111, 168, 238, 215, 143, 194, 191, 133, + 181, 143, 198, 94, 193, 226, 97, 236, 137, 123, + 131, 35, 134, 209, 161, 184, 125, 216, 47, 29, + 15, 240, 30, 54, 162, 105, 236, 167, 222, 175, + 118, 157, 86, 159, 95, 39, 60, 218, 32, 234, + 115, 26, 253, 129, 156, 206, 50, 204, 136, 237, + 40, 134, 181, 93, 197, 207, 232, 71, 228, 69, + 148, 203, 204, 4, 183, 207, 184, 115, 181, 182, + 55, 134, 206, 108, 32, 123, 143, 86, 188, 189, + 235, 40, 171, 78, 157, 226, 74, 123, 0, 160, + 167, 200, 47, 33, 139, 240, 94, 182, 106, 55, + 88, 32, 196, 130, 120, 184, 149, 122, 41, 163, + 55, 49, 29, 229, 90, 93, 105, 2, 29, 63, + 169, 141, 152, 189, 193, 172, 86, 179, 80, 173, + 51, 144, 208, 244, 112, 230, 211, 186, 162, 211, + 136, 199, 102, 13, 164, 99, 174, 185, 154, 56, + 118, 4, 39, 189, 110, 23, 193, 183, 224, 176, + 26, 18, 30, 225, 164, 188, 243, 179, 74, 22, + 60, 36, 34, 207, 45, 36, 155, 117, 145, 168, + 219, 218, 114, 192, 21, 27, 189, 31, 175, 198, + 236, 47, 239, 63, 125, 195, 190, 127, 255, 230, + 221, 55, 236, 167, 191, 254, 237, 19, 227, 77, + 60, 54, 230, 54, 120, 125, 22, 34, 234, 50, + 197, 235, 90, 122, 238, 111, 63, 228, 162, 180, + 91, 44, 85, 227, 55, 144, 136, 138, 99, 50, + 244, 105, 41, 236, 247, 120, 133, 119, 191, 193, + 19, 61, 227, 176, 208, 182, 230, 203, 109, 134, + 91, 148, 2, 156, 84, 149, 38, 188, 29, 141, + 139, 62, 31, 179, 183, 81, 133, 99, 158, 252, + 110, 100, 134, 42, 218, 232, 155, 2, 31, 37, + 197, 170, 231, 25, 94, 148, 183, 72, 163, 187, + 97, 181, 201, 1, 233, 149, 217, 2, 179, 68, + 45, 170, 52, 143, 170, 189, 46, 141, 255, 166, + 0, 203, 193, 0, 89, 109, 220, 62, 8, 103, + 14, 52, 197, 196, 253, 132, 69, 219, 166, 216, + 68, 77, 10, 105, 106, 182, 7, 204, 223, 111, + 202, 102, 47, 215, 167, 194, 6, 191, 139, 226, + 38, 219, 179, 44, 5, 245, 198, 21, 198, 6, + 81, 50, 115, 6, 163, 134, 44, 178, 163, 42, + 162, 57, 132, 127, 201, 100, 108, 163, 216, 150, + 192, 86, 226, 60, 242, 86, 137, 246, 171, 17, + 115, 124, 188, 140, 87, 40, 182, 75, 45, 68, + 170, 145, 108, 138, 44, 145, 45, 232, 59, 228, + 23, 107, 242, 136, 116, 107, 50, 27, 79, 71, + 236, 237, 199, 15, 223, 249, 246, 145, 186, 222, + 139, 172, 151, 148, 95, 49, 127, 114, 128, 215, + 179, 243, 68, 18, 6, 62, 1, 49, 160, 105, + 132, 183, 228, 26, 46, 228, 61, 97, 2, 175, + 162, 229, 231, 166, 123, 13, 85, 135, 64, 167, + 148, 48, 213, 200, 161, 245, 71, 182, 92, 67, + 192, 65, 157, 211, 168, 233, 189, 200, 200, 250, + 177, 4, 133, 195, 88, 71, 112, 6, 58, 114, + 156, 151, 22, 27, 148, 108, 190, 114, 193, 153, + 238, 161, 221, 255, 118, 253, 194, 79, 194, 218, + 92, 199, 96, 168, 133, 168, 45, 226, 223, 79, + 174, 58, 186, 72, 23, 110, 48, 79, 233, 168, + 163, 184, 54, 173, 235, 51, 6, 45, 48, 218, + 172, 45, 81, 154, 195, 172, 121, 42, 233, 9, + 169, 160, 16, 128, 179, 216, 232, 80, 172, 14, + 169, 108, 153, 162, 183, 47, 129, 188, 209, 215, + 147, 175, 29, 149, 22, 93, 193, 91, 81, 30, + 129, 33, 219, 102, 25, 92, 56, 131, 190, 47, + 234, 246, 184, 147, 80, 64, 4, 131, 52, 97, + 127, 250, 51, 187, 152, 122, 243, 113, 97, 102, + 196, 61, 129, 52, 192, 0, 213, 12, 71, 22, + 0, 55, 217, 239, 66, 178, 33, 12, 237, 13, + 254, 94, 67, 254, 247, 102, 69, 202, 43, 170, + 139, 35, 67, 217, 76, 206, 217, 226, 155, 216, + 251, 24, 59, 65, 183, 4, 211, 114, 158, 126, + 169, 45, 99, 32, 110, 234, 219, 109, 84, 184, + 74, 164, 40, 197, 249, 184, 13, 75, 156, 140, + 211, 215, 132, 86, 166, 175, 162, 188, 105, 87, + 31, 118, 85, 42, 183, 142, 147, 78, 137, 207, + 10, 249, 86, 148, 19, 20, 75, 95, 6, 214, + 35, 130, 13, 101, 139, 66, 2, 17, 120, 223, + 63, 76, 174, 252, 202, 37, 126, 72, 251, 49, + 205, 133, 232, 10, 8, 165, 64, 62, 32, 96, + 23, 94, 49, 164, 152, 6, 112, 148, 218, 139, + 50, 159, 152, 187, 88, 224, 73, 186, 248, 46, + 66, 208, 51, 131, 148, 80, 244, 60, 103, 159, + 157, 230, 167, 37, 232, 199, 115, 71, 39, 138, + 18, 235, 162, 26, 156, 140, 184, 159, 217, 182, + 194, 27, 239, 207, 52, 35, 20, 212, 43, 115, + 180, 188, 151, 247, 204, 49, 58, 123, 121, 221, + 190, 184, 178, 103, 91, 97, 241, 179, 114, 94, + 106, 81, 237, 102, 29, 112, 186, 29, 85, 224, + 121, 207, 157, 16, 187, 98, 97, 177, 95, 50, + 30, 53, 4, 125, 157, 142, 29, 201, 140, 37, + 115, 122, 224, 137, 244, 204, 170, 40, 117, 164, + 212, 95, 224, 70, 184, 33, 235, 215, 66, 225, + 190, 124, 158, 203, 54, 157, 142, 33, 250, 76, + 136, 176, 61, 178, 134, 212, 235, 79, 125, 101, + 107, 204, 37, 188, 21, 149, 4, 178, 92, 58, + 44, 68, 218, 200, 106, 33, 57, 201, 150, 32, + 59, 103, 220, 92, 140, 155, 224, 49, 8, 242, + 22, 71, 205, 97, 152, 185, 131, 176, 147, 107, + 143, 195, 16, 127, 27, 229, 101, 44, 187, 227, + 81, 125, 99, 172, 15, 192, 187, 128, 52, 51, + 245, 23, 152, 0, 80, 182, 49, 94, 67, 2, + 187, 134, 89, 210, 23, 200, 21, 189, 228, 26, + 30, 231, 40, 7, 33, 25, 218, 179, 45, 95, + 24, 110, 205, 63, 132, 150, 209, 15, 208, 248, + 199, 244, 47, 135, 215, 13, 130, 13, 236, 238, + 151, 41, 36, 139, 11, 195, 115, 226, 67, 168, + 27, 180, 116, 0, 143, 45, 79, 252, 115, 174, + 28, 216, 226, 116, 184, 195, 240, 185, 239, 184, + 194, 45, 200, 225, 80, 95, 42, 35, 143, 101, + 250, 82, 182, 72, 168, 150, 139, 7, 29, 211, + 58, 213, 98, 43, 103, 9, 69, 111, 103, 29, + 95, 204, 113, 22, 165, 198, 9, 26, 1, 217, + 147, 231, 168, 68, 230, 165, 35, 182, 248, 106, + 4, 201, 233, 103, 8, 211, 179, 17, 68, 204, + 98, 195, 69, 50, 233, 47, 47, 224, 40, 246, + 204, 31, 245, 158, 98, 243, 115, 127, 21, 225, + 179, 191, 249, 171, 209, 28, 86, 90, 140, 166, + 103, 136, 194, 104, 122, 14, 40, 244, 65, 207, + 139, 64, 106, 77, 207, 26, 98, 157, 190, 233, + 197, 205, 243, 251, 84, 179, 122, 17, 62, 237, + 99, 13, 126, 70, 211, 211, 126, 246, 212, 156, + 7, 5, 166, 83, 131, 248, 15, 192, 62, 235, + 135, 13, 222, 77, 171, 252, 67, 193, 159, 247, + 131, 223, 226, 143, 170, 184, 219, 63, 136, 245, + 21, 79, 210, 10, 162, 198, 227, 8, 224, 108, + 88, 0, 3, 250, 185, 205, 97, 183, 185, 166, + 71, 1, 31, 204, 164, 151, 253, 224, 137, 65, + 1, 46, 241, 32, 46, 197, 25, 62, 62, 39, + 74, 26, 143, 195, 169, 1, 129, 214, 84, 224, + 236, 91, 204, 179, 80, 63, 222, 242, 192, 62, + 64, 21, 172, 97, 87, 13, 241, 24, 216, 107, + 39, 237, 62, 15, 39, 143, 128, 208, 73, 29, + 225, 209, 10, 223, 117, 112, 115, 119, 34, 147, + 222, 215, 236, 233, 218, 127, 102, 68, 107, 30, + 115, 122, 129, 31, 114, 191, 71, 213, 79, 7, + 74, 167, 210, 95, 99, 74, 182, 56, 0, 80, + 160, 98, 64, 244, 85, 59, 251, 14, 54, 48, + 21, 160, 236, 144, 98, 153, 191, 238, 137, 31, + 149, 23, 97, 42, 132, 72, 249, 174, 31, 12, + 85, 158, 31, 133, 41, 143, 207, 24, 98, 78, + 151, 93, 67, 219, 70, 229, 145, 152, 246, 201, + 255, 77, 218, 21, 111, 235, 166, 216, 24, 68, + 67, 254, 144, 224, 35, 83, 95, 166, 20, 199, + 152, 249, 151, 168, 136, 58, 217, 233, 164, 222, + 230, 231, 240, 201, 5, 229, 226, 11, 44, 179, + 33, 171, 175, 44, 254, 15, 78, 28, 94, 88, + 83, 232, 243, 53, 175, 141, 232, 124, 248, 24, + 196, 222, 130, 174, 184, 255, 225, 1, 245, 25, + 58, 229, 233, 237, 130, 205, 135, 190, 85, 243, + 1, 118, 119, 184, 227, 234, 137, 9, 7, 236, + 149, 74, 93, 82, 219, 197, 13, 3, 149, 38, + 63, 20, 236, 240, 209, 217, 71, 181, 24, 150, + 36, 82, 212, 161, 77, 148, 111, 163, 140, 246, + 0, 21, 42, 250, 240, 57, 218, 1, 219, 48, + 15, 210, 6, 142, 40, 239, 231, 14, 204, 196, + 128, 153, 113, 152, 153, 113, 146, 181, 1, 254, + 223, 228, 45, 142, 54, 239, 131, 150, 125, 52, + 181, 125, 39, 252, 125, 226, 125, 3, 83, 81, + 134, 66, 119, 196, 227, 122, 42, 232, 179, 61, + 111, 60, 146, 253, 34, 137, 118, 238, 2, 244, + 9, 231, 158, 132, 44, 212, 25, 175, 236, 31, + 209, 78, 201, 99, 145, 143, 137, 189, 153, 165, + 171, 139, 52, 7, 194, 197, 23, 199, 220, 199, + 139, 181, 174, 26, 169, 61, 193, 67, 111, 137, + 12, 43, 218, 79, 0, 26, 95, 206, 246, 127, + 173, 97, 131, 70, 126, 47, 219, 144, 207, 207, + 254, 71, 168, 104, 55, 203, 15, 149, 206, 127, + 175, 226, 81, 141, 177, 75, 213, 35, 20, 235, + 253, 23, 0, 31, 114, 59, 11, 11, 62, 67, + 52, 120, 55, 77, 247, 86, 56, 121, 62, 37, + 47, 177, 73, 152, 182, 174, 61, 130, 158, 245, + 221, 178, 232, 222, 151, 235, 41, 100, 157, 24, + 77, 178, 180, 26, 178, 175, 126, 155, 190, 155, + 190, 249, 189, 109, 157, 27, 205, 110, 151, 93, + 49, 149, 131, 141, 59, 8, 110, 201, 211, 125, + 166, 8, 63, 238, 89, 165, 238, 248, 164, 238, + 17, 189, 151, 247, 136, 140, 190, 15, 88, 75, + 252, 81, 186, 236, 176, 125, 164, 211, 60, 71, + 20, 249, 142, 85, 145, 87, 135, 131, 57, 223, + 5, 219, 42, 101, 11, 60, 123, 208, 4, 80, + 209, 245, 202, 58, 48, 236, 92, 87, 239, 45, + 110, 15, 20, 234, 229, 106, 221, 194, 48, 199, + 253, 3, 116, 101, 26, 33, 49, 157, 238, 221, + 119, 139, 231, 114, 144, 236, 199, 243, 190, 167, + 200, 101, 247, 30, 248, 144, 106, 254, 166, 211, + 64, 16, 92, 212, 94, 245, 87, 175, 60, 106, + 196, 35, 62, 164, 192, 150, 190, 254, 110, 107, + 155, 44, 147, 154, 245, 255, 251, 249, 70, 67, + 19, 123, 247, 39, 138, 94, 58, 174, 124, 109, + 28, 93, 122, 71, 169, 99, 208, 215, 214, 161, + 166, 177, 160, 195, 166, 246, 184, 73, 67, 192, + 34, 187, 53, 200, 185, 242, 111, 119, 186, 53, + 226, 123, 136, 97, 100, 138, 65, 236, 110, 136, + 176, 160, 205, 206, 229, 102, 147, 158, 9, 1, + 237, 116, 252, 197, 23, 48, 222, 178, 122, 157, + 175, 250, 14, 174, 148, 43, 69, 116, 40, 167, + 197, 119, 93, 208, 69, 190, 128, 158, 157, 221, + 220, 204, 224, 207, 28, 95, 102, 152, 226, 115, + 43, 66, 237, 209, 197, 30, 121, 130, 165, 189, + 130, 231, 161, 5, 61, 232, 208, 49, 151, 175, + 34, 239, 60, 148, 105, 172, 53, 124, 59, 209, + 119, 37, 159, 152, 211, 30, 73, 116, 6, 0, + 83, 172, 96, 37, 158, 65, 148, 215, 35, 212, + 181, 11, 255, 84, 124, 26, 186, 111, 111, 44, + 173, 162, 61, 49, 56, 112, 127, 147, 30, 19, + 49, 132, 67, 39, 6, 107, 126, 23, 36, 233, + 10, 31, 33, 163, 135, 47, 212, 1, 26, 72, + 142, 92, 55, 207, 19, 47, 192, 161, 109, 102, + 187, 134, 125, 132, 132, 223, 235, 122, 203, 159, + 152, 88, 244, 66, 145, 228, 25, 67, 95, 51, + 223, 229, 5, 243, 3, 46, 176, 101, 7, 42, + 223, 225, 170, 130, 34, 248, 153, 212, 81, 216, + 80, 140, 158, 75, 202, 217, 103, 193, 52, 165, + 190, 208, 170, 67, 216, 0, 111, 142, 225, 145, + 250, 232, 85, 181, 231, 215, 171, 29, 156, 43, + 111, 207, 40, 123, 80, 247, 75, 142, 158, 127, + 116, 213, 203, 252, 136, 187, 219, 7, 156, 75, + 127, 29, 236, 240, 2, 226, 185, 102, 235, 196, + 206, 247, 57, 0, 169, 243, 64, 138, 61, 119, + 88, 48, 29, 203, 58, 82, 149, 12, 219, 186, + 73, 75, 244, 124, 161, 214, 175, 71, 210, 23, + 241, 36, 176, 56, 5, 165, 227, 108, 244, 184, + 202, 205, 30, 33, 188, 246, 12, 149, 188, 133, + 194, 147, 29, 49, 25, 15, 109, 247, 151, 199, + 72, 230, 24, 181, 31, 16, 15, 1, 233, 23, + 222, 189, 253, 81, 239, 98, 71, 93, 76, 150, + 187, 33, 255, 19, 127, 122, 203, 112, 96, 223, + 35, 111, 203, 247, 157, 197, 119, 22, 53, 162, + 197, 64, 96, 145, 122, 218, 3, 245, 79, 237, + 187, 23, 52, 184, 47, 173, 127, 107, 100, 156, + 40, 118, 58, 159, 78, 61, 239, 193, 193, 207, + 23, 58, 140, 163, 43, 107, 254, 221, 27, 205, + 24, 146, 13, 189, 72, 236, 161, 2, 232, 217, + 49, 30, 177, 155, 106, 227, 110, 168, 158, 118, + 100, 163, 217, 252, 244, 236, 252, 197, 203, 139, + 87, 211, 232, 58, 78, 248, 242, 205, 183, 111, + 223, 189, 255, 78, 38, 118, 148, 33, 202, 196, + 222, 120, 229, 134, 120, 231, 2, 101, 249, 186, + 81, 61, 180, 251, 61, 48, 183, 250, 132, 155, + 41, 218, 41, 17, 95, 245, 149, 241, 219, 217, + 120, 102, 228, 140, 98, 219, 66, 178, 233, 188, + 199, 13, 19, 101, 236, 15, 242, 206, 43, 86, + 68, 126, 252, 135, 137, 205, 27, 179, 196, 110, + 247, 24, 23, 176, 156, 250, 160, 121, 149, 202, + 17, 145, 120, 240, 53, 100, 179, 115, 31, 103, + 197, 45, 38, 47, 230, 184, 95, 11, 60, 85, + 7, 167, 188, 224, 239, 83, 101, 6, 63, 254, + 214, 59, 130, 204, 219, 177, 248, 78, 229, 176, + 179, 94, 148, 116, 238, 171, 106, 22, 26, 251, + 78, 71, 83, 123, 139, 213, 206, 163, 126, 230, + 83, 126, 94, 227, 208, 111, 197, 226, 249, 165, + 92, 48, 142, 176, 14, 40, 182, 1, 234, 9, + 3, 186, 141, 160, 31, 48, 19, 15, 35, 87, + 124, 217, 255, 218, 41, 247, 70, 38, 109, 57, + 165, 111, 180, 106, 130, 184, 164, 56, 154, 130, + 190, 81, 15, 64, 3, 154, 186, 63, 102, 80, + 230, 157, 50, 184, 255, 50, 225, 30, 237, 71, + 228, 243, 233, 146, 75, 247, 60, 143, 160, 155, + 150, 71, 73, 186, 139, 233, 109, 148, 117, 30, + 162, 237, 194, 144, 146, 164, 211, 56, 249, 164, + 73, 72, 51, 165, 47, 214, 63, 219, 11, 141, + 170, 9, 192, 209, 191, 161, 184, 166, 171, 26, + 251, 150, 144, 208, 5, 52, 53, 83, 188, 159, + 143, 45, 22, 230, 19, 221, 248, 166, 201, 178, + 204, 100, 249, 116, 114, 23, 236, 118, 187, 0, + 215, 192, 202, 5, 237, 162, 120, 210, 62, 212, + 45, 238, 44, 95, 9, 192, 238, 45, 47, 252, + 252, 191, 142, 59, 83, 186, 151, 120, 5, 125, + 66, 52, 255, 17, 187, 24, 198, 233, 158, 86, + 83, 208, 171, 142, 31, 100, 52, 198, 13, 218, + 131, 166, 99, 201, 82, 191, 111, 165, 167, 82, + 74, 47, 156, 204, 249, 165, 161, 37, 84, 128, + 177, 68, 254, 35, 236, 103, 177, 64, 64, 61, + 81, 146, 64, 236, 168, 125, 130, 111, 151, 245, + 213, 55, 205, 143, 202, 193, 240, 125, 43, 188, + 231, 216, 188, 173, 113, 132, 254, 1, 131, 79, + 197, 19, 4, 249, 170, 140, 188, 232, 209, 250, + 54, 164, 154, 245, 70, 17, 73, 101, 202, 134, + 177, 181, 71, 199, 187, 138, 125, 76, 162, 237, + 189, 50, 31, 202, 234, 142, 198, 197, 125, 90, + 66, 125, 244, 187, 97, 154, 184, 244, 99, 181, + 166, 231, 34, 28, 113, 246, 218, 69, 144, 38, + 230, 96, 85, 59, 236, 73, 61, 151, 237, 179, + 74, 176, 126, 56, 153, 8, 149, 192, 7, 40, + 188, 207, 78, 244, 176, 128, 82, 77, 235, 1, + 253, 30, 183, 64, 3, 213, 107, 109, 252, 6, + 78, 46, 174, 123, 125, 85, 209, 119, 200, 30, + 47, 31, 39, 95, 89, 226, 27, 14, 47, 69, + 233, 214, 48, 190, 214, 73, 119, 36, 126, 248, + 137, 242, 199, 193, 204, 241, 6, 253, 118, 114, + 232, 89, 14, 23, 131, 206, 80, 37, 209, 126, + 123, 52, 253, 131, 255, 229, 147, 247, 23, 34, + 86, 174, 30, 55, 231, 108, 101, 232, 185, 42, + 209, 146, 208, 191, 65, 163, 212, 225, 154, 110, + 9, 209, 43, 175, 168, 84, 146, 208, 20, 131, + 113, 250, 17, 172, 67, 120, 225, 71, 73, 81, + 108, 110, 245, 155, 112, 232, 247, 61, 163, 16, + 68, 203, 234, 56, 142, 245, 134, 33, 121, 187, + 1, 201, 56, 70, 255, 186, 133, 239, 193, 74, + 47, 65, 191, 84, 222, 221, 124, 81, 202, 145, + 87, 126, 250, 21, 144, 86, 233, 188, 43, 101, + 8, 125, 123, 1, 175, 204, 104, 234, 253, 100, + 32, 11, 22, 143, 170, 183, 134, 126, 116, 234, + 33, 48, 205, 41, 49, 116, 206, 65, 78, 174, + 254, 231, 127, 1, 232, 152, 205, 79, 134, 110, + 0, 0}; diff --git a/src/os/host-main.c b/src/os/host-main.c new file mode 100644 index 0000000..28a2468 --- /dev/null +++ b/src/os/host-main.c @@ -0,0 +1,149 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Name: host-main.c +** What: Host environment main entry point +** Note: OS independent +** Author: Carl Sassenrath +** Purpose: +** Provides the outer environment that calls the REBOL lib. +** This module is more or less just an example and includes +** a very simple console prompt. +** +************************************************************************ +** +** WARNING to PROGRAMMERS: +** +** This open source code is strictly managed to maintain +** source consistency according to our standards, not yours. +** +** 1. Keep code clear and simple. +** 2. Document odd code, your reasoning, or gotchas. +** 3. Use our source style for code, indentation, comments, etc. +** 4. It must work on Win32, Linux, OS X, BSD, big/little endian. +** 5. Test your code really well before submitting it. +** +***********************************************************************/ + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#define OS_LIB_TABLE // include the host-lib dispatch table + +#include "reb-host.h" // standard host include files +#include "host-lib.h" // OS host library (dispatch table) +#include "rebol-lib.h" // REBOL library (function prototypes) +#include "host-init.h" // custom REBOL boot code + +/**********************************************************************/ + +#define PROMPT_STR ">> " +#define RESULT_STR "== " + +REBARGS Main_Args; + +#ifdef TO_WIN32 +HINSTANCE App_Instance = 0; +#endif + +// Host bare-bones stdio functs: +void Open_StdIO(void); +void Put_Str(char *buf); +REBYTE *Get_Str(); + +void Host_Crash(REBYTE *reason) { + OS_Crash("REBOL host failure", reason); +} + + +/*********************************************************************** +** +** MAIN ENTRY POINT +** +** Win32 args: +** inst: current instance of the application (app handle) +** prior: always NULL (use a mutex for single inst of app) +** cmd: command line string (or use GetCommandLine) +** show: how app window is to be shown (e.g. maximize, minimize, etc.) +** +** Win32 return: +** If the function succeeds, terminating when it receives a WM_QUIT +** message, it should return the exit value contained in that +** message's wParam parameter. If the function terminates before +** entering the message loop, it should return zero. +** +** Posix args: as you would expect in C. +** Posix return: ditto. +** +***********************************************************************/ + +#ifdef TO_WIN32 +int WINAPI WinMain(HINSTANCE inst, HINSTANCE prior, LPSTR cmd, int show) +#else +int main(int argc, char **argv) +#endif +{ + REBYTE vers[8]; + REBYTE *line; + REBINT n; + +#ifdef TO_WIN32 // In Win32 get args manually: + int argc; + REBCHR **argv; + // Fetch the win32 unicoded program arguments: + argv = CommandLineToArgvW(GetCommandLineW(), &argc); + App_Instance = inst; +#endif + + Host_Lib = &Host_Lib_Init; + + Parse_Args(argc, argv, &Main_Args); + + vers[0] = 5; // len + Reb_Version(&vers[0]); + + Open_StdIO(); // also sets up interrupt handler + + // Initialize the REBOL library: + if (!Host_Lib) Host_Crash("Missing host lib"); + n = Reb_Init(&Main_Args, Host_Lib); + if (n == 1) Host_Crash("REBOL DLL wrong size"); + if (n == 2) Host_Crash("REBOL DLL wrong version/checksum"); + + // Initialize host bundled source code: + Reb_Do_Binary(Reb_Init_Code, REB_INIT_SIZE, 0, 0); + + // Run REBOL's mezzanine bootstrap: + n = (Main_Args.options & RO_NO_BOOT) ? TRUE : Reb_Start(0); // TRUE on halt + + // Console line input loop (just an example, can be improved): + if ( + !Main_Args.script // no script was provided + || n // script was halted + || Main_Args.options & RO_HALT // --halt option + ){ + while (TRUE) { + Put_Str(PROMPT_STR); + if ((line = Get_Str())) { + Reb_Do_String(line); + Reb_Print_TOS(0, RESULT_STR); + OS_Free(line); + } + else break; // EOS + } + } + + OS_Call_Device(RDI_STDIO, RDC_CLOSE); + + return 0; +} diff --git a/src/os/host-stdio.c b/src/os/host-stdio.c new file mode 100644 index 0000000..3a007b7 --- /dev/null +++ b/src/os/host-stdio.c @@ -0,0 +1,159 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Simple helper functions for host-side standard I/O +** Build: A95 +** Date: 27-Nov-2009 +** File: host-stdio.c +** Author: Carl Sassenrath +** Caution: OS independent +** Purpose: +** Interfaces to the stdio device for standard I/O on the host. +** All stdio within REBOL uses UTF-8 encoding so the functions +** shown here operate on UTF-8 bytes, regardless of the OS. +** The conversion to wide-chars for OSes like Win32 is done in +** the StdIO Device code. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include "reb-host.h" +#include "host-lib.h" + +void Host_Crash(REBYTE *reason); + +// Temporary globals: (either move or remove?!) +REBREQ Std_IO_Req; +static REBYTE *inbuf; +static REBCNT inbuf_len = 32*1024; + +static REBYTE *Get_Next_Line() +{ + REBYTE *bp = inbuf; + REBYTE *out; + REBCNT len; + + // Scan for line terminator or end: + for (bp = inbuf; *bp != CR && *bp != LF && *bp != 0; bp++); + + // If found, copy the line and remove it from buffer: + if (*bp) { + if (*bp == CR && bp[1] == LF) bp++; + len = bp - inbuf; + out = OS_Make(len + 2); + COPY_BYTES(out, inbuf, len+1); + out[len+1] = 0; + MOVE_MEM(inbuf, bp+1, 1+strlen(bp+1)); + return out; + } + + return 0; // more input needed +} + +static int Fetch_Buf() +{ + REBCNT len = strlen(inbuf); + + Std_IO_Req.data = inbuf + len; + Std_IO_Req.length = inbuf_len - len - 1; + Std_IO_Req.actual = 0; + + OS_Do_Device(&Std_IO_Req, RDC_READ); + + // If error, don't crash, just ignore it: + if (Std_IO_Req.error) return 0; //Host_Crash("stdio read"); + + // Terminate (LF) last line? + if (len > 0 && Std_IO_Req.actual == 0) { + inbuf[len++] = LF; + inbuf[len] = 0; + return TRUE; + } + + // Null terminate buffer: + len = Std_IO_Req.actual; + Std_IO_Req.data[len] = 0; + return len > 0; +} + + +/*********************************************************************** +** +*/ void Open_StdIO(void) +/* +** Open REBOL's standard IO device. This same device is used +** by both the host code and the R3 DLL itself. +** +** This must be done before any other initialization is done +** in order to output banners or errors. +** +***********************************************************************/ +{ + CLEARS(&Std_IO_Req); + Std_IO_Req.clen = sizeof(Std_IO_Req); + Std_IO_Req.device = RDI_STDIO; + + OS_Do_Device(&Std_IO_Req, RDC_OPEN); + + if (Std_IO_Req.error) Host_Crash("stdio open"); + + inbuf = OS_Make(inbuf_len); + inbuf[0] = 0; +} + + +/*********************************************************************** +** +*/ REBYTE *Get_Str() +/* +** Get input of a null terminated UTF-8 string. +** Divides the input into lines. +** Buffers multiple lines if needed. +** Returns NULL on end of stream. +** +***********************************************************************/ +{ + REBYTE *line; + + if ((line = Get_Next_Line())) return line; + + if (Fetch_Buf()) return Get_Next_Line(); + + return 0; +} + + +/*********************************************************************** +** +*/ void Put_Str(REBYTE *buf) +/* +** Outputs a null terminated UTF-8 string. +** If buf is larger than StdIO Device allows, error out. +** OS dependent line termination must be done prior to call. +** +***********************************************************************/ +{ + Std_IO_Req.length = strlen(buf); + Std_IO_Req.data = (REBYTE*)buf; + Std_IO_Req.actual = 0; + + OS_Do_Device(&Std_IO_Req, RDC_WRITE); + + if (Std_IO_Req.error) Host_Crash("stdio write"); +} diff --git a/src/os/win32/dev-clipboard.c b/src/os/win32/dev-clipboard.c new file mode 100644 index 0000000..b52751f --- /dev/null +++ b/src/os/win32/dev-clipboard.c @@ -0,0 +1,194 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: Clipboard access for Win32 +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-clipboard.c +** Author: Carl Sassenrath +** Purpose: +** Provides a very simple interface to the clipboard for text. +** May be expanded in the future for images, etc. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include + +#include "reb-host.h" +#include "host-lib.h" +#include "sys-net.h" + + +/*********************************************************************** +** +*/ DEVICE_CMD Open_Clipboard(REBREQ *req) +/* +***********************************************************************/ +{ + SET_OPEN(req); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Close_Clipboard(REBREQ *req) +/* +***********************************************************************/ +{ + SET_CLOSED(req); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Read_Clipboard(REBREQ *req) +/* +***********************************************************************/ +{ + HANDLE data; + REBUNI *cp; + REBUNI *bin; + REBINT len; + + req->actual = 0; + + // If there is no clipboard data: + if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) { + req->error = 10; + return DR_ERROR; + } + + if (!OpenClipboard(NULL)) { + req->error = 20; + return DR_ERROR; + } + + // Read the UTF-8 data: + if ((data = GetClipboardData(CF_UNICODETEXT)) == NULL) { + CloseClipboard(); + req->error = 30; + return DR_ERROR; + } + + cp = GlobalLock(data); + if (!cp) { + GlobalUnlock(data); + CloseClipboard(); + req->error = 40; + return DR_ERROR; + } + + len = LEN_STR(cp); // wide chars + bin = OS_Make((len+1) * sizeof(REBCHR)); + COPY_STR(bin, cp, len); + + GlobalUnlock(data); + + CloseClipboard(); + + SET_FLAG(req->flags, RRF_WIDE); + req->data = (REBYTE *)bin; + req->actual = len * sizeof(REBCHR); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Write_Clipboard(REBREQ *req) +/* +** Works for Unicode and ASCII strings. +** Length is number of bytes passed (not number of chars). +** +***********************************************************************/ +{ + HANDLE data; + REBYTE *bin; + REBCNT err; + REBINT len = req->length; // in bytes + + req->actual = 0; + + data = GlobalAlloc(GHND, len + 4); + if (data == NULL) { + req->error = 5; + return DR_ERROR; + } + + // Lock and copy the string: + bin = GlobalLock(data); + if (bin == NULL) { + req->error = 10; + return DR_ERROR; + } + + COPY_MEM(bin, req->data, len); + bin[len] = 0; + GlobalUnlock(data); + + if (!OpenClipboard(NULL)) { + req->error = 20; + return DR_ERROR; + } + + EmptyClipboard(); + + err = !SetClipboardData(GET_FLAG(req->flags, RRF_WIDE) ? CF_UNICODETEXT : CF_TEXT, data); + + CloseClipboard(); + + if (err) { + req->error = 50; + return DR_ERROR; + } + + req->actual = len; + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Poll_Clipboard(REBREQ *req) +/* +***********************************************************************/ +{ + return DR_DONE; +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = +{ + 0, + 0, + Open_Clipboard, + Close_Clipboard, + Read_Clipboard, + Write_Clipboard, + Poll_Clipboard, +}; + +DEFINE_DEV(Dev_Clipboard, "Clipboard", 1, Dev_Cmds, RDC_MAX, 0); diff --git a/src/os/win32/dev-event.c b/src/os/win32/dev-event.c new file mode 100644 index 0000000..b582dd6 --- /dev/null +++ b/src/os/win32/dev-event.c @@ -0,0 +1,203 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: Event handler for Win32 +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-event.c +** Author: Carl Sassenrath +** Purpose: +** Processes events to pass to REBOL. Note that events are +** used for more than just windowing. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include "reb-host.h" +#include "host-lib.h" + +void Done_Device(int handle, int error); + +// Move or remove globals? !? +HWND Event_Handle = 0; // Used for async DNS +static int Timer_Id = 0; // The timer we are using + +extern HINSTANCE App_Instance; // From Main module. + + +/*********************************************************************** +** +*/ LRESULT CALLBACK REBOL_Event_Proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +/* +** The minimal default event handler. +** +***********************************************************************/ +{ + switch(msg) { + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + // Default processing that we do not care about: + return DefWindowProc(hwnd, msg, wparam, lparam); + } + return 0; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Init_Events(REBREQ *dr) +/* +** Initialize the event device. +** +** Create a hidden window to handle special events, +** such as timers and async DNS. +** +***********************************************************************/ +{ + REBDEV *dev = (REBDEV*)dr; // just to keep compiler happy + WNDCLASSEX wc = {0}; + + // Register event object class: + wc.cbSize = sizeof(wc); + wc.lpszClassName = TEXT("REBOL-Events"); + wc.hInstance = App_Instance; + wc.lpfnWndProc = REBOL_Event_Proc; + if (!RegisterClassEx(&wc)) return DR_ERROR; + + // Create the hidden window: + Event_Handle = CreateWindowEx( + 0, + wc.lpszClassName, + wc.lpszClassName, + 0,0,0,0,0,0, + NULL, App_Instance, NULL + ); + + if (!Event_Handle) return DR_ERROR; + + SET_FLAG(dev->flags, RDF_INIT); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Poll_Events(REBREQ *req) +/* +** Poll for events and process them. +** Returns 1 if event found, else 0. +** +** MS Notes: +** +** "The PeekMessage function normally does not remove WM_PAINT +** messages from the queue. WM_PAINT messages remain in the queue +** until they are processed." +** +***********************************************************************/ +{ + MSG msg; + int flag = DR_DONE; + + // Are there messages to process? + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + flag = DR_PEND; + if (msg.message == WM_TIMER) + break; + if (msg.message == WM_DNS) + Done_Device(msg.wParam, msg.lParam>>16); // error code + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return flag; // different meaning compared to most commands +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Query_Events(REBREQ *req) +/* +** Wait for an event or a timeout sepecified by req->length. +** This is used by WAIT as the main timing method. +** +***********************************************************************/ +{ + MSG msg; + + // Set timer (we assume this is very fast): + Timer_Id = SetTimer(0, Timer_Id, req->length, 0); + + // Wait for message or the timer: + if (GetMessage(&msg, NULL, 0, 0)) { + //printf("Msg: %d\n", msg.message); + if (msg.message == WM_DNS) + Done_Device(msg.wParam, msg.lParam>>16); // error code + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // Quickly check for other events: + Poll_Events(0); + + //if (Timer_Id) KillTimer(0, Timer_Id); + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Connect_Events(REBREQ *req) +/* +** Simply keeps the request pending for polling purposes. +** Use Abort_Device to remove it. +** +***********************************************************************/ +{ + return DR_PEND; // keep pending +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = { + Init_Events, // init device driver resources + 0, // RDC_QUIT, // cleanup device driver resources + 0, // RDC_OPEN, // open device unit (port) + 0, // RDC_CLOSE, // close device unit + 0, // RDC_READ, // read from unit + 0, // RDC_WRITE, // write to unit + Poll_Events, + Connect_Events, + Query_Events, +}; + +DEFINE_DEV(Dev_Event, "OS Events", 1, Dev_Cmds, RDC_MAX, 0); diff --git a/src/os/win32/dev-file.c b/src/os/win32/dev-file.c new file mode 100644 index 0000000..9fa2270 --- /dev/null +++ b/src/os/win32/dev-file.c @@ -0,0 +1,462 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: File access for Win32 +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-file.c +** Author: Carl Sassenrath +** Purpose: File open, close, read, write, and other actions. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include + +#include "reb-host.h" +#include "host-lib.h" + +// MSDN V6 missed this define: +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + + +/*********************************************************************** +** +** Local Functions +** +***********************************************************************/ + +static BOOL Seek_File_64(REBREQ *file) +{ + // Performs seek and updates index value. TRUE on scuccess. + // On error, returns FALSE and sets file->error field. + HANDLE h = (HANDLE)file->handle; + DWORD result; + DWORD highint; + + if (file->file.index == -1) { + // Append: + highint = 0; + result = SetFilePointer(h, 0, &highint, FILE_END); + } + else { + // Line below updates indexh if it is affected: + highint = (long)(file->file.index >> 32); + result = SetFilePointer(h, (long)(file->file.index), &highint, FILE_BEGIN); + } + + if (result == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + file->error = -RFE_NO_SEEK; + return 0; + } + + file->file.index = ((i64)highint << 32) + result; + + return 1; +} + + +/*********************************************************************** +** +*/ static int Read_Directory(REBREQ *dir, REBREQ *file) +/* +** This function will read a file directory, one file entry +** at a time, then close when no more files are found. +** +** Procedure: +** +** This function is passed directory and file arguments. +** The dir arg provides information about the directory to read. +** The file arg is used to return specific file information. +** +** To begin, this function is called with a dir->handle that +** is set to zero and a dir->file.path string for the directory. +** +** The directory is opened and a handle is stored in the dir +** structure for use on subsequent calls. If an error occurred, +** dir->error is set to the error code and -1 is returned. +** The dir->size field can be set to the number of files in the +** dir, if it is known. The dir->file.index field can be used by this +** function to store information between calls. +** +** If the open succeeded, then information about the first file +** is stored in the file argument and the function returns 0. +** On an error, the dir->error is set, the dir is closed, +** dir->handle is nulled, and -1 is returned. +** +** The caller loops until all files have been obtained. This +** action should be uninterrupted. (The caller should not perform +** additional OS or IO operations between calls.) +** +** When no more files are found, the dir is closed, dir->handle +** is nulled, and 1 is returned. No file info is returned. +** (That is, this function is called one extra time. This helps +** for OSes that may deallocate file strings on dir close.) +** +** Note that the dir->file.path can contain wildcards * and ?. The +** processing of these can be done in the OS (if supported) or +** by a separate filter operation during the read. +** +** Store file date info in file->file.index or other fields? +** Store permissions? Ownership? Groups? Or, require that +** to be part of a separate request? +** +***********************************************************************/ +{ + WIN32_FIND_DATA info; + HANDLE h= (HANDLE)(dir->handle); + REBCHR *cp = 0; + + if (!h) { + + // Read first file entry: + h = FindFirstFile(dir->file.path, &info); + if (h == INVALID_HANDLE_VALUE) { + dir->error = -RFE_OPEN_FAIL; + return DR_ERROR; + } + dir->handle = h; + CLR_FLAG(dir->flags, RRF_DONE); + cp = info.cFileName; + + } + + // Skip over the . and .. dir cases: + while (cp == 0 || (cp[0] == '.' && (cp[1] == 0 || cp[1] == '.'))) { + + // Read next file entry, or error: + if (!FindNextFile(h, &info)) { + dir->error = GetLastError(); + FindClose(h); + dir->handle = 0; + if (dir->error != ERROR_NO_MORE_FILES) return DR_ERROR; + dir->error = 0; + SET_FLAG(dir->flags, RRF_DONE); // no more files + return DR_DONE; + } + cp = info.cFileName; + + } + + file->modes = 0; + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) SET_FLAG(file->modes, RFM_DIR); + COPY_STR(file->file.path, info.cFileName, MAX_FILE_NAME); + file->file.size = ((i64)info.nFileSizeHigh << 32) + info.nFileSizeLow; + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Open_File(REBREQ *file) +/* +** Open the specified file with the given modes. +** +** Notes: +** 1. The file path is provided in REBOL format, and must be +** converted to local format before it is used. +** 2. REBOL performs the required access security check before +** calling this function. +** 3. REBOL clears necessary fields of file structure before +** calling (e.g. error and size fields). +** +** !! Confirm that /seek /append works properly. +** +***********************************************************************/ +{ + DWORD attrib = FILE_ATTRIBUTE_NORMAL; + DWORD access = 0; + DWORD create = 0; + HANDLE h; + BY_HANDLE_FILE_INFORMATION info; + + // Set the access, creation, and attribute for file creation: + if (GET_FLAG(file->modes, RFM_READ)) { + access |= GENERIC_READ; + create = OPEN_EXISTING; + } + + if (GET_FLAGS(file->modes, RFM_WRITE, RFM_APPEND)) { + access |= GENERIC_WRITE; + if ( + GET_FLAG(file->modes, RFM_NEW) || + !( + GET_FLAG(file->modes, RFM_READ) || + GET_FLAG(file->modes, RFM_APPEND) || + GET_FLAG(file->modes, RFM_SEEK) + ) + ) create = CREATE_ALWAYS; + else create = OPEN_ALWAYS; + } + + attrib |= GET_FLAG(file->modes, RFM_SEEK) ? FILE_FLAG_RANDOM_ACCESS : FILE_FLAG_SEQUENTIAL_SCAN; + + if (GET_FLAG(file->modes, RFM_READONLY)) + attrib |= FILE_ATTRIBUTE_READONLY; + + if (!access) { + file->error = -RFE_NO_MODES; + goto fail; + } + + // Open the file (yes, this is how windows does it, the nutty kids): + h = CreateFile(file->file.path, access, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, create, attrib, 0); + if (h == INVALID_HANDLE_VALUE) { + file->error = -RFE_OPEN_FAIL; + goto fail; + } + + // Confirm that a seek-mode file is actually seekable: + if (GET_FLAG(file->modes, RFM_SEEK)) { + // Below should work because we are seeking to 0: + if (SetFilePointer(h, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + CloseHandle(h); + file->error = -RFE_BAD_SEEK; + goto fail; + } + } + + // Fetch file size (if fails, then size is assumed zero): + if (GetFileInformationByHandle(h, &info)) { + file->file.size = ((i64)(info.nFileSizeHigh) << 32) + info.nFileSizeLow; + file->file.time.l = info.ftLastWriteTime.dwLowDateTime; + file->file.time.h = info.ftLastWriteTime.dwHighDateTime; + } + + file->handle = (void *)h; + + return DR_DONE; + +fail: + return DR_ERROR; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Close_File(REBREQ *file) +/* +** Closes a previously opened file. +** +***********************************************************************/ +{ + if (file->handle) { + CloseHandle((HANDLE)(file->handle)); + file->handle = 0; + } + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Read_File(REBREQ *file) +/* +***********************************************************************/ +{ + if (GET_FLAG(file->modes, RFM_DIR)) { + return Read_Directory(file, (REBREQ*)file->data); + } + + if (!file->handle) { + file->error = -RFE_NO_HANDLE; + return DR_ERROR; + } + + if (file->modes & ((1 << RFM_SEEK) | (1 << RFM_RESEEK))) { + CLR_FLAG(file->modes, RFM_RESEEK); + if (!Seek_File_64(file)) return DR_ERROR; + } + + if (!ReadFile(file->handle, file->data, file->length, &file->actual, 0)) { + file->error = -RFE_BAD_READ; + return DR_ERROR; + } else { + file->file.index += file->actual; + } + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Write_File(REBREQ *file) +/* +** Bug?: update file->size value after write !? +** +***********************************************************************/ +{ + DWORD result; + DWORD size_high, size_low; + + if (!file->handle) { + file->error = -RFE_NO_HANDLE; + return DR_ERROR; + } + + if (GET_FLAG(file->modes, RFM_APPEND)) { + CLR_FLAG(file->modes, RFM_APPEND); + SetFilePointer(file->handle, 0, 0, FILE_END); + } + + if (file->modes & ((1 << RFM_SEEK) | (1 << RFM_RESEEK) | (1 << RFM_TRUNCATE))) { + CLR_FLAG(file->modes, RFM_RESEEK); + if (!Seek_File_64(file)) return DR_ERROR; + if (GET_FLAG(file->modes, RFM_TRUNCATE)) + SetEndOfFile(file->handle); + } + + if (file->length != 0) { + if (!WriteFile(file->handle, file->data, file->length, &file->actual, 0)) { + result = GetLastError(); + if (result == ERROR_HANDLE_DISK_FULL) file->error = -RFE_DISK_FULL; + else file->error = -RFE_BAD_WRITE; + return DR_ERROR; + } + } + + size_low = GetFileSize(file->handle, &size_high); + if (size_low == 0xffffffff) { + result = GetLastError(); + file->error = -RFE_BAD_WRITE; + return DR_ERROR; + } + + file->file.size = ((i64)size_high << 32) + (i64)size_low; + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Query_File(REBREQ *file) +/* +** Obtain information about a file. Return TRUE on success. +** On error, return FALSE and set file->error code. +** +** Note: time is in local format and must be converted +** +***********************************************************************/ +{ + WIN32_FILE_ATTRIBUTE_DATA info; + + if (!GetFileAttributesEx(file->file.path, GetFileExInfoStandard, &info)) { + file->error = GetLastError(); + return DR_ERROR; + } + + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) SET_FLAG(file->modes, RFM_DIR); + else CLR_FLAG(file->modes, RFM_DIR); + file->file.size = ((i64)info.nFileSizeHigh << 32) + (i64)info.nFileSizeLow; + file->file.time.l = info.ftLastWriteTime.dwLowDateTime; + file->file.time.h = info.ftLastWriteTime.dwHighDateTime; + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Create_File(REBREQ *file) +/* +***********************************************************************/ +{ + if (GET_FLAG(file->modes, RFM_DIR)) { + if (CreateDirectory(file->file.path, 0)) return DR_DONE; + file->error = GetLastError(); + return DR_ERROR; + } else + return Open_File(file); +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Delete_File(REBREQ *file) +/* +** Delete a file or directory. Return TRUE if it was done. +** The file->file.path provides the directory path and name. +** For errors, return FALSE and set file->error to error code. +** +** Note: Dirs must be empty to succeed +** +***********************************************************************/ +{ + if (GET_FLAG(file->modes, RFM_DIR)) { + if (RemoveDirectory(file->file.path)) return DR_DONE; + } else + if (DeleteFile(file->file.path)) return DR_DONE; + + file->error = GetLastError(); + return DR_ERROR; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Rename_File(REBREQ *file) +/* +** Rename a file or directory. +** Note: cannot rename across file volumes. +** +***********************************************************************/ +{ + if (MoveFile((REBCHR*)(file->file.path), (REBCHR*)(file->data))) return DR_DONE; + file->error = GetLastError(); + return DR_ERROR; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Poll_File(REBREQ *file) +/* +***********************************************************************/ +{ + return DR_DONE; // files are synchronous (currently) +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = { + 0, + 0, + Open_File, + Close_File, + Read_File, + Write_File, + Poll_File, + 0, // connect + Query_File, + 0, // modify + Create_File, + Delete_File, + Rename_File, +}; + +DEFINE_DEV(Dev_File, "File IO", 1, Dev_Cmds, RDC_MAX, sizeof(REBREQ)); diff --git a/src/os/win32/dev-stdio.c b/src/os/win32/dev-stdio.c new file mode 100644 index 0000000..fae6f37 --- /dev/null +++ b/src/os/win32/dev-stdio.c @@ -0,0 +1,373 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: Device: Standard I/O for Win32 +** Build: A95 +** Date: 27-Nov-2009 +** File: dev-stdio.c +** Author: Carl Sassenrath +** Purpose: +** Provides basic I/O streams support for redirection and +** opening a console window if necessary. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include + +#include +#include + +#include "reb-host.h" +#include "host-lib.h" + +#define BUF_SIZE (16*1024) // MS restrictions apply + +#define SF_DEV_NULL 31 // local flag to mark NULL device + +static HANDLE Std_Out = 0; +static HANDLE Std_Inp = 0; +static HANDLE Std_Echo = 0; +static REBCHR *Std_Buf = 0; // for input and output + +static BOOL Redir_Out = 0; +static BOOL Redir_Inp = 0; + +// Special access: +extern REBDEV *Devices[]; +extern REBOL_API void Reb_Escape(int level); + +//********************************************************************** + +BOOL WINAPI Handle_Break(DWORD dwCtrlType) +{ + // Handle the MS CMD console CTRL-C, BREAK, and other events: + if (dwCtrlType >= CTRL_CLOSE_EVENT) OS_Exit(100); // close button, shutdown, etc. + Reb_Escape(0); + return TRUE; // We handled it +} + +#ifdef DEBUG_METHOD +// Because this file deals with stdio, we must avoid using stdio for debug. +// This funtion is of use wne needed. +static dbgout(char *fmt, int d, char *s) +{ + char buf[255]; + FILE *f = fopen("dbgout.txt", "w"); + sprintf(buf, fmt, d, s); + fwrite(buf, strlen(buf), 1, f); + fclose(f); +} +// example: dbgout("handle: %x %s\n", hdl, name); +#endif + +#ifdef NOT_USED +static void attach_console(void) { + void *h = LoadLibraryW(TEXT("kernel32.dll")); + (BOOL (_stdcall *)(DWORD))GetProcAddress(h, "AttachConsole")(-1); + FreeLibrary(h); +} +#endif + + +/*********************************************************************** +** +*/ DEVICE_CMD Quit_IO(REBREQ *dr) +/* +***********************************************************************/ +{ + REBDEV *dev = (REBDEV*)dr; // just to keep compiler happy above + + //if (GET_FLAG(dev->flags, RDF_OPEN)) FreeConsole(); + CLR_FLAG(dev->flags, RDF_OPEN); + return DR_DONE; +} + +/*********************************************************************** +** +*/ DEVICE_CMD Open_IO(REBREQ *req) +/* +***********************************************************************/ +{ + REBDEV *dev; + REBCHR *title = TEXT("REBOL 3 Alpha"); + HANDLE win; + + dev = Devices[req->device]; + + // Avoid opening the console twice (compare dev and req flags): + if (GET_FLAG(dev->flags, RDF_OPEN)) { + // Device was opened earlier as null, so req must have that flag: + if (GET_FLAG(dev->flags, SF_DEV_NULL)) + SET_FLAG(req->modes, RDM_NULL); + SET_FLAG(req->flags, RRF_OPEN); + return DR_DONE; // Do not do it again + } + + if (!GET_FLAG(req->modes, RDM_NULL)) { + + // Get the raw stdio handles: + Std_Out = GetStdHandle(STD_OUTPUT_HANDLE); + Std_Inp = GetStdHandle(STD_INPUT_HANDLE); + //Std_Err = GetStdHandle(STD_ERROR_HANDLE); + Std_Echo = 0; + + Redir_Out = (GetFileType(Std_Out) != 0); + Redir_Inp = (GetFileType(Std_Inp) != 0); + + // attach_console(); // merges streams, not good + + // If output not redirected, open a console: + if (!Redir_Out) { + if (!AllocConsole()) { + req->error = GetLastError(); + return DR_ERROR; + } + + SetConsoleTitle(title); + + // The goof-balls at MS seem to require this: + // See: http://support.microsoft.com/kb/124103 + Sleep(40); + win = FindWindow(NULL, title); // What if more than one open ?! + if (win) { + SetForegroundWindow(win); + BringWindowToTop(win); + } + + // Get the new stdio handles: + Std_Out = GetStdHandle(STD_OUTPUT_HANDLE); + + if (!Redir_Inp) Std_Inp = GetStdHandle(STD_INPUT_HANDLE); + } + + Std_Buf = OS_Make(BUF_SIZE * sizeof(REBCHR)); + + // Handle stdio CTRL-C interrupt: + SetConsoleCtrlHandler(Handle_Break, TRUE); + } + else + SET_FLAG(dev->flags, SF_DEV_NULL); + + SET_FLAG(req->flags, RRF_OPEN); + SET_FLAG(dev->flags, RDF_OPEN); + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Close_IO(REBREQ *req) +/* + ***********************************************************************/ +{ + REBDEV *dev = Devices[req->device]; + + if (GET_FLAG(dev->flags, RDF_OPEN)) { + OS_Free(Std_Buf); + Std_Buf = 0; + //FreeConsole(); // problem: causes a delay + } + + if (Std_Echo) { + CloseHandle(Std_Echo); + Std_Echo = 0; + } + + CLR_FLAG(req->flags, RRF_OPEN); + //CLR_FLAG(dev->flags, RDF_OPEN); + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Write_IO(REBREQ *req) +/* +** Low level "raw" standard output function. +** +** Allowed to restrict the write to a max OS buffer size. +** +** Returns the number of chars written. +** +***********************************************************************/ +{ + long len; + long total = 0; + BOOL ok = FALSE; + + if (GET_FLAG(req->modes, RDM_NULL)) { + req->actual = req->length; + return DR_DONE; + } + + if (Std_Out) { + + if (Redir_Out) { // Always UTF-8 + ok = WriteFile(Std_Out, req->data, req->length, &total, 0); + } + else { + // Convert UTF-8 buffer to Win32 wide-char format for console. + // Thankfully, MS provides something other than mbstowcs(); + // however, if our buffer overflows, it's an error. There's no + // efficient way at this level to split-up the input data, + // because its UTF-8 with variable char sizes. + len = MultiByteToWideChar(CP_UTF8, 0, req->data, req->length, Std_Buf, BUF_SIZE); + if (len > 0) // no error + ok = WriteConsoleW(Std_Out, Std_Buf, len, &total, 0); + } + + if (!ok) { + req->error = GetLastError(); + return DR_ERROR; + } + + req->actual = req->length; // do not use "total" (can be byte or wide) + + //if (GET_FLAG(req->flags, RRF_FLUSH)) { + // FLUSH(); + //} + } + + if (Std_Echo) { // always UTF-8 + WriteFile(Std_Echo, req->data, req->length, &total, 0); + //FlushFileBuffers(Std_Echo); + } + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Read_IO(REBREQ *req) +/* +** Low level "raw" standard input function. +** +** The request buffer must be long enough to hold result. +** +** Result is NOT terminated (the actual field has length.) +** +***********************************************************************/ +{ + long total = 0; + int len; + BOOL ok; + + if (GET_FLAG(req->modes, RDM_NULL)) { + req->data[0] = 0; + return DR_DONE; + } + + req->actual = 0; + + if (Std_Inp) { + + if (Redir_Inp) { // always UTF-8 + len = MIN(req->length, BUF_SIZE); + ok = ReadFile(Std_Inp, req->data, len, &total, 0); + } + else { + ok = ReadConsoleW(Std_Inp, Std_Buf, BUF_SIZE-1, &total, 0); + if (ok) { + total = WideCharToMultiByte(CP_UTF8, 0, Std_Buf, total, req->data, req->length, 0, 0); + if (!total) ok = FALSE; + } + } + + if (!ok) { + req->error = GetLastError(); + return DR_ERROR; + } + + req->actual = total; + } + + return DR_DONE; +} + + +/*********************************************************************** +** +*/ DEVICE_CMD Open_Echo(REBREQ *req) +/* +** Open a file for low-level console echo (output). +** +***********************************************************************/ +{ + if (Std_Echo) { + CloseHandle(Std_Echo); + Std_Echo = 0; + } + + if (req->file.path) { + Std_Echo = CreateFile(req->file.path, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); + if (Std_Echo == INVALID_HANDLE_VALUE) { + Std_Echo = 0; + req->error = GetLastError(); + return DR_ERROR; + } + } + + return DR_DONE; +} + + +/*********************************************************************** +** +** Command Dispatch Table (RDC_ enum order) +** +***********************************************************************/ + +static DEV_CMD Dev_Cmds[RDC_MAX] = +{ + 0, // init + Quit_IO, + Open_IO, + Close_IO, + Read_IO, + Write_IO, + 0, // poll + 0, // connect + 0, // query + 0, // modify + Open_Echo, // CREATE used for opening echo file +}; + +DEFINE_DEV(Dev_StdIO, "Standard IO", 1, Dev_Cmds, RDC_MAX, 0); + + + +//*** Old fragments *************************************************** + +#if OLD_CONSOLE_FILE_IO + int cfh; // C file handle + FILE *file; + + cfh = _open_osfhandle((long)Std_Out, _O_TEXT); + file = _fdopen(cfh, "w"); + *stdout = *file; + setvbuf(stdout, NULL, _IONBF, 0); + + cfh = _open_osfhandle((long)Std_Inp, _O_TEXT); + file = _fdopen(cfh, "r"); + *stdin = *file; +#endif diff --git a/src/os/win32/host-lib.c b/src/os/win32/host-lib.c new file mode 100644 index 0000000..a9079c0 --- /dev/null +++ b/src/os/win32/host-lib.c @@ -0,0 +1,648 @@ +/*********************************************************************** +** +** REBOL 3.0 "Invasion" +** Copyright 2009 REBOL Technologies +** All rights reserved. +** +************************************************************************ +** +** Title: OS API function library called by REBOL interpreter +** Build: A95 +** Date: 27-Nov-2009 +** File: host-lib.c +** Author: Carl Sassenrath +** Purpose: +** This module provides the functions that REBOL calls +** to interface to the native (host) operating system. +** REBOL accesses these functions through the structure +** defined in host-lib.h (auto-generated, do not modify). +** +** Flags: compile with -DUNICODE for Win32 wide char API +** +** Special note: +** This module is parsed for function declarations used to +** build prototypes, tables, and other definitions. To change +** function arguments requires a rebuild of the REBOL library. +** +************************************************************************ +** +** NOTE to PROGRAMMERS: +** +** 1. Keep code clear and simple. +** 2. Document unusual code, reasoning, or gotchas. +** 3. Use same style for code, vars, indent(4), comments, etc. +** 4. Keep in mind Linux, OS X, BSD, big/little endian CPUs. +** 5. Test everything, then test it again. +** +***********************************************************************/ + +#include +#include +#include + +#include "reb-host.h" +#include "host-lib.h" + +// Semaphore lock to sync sub-task launch: +static void *Task_Ready; + + +/*********************************************************************** +** +*/ void Convert_Date(SYSTEMTIME *stime, REBOL_DAT *dat, long zone) +/* +** Convert local format of system time into standard date +** and time structure. +** +***********************************************************************/ +{ + dat->year = stime->wYear; + dat->month = stime->wMonth; + dat->day = stime->wDay; + dat->time = stime->wHour * 3600 + stime->wMinute * 60 + stime->wSecond; + dat->nano = 1000000 * stime->wMilliseconds; + dat->zone = zone; +} + +/*********************************************************************** +** +*/ static void Insert_Command_Arg(REBCHR *cmd, REBCHR *arg, REBINT limit) +/* +** Insert an argument into a command line at the %1 position, +** or at the end if there is no %1. (An INSERT action.) +** Do not exceed the specified limit length. +** +** Too bad std Clib does not provide INSERT or REPLACE functions. +** +***********************************************************************/ +{ + #define HOLD_SIZE 2000 + REBCHR *spot; + REBCHR hold[HOLD_SIZE+4]; + + if ((REBINT)LEN_STR(cmd) >= limit) return; // invalid case, ignore it. + + // Find %1: + spot = FIND_STR(cmd, TEXT("%1")); + + if (spot) { + // Save rest of cmd line (such as end quote, -flags, etc.) + COPY_STR(hold, spot+2, HOLD_SIZE); + + // Terminate at the arg location: + spot[0] = 0; + + // Insert the arg: + JOIN_STR(spot, arg, limit - LEN_STR(cmd) - 1); + + // Add back the rest of cmd: + JOIN_STR(spot, hold, limit - LEN_STR(cmd) - 1); + } + else { + JOIN_STR(cmd, TEXT(" "), 1); + JOIN_STR(cmd, arg, limit - LEN_STR(cmd) - 1); + } +} + + +/*********************************************************************** +** +** OS Library Functions +** +***********************************************************************/ + +/*********************************************************************** +** +*/ void *OS_Make(size_t size) +/* +** Allocate memory of given size. +** +** This is necessary because some environments may use their +** own specific memory allocation (e.g. private heaps). +** +***********************************************************************/ +{ + return malloc(size); +} + + +/*********************************************************************** +** +*/ void OS_Free(void *mem) +/* +** Free memory allocated in this OS environment. (See OS_Make) +** +***********************************************************************/ +{ + free(mem); +} + + +/*********************************************************************** +** +*/ void OS_Exit(int code) +/* +** Called in cases where REBOL needs to quit immediately +** without returning from the main() function. +** +***********************************************************************/ +{ + OS_Call_Device(RDI_STDIO, RDC_CLOSE); // close echo + exit(code); +} + + +/*********************************************************************** +** +*/ void OS_Crash(const REBYTE *title, const REBYTE *content) +/* +** Tell user that REBOL has crashed. This function must use +** the most obvious and reliable method of displaying the +** crash message. +** +** If the title is NULL, then REBOL is running in a server mode. +** In that case, we do not want the crash message to appear on +** the screen, because the system may be unattended. +** +** On some systems, the error may be recorded in the system log. +** +***********************************************************************/ +{ + // Echo crash message if echo file is open: + ///PUTE(content); + OS_Call_Device(RDI_STDIO, RDC_CLOSE); // close echo + + // A title tells us we should alert the user: + if (title) { + // OS_Put_Str(title); + // OS_Put_Str(":\n"); + // Use ASCII only (in case we are on non-unicode win32): + MessageBoxA(NULL, content, title, MB_ICONHAND); + } + // OS_Put_Str(content); + exit(100); +} + + +/*********************************************************************** +** +*/ REBCHR *OS_Form_Error(int errnum, REBCHR *str, int len) +/* +** Translate OS error into a string. The str is the string +** buffer and the len is the length of the buffer. +** +***********************************************************************/ +{ + LPVOID lpMsgBuf; + int ok; + + if (!errnum) errnum = GetLastError(); + + ok = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errnum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL); + + len--; // termination + + if (!ok) COPY_STR(str, TEXT("unknown error"), len); + else { + COPY_STR(str, lpMsgBuf, len); + LocalFree(lpMsgBuf); + } + return str; +} + + +/*********************************************************************** +** +*/ BOOL OS_Get_Boot_Path(REBCHR *name) +/* +** Used to determine the program file path for REBOL. +** This is the path stored in system->options->boot and +** it is used for finding default boot files. +** +***********************************************************************/ +{ + return (GetModuleFileName(0, name, MAX_FILE_NAME) > 0); +} + + +/*********************************************************************** +** +*/ REBCHR *OS_Get_Locale(int what) +/* +** Used to obtain locale information from the system. +** The returned value must be freed with OS_FREE_MEM. +** +***********************************************************************/ +{ + LCTYPE type; + int len; + REBCHR *data; + LCTYPE types[] = { + LOCALE_SENGLANGUAGE, + LOCALE_SNATIVELANGNAME, + LOCALE_SENGCOUNTRY, + LOCALE_SCOUNTRY, + }; + + type = types[what]; + + len = GetLocaleInfo(0, type, 0, 0); + data = MAKE_STR(len); + len = GetLocaleInfo(0, type, data, len); + + return data; +} + + +/*********************************************************************** +** +*/ REBCHR *OS_Get_Env(REBCHR *var, int mode) +/* +** Get a value from the environment. +** Returns string for success or zero if missing. +** Return string should be copied not stored or changed. +** +***********************************************************************/ +{ +#ifdef UNICODE + return _wgetenv(var); +#else + return getenv(var); +#endif +} + + +/*********************************************************************** +** +*/ int OS_Set_Env(REBCHR *expr, int mode) +/* +** Set a value from the environment. +** Returns 0 for success and <0 for errors. +** +***********************************************************************/ +{ +#ifdef UNICODE + return _wputenv(expr); +#else + return putenv(expr); +#endif +} + + +/*********************************************************************** +** +*/ REBCHR *OS_List_Env(void) +/* +***********************************************************************/ +{ + REBCHR *env = GetEnvironmentStrings(); + REBCNT n, len = 0; + REBCHR *str; + + str = env; + while (n = LEN_STR(str)) { + len += n + 1; + str = env + len; // next + } + len++; + + str = OS_Make(len * sizeof(REBCHR)); + MOVE_MEM(str, env, len * sizeof(REBCHR)); + + FreeEnvironmentStrings(env); + + return str; +} + + +/*********************************************************************** +** +*/ void OS_Get_Time(REBOL_DAT *dat) +/* +** Get the current system date/time in UTC plus zone offset (mins). +** +***********************************************************************/ +{ + SYSTEMTIME stime; + TIME_ZONE_INFORMATION tzone; + + GetSystemTime(&stime); + + if (TIME_ZONE_ID_DAYLIGHT == GetTimeZoneInformation(&tzone)) + tzone.Bias += tzone.DaylightBias; + + Convert_Date(&stime, dat, -tzone.Bias); +} + + +/*********************************************************************** +** +*/ i64 OS_Delta_Time(i64 base, int flags) +/* +** Return time difference in microseconds. If base = 0, then +** return the counter. If base != 0, compute the time difference. +** +** Note: Requires high performance timer. +** Q: If not found, use timeGetTime() instead ?! +** +***********************************************************************/ +{ + LARGE_INTEGER freq; + LARGE_INTEGER time; + + if (!QueryPerformanceCounter(&time)) + OS_Crash("Missing resource", "High performance timer"); + + if (base == 0) return time.QuadPart; // counter (may not be time) + + QueryPerformanceFrequency(&freq); + + return ((time.QuadPart - base) * 1000) / (freq.QuadPart / 1000); +} + + +/*********************************************************************** +** +*/ int OS_Get_Current_Dir(REBCHR **path) +/* +** Return the current directory path as a string and +** its length in chars (not bytes). +** +** The result should be freed after copy/conversion. +** +***********************************************************************/ +{ + int len; + + len = GetCurrentDirectory(0, NULL); // length, incl terminator. + *path = MAKE_STR(len); + GetCurrentDirectory(len, *path); + len--; // less terminator + + return len; // Be sure to call free() after usage +} + + +/*********************************************************************** +** +*/ BOOL OS_Set_Current_Dir(REBCHR *path) +/* +** Set the current directory to local path. Return FALSE +** on failure. +** +***********************************************************************/ +{ + return SetCurrentDirectory(path); +} + + +/*********************************************************************** +** +*/ void OS_File_Time(REBREQ *file, REBOL_DAT *dat) +/* +** Convert file.time to REBOL date/time format. +** Time zone is UTC. +** +***********************************************************************/ +{ + SYSTEMTIME stime; + TIME_ZONE_INFORMATION tzone; + + if (TIME_ZONE_ID_DAYLIGHT == GetTimeZoneInformation(&tzone)) + tzone.Bias += tzone.DaylightBias; + + FileTimeToSystemTime((FILETIME *)(&(file->file.time)), &stime); + Convert_Date(&stime, dat, -tzone.Bias); +} + + +/*********************************************************************** +** +*/ void *OS_Open_Library(REBCHR *path, REBCNT *error) +/* +** Load a DLL library and return the handle to it. +** If zero is returned, error indicates the reason. +** +***********************************************************************/ +{ + void *dll = LoadLibraryW(path); + *error = GetLastError(); + + return dll; +} + + +/*********************************************************************** +** +*/ void OS_Close_Library(void *dll) +/* +** Free a DLL library opened earlier. +** +***********************************************************************/ +{ + FreeLibrary((HINSTANCE)dll); +} + + +/*********************************************************************** +** +*/ void *OS_Find_Function(void *dll, char* funcname) +/* +** Get a DLL function address from its string name. +** +***********************************************************************/ +{ + void *fp = GetProcAddress((HMODULE)dll, funcname); + //DWORD err = GetLastError(); + + return fp; +} + + +/*********************************************************************** +** +*/ REBINT OS_Create_Thread(CFUNC init, void *arg, REBCNT stack_size) +/* +** Creates a new thread for a REBOL task datatype. +** +** NOTE: +** For this to work, the multithreaded library option is +** needed in the C/C++ code generation settings. +** +** The Task_Ready stops return until the new task has been +** initialized (to avoid unknown new thread state). +** +***********************************************************************/ +{ + REBINT thread; + + Task_Ready = CreateEvent(NULL, TRUE, FALSE, TEXT("REBOL_Task_Launch")); + if (!Task_Ready) return -1; + + thread = _beginthread(init, stack_size, arg); + + if (thread) WaitForSingleObject(Task_Ready, 2000); + CloseHandle(Task_Ready); + + return 1; +} + + +/*********************************************************************** +** +*/ void OS_Delete_Thread(void) +/* +** Can be called by a REBOL task to terminate its thread. +** +***********************************************************************/ +{ + _endthread(); +} + + +/*********************************************************************** +** +*/ void OS_Task_Ready(REBINT tid) +/* +** Used for new task startup to resume the thread that +** launched the new task. +** +***********************************************************************/ +{ + SetEvent(Task_Ready); +} + + +/*********************************************************************** +** +*/ int OS_Create_Process(REBCHR *call, int reserved) +/* +** Return zero on error. +** +***********************************************************************/ +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + REBOOL is_NT; + OSVERSIONINFO info; + REBINT result; + + GetVersionEx(&info); + is_NT = info.dwPlatformId >= VER_PLATFORM_WIN32_NT; + + si.cb = sizeof(si); + si.lpReserved = NULL; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.dwFlags = STARTF_USESHOWWINDOW; + si.dwFlags |= STARTF_USESTDHANDLES; + si.wShowWindow = SW_SHOWNORMAL; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + result = CreateProcess( + NULL, // Executable name + call, // Command to execute + NULL, // Process security attributes + NULL, // Thread security attributes + FALSE, // Inherit handles + CREATE_DEFAULT_ERROR_MODE, // Creation flags + NULL, // Environment + NULL, // Current directory + &si, // Startup information + &pi // Process information + ); + + return result; +} + + +/*********************************************************************** +** +*/ int OS_Browse(REBCHR *url, int reserved) +/* +***********************************************************************/ +{ + #define MAX_BRW_PATH 2044 + long flag; + long len; + long type; + HKEY key; + REBCHR *path; + HWND hWnd = GetFocus(); + + if (RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("http\\shell\\open\\command"), 0, KEY_READ, &key) != ERROR_SUCCESS) + return 0; + + if (!url) url = TEXT(""); + + path = MAKE_STR(MAX_BRW_PATH+4); + len = MAX_BRW_PATH; + + flag = RegQueryValueEx(key, TEXT(""), 0, &type, (LPBYTE)path, &len); + RegCloseKey(key); + if (flag != ERROR_SUCCESS) { + FREE_MEM(path); + return 0; + } + //if (ExpandEnvironmentStrings(&str[0], result, len)) + + Insert_Command_Arg(path, url, MAX_BRW_PATH); + + len = OS_Create_Process(path, 0); + + FREE_MEM(path); + return len; +} + + +/*********************************************************************** +** +*/ BOOL OS_Request_File(REBRFR *fr) +/* +***********************************************************************/ +{ + OPENFILENAME ofn = {0}; + BOOL ret; + //int err; + REBCHR *filters = TEXT("All files\0*.*\0REBOL scripts\0*.r\0Text files\0*.txt\0" ); + + ofn.lStructSize = sizeof(ofn); + + // ofn.hwndOwner = WIN_WIN(win); // Must find a way to set this + + ofn.lpstrTitle = fr->title; + ofn.lpstrInitialDir = fr->dir; + ofn.lpstrFile = fr->files; + ofn.lpstrFilter = fr->filter ? fr->filter : filters; + ofn.nMaxFile = fr->len; + ofn.lpstrFileTitle = 0; + ofn.nMaxFileTitle = 0; + + ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_NOCHANGEDIR; //|OFN_NONETWORKBUTTON; //; + + if (GET_FLAG(fr->flags, FRF_MULTI)) ofn.Flags |= OFN_ALLOWMULTISELECT; + + if (GET_FLAG(fr->flags, FRF_SAVE)) + ret = GetSaveFileName(&ofn); + else + ret = GetOpenFileName(&ofn); + + //if (!ret) + // err = CommDlgExtendedError(); // CDERR_FINDRESFAILURE + + return ret; +}