diff --git a/ChangeLog b/ChangeLog index 08dbbbc8..1035c101 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2020-12-19 (12.20) + SDL: Update editor popup appearance + +2020-11-19 (12.20) + COMMON: Fix to allow multiple modules and units within saem program + +2020-11-13 (12.20) + ANDROID: add option to preserve user data when app uninstalled + +2020-11-10 (12.20) + ANDROID: change path label when displaying project folder + COMMON: ensure INT datatype is always 64 bits + +2020-11-01 (12.20) + CONSOLE: give error when file not found + +2020-10-31 (12.20) + SDL: implemented restoring the cursor position + +2020-10-29 (12.20) + COMMON: Implemented c-styles escape sequences + +2020-10-27 (12.20) + COMMON: add support for inline assignment of export variables + COMMON: add support for unit alias names + +2020-10-21 (12.20) + ANDROID: upgrade to target android 29 + +2020-10-21 (12.20) + COMMON: Fix to allow c-modules to be called from units + 2020-07-16 (12.19) COMMON: Fix 'Print #' on Windows to use Windows line endings diff --git a/autogen.sh b/autogen.sh index b54443c1..c4285e20 100644 --- a/autogen.sh +++ b/autogen.sh @@ -1,6 +1,6 @@ # This file is part of SmallBASIC # -# Copyright(C) 2001-2015 Chris Warren-Smith. +# Copyright(C) 2001-2020 Chris Warren-Smith. # # This program is distributed under the terms of the GPL v2.0 or later # Download the GNU Public License (GPL) from www.gnu.org @@ -8,7 +8,8 @@ ln -sf README.md README -git submodule update -- +git submodule init +git submodule update pkg-config --version > /dev/null || echo "Please install pkg-config" diff --git a/configure.ac b/configure.ac index 91490042..7daf6c7b 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [12.19]) +AC_INIT([smallbasic], [12.20]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET @@ -59,8 +59,6 @@ function checkForWindows() { *darwin*) ;; *) - dnl backlinking support for modules - LDFLAGS="${LDFLAGS} -export-dynamic" esac AM_CONDITIONAL(WITH_WIN32, test x"$win32" = "xyes") } @@ -219,7 +217,6 @@ function buildSDL() { AC_DEFINE(_SDL, 1, [Defined when building SDL version]) AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) - AC_DEFINE(IMPL_DEV_DELAY, 1, [Driver implements dev_delay()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) BUILD_SUBDIRS="src/common src/platform/sdl" @@ -235,7 +232,6 @@ function buildAndroid() { AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) AC_DEFINE(_ANDROID, 1, [Defined for Android build.]) AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) - AC_DEFINE(IMPL_DEV_DELAY, 1, [Driver implements dev_delay()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) BUILD_SUBDIRS="src/platform/android" @@ -260,7 +256,6 @@ function buildConsole() { ) AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, test $win32 = yes) - AC_DEFINE(BUILD_CONSOLE, 1, [Building a console based system.]) if test $win32 = yes; then dnl test whether to build using mingw @@ -287,7 +282,7 @@ function buildConsole() { TARGET="Building Cygwin MinGW console version." AC_DEFINE(__MINGW32__, 1, [as above]) AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) - PACKAGE_LIBS="${PACKAGE_LIBS} -Wl,-Bstatic -mconsole -lmingw32 -lwsock32 -lws2_32 -static-libgcc" + PACKAGE_LIBS="${PACKAGE_LIBS} -Wl,-Bstatic -mconsole -lwsock32 -lws2_32 -static-libgcc" BUILD_SUBDIRS="src/common src/platform/console" fi AC_DEFINE(_Win32, 1, [Windows build]) @@ -320,7 +315,6 @@ function buildWeb() { BUILD_SUBDIRS="src/common src/platform/web" AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, false) AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) - AC_DEFINE(IMPL_DEV_DELAY, 1, [Driver implements dev_delay()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) AC_DEFINE(USE_TERM_IO, 0, [dont use the termios library.]) AC_DEFINE(IMPL_DEV_ENV, 1, [Driver implements dev_env funcs]) @@ -374,7 +368,6 @@ function buildFLTK() { AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) AC_DEFINE(_FLTK, 1, [Defined for FLTK build.]) AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) - AC_DEFINE(IMPL_DEV_DELAY, 1, [Driver implements dev_delay()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) BUILD_SUBDIRS="src/common src/platform/fltk" diff --git a/debian/changelog b/debian/changelog index 26518299..42012856 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +smallbasic (12.20) unstable; urgency=low + * Various see web site + + -- Chris Warren-Smith Fri, 16 Oct 2020 09:45:25 +1000 + smallbasic (0.12.19) unstable; urgency=low * Various see web site diff --git a/samples/distro-examples/tests/call_tau.bas b/samples/distro-examples/tests/call_tau.bas index 05bd36e7..abb1c70e 100644 --- a/samples/distro-examples/tests/call_tau.bas +++ b/samples/distro-examples/tests/call_tau.bas @@ -1,19 +1,19 @@ ' example: using a unit, 0.9 ' -Import Tau, PreDef +Import Tau as taz, PreDef -? "Tau's exported variable: ", tau.expvar -? "Function fooF : ", tau.fooF("Hi") +? "Tau's exported variable: ", taz.expvar +? "Function fooF : ", taz.fooF("Hi") ? "Procedure fooP : " -tau.fooP "Hi" +taz.fooP "Hi" ' reverse var-update -tau.expvar = "message from main" -tau.print_expvar -? tau.expvar -tau.build_ta -? tau.ta +taz.expvar = "message from main" +taz.print_expvar +? taz.expvar +taz.build_ta +? taz.ta 'tau.cerr rem check system-variables @@ -31,8 +31,8 @@ foyer.name= "my name is PI" ? "end" for i = 0 to 1000 - tau.addRoom(foyer,x) - jj = tau.calcRoomSize(foyer,x) + taz.addRoom(foyer,x) + jj = taz.calcRoomSize(foyer,x) next i sub addRoom(the_thing, d) @@ -40,3 +40,10 @@ sub addRoom(the_thing, d) end addRoom(foyer,x) + +if (Taz.LIGHTGRAY != rgb(200, 200, 200)) then + print "Error importing inline assigned export variable" +endif +if (Taz.YELLOW != rgb(253, 249, 0)) then + print "Error importing inline assigned export variable" +endif diff --git a/samples/distro-examples/tests/strings.bas b/samples/distro-examples/tests/strings.bas index e7e65c58..b7f18f1c 100644 --- a/samples/distro-examples/tests/strings.bas +++ b/samples/distro-examples/tests/strings.bas @@ -177,7 +177,7 @@ if expect != seq(0, 2*pi, 360/15+1) then throw "SEQ error" s="Hello\033There" if (27 != asc(mid(s, 6, 1))) then throw "err" rem Non escaping '\' should appear verbatim -s= "a\c\e" +s= "a\\c\\e" if mid(s, 2, 1) != "\\" then throw s if mid(s, 4, 1) != "\\" then throw s @@ -189,3 +189,12 @@ for c in s1 next c if (s1 <> s2) then throw s2 +if (asc("\a") != 7) then throw "err1" +if (asc("\b") != 8) then throw "err2" +if (asc("\e") != 27) then throw "err3" +if (asc("\f") != 12) then throw "err4" +if (asc("\n") != 10) then throw "err5" +if (asc("\r") != 13) then throw "err6" +if (asc("\t") != 9) then throw "err7" +if (asc("\v") != 11) then throw "err8" + diff --git a/samples/distro-examples/tests/tau.bas b/samples/distro-examples/tests/tau.bas index 31812007..6033fa84 100644 --- a/samples/distro-examples/tests/tau.bas +++ b/samples/distro-examples/tests/tau.bas @@ -3,10 +3,14 @@ Import TauChild Export expvar, foof, foop export addRoom,calcRoomSize -export print_expvar, ta, build_ta, cerr +export print_expvar, ta, build_ta, cerr expvar = "Tau's exported variable" +export const LIGHTGRAY = rgb(200, 200, 200) +export const GRAY = rgb(130, 130, 130) +export const DARKGRAY = rgb(80, 80, 80), const YELLOW = rgb(253, 249, 0) + func fooF(x) foof = "Tau's fooF("+x+") is here" end diff --git a/src/common/bc.c b/src/common/bc.c index c5498db8..3e93c5da 100644 --- a/src/common/bc.c +++ b/src/common/bc.c @@ -9,6 +9,36 @@ #include "common/bc.h" #include "common/smbas.h" +#include "common/str.h" + +/* + * string escape codes + */ +const char escapes[][2] = { + {'a', 0x07}, + {'b', 0x08}, + {'e', 0x1b}, + {'f', 0x0c}, + {'n', 0x0a}, + {'r', 0x0d}, + {'t', 0x09}, + {'v', 0x0b}, +}; + +/* + * whether the character is an escape + */ +int bc_is_escape(char c, char *value) { + int len = sizeof(escapes) / sizeof(escapes[0]); + int result = 0; + for (int i = 0; i < len && !result; i++) { + if (escapes[i][0] == c) { + *value = escapes[i][1]; + result = 1; + } + } + return result; +} /* * Create a bytecode segment @@ -182,23 +212,18 @@ void bc_add_strn(bc_t *bc, const char *str, int len) { * returns a pointer of src to the next "element" */ char *bc_store_string(bc_t *bc, char *src) { - char *p = src; - char *np = NULL; + // skip past opening quotes + char *p = src + 1; char *base = src + 1; - int len = 0; + char escape = 0; + cstr cs; + cstr_init(&cs, 5); - // skip past opening quotes - p++; while (*p) { if ((*p == '\\' && ((*(p + 1) == '\"') || *(p + 1) == '\\')) || *p == V_JOIN_LINE) { // escaped quote " or escaped escape - int seglen = p - base; - np = np ? realloc(np, len + seglen + 1) : malloc(seglen + 1); - strncpy(np + len, base, seglen); - // add next segment - len += seglen; - np[len] = 0; + cstr_append_i(&cs, base, p - base); if (*p == V_JOIN_LINE) { // skip null newline @@ -211,6 +236,11 @@ char *bc_store_string(bc_t *bc, char *src) { // include " (or \ ) in next segment base = ++p; + } else if (*p == '\\' && bc_is_escape(*(p + 1), &escape)) { + char code[] = {escape, '\0'}; + cstr_append_i(&cs, base, p - base); + cstr_append_i(&cs, code, 1); + base = (++p) + 1; } else if (*p == V_QUOTE) { // revert hidden quote *p = '\"'; @@ -220,17 +250,14 @@ char *bc_store_string(bc_t *bc, char *src) { *p = '\n'; } else if (*p == '\"') { // end of string detected - int seglen = p - base; - np = np ? realloc(np, len + seglen + 1) : malloc(seglen + 1); - memcpy(np + len, base, seglen); - bc_add_strn(bc, np, len + seglen); - free(np); + cstr_append_i(&cs, base, p - base); + bc_add_strn(bc, cs.buf, cs.length); p++; - return p; + break; } p++; } - free(np); + free(cs.buf); return p; } diff --git a/src/common/brun.c b/src/common/brun.c index fcf62cc8..15cc761a 100644 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -266,7 +266,7 @@ void exec_setup_predefined_variables() { setsysvar_str(SYSVAR_COMMAND, opt_command); setsysvar_int(SYSVAR_SELF, 0); setsysvar_var(SYSVAR_NONE, 0, V_NIL); - setsysvar_num(SYSVAR_MAXINT, VAR_MAX_INT); + setsysvar_int(SYSVAR_MAXINT, VAR_MAX_INT); #if defined(_ANDROID) if (getenv("HOME_DIR")) { @@ -1094,6 +1094,63 @@ void dump_stack() { } while (1); } +// load libraries - each library is loaded on new task +void brun_load_libraries(int tid) { + // reset symbol mapping + for (int i = 0; i < prog_symcount; i++) { + prog_symtable[i].task_id = prog_symtable[i].exp_idx = -1; + } + // for each library + for (int i = 0; i < prog_libcount; i++) { + if (prog_libtable[i].type == 1) { + // === SB Unit === + // create task + int lib_tid = brun_create_task(prog_libtable[i].lib, 0, 1); + activate_task(tid); + + // update lib-table's task-id field (in this code; not in lib's code) + prog_libtable[i].tid = lib_tid; + + // update lib-symbols's task-id field (in this code; not in lib's code) + for (int j = 0; j < prog_symcount; j++) { + char *pname = strrchr(prog_symtable[j].symbol, '.') + 1; + // the name without the 'class' + if ((prog_symtable[j].lib_id & (~UID_UNIT_BIT)) == prog_libtable[i].id) { + // find symbol by name (for sure) and update it + // this is required because lib may be newer than + // parent + for (int k = 0; k < taskinfo(lib_tid)->sbe.exec.expcount; k++) { + if (strcmp(pname, taskinfo(lib_tid)->sbe.exec.exptable[k].symbol) == 0) { + prog_symtable[j].exp_idx = k; + // adjust sid (sid is <-> exp_idx in lib) + prog_symtable[j].task_id = lib_tid; + // connect the library + break; + } + } + } + } + } else { + // === C Module === + // update lib-table's task-id field (in this code; not in lib's code) + prog_libtable[i].tid = -1; // not a task + int lib_id = prog_libtable[i].id; + slib_import(lib_id, 0); + + // update lib-symbols's task-id field (in this code; not in lib's code) + for (int j = 0; j < prog_symcount; j++) { + if (prog_symtable[j].lib_id == lib_id) { + prog_symtable[j].exp_idx = slib_get_kid(lib_id, prog_symtable[j].symbol); + prog_symtable[j].task_id = -1; + } + } + } + + // return + activate_task(tid); + } +} + /* * RUN byte-code * @@ -1254,62 +1311,10 @@ int brun_create_task(const char *filename, byte *preloaded_bc, int libf) { prog_ip = 0; exec_setup_predefined_variables(); - - // load libraries - each library is loaded on new task if (prog_libcount) { - // reset symbol mapping - for (int i = 0; i < prog_symcount; i++) { - prog_symtable[i].task_id = prog_symtable[i].exp_idx = -1; - } - // for each library - for (int i = 0; i < prog_libcount; i++) { - if (prog_libtable[i].type == 1) { - // === SB Unit === - // create task - int lib_tid = brun_create_task(prog_libtable[i].lib, 0, 1); - activate_task(tid); - - // update lib-table's task-id field (in this code; not in lib's code) - prog_libtable[i].tid = lib_tid; - - // update lib-symbols's task-id field (in this code; not in lib's code) - for (int j = 0; j < prog_symcount; j++) { - char *pname = strrchr(prog_symtable[j].symbol, '.') + 1; - // the name without the 'class' - if ((prog_symtable[j].lib_id & (~UID_UNIT_BIT)) == prog_libtable[i].id) { - // find symbol by name (for sure) and update it - // this is required because lib may be newer than - // parent - for (int k = 0; k < taskinfo(lib_tid)->sbe.exec.expcount; k++) { - if (strcmp(pname, taskinfo(lib_tid)->sbe.exec.exptable[k].symbol) == 0) { - prog_symtable[j].exp_idx = k; - // adjust sid (sid is <-> exp_idx in lib) - prog_symtable[j].task_id = lib_tid; - // connect the library - break; - } - } - } - } - } else { - // === C Module === - // update lib-table's task-id field (in this code; not in lib's code) - prog_libtable[i].tid = -1; // not a task - int lib_id = prog_libtable[i].id; - slib_import(lib_id, 0); - - // update lib-symbols's task-id field (in this code; not in lib's code) - for (int j = 0; j < prog_symcount; j++) { - prog_symtable[j].exp_idx = slib_get_kid(lib_id, prog_symtable[j].symbol); - // adjust sid (sid is <-> exp_idx in lib) - prog_symtable[j].task_id = -1; // connect the library - } - } - - // return - activate_task(tid); - } + brun_load_libraries(tid); } + return tid; } diff --git a/src/common/device.c b/src/common/device.c index 286881e1..00c4c364 100644 --- a/src/common/device.c +++ b/src/common/device.c @@ -16,10 +16,6 @@ #include "common/keymap.h" #include "common/inet.h" -#ifdef __MINGW32__ -#define usleep(s) Sleep((DWORD)((s+500)/1000)) -#endif - /** * initialize all drivers */ @@ -75,236 +71,13 @@ int dev_events(int wait_flag) { return osd_events(wait_flag); } -#ifndef IMPL_DEV_DELAY - -/** - * delay for a specified amount of milliseconds - */ -void dev_delay(uint32_t ms) { -#if defined(_Win32) - Sleep(ms); -#elif defined(_DOS) - delay(ms); -#else // Unix - usleep(ms * 1000); -#endif -} -#endif - -#if defined(BUILD_CONSOLE) - -/** - * return the character (multibyte charsets support) - */ -int dev_input_char2str(int ch, byte * cstr) { - memset(cstr, 0, 3); - - switch (os_charset) { - case enc_sjis: // Japan - if (IsJISFont(ch)) { - cstr[0] = ch >> 8; - } - cstr[1] = ch & 0xFF; - break; - case enc_big5: // China/Taiwan (there is another charset for - // China but I don't know the difference) - if (IsBig5Font(ch)) { - cstr[0] = ch >> 8; - } - cstr[1] = ch & 0xFF; - break; - case enc_gmb: // Generic multibyte - if (IsGMBFont(ch)) { - cstr[0] = ch >> 8; - } - cstr[1] = ch & 0xFF; - break; - case enc_unicode: // Unicode - cstr[0] = ch >> 8; - cstr[1] = ch & 0xFF; - break; - default: - cstr[0] = ch; - }; - return strlen((char *)cstr); -} - -/** - * return the character size at pos! (multibyte charsets support) - */ -int dev_input_count_char(byte *buf, int pos) { - int count, ch; - byte cstr[3]; - - if (os_charset != enc_utf8) { - ch = buf[0]; - ch = ch << 8; - ch = ch + buf[1]; - count = dev_input_char2str(ch, cstr); - } else { - count = 1; - } - return count; -} - -/** - * stores a character at 'pos' position - */ -int dev_input_insert_char(int ch, char *dest, int pos, int replace_mode) { - byte cstr[3]; - int count, remain; - char *buf; - - count = dev_input_char2str(ch, cstr); - - // store character into buffer - if (replace_mode) { - // overwrite mode - remain = strlen((dest + pos)); - buf = malloc(remain + 1); - strcpy(buf, dest + pos); - memcpy(dest + pos, cstr, count); - dest[pos + count] = '\0'; - - if (os_charset != enc_utf8) { - count = dev_input_char2str(((buf[0] << 8) | buf[1]), cstr); - } else { - count = 1; - } - if (buf[0]) { // not a '\0' - strcat(dest, (buf + count)); - } - free(buf); - } else { - // insert mode - remain = strlen((dest + pos)); - buf = malloc(remain + 1); - strcpy(buf, dest + pos); - memcpy(dest + pos, cstr, count); - dest[pos + count] = '\0'; - strcat(dest, buf); - free(buf); - } - - return count; -} - -/** - * removes the character at 'pos' position - */ -int dev_input_remove_char(char *dest, int pos) { - byte cstr[3]; - int count, remain; - char *buf; - - if (dest[pos]) { - if (os_charset != enc_utf8) { - count = dev_input_char2str(((dest[pos] << 8) | dest[pos + 1]), cstr); - } else { - count = 1; - } - remain = strlen((dest + pos + 1)); - buf = malloc(remain + 1); - strcpy(buf, dest + pos + count); - - dest[pos] = '\0'; - strcat(dest, buf); - free(buf); - return count; - } - return 0; -} - -/** - * gets a string (INPUT) - */ -char *dev_gets(char *dest, int size) { - long int ch = 0; - uint16_t pos, len = 0; - int replace_mode = 0; - - *dest = '\0'; - pos = 0; - do { - len = strlen(dest); - ch = fgetc(stdin); - switch (ch) { - case -1: - case -2: - case 0xFFFF: - dest[pos] = '\0'; - return dest; - case 0: - case 10: - case 13: // ignore - break; - case SB_KEY_HOME: - pos = 0; - break; - case SB_KEY_END: - pos = len; - break; - case SB_KEY_BACKSPACE: // backspace - if (pos > 0) { - pos -= dev_input_remove_char(dest, pos - 1); - len = strlen(dest); - } else { - dev_beep(); - } - break; - case SB_KEY_DELETE: // delete - if (pos < len) { - dev_input_remove_char(dest, pos); - len = strlen(dest); - } else - dev_beep(); - break; - case SB_KEY_INSERT: - replace_mode = !replace_mode; - break; - case SB_KEY_LEFT: - if (pos > 0) { - pos -= dev_input_count_char((byte *)dest, pos); - } else { - dev_beep(); - } - break; - case SB_KEY_RIGHT: - if (pos < len) { - pos += dev_input_count_char((byte *)dest, pos); - } else { - dev_beep(); - } - break; - default: - if ((ch & 0xFF00) != 0xFF00) { // Not an hardware key - pos += dev_input_insert_char(ch, dest, pos, replace_mode); - } else { - ch = 0; - } - // check the size - len = strlen(dest); - if (len >= (size - 2)) { - break; - } - } - } while (ch != '\n' && ch != '\r'); - dest[len] = '\0'; - dev_print(dest); - return dest; -} - -#endif // #if defined BUILD_CONSOLE - /** * BEEP */ void dev_beep() { -#if !defined(BUILD_CONSOLE) if (!opt_mute_audio) { osd_beep(); } -#endif } /** @@ -385,12 +158,6 @@ void log_printf(const char *format, ...) { } } -#if defined(BUILD_CONSOLE) -void v_create_form(var_p_t var) {} -void v_create_window(var_p_t var) {} -void dev_show_page() {} -#endif - #if !defined(_SDL) void dev_trace_line(int lineNo) { dev_printf("<%d>", lineNo); @@ -408,9 +175,9 @@ void lwrite(const char *buf) { */ void panic(const char *fmt, ...) { va_list argp; - va_start(argp, fmt); + va_start(argp, fmt); vfprintf(stderr, fmt, argp); - dev_print("Fatal error"); - va_end(argp); + dev_print("\nFatal error\n"); + va_end(argp); exit(EXIT_FAILURE); } diff --git a/src/common/extlib.c b/src/common/extlib.c index 8d7c6896..377027c4 100644 --- a/src/common/extlib.c +++ b/src/common/extlib.c @@ -11,37 +11,36 @@ #include #endif -#if defined(__CYGWIN__) - #include - #include - #define WIN_EXTLIB - #define LIB_EXT ".dll" - #define DEFAULT_PATH "c:/sbasic/lib" -#elif defined(__MINGW32__) +#include "common/smbas.h" + +#if defined(__MINGW32__) #include + #include #define WIN_EXTLIB #define LIB_EXT ".dll" - #define DEFAULT_PATH "c:/sbasic/lib" -#elif (defined(__linux__) || defined(__midipix__) || defined(__MACH__)) && defined(_UnixOS) +#elif defined(_UnixOS) #include #define LNX_EXTLIB #define LIB_EXT ".so" - #define DEFAULT_PATH "/usr/lib/sbasic/modules:/usr/local/lib/sbasic/modules" #endif #if defined(LNX_EXTLIB) || defined(WIN_EXTLIB) -#include "common/smbas.h" #include "common/extlib.h" #include "common/pproc.h" #include -#define MAX_SLIBS 256 +#define MAX_SLIBS 64 #define MAX_PARAM 16 #define TABLE_GROW_SIZE 16 #define NAME_SIZE 256 #define PATH_SIZE 1024 typedef int (*sblib_exec_fn)(int, int, slib_par_t *, var_t *); +typedef int (*sblib_getname_fn) (int, char *); +typedef int (*sblib_count_fn) (void); +typedef int (*sblib_init_fn) (const char *); +typedef void (*sblib_close_fn) (void); +typedef const char *(*sblib_get_module_name_fn) (void); typedef struct { char name[NAME_SIZE]; @@ -51,19 +50,17 @@ typedef struct { sblib_exec_fn sblib_func_exec; uint32_t id; uint32_t flags; - uint32_t first_proc; - uint32_t first_func; uint8_t imported; + uint32_t proc_count; + uint32_t func_count; + uint32_t proc_list_size; + uint32_t func_list_size; + ext_func_node_t *func_list; + ext_proc_node_t *proc_list; } slib_t; -static slib_t slib_table[MAX_SLIBS]; +static slib_t slib_list[MAX_SLIBS]; static uint32_t slib_count; -static uint32_t extprocsize; -static uint32_t extproccount; -static uint32_t extfuncsize; -static uint32_t extfunccount; -static ext_func_node_t *extfunctable; -static ext_proc_node_t *extproctable; #if defined(LNX_EXTLIB) int slib_llopen(slib_t *lib) { @@ -87,27 +84,25 @@ int slib_llclose(slib_t *lib) { return 1; } -#else // WIN_EXTLIB -#if defined(__CYGWIN__) -int slib_llopen(slib_t *lib) { - char win32Path[PATH_SIZE]; - cygwin_conv_path(CCP_POSIX_TO_WIN_A, lib->fullname, win32Path, sizeof(win32Path)); - lib->handle = LoadLibrary(win32Path); - if (lib->handle == NULL) { - sc_raise("LIB: error on loading %s\n", win32Path); - } - return (lib->handle != NULL); -} - #elif defined(WIN_EXTLIB) int slib_llopen(slib_t *lib) { - lib->handle = LoadLibrary(lib->fullname); + lib->handle = LoadLibraryA(lib->fullname); if (lib->handle == NULL) { - sc_raise("LIB: error on loading %s\n", lib->name); + int error = GetLastError(); + switch (error) { + case ERROR_MOD_NOT_FOUND: + sc_raise("LIB: DLL dependency error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); + break; + case ERROR_DYNLINK_FROM_INVALID_RING: + sc_raise("LIB: DLL build error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); + break; + default: + sc_raise("LIB: error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); + break; + } } return (lib->handle != NULL); } -#endif void *slib_getoptptr(slib_t *lib, const char *name) { return GetProcAddress((HMODULE) lib->handle, name); @@ -123,88 +118,90 @@ int slib_llclose(slib_t *lib) { } #endif +/** + * returns slib_t* for the given id + */ +slib_t *get_lib(int lib_id) { + if (lib_id < 0 || lib_id >= slib_count) { + return NULL; + } + return &slib_list[lib_id]; +} + /** * add an external procedure to the list */ int slib_add_external_proc(const char *proc_name, int lib_id) { - // scan for conflicts - for (int i = 0; i < extproccount; i++) { - if (strcmp(extproctable[i].name, proc_name) == 0) { - sc_raise("LIB: duplicate proc %s", proc_name); - return -1; - } - } - if (extproctable == NULL) { - extprocsize = TABLE_GROW_SIZE; - extproctable = (ext_proc_node_t *)malloc(sizeof(ext_proc_node_t) * extprocsize); - } else if (extprocsize <= (extproccount + 1)) { - extprocsize += TABLE_GROW_SIZE; - extproctable = (ext_proc_node_t *) - realloc(extproctable, sizeof(ext_proc_node_t) * extprocsize); + slib_t *lib = get_lib(lib_id); + + if (lib->proc_list == NULL) { + lib->proc_list_size = TABLE_GROW_SIZE; + lib->proc_list = (ext_proc_node_t *)malloc(sizeof(ext_proc_node_t) * lib->proc_list_size); + } else if (lib->proc_list_size <= (lib->proc_count + 1)) { + lib->proc_list_size += TABLE_GROW_SIZE; + lib->proc_list = (ext_proc_node_t *)realloc(lib->proc_list, sizeof(ext_proc_node_t) * lib->proc_list_size); } - extproctable[extproccount].lib_id = lib_id; - extproctable[extproccount].symbol_index = 0; - strlcpy(extproctable[extproccount].name, proc_name, sizeof(extproctable[extproccount].name)); - strupper(extproctable[extproccount].name); + lib->proc_list[lib->proc_count].lib_id = lib_id; + lib->proc_list[lib->proc_count].symbol_index = 0; + strlcpy(lib->proc_list[lib->proc_count].name, proc_name, sizeof(lib->proc_list[lib->proc_count].name)); + strupper(lib->proc_list[lib->proc_count].name); if (opt_verbose) { - log_printf("LIB: %d, Idx: %d, PROC '%s'\n", lib_id, extproccount, - extproctable[extproccount].name); + log_printf("LIB: %d, Idx: %d, PROC '%s'\n", lib_id, lib->proc_count, + lib->proc_list[lib->proc_count].name); } - extproccount++; - return extproccount - 1; + lib->proc_count++; + return lib->proc_count - 1; } /** * Add an external function to the list */ int slib_add_external_func(const char *func_name, uint32_t lib_id) { - // scan for conflicts - for (int i = 0; i < extfunccount; i++) { - if (strcmp(extfunctable[i].name, func_name) == 0) { - sc_raise("LIB: duplicate func %s", func_name); - return -1; - } - } - if (extfunctable == NULL) { - extfuncsize = TABLE_GROW_SIZE; - extfunctable = (ext_func_node_t *)malloc(sizeof(ext_func_node_t) * extfuncsize); - } else if (extfuncsize <= (extfunccount + 1)) { - extfuncsize += TABLE_GROW_SIZE; - extfunctable = (ext_func_node_t *) - realloc(extfunctable, sizeof(ext_func_node_t) * extfuncsize); + slib_t *lib = get_lib(lib_id); + + if (lib->func_list == NULL) { + lib->func_list_size = TABLE_GROW_SIZE; + lib->func_list = (ext_func_node_t *)malloc(sizeof(ext_func_node_t) * lib->func_list_size); + } else if (lib->func_list_size <= (lib->func_count + 1)) { + lib->func_list_size += TABLE_GROW_SIZE; + lib->func_list = (ext_func_node_t *) + realloc(lib->func_list, sizeof(ext_func_node_t) * lib->func_list_size); } - extfunctable[extfunccount].lib_id = lib_id; - extfunctable[extfunccount].symbol_index = 0; - strlcpy(extfunctable[extfunccount].name, func_name, sizeof(extfunctable[extfunccount].name)); - strupper(extfunctable[extfunccount].name); + lib->func_list[lib->func_count].lib_id = lib_id; + lib->func_list[lib->func_count].symbol_index = 0; + strlcpy(lib->func_list[lib->func_count].name, func_name, sizeof(lib->func_list[lib->func_count].name)); + strupper(lib->func_list[lib->func_count].name); if (opt_verbose) { - log_printf("LIB: %d, Idx: %d, FUNC '%s'\n", lib_id, extfunccount, - extfunctable[extfunccount].name); + log_printf("LIB: %d, Idx: %d, FUNC '%s'\n", lib_id, lib->func_count, + lib->func_list[lib->func_count].name); } - extfunccount++; - return extfunccount - 1; + lib->func_count++; + return lib->func_count - 1; } /** * returns the ID of the keyword */ int slib_get_kid(int lib_id, const char *name) { - const char *dot = strchr(name, '.'); - const char *field = (dot != NULL ? dot + 1 : name); - for (int i = 0; i < extproccount; i++) { - if (extproctable[i].lib_id == lib_id && - strcmp(extproctable[i].name, field) == 0) { - return i; + slib_t *lib = get_lib(lib_id); + if (lib != NULL) { + const char *dot = strchr(name, '.'); + const char *field = (dot != NULL ? dot + 1 : name); + for (int i = 0; i < lib->proc_count; i++) { + if (lib->proc_list[i].lib_id == lib_id && + strcmp(lib->proc_list[i].name, field) == 0) { + return i; + } } - } - for (int i = 0; i < extfunccount; i++) { - if (extfunctable[i].lib_id == lib_id && - strcmp(extfunctable[i].name, field) == 0) { - return i; + for (int i = 0; i < lib->func_count; i++) { + if (lib->func_list[i].lib_id == lib_id && + strcmp(lib->func_list[i].name, field) == 0) { + return i; + } } } return -1; @@ -215,7 +212,7 @@ int slib_get_kid(int lib_id, const char *name) { */ int slib_get_module_id(const char *name, const char *alias) { for (int i = 0; i < slib_count; i++) { - slib_t *lib = &slib_table[i]; + slib_t *lib = &slib_list[i]; if (strcasecmp(lib->name, name) == 0) { strcpy(lib->name, alias); return i; @@ -226,24 +223,21 @@ int slib_get_module_id(const char *name, const char *alias) { } void slib_import_routines(slib_t *lib, int comp) { + int total = 0; char buf[SB_KEYWORD_SIZE]; - int (*fgetname) (int, char *); - int (*fcount) (void); - lib->first_proc = extproccount; - lib->first_func = extfunccount; lib->sblib_func_exec = slib_getoptptr(lib, "sblib_func_exec"); lib->sblib_proc_exec = slib_getoptptr(lib, "sblib_proc_exec"); - - fcount = slib_getoptptr(lib, "sblib_proc_count"); - fgetname = slib_getoptptr(lib, "sblib_proc_getname"); + sblib_count_fn fcount = slib_getoptptr(lib, "sblib_proc_count"); + sblib_getname_fn fgetname = slib_getoptptr(lib, "sblib_proc_getname"); if (fcount && fgetname) { int count = fcount(); + total += count; for (int i = 0; i < count; i++) { if (fgetname(i, buf)) { strupper(buf); - if (slib_add_external_proc(buf, lib->id) == -1) { + if (!lib->imported && slib_add_external_proc(buf, lib->id) == -1) { break; } else if (comp) { char name[NAME_SIZE]; @@ -262,10 +256,11 @@ void slib_import_routines(slib_t *lib, int comp) { if (fcount && fgetname) { int count = fcount(); + total += count; for (int i = 0; i < count; i++) { if (fgetname(i, buf)) { strupper(buf); - if (slib_add_external_func(buf, lib->id) == -1) { + if (!lib->imported && slib_add_external_func(buf, lib->id) == -1) { break; } else if (comp) { char name[NAME_SIZE]; @@ -278,16 +273,10 @@ void slib_import_routines(slib_t *lib, int comp) { } } } -} -/** - * returns slib_t* for the given id - */ -slib_t *get_lib(int lib_id) { - if (lib_id < 0 || lib_id >= slib_count) { - return NULL; + if (!total) { + log_printf("LIB: module '%s' has no exports\n", lib->name); } - return &slib_table[lib_id]; } /** @@ -295,17 +284,22 @@ slib_t *get_lib(int lib_id) { */ void slib_import(int lib_id, int comp) { slib_t *lib = get_lib(lib_id); - if (lib && !lib->imported) { + if (lib && (comp || !lib->imported)) { slib_import_routines(lib, comp); lib->imported = 1; } + if (lib && !comp) { + sblib_init_fn minit = slib_getoptptr(lib, "sblib_init"); + if (minit && !minit(gsb_last_file)) { + rt_raise("LIB: %s->sblib_init(), failed", lib->name); + } + } } /** - * opens the library and invokes the init function + * opens the library */ void slib_open(const char *fullname, const char *name) { - int success = 0; int name_index = 0; if (strncmp(name, "lib", 3) == 0) { @@ -313,7 +307,7 @@ void slib_open(const char *fullname, const char *name) { name_index = 3; } - slib_t *lib = &slib_table[slib_count]; + slib_t *lib = &slib_list[slib_count]; memset(lib, 0, sizeof(slib_t)); strlcpy(lib->name, name + name_index, NAME_SIZE); strlcpy(lib->fullname, fullname, PATH_SIZE); @@ -321,48 +315,41 @@ void slib_open(const char *fullname, const char *name) { lib->imported = 0; if (!opt_quiet) { - log_printf("LIB: importing %s", fullname); + log_printf("LIB: registering '%s'", fullname); } if (slib_llopen(lib)) { - success = 1; - - // init - int (*minit) (void); - minit = slib_getoptptr(lib, "sblib_init"); - if (minit) { - if (!minit()) { - sc_raise("LIB: %s->sblib_init(), failed", lib->name); - success = 0; - } - } - + slib_count++; // override default name - const char *(*get_module_name) (void); - get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); + sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); if (get_module_name) { strlcpy(lib->name, get_module_name(), NAME_SIZE); } } else { sc_raise("LIB: can't open %s", fullname); } - if (success) { - slib_count++; - if (!opt_quiet) { - log_printf("... done\n"); - } +} + +/** + * whether name ends with LIB_EXT and does not contain '-', eg libstdc++-6.dll + */ +int slib_is_module(const char *name) { + int result = 0; + if (name && name[0] != '\0') { + int offs = strlen(name) - (sizeof(LIB_EXT) - 1); + result = offs > 0 && strchr(name, '-') == NULL && strcasecmp(name + offs, LIB_EXT) == 0; } + return result; } void slib_open_path(const char *path, const char *name) { - char *p; - if (((p = strstr(name, LIB_EXT)) != NULL) && strcmp(p, LIB_EXT) == 0) { + if (slib_is_module(name)) { // ends with LIB_EXT char full[PATH_SIZE]; char libname[NAME_SIZE]; // copy name without extension strlcpy(libname, name, sizeof(libname)); - p = strchr(libname, '.'); + char *p = strchr(libname, '.'); *p = '\0'; // copy full path to name @@ -396,7 +383,7 @@ void slib_scan_path(const char *path) { } } } else if (!opt_quiet) { - log_printf("LIB: module path %s not found.\n", path); + log_printf("LIB: module path '%s' not found.\n", path); } } @@ -408,6 +395,7 @@ void slib_init_path() { // null terminate the current path *sep = '\0'; slib_scan_path(path); + *sep = ':'; path = sep + 1; } else { slib_scan_path(path); @@ -423,21 +411,15 @@ void slib_init() { slib_count = 0; if (!prog_error && opt_loadmod) { - if (!opt_quiet) { - log_printf("LIB: scanning for modules...\n"); - } - if (opt_modpath[0] == '\0') { const char *modpath = getenv("SBASICPATH"); if (modpath != NULL) { strlcpy(opt_modpath, modpath, OPT_MOD_SZ); } } - - if (opt_modpath[0] == '\0') { - strcpy(opt_modpath, DEFAULT_PATH); + if (!opt_quiet) { + log_printf("LIB: scanning for modules in '%s'\n", opt_modpath); } - slib_init_path(); } } @@ -447,26 +429,22 @@ void slib_init() { */ void slib_close() { for (int i = 0; i < slib_count; i++) { - slib_t *lib = &slib_table[i]; + slib_t *lib = &slib_list[i]; if (lib->handle) { - void (*mclose) (void); - mclose = slib_getoptptr(lib, "sblib_close"); + sblib_close_fn mclose = slib_getoptptr(lib, "sblib_close"); if (mclose) { mclose(); } slib_llclose(lib); } - } - if (slib_count) { - free(extproctable); - free(extfunctable); - slib_count = 0; - extproctable = NULL; - extprocsize = 0; - extproccount = 0; - extfunctable = NULL; - extfuncsize = 0; - extfunccount = 0; + free(lib->proc_list); + free(lib->func_list); + lib->proc_count = 0; + lib->func_count = 0; + lib->proc_list_size = 0; + lib->func_list_size = 0; + lib->func_list = NULL; + lib->proc_list = NULL; } } @@ -566,9 +544,9 @@ int slib_exec(slib_t *lib, var_t *ret, int index, int proc) { int success; v_init(ret); if (proc) { - success = lib->sblib_proc_exec(index - lib->first_proc, pcount, ptable, ret); + success = lib->sblib_proc_exec(index, pcount, ptable, ret); } else { - success = lib->sblib_func_exec(index - lib->first_func, pcount, ptable, ret); + success = lib->sblib_func_exec(index, pcount, ptable, ret); } // error @@ -597,6 +575,7 @@ int slib_procexec(int lib_id, int index) { slib_t *lib = get_lib(lib_id); if (lib && lib->sblib_proc_exec) { var_t ret; + v_init(&ret); result = slib_exec(lib, &ret, index, 1); v_free(&ret); } else { @@ -622,7 +601,7 @@ int slib_funcexec(int lib_id, int index, var_t *ret) { void *slib_get_func(const char *name) { void *result = NULL; for (int i = 0; i < slib_count && result == NULL; i++) { - slib_t *lib = &slib_table[i]; + slib_t *lib = &slib_list[i]; if (lib->imported) { result = slib_getoptptr(lib, name); } @@ -637,8 +616,7 @@ int slib_procexec(int lib_id, int index) { return 0; } int slib_get_kid(int lib_id, const char *name) { return -1; } int slib_get_module_id(const char *name, const char *alias) { return -1; } void slib_close() {} -int slib_events(int wait_flag) { return 0; } void slib_init(int mcount, const char *mlist) {} -void slib_setup_comp(int lib_id) {} -void *slib_get_func(const char *name) {} +void *slib_get_func(const char *name) { return 0; } +void slib_import(int lib_id, int comp) {} #endif diff --git a/src/common/hashmap.c b/src/common/hashmap.c index ff5363e8..75c63f2f 100644 --- a/src/common/hashmap.c +++ b/src/common/hashmap.c @@ -125,6 +125,7 @@ void hashmap_create(var_p_t map, int size) { v_free(map); map->type = V_MAP; map->v.m.count = 0; + map->v.m.id = -1; if (size == 0) { map->v.m.size = MAP_SIZE; } else { diff --git a/src/common/scan.c b/src/common/scan.c index 66df2009..4c122a28 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -47,6 +47,7 @@ const int LEN_ANTIALIAS = STRLEN(LCN_ANTIALIAS); const int LEN_LDMODULES = STRLEN(LCN_LOAD_MODULES); const int LEN_AUTOLOCAL = STRLEN(LCN_AUTOLOCAL); const int LEN_AS_WRS = STRLEN(LCN_AS_WRS); +const int LEN_CONST = STRLEN(LCN_CONST); #define KW_TYPE_LINE_BYTES 5 @@ -142,11 +143,12 @@ bc_symbol_rec_t *add_imptable_rec(const char *proc_name, int lib_id, int symbol_ } // store lib-record -void add_libtable_rec(const char *lib, int uid, int type) { +void add_libtable_rec(const char *lib, const char *alias, int uid, int type) { bc_lib_rec_t *imlib = (bc_lib_rec_t *)malloc(sizeof(bc_lib_rec_t)); memset(imlib, 0, sizeof(bc_lib_rec_t)); strlcpy(imlib->lib, lib, sizeof(imlib->lib)); + strlcpy(imlib->alias, alias, sizeof(imlib->alias)); imlib->id = uid; imlib->type = type; @@ -580,7 +582,7 @@ int comp_check_labels() { } /* - * returns true if 'name' is a unit or c-module + * returns 1 if 'name' is a unit, 2 if 'name' c-module otherwise 0 */ int comp_check_lib(const char *name) { char tmp[SB_KEYWORD_SIZE + 1]; @@ -593,11 +595,11 @@ int comp_check_lib(const char *name) { bc_lib_rec_t *lib = comp_libtable.elem[i]; // remove any file path component from the name - char *dir_sep = strrchr(lib->lib, OS_DIRSEP); - char *lib_name = dir_sep ? dir_sep + 1 : lib->lib; + char *dir_sep = strrchr(lib->alias, OS_DIRSEP); + char *lib_name = dir_sep ? dir_sep + 1 : lib->alias; if (strcasecmp(lib_name, tmp) == 0) { - return 1; + return lib->type == 0 ? 2 : 1; } } } @@ -636,9 +638,7 @@ int comp_create_var(const char *name) { * add external variable */ int comp_add_external_var(const char *name, int lib_id) { - int idx; - - idx = comp_create_var(name); + int idx = comp_create_var(name); comp_vartable[idx].lib_id = lib_id; if (lib_id & UID_UNIT_BIT) { @@ -678,15 +678,22 @@ bid_t comp_var_getID(const char *var_name) { // // If the name is not found in comp_libtable then it // is treated as a structure reference - if (dot != NULL && comp_check_lib(tmp)) { - for (i = 0; i < comp_varcount; i++) { - if (strcasecmp(comp_vartable[i].name, tmp) == 0) { - return i; + if (dot != NULL) { + int module_type = comp_check_lib(tmp); + if (module_type) { + for (i = 0; i < comp_varcount; i++) { + if (strcasecmp(comp_vartable[i].name, tmp) == 0) { + return i; + } + } + if (module_type == 2) { + *dot = '\0'; + sc_raise(MSG_MODULE_NO_MEMBER, tmp, dot + 1); + } else { + sc_raise(MSG_MEMBER_DOES_NOT_EXIST, tmp); } + return 0; } - - sc_raise(MSG_MEMBER_DOES_NOT_EXIST, tmp); - return 0; } // // search in global name-space @@ -1914,19 +1921,11 @@ void comp_cmd_option(char *src) { /** * stores export symbols (in pass2 will be checked again) */ -void bc_store_exports(const char *slist) { +void bc_store_exports(char *slist) { char_p_t pars[MAX_PARAMS]; - int count = 0, i; - char *newlist; - - newlist = (char *)malloc(strlen(slist) + 3); - strcpy(newlist, "("); - strcat(newlist, slist); - strcat(newlist, ")"); - - comp_getlist_insep(newlist, pars, "()", MAX_PARAMS, &count); - + int count = comp_getlist(slist, pars, MAX_PARAMS); int offset; + if (comp_exptable.count) { offset = comp_exptable.count; comp_exptable.count += count; @@ -1938,14 +1937,24 @@ void bc_store_exports(const char *slist) { comp_exptable.elem = (unit_sym_t **)malloc(comp_exptable.count * sizeof(unit_sym_t *)); } - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { unit_sym_t *sym = (unit_sym_t *)malloc(sizeof(unit_sym_t)); memset(sym, 0, sizeof(unit_sym_t)); - strlcpy(sym->symbol, pars[i], sizeof(sym->symbol)); + + char var_name[SB_KEYWORD_SIZE + 1]; + comp_prepare_name(var_name, pars[i], SB_KEYWORD_SIZE); + if (strncmp(LCN_CONST, var_name, LEN_CONST) == 0) { + char *next = pars[i] + LEN_CONST; + comp_prepare_name(var_name, next, SB_KEYWORD_SIZE); + } + strlcpy(sym->symbol, var_name, sizeof(sym->symbol)); comp_exptable.elem[offset + i] = sym; - } - free(newlist); + if (strlen(var_name) != strlen(pars[i])) { + // handle same line variable assignment, eg export blah = foo + comp_text_line(pars[i], 1); + } + } } void comp_get_unary(const char *p, int *ladd, int *linc, int *ldec, int *leqop) { @@ -4297,9 +4306,9 @@ void comp_preproc_import(const char *slist) { if (uid != -1) { // store C module lib-record slib_import(uid, 1); - add_libtable_rec(alias, uid, 0); + add_libtable_rec(alias, alias, uid, 0); } else { - uid = open_unit(buf); + uid = open_unit(buf, alias); if (uid < 0) { sc_raise(MSG_UNIT_NOT_FOUND, buf); return; @@ -4310,7 +4319,7 @@ void comp_preproc_import(const char *slist) { return; } // store lib-record - add_libtable_rec(buf, uid, 1); + add_libtable_rec(buf, alias, uid, 1); // clean up close_unit(uid); @@ -4350,18 +4359,16 @@ void comp_preproc_unit(char *name) { SKIP_SPACES(p); - if (!is_alpha(*p)) { + if (is_alpha(*p)) { + p = get_unit_name(p, comp_unit_name); + comp_unit_flag = 1; + SKIP_SPACES(p); + if (*p != '\n' && *p != ':') { + sc_raise(MSG_UNIT_ALREADY_DEFINED); + } + } else { sc_raise(MSG_INVALID_UNIT_NAME); } - - p = get_unit_name(p, comp_unit_name); - comp_unit_flag = 1; - - SKIP_SPACES(p); - - if (*p != '\n' && *p != ':') { - sc_raise(MSG_UNIT_ALREADY_DEFINED); - } } /** @@ -4707,7 +4714,11 @@ int comp_pass1(const char *section, const char *text) { comp_line = comp_udptable[i].pline; char *dot = strchr(comp_udptable[i].name, '.'); if (dot) { - sc_raise(MSG_UNDEFINED_MAP, comp_udptable[i].name); + if (comp_check_lib(comp_udptable[i].name) == 2) { + sc_raise(MSG_MODULE_NO_RETURN, comp_udptable[i].name); + } else { + sc_raise(MSG_UNDEFINED_MAP, comp_udptable[i].name); + } } else { if (comp_is_func(comp_udptable[i].name) != -1) { sc_raise(MSG_FUNC_NOT_ASSIGNED, comp_udptable[i].name); diff --git a/src/common/smbas.h b/src/common/smbas.h index ab463193..8fb9b5e0 100644 --- a/src/common/smbas.h +++ b/src/common/smbas.h @@ -53,6 +53,7 @@ typedef struct { */ typedef struct { char lib[OS_FILENAME_SIZE + 1]; /**< library name */ + char alias[OS_FILENAME_SIZE + 1]; /**< library name alias */ int type; /**< library type (unit, c-module) */ int id; /**< lib-id in this byte-code */ int tid; /**< task id (updated on loading) */ diff --git a/src/common/str.c b/src/common/str.c index 52af5462..4cf76b57 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -1092,17 +1092,23 @@ void hex_dump(const unsigned char *block, int size) { void cstr_init(cstr *cs, int size) { cs->length = 0; - cs->size = size; + cs->size = size < 1 ? 1 : size; cs->buf = malloc(size); cs->buf[0] = '\0'; } void cstr_append(cstr *cs, const char *str) { - int len = strlen(str); - if (cs->size - cs->length < len + 1) { - cs->size += len + 1; - cs->buf = realloc(cs->buf, cs->size); + cstr_append_i(cs, str, strlen(str)); +} + +void cstr_append_i(cstr *cs, const char *str, int len) { + if (len > 0) { + if (cs->size - cs->length < len + 1) { + cs->size += len + 1; + cs->buf = realloc(cs->buf, cs->size); + } + strlcat(cs->buf, str, cs->size); + cs->length += len; + cs->buf[cs->length] = '\0'; } - strcat(cs->buf, str); - cs->length += len; } diff --git a/src/common/str.h b/src/common/str.h index 988eaa0a..3e85357b 100644 --- a/src/common/str.h +++ b/src/common/str.h @@ -418,6 +418,13 @@ void cstr_init(cstr *cs, int size); */ void cstr_append(cstr *cs, const char *str); +/** + * @ingroup str + * + * append to string buffer + */ +void cstr_append_i(cstr *cs, const char *str, int len); + #if defined(__cplusplus) } #endif diff --git a/src/common/sys.h b/src/common/sys.h index fa28742a..d391ad31 100644 --- a/src/common/sys.h +++ b/src/common/sys.h @@ -44,7 +44,7 @@ extern "C" { #define VAR_MAX_INT LONG_MAX #define VAR_NUM_FMT "%f" -#define VAR_INT_FMT "%ld" +#define VAR_INT_FMT "%lld" #define VAR_INT_NUM_FMT "%.0f" #define OS_PATHNAME_SIZE 1024 @@ -57,7 +57,7 @@ extern "C" { #define OS_LINESEPARATOR "\r\n" #else #define SB_VERSYS "Unix" - #define OS_LINESEPARATOR "\r\n" + #define OS_LINESEPARATOR "\n" #endif #if UINTPTR_MAX == 0xffffffff diff --git a/src/common/units.c b/src/common/units.c index a3b19202..06b9c119 100644 --- a/src/common/units.c +++ b/src/common/units.c @@ -99,7 +99,7 @@ int find_unit(const char *name, char *file) { * @param file is the filename * @return the unit handle or -1 on error */ -int open_unit(const char *file) { +int open_unit(const char *file, const char *alias) { int h; unit_t u; int uid = -1; @@ -169,6 +169,7 @@ int open_unit(const char *file) { // setup the rest strcpy(u.name, unitname); + strcpy(u.hdr.base, alias); u.status = unit_loaded; // add unit diff --git a/src/common/units.h b/src/common/units.h index c24ea0d5..2af64a80 100644 --- a/src/common/units.h +++ b/src/common/units.h @@ -114,9 +114,10 @@ int find_unit(const char *name, char *file); * open unit * * @param file is the filename + * @param alias alternative unit name * @return the unit handle or -1 on error */ -int open_unit(const char *file); +int open_unit(const char *file, const char *alias); /** * @ingroup exec diff --git a/src/common/var.c b/src/common/var.c index aa9983c6..f23bdffa 100644 --- a/src/common/var.c +++ b/src/common/var.c @@ -524,6 +524,7 @@ void v_set(var_t *dest, const var_t *src) { break; case V_FUNC: dest->v.fn.cb = src->v.fn.cb; + dest->v.fn.id = src->v.fn.id; break; case V_NIL: dest->type = V_NIL; @@ -563,12 +564,14 @@ void v_move(var_t *dest, const var_t *src) { dest->v.m.map = src->v.m.map; dest->v.m.count = src->v.m.count; dest->v.m.size = src->v.m.size; + dest->v.m.id = src->v.m.id; break; case V_REF: dest->v.ref = src->v.ref; break; case V_FUNC: dest->v.fn.cb = src->v.fn.cb; + dest->v.fn.id = src->v.fn.id; break; case V_NIL: dest->type = V_NIL; @@ -795,4 +798,5 @@ void v_create_func(var_p_t map, const char *name, method cb) { var_p_t v_func = map_add_var(map, name, 0); v_func->type = V_FUNC; v_func->v.fn.cb = cb; + v_func->v.fn.id = 0; } diff --git a/src/common/var.h b/src/common/var.h index 425f86b1..b3f6144a 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -202,15 +202,6 @@ void v_init_pool(void); */ void v_pool_free(var_t *var); -/** - * @ingroup var - * - * creates a new variable - * - * @return a newly created var_t object - */ -var_t *v_new(void); - /** * < returns the integer value of variable v * @ingroup var diff --git a/src/common/var_map.c b/src/common/var_map.c index 5e9946f2..a363a0bc 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -290,6 +290,7 @@ void map_set(var_p_t dest, const var_p_t src) { hashmap_create(dest, src->v.m.count); hashmap_foreach(src, map_set_cb, &cb); dest->v.m.count = src->v.m.count; + dest->v.m.id = src->v.m.id; } } diff --git a/src/include/module.h b/src/include/module.h index abe94f4a..a8ea43b3 100644 --- a/src/include/module.h +++ b/src/include/module.h @@ -31,7 +31,7 @@ typedef struct { * * @return non-zero on success */ -int sblib_init(void); +int sblib_init(const char *sourceFile); /** * @ingroup modlib diff --git a/src/include/var.h b/src/include/var.h index 11819e4f..f5c09282 100644 --- a/src/include/var.h +++ b/src/include/var.h @@ -15,7 +15,7 @@ // https://en.wikipedia.org/wiki/Dependency_inversion_principle typedef double var_num_t; -typedef long int var_int_t; +typedef long long int var_int_t; #define MAXDIM 6 #define OS_INTSZ sizeof(var_int_t) @@ -32,7 +32,7 @@ typedef long int var_int_t; /* * Variable - types */ -#define V_INT 0 /**< variable type, 32bit integer @ingroup var */ +#define V_INT 0 /**< variable type, 64bit integer @ingroup var */ #define V_NUM 1 /**< variable type, 64bit float (same as V_NUM) @ingroup var */ #define V_STR 2 /**< variable type, string @ingroup var */ #define V_ARRAY 3 /**< variable type, array of variables @ingroup var */ @@ -71,6 +71,7 @@ typedef struct var_s { void *map; uint32_t count; uint32_t size; + uint32_t id; } m; // reference variable @@ -79,6 +80,7 @@ typedef struct var_s { // object method struct { method cb; + uint32_t id; } fn; // generic ptr (string) @@ -118,6 +120,15 @@ typedef struct var_s { typedef var_t *var_p_t; +/** + * @ingroup var + * + * creates a new variable + * + * @return a newly created var_t object + */ +var_t *v_new(void); + /** * @ingroup var * diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index 73664ac7..07507c20 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -477,6 +477,7 @@ struct proc_keyword_s proc_table[] = { #define LCN_LOAD_MODULES "LOAD MODULES" #define LCN_AUTOLOCAL "AUTOLOCAL" #define LCN_AS_WRS "AS " +#define LCN_CONST "CONST" /* system variables */ #define LCN_SV_SBVER "SBVER" diff --git a/src/languages/messages.en.h b/src/languages/messages.en.h index f23f74cf..18709da0 100644 --- a/src/languages/messages.en.h +++ b/src/languages/messages.en.h @@ -86,6 +86,8 @@ #define MSG_MISSING_FOR "NEXT: Missing FOR on the same level" #define MSG_MISSING_IF "ENDIF: Missing IF on the same level" #define MSG_MEMBER_DOES_NOT_EXIST "Unit has no member named '%s'\n" +#define MSG_MODULE_NO_MEMBER "Module '%s' has no member '%s'\n" +#define MSG_MODULE_NO_RETURN "Module FUNC '%s' result not assigned\n" #define MSG_CANT_OPEN_FILE_AT "Can't open '%s' at '%s'\n" #define MSG_CANT_OPEN_FILE "Can't open '%s'\n" #define MSG_GRMODE_ERR "GRMODE, usage:x[x]\nExample: OPTION PREDEF GRMODE=640x480x4\n" diff --git a/src/lib/lodepng b/src/lib/lodepng index 34628e89..7fdcc96a 160000 --- a/src/lib/lodepng +++ b/src/lib/lodepng @@ -1 +1 @@ -Subproject commit 34628e89e80cd007179b25b0b2695e6af0f57fac +Subproject commit 7fdcc96a5e5864eee72911c3ca79b1d9f0d12292 diff --git a/src/lib/miniaudio b/src/lib/miniaudio index b80f7f94..db53994f 160000 --- a/src/lib/miniaudio +++ b/src/lib/miniaudio @@ -1 +1 @@ -Subproject commit b80f7f949152f93a0af499b4d6d07b8e60d0e673 +Subproject commit db53994f7cbd29a585023cf65fee0021fd85248e diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 7918d71a..7e795b2e 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -8,9 +8,9 @@ android { defaultConfig { applicationId 'net.sourceforge.smallbasic' minSdkVersion 16 - targetSdkVersion 28 - versionCode 40 - versionName "12.19" + targetSdkVersion 29 + versionCode 41 + versionName "12.20" resConfigs "en" } @@ -47,6 +47,6 @@ android { } dependencies { - implementation 'androidx.core:core:1.0.2' - testImplementation 'junit:junit:4.12' + implementation 'androidx.core:core:1.3.2' + testImplementation 'junit:junit:4.13.1' } diff --git a/src/platform/android/app/src/main/AndroidManifest.xml b/src/platform/android/app/src/main/AndroidManifest.xml index 8c5a63c6..b4a995bf 100644 --- a/src/platform/android/app/src/main/AndroidManifest.xml +++ b/src/platform/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,9 @@ diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 4ef23fe2..2797ee3c 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -14,6 +14,7 @@ const colText2 = theme.text4 const colNav = theme.text1 const colNav2 = theme.text6 const menu_gap = -(char_w / 2) +const is_android = instr(sbver, "Android") != 0 const is_sdl = instr(sbver, "SDL") != 0 const onlineUrl = "http://smallbasic.github.io/samples/index.bas" const idxEdit = 6 @@ -273,7 +274,7 @@ sub loadFileList(path, byref basList) end dirwalk path, "", use walker(x) - if (path = "/" && !is_sdl) then + if (path = "/" && is_android) then ext_storage = env("EXTERNAL_DIR") if (len(ext_storage) > 0) then emptyNode.name = mid(ext_storage, 2) @@ -294,7 +295,7 @@ sub loadFileList(path, byref basList) end sub listFiles(byref frm, path, sortDir, byref basList) - local lastItem, bn, abbr, gap, n, lab, name, txtcol, i + local lastItem, bn, abbr, gap, n, lab, name, txtcol, i, pathx local name_col = colNav local size_col = colNav local date_col = colNav @@ -324,7 +325,20 @@ sub listFiles(byref frm, path, sortDir, byref basList) date_col = colNav2 end select - bn = mk_bn(0, "Files in " + path, colText) + if (is_android) then + pathx = mid(path, 1, len(path) - 1) + if (pathx == env("LEGACY_DIR")) then + bn = mk_bn(0, "SmallBASIC legacy project files", colText2) + elseif (pathx == env("INTERNAL_DIR")) then + bn = mk_bn(0, "Temporary files", colText2) + elseif (pathx == env("EXTERNAL_DIR")) then + bn = mk_bn(0, "SmallBASIC project files", colText) + else + bn = mk_bn(0, "Files in " + path, colText) + endif + else + bn = mk_bn(0, "Files in " + path, colText) + endif bn.type = "label" bn.x = 0 bn.y = -lineSpacing @@ -602,6 +616,17 @@ sub manageFiles() cls end +func selectDir(s) + local result = s + if (is_android && s != env("EXTERNAL_DIR") && (s == env("LEGACY_DIR") || s == env("INTERNAL_DIR"))) then + wnd.ask("Would you like to navigate to the SmallBASIC folder instead?", "Temporary file location") + if (wnd.answer == 0) then + result = env("EXTERNAL_DIR") + endif + endif + return result +end + func changeDir(s) try chdir s @@ -669,6 +694,7 @@ sub main if (isdir(frm.value)) then cls + frm.value = selectDir(frm.value) if (changeDir(frm.value)) then path = frm.value endif diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index fa2f7b7b..ce91dcf5 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -50,6 +50,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -62,6 +63,9 @@ import java.net.Socket; import java.net.SocketException; import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -601,11 +605,13 @@ public void run() { if (imm != null) { if (show) { String id = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if ("com.sec.android.inputmethod/.SamsungKeypad".equals(id)) { + if (id != null && id.toLowerCase().contains("samsung")) { + imm.showInputMethodPicker(); String message = getResources().getString(R.string.samsung_keyboard); Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); + } else { + imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); } - imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); } else { imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } @@ -868,6 +874,28 @@ private boolean isPublicStorage(String dir) { return result; } + private void migrateFiles(File fromDir, File toDir) { + FilenameFilter filter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name != null && name.endsWith(".bas"); + } + }; + File[] toFiles = toDir.listFiles(filter); + File[] fromFiles = fromDir.listFiles(filter); + if ((toFiles == null || toFiles.length == 0) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // only attempt file copy into a clean destination folder + for (File file : fromFiles) { + try { + Path to = toDir.toPath().resolve(file.getName()); + Files.copy(file.toPath(), to); + } catch (IOException e) { + Log.d(TAG, "failed to copy: ", e); + } + } + } + } + private boolean permitted(String permission) { return (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED); } @@ -1005,7 +1033,17 @@ private void sendResponse(Socket socket, String content) throws IOException { private void setupStorageEnvironment(boolean external) { setenv("INTERNAL_DIR", getInternalStorage()); if (external) { - setenv("EXTERNAL_DIR", getExternalStorage()); + String externalDir = getExternalStorage(); + File files = getExternalFilesDir(null); + if (files != null) { + String externalFiles = files.getAbsolutePath(); + if (!externalDir.equals(externalFiles) && isPublicStorage(externalFiles)) { + migrateFiles(new File(externalDir), files); + setenv("LEGACY_DIR", externalDir); + externalDir = externalFiles; + } + } + setenv("EXTERNAL_DIR", externalDir); } } diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index b25df99f..fd77e825 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "com.github.ben-manes:gradle-versions-plugin:0.22.0" } } diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index c9d41798..7d18de6c 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -536,8 +536,15 @@ void Runtime::loadConfig() { } s = settings.get(PATH_KEY); if (s) { - trace("path = %s", s->c_str()); - chdir(s->c_str()); + const char *legacy = getenv("LEGACY_DIR"); + if (storage != nullptr && legacy != nullptr && strcmp(legacy, s->c_str()) == 0) { + // don't restore the legacy folder + trace("path = %s", storage); + chdir(storage); + } else { + trace("path = %s", s->c_str()); + chdir(s->c_str()); + } } s = settings.get(MUTE_AUDIO_KEY); if (s && s->toInteger() == 1) { @@ -1064,7 +1071,7 @@ void System::editSource(strlib::String loadPath, bool restoreOnExit) { case SB_KEY_F(1): widget = helpWidget; helpWidget->createKeywordIndex(); - helpWidget->show(); + helpWidget->showPopup(-4, -2); helpWidget->setFocus(true); runtime->showKeypad(false); showStatus = false; diff --git a/src/platform/console/Makefile.am b/src/platform/console/Makefile.am index ccdd8daa..e522d821 100644 --- a/src/platform/console/Makefile.am +++ b/src/platform/console/Makefile.am @@ -12,6 +12,7 @@ bin_PROGRAMS = sbasic sbasic_SOURCES = \ ../../lib/lodepng/lodepng.cpp ../../lib/lodepng/lodepng.h \ main.cpp \ + input.cpp \ device.cpp \ image.cpp \ decomp.c diff --git a/src/platform/console/device.cpp b/src/platform/console/device.cpp index cca2d7c8..2da76e86 100644 --- a/src/platform/console/device.cpp +++ b/src/platform/console/device.cpp @@ -13,6 +13,8 @@ #include "common/extlib.h" #include "common/smbas.h" +#define WAIT_INTERVAL 5 + typedef void (*settextcolor_fn)(long fg, long bg); typedef void (*setpenmode_fn)(int enable); typedef int (*getpen_fn)(int code); @@ -120,6 +122,8 @@ int osd_devinit() { } os_graf_mx = opt_pref_width; os_graf_my = opt_pref_height; + setsysvar_int(SYSVAR_XMAX, os_graf_mx); + setsysvar_int(SYSVAR_YMAX, os_graf_my); if (p_write == NULL) { p_write = default_write; @@ -331,5 +335,27 @@ int osd_textheight(const char *str) { return result; } +// delay while pumping events +void dev_delay(uint32_t timeout) { + uint32_t slept = 0; + uint32_t now = dev_get_millisecond_count(); + while (1) { + if (osd_events(0) < 0) { + break; + } + if (dev_get_millisecond_count() - now > timeout) { + break; + } + usleep(WAIT_INTERVAL * 1000); + slept += WAIT_INTERVAL; + if (timeout > 0 && slept > timeout) { + break; + } + } +} + // unused void dev_log_stack(const char *keyword, int type, int line) {} +void v_create_form(var_p_t var) {} +void v_create_window(var_p_t var) {} +void dev_show_page() {} diff --git a/src/platform/console/input.cpp b/src/platform/console/input.cpp new file mode 100644 index 00000000..87a5efa2 --- /dev/null +++ b/src/platform/console/input.cpp @@ -0,0 +1,214 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2018 Chris Warren-Smith. +// Copyright(C) 2000 Nicholas Christopoulos +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#include "config.h" +#include "common/device.h" +#include "common/smbas.h" +#include "common/keymap.h" + +/** + * return the character (multibyte charsets support) + */ +int dev_input_char2str(int ch, byte * cstr) { + memset(cstr, 0, 3); + + switch (os_charset) { + case enc_sjis: // Japan + if (IsJISFont(ch)) { + cstr[0] = ch >> 8; + } + cstr[1] = ch & 0xFF; + break; + case enc_big5: // China/Taiwan (there is another charset for + // China but I don't know the difference) + if (IsBig5Font(ch)) { + cstr[0] = ch >> 8; + } + cstr[1] = ch & 0xFF; + break; + case enc_gmb: // Generic multibyte + if (IsGMBFont(ch)) { + cstr[0] = ch >> 8; + } + cstr[1] = ch & 0xFF; + break; + case enc_unicode: // Unicode + cstr[0] = ch >> 8; + cstr[1] = ch & 0xFF; + break; + default: + cstr[0] = ch; + }; + return strlen((char *)cstr); +} + +/** + * return the character size at pos! (multibyte charsets support) + */ +int dev_input_count_char(byte *buf, int pos) { + int count, ch; + byte cstr[3]; + + if (os_charset != enc_utf8) { + ch = buf[0]; + ch = ch << 8; + ch = ch + buf[1]; + count = dev_input_char2str(ch, cstr); + } else { + count = 1; + } + return count; +} + +/** + * stores a character at 'pos' position + */ +int dev_input_insert_char(int ch, char *dest, int pos, int replace_mode) { + byte cstr[3]; + int count, remain; + char *buf; + + count = dev_input_char2str(ch, cstr); + + // store character into buffer + if (replace_mode) { + // overwrite mode + remain = strlen((dest + pos)); + buf = (char *)malloc(remain + 1); + strcpy(buf, dest + pos); + memcpy(dest + pos, cstr, count); + dest[pos + count] = '\0'; + + if (os_charset != enc_utf8) { + count = dev_input_char2str(((buf[0] << 8) | buf[1]), cstr); + } else { + count = 1; + } + if (buf[0]) { // not a '\0' + strcat(dest, (buf + count)); + } + free(buf); + } else { + // insert mode + remain = strlen((dest + pos)); + buf = (char *)malloc(remain + 1); + strcpy(buf, dest + pos); + memcpy(dest + pos, cstr, count); + dest[pos + count] = '\0'; + strcat(dest, buf); + free(buf); + } + + return count; +} + +/** + * removes the character at 'pos' position + */ +int dev_input_remove_char(char *dest, int pos) { + byte cstr[3]; + int count, remain; + char *buf; + + if (dest[pos]) { + if (os_charset != enc_utf8) { + count = dev_input_char2str(((dest[pos] << 8) | dest[pos + 1]), cstr); + } else { + count = 1; + } + remain = strlen((dest + pos + 1)); + buf = (char *)malloc(remain + 1); + strcpy(buf, dest + pos + count); + + dest[pos] = '\0'; + strcat(dest, buf); + free(buf); + return count; + } + return 0; +} + +/** + * gets a string (INPUT) + */ +char *dev_gets(char *dest, int size) { + long int ch = 0; + uint16_t pos, len = 0; + int replace_mode = 0; + + *dest = '\0'; + pos = 0; + do { + len = strlen(dest); + ch = fgetc(stdin); + switch (ch) { + case -1: + case -2: + case 0xFFFF: + dest[pos] = '\0'; + return dest; + case 0: + case 10: + case 13: // ignore + break; + case SB_KEY_HOME: + pos = 0; + break; + case SB_KEY_END: + pos = len; + break; + case SB_KEY_BACKSPACE: // backspace + if (pos > 0) { + pos -= dev_input_remove_char(dest, pos - 1); + len = strlen(dest); + } else { + dev_beep(); + } + break; + case SB_KEY_DELETE: // delete + if (pos < len) { + dev_input_remove_char(dest, pos); + len = strlen(dest); + } else + dev_beep(); + break; + case SB_KEY_INSERT: + replace_mode = !replace_mode; + break; + case SB_KEY_LEFT: + if (pos > 0) { + pos -= dev_input_count_char((byte *)dest, pos); + } else { + dev_beep(); + } + break; + case SB_KEY_RIGHT: + if (pos < len) { + pos += dev_input_count_char((byte *)dest, pos); + } else { + dev_beep(); + } + break; + default: + if ((ch & 0xFF00) != 0xFF00) { // Not an hardware key + pos += dev_input_insert_char(ch, dest, pos, replace_mode); + } else { + ch = 0; + } + // check the size + len = strlen(dest); + if (len >= (size - 2)) { + break; + } + } + } while (ch != '\n' && ch != '\r'); + dest[len] = '\0'; + dev_print(dest); + return dest; +} diff --git a/src/platform/console/main.cpp b/src/platform/console/main.cpp index e3b3d276..c6d23e9b 100644 --- a/src/platform/console/main.cpp +++ b/src/platform/console/main.cpp @@ -9,6 +9,7 @@ #include "config.h" #include +#include #include "common/sbapp.h" #include "ui/kwp.h" @@ -25,9 +26,9 @@ void console_init(); static struct option OPTIONS[] = { {"verbose", no_argument, NULL, 'v'}, {"keywords", no_argument, NULL, 'k'}, - {"no-file-perm", no_argument, NULL, 'f'}, + {"no-file-access", no_argument, NULL, 'f'}, {"gen-sbx", no_argument, NULL, 'x'}, - {"module", optional_argument, NULL, 'm'}, + {"module-path", optional_argument, NULL, 'm'}, {"decompile", optional_argument, NULL, 's'}, {"option", optional_argument, NULL, 'o'}, {"cmd", optional_argument, NULL, 'c'}, @@ -38,13 +39,13 @@ static struct option OPTIONS[] = { void show_help() { fprintf(stdout, - "SmallBASIC version %s - kw:%d, pc:%d, fc:%d, ae:%d I=%d N=%d\n\n", + "SmallBASIC version %s - kw:%d, pc:%d, fc:%d, V:%d I=%d N=%d\n\n", SB_STR_VER, kwNULL, (kwNULLPROC - kwCLS) + 1, - (kwNULLFUNC - kwASC) + 1, (int)(65536 / sizeof(var_t)), + (kwNULLFUNC - kwASC) + 1, (int)(sizeof(var_t)), (int)sizeof(var_int_t), (int)sizeof(var_num_t)); fprintf(stdout, "usage: sbasic [options]...\n"); int i = 0; - while (OPTIONS[i].name != NULL) { + while (OPTIONS[i].name != nullptr) { fprintf(stdout, OPTIONS[i].has_arg ? " -%c, --%s=''\n" : " -%c, --%s\n", OPTIONS[i].val, OPTIONS[i].name); @@ -54,7 +55,12 @@ void show_help() { } void show_brief_help() { - fprintf(stdout, "SmallBASIC version %s - use -h for help\n", SB_STR_VER); + if (opt_command[0] != '\0') { + access(opt_command, R_OK); + fprintf(stdout, "sbasic: can't open file '%s': [Errno %d] %s\n", opt_command, errno, strerror(errno)); + } else { + fprintf(stdout, "SmallBASIC version %s - use -h for help\n", SB_STR_VER); + } } void command_help(const char *selection) { @@ -76,9 +82,9 @@ void command_help(const char *selection) { } } if (!found) { - const char *last_package = NULL; + const char *last_package = nullptr; for (int i = 0; i < keyword_help_len; i++) { - if (last_package == NULL || strcmp(last_package, keyword_help[i].package) != 0) { + if (last_package == nullptr || strcmp(last_package, keyword_help[i].package) != 0) { fprintf(stdout, "%s\n", keyword_help[i].package); last_package = keyword_help[i].package; } @@ -154,7 +160,7 @@ bool setup_command_program(const char *program, char **runFile) { FILE *fp = fopen(file, "wb"); bool result; if (fp) { - if (program != NULL) { + if (program != nullptr) { fputs(program, fp); } else { // read from stdin @@ -235,14 +241,15 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { bool result = true; while (result) { int option_index = 0; - int c = getopt_long(argc, argv, "vkfxm::s::o:c:h::", OPTIONS, &option_index); + int c = getopt_long(argc, argv, "vkfxm:s:o:c:h::", OPTIONS, &option_index); if (c == -1 && !option_index) { // no more options for (int i = 1; i < argc; i++) { const char *s = argv[i]; int len = strlen(s); - if (*runFile == NULL && - (strcasecmp(s + len - 4, ".bas") == 0 && access(s, 0) == 0)) { + if (*runFile == nullptr && + ((strcasecmp(s + len - 4, ".bas") == 0 || + strcasecmp(s + len - 4, ".sbx") == 0) && access(s, 0) == 0)) { *runFile = strdup(s); } else { if (opt_command[0]) { @@ -257,7 +264,7 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { case 'h': if (optarg) { command_help(optarg); - } else if (argv[optind] != NULL && argv[optind][0] != '-') { + } else if (argv[optind] != nullptr && argv[optind][0] != '-') { command_help(argv[optind]); } else { show_help(); @@ -310,15 +317,15 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { } } - if (getenv("SBASICPATH") != NULL) { + if (getenv("SBASICPATH") != nullptr) { opt_loadmod = 1; } if (strcmp("--", argv[argc - 1]) == 0) { - if (*runFile != NULL) { + if (*runFile != nullptr) { // run file already set result = false; - } else if (setup_command_program(NULL, runFile)) { + } else if (setup_command_program(nullptr, runFile)) { *tmpFile = true; } else { // failed to read from stdin @@ -326,10 +333,31 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { } } - if (*runFile == NULL && result) { + if (*runFile == nullptr && result) { show_brief_help(); result = false; } + + if (opt_modpath[0] != '\0') { + char *path = opt_modpath; + while (result && path && path[0] != '\0') { + char *sep = strchr(path, ':'); + if (sep) { + *sep = '\0'; + } + if (access(path, R_OK) != 0) { + fprintf(stdout, "sbasic: can't open path '%s': [Errno %d] %s\n", path, errno, strerror(errno)); + result = false; + } + if (sep) { + *sep = ':'; + path = sep + 1; + } else { + path = nullptr; + } + } + } + return result; } @@ -339,10 +367,10 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { int main(int argc, char *argv[]) { opt_autolocal = 0; opt_command[0] = '\0'; + opt_modpath[0] = '\0'; opt_file_permitted = 1; opt_ide = 0; opt_loadmod = 0; - opt_modpath[0] = 0; opt_nosave = 1; opt_pref_height = 0; opt_pref_width = 0; @@ -354,7 +382,7 @@ int main(int argc, char *argv[]) { console_init(); - char *file = NULL; + char *file = nullptr; bool tmpFile = false; if (process_options(argc, argv, &file, &tmpFile)) { char prev_cwd[OS_PATHNAME_SIZE + 1]; diff --git a/src/platform/fltk/win.rc b/src/platform/fltk/win.rc new file mode 100644 index 00000000..6a03df07 --- /dev/null +++ b/src/platform/fltk/win.rc @@ -0,0 +1,2 @@ +101 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "../../../images/sb4w.ico" + diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 926dbb76..f77e78bc 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -13,6 +13,7 @@ #include "ui/textedit.h" #include "platform/sdl/runtime.h" #include "platform/sdl/settings.h" +#include "platform/sdl/syswm.h" using namespace strlib; @@ -119,13 +120,25 @@ void onlineHelp(Runtime *runtime, TextEditInput *widget) { runtime->browseFile(path); } +void showHelpPopup(TextEditHelpWidget *helpWidget) { + helpWidget->showPopup(-8, -2); +} + +void showHelpLineInput(TextEditHelpWidget *helpWidget, int width = 35) { + helpWidget->showPopup(width, 1); +} + +void showHelpSideabar(TextEditHelpWidget *helpWidget) { + helpWidget->showSidebar(); +} + void showRecentFiles(TextEditHelpWidget *helpWidget, String &loadPath) { String fileList; helpWidget->createMessage(); - helpWidget->show(); helpWidget->reload(NULL); getRecentFileList(fileList, loadPath); helpWidget->setText(fileList); + showHelpPopup(helpWidget); } void showSelectionCount(AnsiWidget *out, TextEditInput *widget) { @@ -168,6 +181,20 @@ void exportBuffer(AnsiWidget *out, const char *text, String &dest, String &token out->setStatus(buffer); } +bool externalExec(AnsiWidget *out, TextEditInput *editWidget, String &loadPath) { + bool result; + if (editWidget->getTextLength() && !g_exportAddr.empty() && g_exportAddr.indexOf("sbasic", 0) != -1) { + launch(g_exportAddr, loadPath); + result = true; + } else if (editWidget->getTextLength() && !g_exportAddr.empty() && !g_exportToken.empty()) { + exportBuffer(out, editWidget->getText(), g_exportAddr, g_exportToken); + result = true; + } else { + result = false; + } + return result; +} + void System::editSource(String loadPath, bool restoreOnExit) { logEntered(); @@ -213,7 +240,7 @@ void System::editSource(String loadPath, bool restoreOnExit) { helpWidget->setText(gsb_last_errmsg); helpWidget->createStackTrace(gsb_last_errmsg, gsb_last_line, _stackTrace); widget = helpWidget; - helpWidget->show(); + showHelpSideabar(helpWidget); _output->setStatus("Error. Esc=Close, Up/Down=Caller"); } else { _output->setStatus(!gsb_last_errmsg[0] ? "Error" : gsb_last_errmsg); @@ -284,6 +311,7 @@ void System::editSource(String loadPath, bool restoreOnExit) { break; case SB_KEY_CTRL('s'): saveFile(editWidget, loadPath); + saveRecentPosition(loadPath, editWidget->getCursorPos()); break; case SB_KEY_CTRL('c'): case SB_KEY_CTRL('x'): @@ -299,34 +327,35 @@ void System::editSource(String loadPath, bool restoreOnExit) { _output->setStatus("Keyword Help. F2=online, Esc=Close"); widget = helpWidget; helpWidget->createKeywordIndex(); - helpWidget->show(); + showHelpPopup(helpWidget); break; case SB_KEY_F(2): redraw = false; onlineHelp((Runtime *)this, editWidget); break; case SB_KEY_F(4): - if (editWidget->getTextLength() && !g_exportAddr.empty() && !g_exportToken.empty()) { - exportBuffer(_output, editWidget->getText(), g_exportAddr, g_exportToken); + if (externalExec(_output, editWidget, loadPath)) { break; } // else fallthru to F3 handler case SB_KEY_F(3): if (editWidget->getTextLength()) { saveFile(editWidget, loadPath); - _output->setStatus("Export to mobile SmallBASIC. Enter :"); + saveRecentPosition(loadPath, editWidget->getCursorPos()); + _output->setStatus("Export to SmallBASIC. Enter : | "); widget = helpWidget; helpWidget->createLineEdit(g_exportAddr); - helpWidget->show(); + showHelpLineInput(helpWidget, 60); inputMode = kExportAddr; } break; case SB_KEY_F(5): saveFile(editWidget, loadPath); + saveRecentPosition(loadPath, editWidget->getCursorPos()); _output->setStatus("Debug. F6=Step, F7=Continue, Esc=Close"); widget = helpWidget; helpWidget->createMessage(); - helpWidget->show(); + showHelpSideabar(helpWidget); ((Runtime *)this)->debugStart(editWidget, loadPath.c_str()); statusMessage.resetCursor(editWidget); break; @@ -347,46 +376,46 @@ void System::editSource(String loadPath, bool restoreOnExit) { _output->setStatus("Enter program command line, Esc=Close"); widget = helpWidget; helpWidget->createLineEdit(opt_command); - helpWidget->show(); + showHelpLineInput(helpWidget); inputMode = kCommand; break; case SB_KEY_CTRL('h'): _output->setStatus("Keystroke help. Esc=Close"); widget = helpWidget; helpWidget->createHelp(); - helpWidget->show(); + showHelpSideabar(helpWidget); break; case SB_KEY_CTRL('l'): _output->setStatus("Outline. Esc=Close"); widget = helpWidget; helpWidget->createOutline(); - helpWidget->show(); + showHelpSideabar(helpWidget); break; case SB_KEY_CTRL('f'): _output->setStatus("Find in buffer. Esc=Close"); widget = helpWidget; helpWidget->createSearch(false); - helpWidget->show(); + showHelpLineInput(helpWidget); statusMessage.resetCursor(editWidget); break; case SB_KEY_CTRL('n'): _output->setStatus("Replace string. Esc=Close"); widget = helpWidget; helpWidget->createSearch(true); - helpWidget->show(); + showHelpLineInput(helpWidget); statusMessage.resetCursor(editWidget); break; case SB_KEY_ALT('g'): _output->setStatus("Goto line. Esc=Close"); widget = helpWidget; helpWidget->createGotoLine(); - helpWidget->show(); + showHelpLineInput(helpWidget, 5); break; case SB_KEY_CTRL(' '): _output->setStatus("Auto-complete. Esc=Close"); widget = helpWidget; helpWidget->createCompletionHelp(); - helpWidget->show(); + showHelpSideabar(helpWidget); break; case SB_KEY_INSERT: statusMessage.setInsert(); @@ -437,7 +466,9 @@ void System::editSource(String loadPath, bool restoreOnExit) { } if (getRecentFile(recentFile, event.key - SB_KEY_ALT('1'))) { if (loadSource(recentFile.c_str())) { + saveRecentPosition(loadPath, editWidget->getCursorPos()); editWidget->reload(_programSrc); + editWidget->setCursorPos(getRecentPosition(recentFile)); statusMessage.setFilename(recentFile); statusMessage.update(editWidget, _output, true); setLoadPath(recentFile); @@ -471,9 +502,16 @@ void System::editSource(String loadPath, bool restoreOnExit) { switch (inputMode) { case kExportAddr: g_exportAddr = helpWidget->getText(); - inputMode = kExportToken; - helpWidget->createLineEdit(g_exportToken); - _output->setStatus("Enter token. Esc=Close"); + if (g_exportAddr.indexOf("sbasic", 0) == -1) { + inputMode = kExportToken; + helpWidget->createLineEdit(g_exportToken); + _output->setStatus("Enter token. Esc=Close"); + } else { + inputMode = kInit; + widget = editWidget; + helpWidget->hide(); + launch(g_exportAddr, loadPath); + } break; case kExportToken: g_exportToken = helpWidget->getText(); @@ -484,6 +522,7 @@ void System::editSource(String loadPath, bool restoreOnExit) { break; case kCommand: strlcpy(opt_command, helpWidget->getText(), sizeof(opt_command)); + statusMessage._dirty = !editWidget->isDirty(); inputMode = kInit; widget = editWidget; helpWidget->hide(); @@ -492,19 +531,18 @@ void System::editSource(String loadPath, bool restoreOnExit) { break; } } else if (helpWidget->closeOnEnter() && helpWidget->isVisible()) { - statusMessage._dirty = !widget->isDirty(); + statusMessage._dirty = !editWidget->isDirty(); widget = editWidget; helpWidget->hide(); helpWidget->cancelMode(); } redraw = true; } else if (helpWidget->replaceDoneMode()) { - statusMessage._dirty = !widget->isDirty(); + statusMessage._dirty = !editWidget->isDirty(); widget = editWidget; helpWidget->hide(); helpWidget->cancelMode(); } - helpWidget->setFocus(widget == helpWidget); editWidget->setFocus(widget == editWidget); if (helpWidget->searchMode()) { @@ -523,6 +561,7 @@ void System::editSource(String loadPath, bool restoreOnExit) { int choice = ask("Save changes?", message, isBack()); if (choice == 0) { saveFile(editWidget, loadPath); + saveRecentPosition(loadPath, editWidget->getCursorPos()); } else if (choice == 2) { // cancel _state = kEditState; diff --git a/src/platform/sdl/main.cpp b/src/platform/sdl/main.cpp index f0b787d4..26bc8fa5 100644 --- a/src/platform/sdl/main.cpp +++ b/src/platform/sdl/main.cpp @@ -40,18 +40,18 @@ const char *FONTS[] = { }; static struct option OPTIONS[] = { - {"help", no_argument, NULL, 'h'}, - {"verbose", no_argument, NULL, 'v'}, - {"keywords", no_argument, NULL, 'k'}, - {"command", optional_argument, NULL, 'c'}, - {"font", optional_argument, NULL, 'f'}, - {"run", optional_argument, NULL, 'r'}, - {"run-live", optional_argument, NULL, 'x'}, - {"run-n-wait",optional_argument, NULL, 'n'}, - {"module", optional_argument, NULL, 'm'}, - {"edit", optional_argument, NULL, 'e'}, - {"debug", optional_argument, NULL, 'd'}, - {"debugPort", optional_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"keywords", no_argument, NULL, 'k'}, + {"command", optional_argument, NULL, 'c'}, + {"font", optional_argument, NULL, 'f'}, + {"run", optional_argument, NULL, 'r'}, + {"run-live", optional_argument, NULL, 'x'}, + {"run-n-wait", optional_argument, NULL, 'n'}, + {"module-path", optional_argument, NULL, 'm'}, + {"edit", optional_argument, NULL, 'e'}, + {"debug", optional_argument, NULL, 'd'}, + {"debugPort", optional_argument, NULL, 'p'}, {0, 0, 0, 0} }; diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index d8b87f1e..fd32723d 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -34,11 +34,9 @@ #define WAIT_INTERVAL 5 #define COND_WAIT_TIME 250 -#define PAUSE_DEBUG_LAUNCH 250 +#define PAUSE_DEBUG_LAUNCH 750 #define PAUSE_DEBUG_STEP 50 #define MAIN_BAS "__main_bas__" -#define AMPLITUDE 22000 -#define FREQUENCY 44100 #define OPTIONS_BOX_WIDTH_EXTRA 1 #define OPTIONS_BOX_BG 0xd2d1d0 #define OPTIONS_BOX_FG 0x3e3f3e @@ -779,6 +777,12 @@ SDL_Rect Runtime::getWindowRect() { result = _windowRect; } else { setWindowRect(result); +#if defined(__linux__) + int top, left,bottom, right; + SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right); + // subtract the X11 border + result.y -= top; +#endif } return result; } diff --git a/src/platform/sdl/settings.cpp b/src/platform/sdl/settings.cpp index be5143c4..3a3880aa 100644 --- a/src/platform/sdl/settings.cpp +++ b/src/platform/sdl/settings.cpp @@ -179,6 +179,12 @@ void restoreSettings(SDL_Rect &rect, int &fontScale, bool debug, bool restoreDir if (len > 1) { recentPath[i].clear(); recentPath[i].append(fp, len); + + int indexColon = recentPath[i].indexOf(':', 0); + if (indexColon != -1) { + recentPosition[i] = recentPath[i].substring(indexColon + 1).toInteger(); + recentPath[i] = recentPath[i].substring(0, indexColon); + } } else { break; } @@ -243,7 +249,7 @@ void saveSettings(SDL_Rect &rect, int fontScale, bool debug) { // save the recent editor paths for (int i = 0; i < NUM_RECENT_ITEMS; i++) { if (!recentPath[i].empty()) { - fprintf(fp, "%s\n", recentPath[i].c_str()); + fprintf(fp, "%s:%d\n", recentPath[i].c_str(), recentPosition[i]); } } fclose(fp); @@ -288,6 +294,26 @@ void getRecentFileList(String &fileList, String ¤t) { } } +int getRecentPosition(const char *fileName) { + int result = 0; + for (int i = 0; i < NUM_RECENT_ITEMS; i++) { + if (recentPath[i].equals(fileName, false)) { + result = recentPosition[i]; + break; + } + } + return result; +} + +void saveRecentPosition(const char *fileName, unsigned cursorPos) { + for (int i = 0; i < NUM_RECENT_ITEMS; i++) { + if (recentPath[i].equals(fileName, false)) { + recentPosition[i] = cursorPos; + break; + } + } +} + void setRecentFile(const char *filename) { bool found = false; for (int i = 0; i < NUM_RECENT_ITEMS && !found; i++) { diff --git a/src/platform/sdl/settings.h b/src/platform/sdl/settings.h index 4d5e1ef4..d69c2c9e 100644 --- a/src/platform/sdl/settings.h +++ b/src/platform/sdl/settings.h @@ -12,11 +12,13 @@ #include void restoreSettings(SDL_Rect &rect, int &fontScale, bool debug, bool restoreDir); +void saveRecentPosition(const char *fileName, unsigned cursorPos); void saveSettings(SDL_Rect &rect, int fontScale, bool debug); String saveGist(const char *buffer, const char *fileName, const char *description); void setRecentFile(const char *path); bool getRecentFile(strlib::String &path, unsigned position); void getRecentFileList(strlib::String &fileList, strlib::String ¤t); +int getRecentPosition(const char *fileName); #endif diff --git a/src/platform/sdl/syswm.cpp b/src/platform/sdl/syswm.cpp index d9064254..4f13f6e9 100644 --- a/src/platform/sdl/syswm.cpp +++ b/src/platform/sdl/syswm.cpp @@ -58,13 +58,13 @@ void launchDebug(const char *file) { } } -void launchExec(const char *file) { +void launch(const char *command, const char *file) { STARTUPINFO info = {sizeof(info)}; PROCESS_INFORMATION processInfo; char cmd[1024]; - sprintf(cmd, "%s -x %s", g_appPath, file); - if (!CreateProcess(g_appPath, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo)) { - appLog("failed to start %d %s %s\n", GetLastError(), g_appPath, cmd); + sprintf(cmd, "%s -x %s", command, file); + if (!CreateProcess(command, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo)) { + appLog("failed to start %d %s %s\n", GetLastError(), command, cmd); } } @@ -126,7 +126,7 @@ void launchDebug(const char *file) { } } -void launchExec(const char *file) { +void launch(const char *command, const char *file) { pid_t pid = fork(); switch (pid) { @@ -135,8 +135,8 @@ void launchExec(const char *file) { break; case 0: // child process - if (execl(g_appPath, g_appPath, "-x", file, (char *)0) == -1) { - fprintf(stderr, "exec failed [%s] %s\n", strerror(errno), g_appPath); + if (execl(command, command, "-x", file, (char *)0) == -1) { + fprintf(stderr, "exec failed [%s] %s\n", strerror(errno), command); exit(1); } break; @@ -166,3 +166,7 @@ void browseFile(SDL_Window *window, const char *url) { } #endif + +void launchExec(const char *file) { + launch(g_appPath, file); +} diff --git a/src/platform/sdl/syswm.h b/src/platform/sdl/syswm.h index a7bf88b9..3e3c8b12 100644 --- a/src/platform/sdl/syswm.h +++ b/src/platform/sdl/syswm.h @@ -15,6 +15,7 @@ void loadIcon(SDL_Window *window); int getStartupFontSize(SDL_Window *window); void launchDebug(const char *file); void launchExec(const char *file); +void launch(const char *command, const char *file); void browseFile(SDL_Window *window, const char *url); #endif diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index d7a1495d..46297898 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -101,9 +101,7 @@ void log(const char *format, ...) { } // allow or deny access -int accept_cb(void *cls, - const struct sockaddr *addr, - socklen_t addrlen) { +MHD_Result accept_cb(void *cls, const struct sockaddr *addr, socklen_t addrlen) { return MHD_YES; } @@ -191,14 +189,14 @@ MHD_Response *get_response(struct MHD_Connection *connection, const char *path) // server callback // see: /usr/share/doc/libmicrohttpd-dev/examples -int access_cb(void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **ptr) { +MHD_Result access_cb(void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) { static int dummy; if (&dummy != *ptr) { // The first time only the headers are valid, @@ -217,7 +215,7 @@ int access_cb(void *cls, strncpy(opt_command, upload_data, size); } - int result; + MHD_Result result; struct MHD_Response *response = get_response(connection, url + 1); if (response != NULL) { result = MHD_queue_response(connection, MHD_HTTP_OK, response); @@ -397,10 +395,9 @@ struct ValueIteratorClosure { int _element; }; -int valueIterator(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value) { +MHD_Result valueIterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { ValueIteratorClosure *closure = (ValueIteratorClosure *)cls; - int result; + MHD_Result result; if (closure->_count++ == closure->_element) { closure->_result = value; result = MHD_NO; @@ -410,8 +407,7 @@ int valueIterator(void *cls, enum MHD_ValueKind kind, return result; } -int countIterator(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value) { +MHD_Result countIterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { return MHD_YES; } diff --git a/src/ui/audio.cpp b/src/ui/audio.cpp index efe38b58..a2a52144 100644 --- a/src/ui/audio.cpp +++ b/src/ui/audio.cpp @@ -11,6 +11,7 @@ #include "config.h" #include +#include #include #include "include/osd.h" #include "ui/strlib.h" @@ -29,6 +30,7 @@ extern "C" { #define MILLIS_TO_MICROS(n) (n * 1000) struct Sound; +static ma_context context; static ma_device device; static ma_device_config config; static strlib::Queue queue; @@ -123,7 +125,7 @@ static void setup_format(ma_format format, ma_uint32 channels, ma_uint32 sampleR config.sampleRate != sampleRate) { audio_close(); setup_config(format, channels, sampleRate); - ma_result result = ma_device_init(nullptr, &config, &device); + ma_result result = ma_device_init(&context, &config, &device); if (result != MA_SUCCESS) { err_throw("Failed to prepare sound device [%d]", result); } @@ -131,7 +133,7 @@ static void setup_format(ma_format format, ma_uint32 channels, ma_uint32 sampleR } static void device_start() { - if (ma_device__get_state(&device) != MA_STATE_STARTED) { + if (ma_device_get_state(&device) != MA_STATE_STARTED) { ma_result result = ma_device_start(&device); if (result != MA_SUCCESS) { err_throw("Failed to start audio [%d]", result); @@ -140,14 +142,28 @@ static void device_start() { } bool audio_open() { - setup_config(DEFAULT_FORMAT, DEFAULT_CHANNELS, DEFAULT_SAMPLE_RATE); - queuePos = 0; - return (ma_device_init(nullptr, &config, &device) == MA_SUCCESS); + bool result; + ma_backend backends[] = { + ma_backend_alsa, + ma_backend_jack, + ma_backend_pulseaudio, + ma_backend_wasapi, + ma_backend_dsound + }; + if (ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), NULL, &context) != MA_SUCCESS) { + result = false; + } else { + queuePos = 0; + setup_config(DEFAULT_FORMAT, DEFAULT_CHANNELS, DEFAULT_SAMPLE_RATE); + result = (ma_device_init(&context, &config, &device) == MA_SUCCESS); + } + return result; } void audio_close() { osd_clear_sound_queue(); ma_device_uninit(&device); + ma_context_uninit(&context); } void osd_audio(const char *path) { diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index 4f6654d5..932786c2 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -486,6 +486,14 @@ void FormEditInput::setFocus(bool focus) { } } +void FormEditInput::clicked(int x, int y, bool pressed) { + if (pressed && g_system->isRunning()) { + dev_clrkb(); + setFocus(true); + focusEdit = this; + } +} + // // FormLineInput // @@ -515,11 +523,9 @@ FormLineInput::~FormLineInput() { } void FormLineInput::clicked(int x, int y, bool pressed) { + FormEditInput::clicked(x, y, pressed); if (pressed && g_system->isRunning()) { AnsiWidget *out = g_system->getOutput(); - dev_clrkb(); - setFocus(true); - focusEdit = this; int charWidth = out->getCharWidth(); int selected = (x - _x) / charWidth; int len = strlen(_buffer); diff --git a/src/ui/inputs.h b/src/ui/inputs.h index bb62c871..aee187af 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -213,6 +213,7 @@ struct FormEditInput : public FormInput { virtual const char *completeKeyword(int index) = 0; virtual int getCompletions(StringList *list, int max) = 0; + void clicked(int x, int y, bool pressed); void setFocus(bool focus); int getControlKey(int key); bool getControlMode() const { return _controlMode; } diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 22be0db4..b57b329c 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -170,7 +170,6 @@ void Screen::drawOverlay(bool vscroll) { List_each(FormInput *, it, _inputs) { FormInput *input = (*it); if (input->_y >= _scrollY - _height && - input->_y + input->_height <= _scrollY + _height && input->isVisible()) { if (input->isDrawTop()) { drawTop = input; @@ -286,35 +285,7 @@ FormInput *Screen::getNextMenu(FormInput *prev, bool up) { void Screen::layoutInputs(int newWidth, int newHeight) { List_each(FormInput *, it, _inputs) { FormInput *r1 = (*it); - if (r1->isResizable()) { - bool right = true; - bool bottom = true; - List_each(FormInput *, subIt, _inputs) { - FormInput *r2 = (*subIt); - if (r1 != r2) { - if (r2->_x > r1->_x && - r2->_y >= r1->_y && - r2->_y <= r1->_y + _height) { - // cant resize over right side sibling - right = false; - } - if (r2->_y > r1->_y && - r2->_x >= r1->_x && - r2->_x <= r1->_x + _width) { - // cant resize over lower side sibling - bottom = false; - } - } - } - if (right) { - r1->_width = newWidth - r1->_x; - } - if (bottom) { - r1->_height = newHeight - r1->_y; - } - } else { - r1->layout(newWidth, newHeight); - } + r1->layout(newWidth, newHeight); } } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 37dfcf85..6b1eea65 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -148,7 +148,7 @@ bool System::execute(const char *bas) { // reset program controlled options opt_antialias = 1; opt_show_page = 0; - opt_quiet = 1; + opt_pref_width = _output->getWidth(); opt_pref_height = _output->getHeight(); opt_base = 0; diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index b5bdf7b7..0636b3cd 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -73,9 +73,10 @@ int textedit_move_to_word_next(EditBuffer *str, int c) { #define TWISTY2_CLOSE " < " #define TWISTY1_LEN 2 #define TWISTY2_LEN 4 -#define HELP_BG 0x73c990 -#define HELP_FG 0x20242a +#define HELP_BG 0x20242a +#define HELP_FG 0x73c990 #define DOUBLE_CLICK_MS 200 +#define SIDE_BAR_WIDTH 30 #if defined(_Win32) #include @@ -249,6 +250,19 @@ const char *find_str(bool allUpper, const char *haystack, const char *needle) { return allUpper ? strstr(haystack, needle) : strcasestr(haystack, needle); } +int shade(int c, float weight) { + uint8_t r = ((uint8_t)(c >> 16)); + uint8_t g = ((uint8_t)(c >> 8)); + uint8_t b = ((uint8_t)(c)); + r = (r * weight); + g = (g * weight); + b = (b * weight); + r = r < 255 ? r : 255; + g = g < 255 ? g : 255; + b = b < 255 ? b : 255; + return (r << 16) + (g << 8) + (b); +} + // // EditTheme // @@ -307,6 +321,28 @@ void EditTheme::selectTheme(const int theme[]) { _row_marker = theme[16]; } +void EditTheme::contrast(EditTheme *other) { + int fg = shade(other->_color, .65); + int bg = shade(other->_background, .65); + _color = fg; + _background = bg; + _selection_color = bg; + _selection_background = shade(bg, .65); + _number_color = fg; + _number_selection_color = fg; + _number_selection_background = bg; + _cursor_color = bg; + _cursor_background = fg; + _match_background = fg; + _row_cursor = bg; + _syntax_comments = bg; + _syntax_text = fg; + _syntax_command = fg; + _syntax_statement = fg; + _syntax_digit = fg; + _row_marker = fg; +} + // // EditBuffer // @@ -377,6 +413,9 @@ char EditBuffer::getChar(int pos) { } int EditBuffer::insertChars(int pos, const char *text, int num) { + if (num == 1 && *text < 0) { + return 0; + } int required = _len + num + 1; if (required >= _size) { _size += (required + GROW_SIZE); @@ -471,9 +510,12 @@ TextEditInput::TextEditInput(const char *text, int chW, int chH, _matchingBrace(-1), _ptY(-1), _pressTick(0), + _xmargin(0), + _ymargin(0), _bottom(false), _dirty(false) { stb_textedit_initialize_state(&_state, false); + _resizable = true; } TextEditInput::~TextEditInput() { @@ -1034,6 +1076,7 @@ void TextEditInput::setCursorRow(int row) { } void TextEditInput::clicked(int x, int y, bool pressed) { + FormEditInput::clicked(x, y, pressed); if (x < _marginWidth) { _ptY = -1; } else if (pressed) { @@ -1042,7 +1085,7 @@ void TextEditInput::clicked(int x, int y, bool pressed) { _state.select_start = wordStart(); _state.select_end = wordEnd(); } else { - stb_textedit_click(&_buf, &_state, x - _marginWidth, y + (_scroll * _charHeight)); + stb_textedit_click(&_buf, &_state, (x - _x) - _marginWidth, (y - _y) + (_scroll * _charHeight)); } _pressTick = tick; } @@ -1068,17 +1111,17 @@ bool TextEditInput::updateUI(var_p_t form, var_p_t field) { } bool TextEditInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw) { - bool focus = hasFocus(); - if (focus) { + bool result = hasFocus() && FormEditInput::selected(pt, scrollX, scrollY, redraw); + if (result) { if (pt.x < _marginWidth) { dragPage(pt.y, redraw); } else { - stb_textedit_drag(&_buf, &_state, pt.x - _marginWidth, - pt.y + scrollY + (_scroll * _charHeight)); + stb_textedit_drag(&_buf, &_state, (pt.x - _x) - _marginWidth, + (pt.y - _y) + scrollY + (_scroll * _charHeight)); redraw = true; } } - return focus; + return result; } void TextEditInput::selectNavigate(bool up) { @@ -1150,6 +1193,22 @@ void TextEditInput::layout(StbTexteditRow *row, int start) const { row->ymax = row->baseline_y_delta = _charHeight; } +void TextEditInput::layout(int w, int h) { + if (_resizable) { + if (_height == _charHeight) { + _x = (w - _width) / 2; + _y = h - (_charHeight * 2.5); + } else if (_width == _charWidth * SIDE_BAR_WIDTH) { + int border = _charWidth * 2; + _height = h - (border * 2); + _x = w - (_width + border); + } else { + _width = w - (_x + _xmargin); + _height = h - (_y + _ymargin); + } + } +} + int TextEditInput::charWidth(int k, int i) const { int result = 0; if (k + i < _buf._len && _buf._buffer[k + i] != '\n') { @@ -1158,6 +1217,12 @@ int TextEditInput::charWidth(int k, int i) const { return result; } +void TextEditInput::calcMargin() { + MAExtent screenSize = maGetScrSize(); + _xmargin = EXTENT_X(screenSize) - (_x + _width); + _ymargin = EXTENT_Y(screenSize) - (_y + _height); +} + void TextEditInput::changeCase() { int start, end; char *selection = getSelection(&start, &end); @@ -1868,7 +1933,7 @@ TextEditHelpWidget::TextEditHelpWidget(TextEditInput *editor, int chW, int chH, _editor(editor), _openPackage(nullptr), _openKeyword(-1) { - _theme = new EditTheme(HELP_BG, HELP_FG); + _theme = new EditTheme(HELP_FG, HELP_BG); hide(); if (overlay) { _x = editor->_width - (chW * HELP_WIDTH); @@ -2005,8 +2070,8 @@ void TextEditHelpWidget::completeWord(int pos) { void TextEditHelpWidget::clicked(int x, int y, bool pressed) { _ptY = -1; if (pressed) { - stb_textedit_click(&_buf, &_state, 0, y + (_scroll * _charHeight)); - if (_mode == kHelpKeyword && x - _x <= _charWidth * 3) { + stb_textedit_click(&_buf, &_state, 0, (y - _y) + (_scroll * _charHeight)); + if (_mode == kHelpKeyword && (x - _x) <= _charWidth * 3) { toggleKeyword(); } } @@ -2332,3 +2397,52 @@ void TextEditHelpWidget::toggleKeyword() { } free(line); } + +void TextEditHelpWidget::showPopup(int cols, int rows) { + if (cols < 0) { + _width = _editor->_width - (_charWidth * -cols); + } else { + _width = _charWidth * cols; + } + if (rows < 0) { + _height = _editor->_height - (_charHeight * -rows); + } else { + _height = _charHeight * rows; + } + if (_width > _editor->_width) { + _width = _editor->_width; + } + if (_height > _editor->_height) { + _height = _editor->_height; + } + _x = (_editor->_width - _width) / 2; + if (rows == 1) { + _y = _editor->_height - (_charHeight * 2.5); + } else { + _y = (_editor->_height - _height) / 2; + } + _theme->contrast(_editor->getTheme()); + calcMargin(); + show(); +} + +void TextEditHelpWidget::showSidebar() { + int border = _charWidth * 2; + _width = _charWidth * SIDE_BAR_WIDTH; + _height = _editor->_height - (border * 2); + _x = _editor->_width - (_width + border); + _y = border; + _theme->contrast(_editor->getTheme()); + calcMargin(); + show(); +} + +void TextEditHelpWidget::draw(int x, int y, int w, int h, int chw) { + TextEditInput::draw(x, y, w, h, chw); + int shadowW = _charWidth / 3; + int shadowH = _charWidth / 3; + + maSetColor(_theme->_selection_background); + maFillRect(x + _width, y + shadowH, shadowW, _height); + maFillRect(x + shadowW, y + _height, _width, shadowH); +} diff --git a/src/ui/textedit.h b/src/ui/textedit.h index 3b9c5fc5..e68e30c8 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -104,12 +104,13 @@ struct TextEditInput : public FormEditInput { void selectAll(); bool isDirty() { return _dirty && _state.undostate.undo_point > 0; } void setDirty(bool dirty) { _dirty = dirty; } - void layout(int w, int h) { _width = w; _height = h; } + void layout(int w, int h); const char *getNodeId(); char *getWordBeforeCursor(); bool replaceNext(const char *text, bool skip); int getCompletions(StringList *list, int max); void selectNavigate(bool up); + EditTheme *getTheme() { return _theme; } protected: enum SyntaxState { @@ -123,6 +124,7 @@ struct TextEditInput : public FormEditInput { void dragPage(int y, bool &redraw); void drawText(int x, int y, const char *str, int length, SyntaxState &state); + void calcMargin(); void changeCase(); void cycleTheme(); void drawLineNumber(int x, int y, int row, bool selected); @@ -168,6 +170,8 @@ struct TextEditInput : public FormEditInput { int _matchingBrace; int _ptY; int _pressTick; + int _xmargin; + int _ymargin; bool _bottom; bool _dirty; }; @@ -203,10 +207,10 @@ struct TextEditHelpWidget : public TextEditInput { void createOutline(); void createSearch(bool replace); void createStackTrace(const char *error, int line, StackTrace &trace); + void draw(int x, int y, int w, int h, int chw); bool edit(int key, int screenWidth, int charWidth); void paste(const char *text); bool isDrawTop() { return true; } - void layout(int w, int h) { _x = w - _width; _height = h; } void reset(HelpMode mode); void cancelMode() { _mode = kNone; } bool closeOnEnter() const; @@ -217,6 +221,8 @@ struct TextEditHelpWidget : public TextEditInput { bool replaceModeWith() const { return _mode == kEnterReplaceWith; } bool replaceDoneMode() const { return _mode == kReplaceDone; } bool selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw); + void showPopup(int cols, int rows); + void showSidebar(); void toggleKeyword(); private: diff --git a/src/ui/theme.h b/src/ui/theme.h index bd8fb493..5ddccee4 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -22,6 +22,7 @@ struct EditTheme { EditTheme(int fg, int bg); void setId(const unsigned themeId); void selectTheme(const int theme[]); + void contrast(EditTheme *other); int _color; int _background;