diff --git a/Makefile b/Makefile index cc391b6..fd841f7 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ BASE_CFLAGS = --std=c99 -Wall -Wextra -Ofast -lm -lreadline -funsigned-char ifndef OS C = gcc -CFLAGS = $(BASE_CFLAGS) +CFLAGS = $(BASE_CFLAGS) -ldl ifeq ($(shell uname -s), Darwin) ifeq ($(shell [ -d ~/.brew/opt/readline/include ] && echo true), true) CFLAGS += -I~/.brew/opt/readline/include diff --git a/README.md b/README.md index bf88b6b..52c5490 100644 --- a/README.md +++ b/README.md @@ -79,4 +79,5 @@ To build CLIBASIC with support for VT escape codes, add `vt` before the rest of - If the file `.clibasic_history` is present in the user's home directory CLIBASIC will automatically save history there. Run `_AUTOCMDHIST`, `_SAVECMDHIST` (without any arguments), or create the file `.clibasic_history` in your home/user folder to enable this feature. Remove the file to disable this feature. - CLIBASIC will look for `~/.clibasicrc`, `~/autorun.bas`, then `~/.autorun.bas` in this order in the user's home directory and run the first file found. - The development scripts are `build.sh` which is for testing if CLIBASIC compiles correctly for Linux and Windows, `package.sh` which creates the zip files for making a CLIBASIC release, `commit.sh` which automates the build and push process, and `release-text.sh` which generates the text for making a CLIBASIC release. +- Include the `clibasic.h` file when making a clibasic extension. diff --git a/clibasic.c b/clibasic.c index f534e51..7f40356 100644 --- a/clibasic.c +++ b/clibasic.c @@ -95,6 +95,7 @@ #include #include #include + #include #else #include #include @@ -115,7 +116,7 @@ // Base defines -char VER[] = "0.26.2"; +char VER[] = "0.27"; #if defined(__linux__) char OSVER[] = "Linux"; @@ -149,6 +150,8 @@ char VER[] = "0.26.2"; // Global vars and functions +#include "clibasic.h" + int progindex = -1; char** progbuf = NULL; char** progfn = NULL; @@ -166,27 +169,19 @@ int progLine = 1; int varmaxct = 0; -typedef struct { - bool inuse; - char* name; - uint8_t type; - int32_t size; - char** data; -} cb_var; - cb_var* vardata = NULL; char gpbuf[CB_BUF_SIZE]; -char* cmd; -int cmdl; -char** tmpargs; -char** arg; -uint8_t* argt; -int32_t* argl; +char* cmd = NULL; +int cmdl = 0; +char** tmpargs = NULL; +char** arg = NULL; +uint8_t* argt = NULL; +int32_t* argl = NULL; int argct = 0; -int cmdpos; +int cmdpos = 0; bool didloop = false; bool lockpl = false; @@ -237,10 +232,12 @@ char prompt[CB_BUF_SIZE]; char pstr[CB_BUF_SIZE]; char cmpstr[CB_BUF_SIZE]; -uint8_t fgc = 15; -uint8_t bgc = 0; -uint32_t truefgc = 0xFFFFFF; -uint32_t truebgc = 0x000000; +/* +uint8_t txtattrib.fgc = 15; +uint8_t txtattrib.bgc = 0; +uint32_t txtattrib.truefgc = 0xFFFFFF; +uint32_t txtattrib.truebgc = 0x000000; +*/ int curx = 0; int cury = 0; @@ -262,21 +259,7 @@ bool sh_silent = false; bool sh_clearAttrib = true; bool sh_restoreAttrib = true; -bool txt_bold = false; -bool txt_italic = false; -bool txt_underln = false; -bool txt_underlndbl = false; -bool txt_underlnsqg = false; -bool txt_strike = false; -bool txt_overln = false; -bool txt_dim = false; -bool txt_blink = false; -bool txt_hidden = false; -bool txt_reverse = false; -int txt_underlncolor = 0; -bool txt_fgc = true; -bool txt_bgc = false; -bool txt_truecolor = false; +cb_txt txtattrib; bool textlock = false; bool sneaktextlock = false; @@ -291,7 +274,7 @@ bool autohist = false; int tab_width = 4; int progargc = 0; -int * oldprogargc = NULL; +int* oldprogargc = NULL; int newprogargc = 0; char** progargs = NULL; char*** oldprogargs = NULL; @@ -320,11 +303,6 @@ cb_goto** proggotodata = NULL; int gotomaxct = 0; int* proggotomaxct = NULL; -typedef struct { - FILE* fptr; - int32_t size; -} cb_file; - cb_file* filedata = NULL; int filemaxct = 0; int fileerror = 0; @@ -341,6 +319,21 @@ int subindex = -1; char* rl_tmpptr = NULL; +typedef struct { + bool inuse; + char* name; + void* lib; + int (*runcmd)(int, char**, char**, uint8_t*, int32_t*); + cb_funcret (*runfunc)(int, char**, char**, uint8_t*, int32_t*, char*); + int (*runlogic)(char*, char**, int32_t, int32_t); + bool (*chkfuncsolve)(char*); + void (*clearGlobals)(void); + bool (*deinit)(void); +} cb_ext; + +int extmaxct = 0; +cb_ext* extdata = NULL; + #ifndef _WIN32 struct termios term, restore; struct termios kbhterm, kbhterm2; @@ -422,6 +415,11 @@ static inline void clearGlobals() { } nfree(gotodata); gotomaxct = 0; + for (int i = extmaxct - 1; i > -1; --i) { + if (extdata[i].inuse && extdata[i].chkfuncsolve) { + extdata[i].clearGlobals(); + } + } } #ifdef _WIN32 @@ -485,6 +483,8 @@ void updatechars() { #endif #ifndef WEXITSTATUS #define WEXITSTATUS(x) ((uint8_t)(x)) +#define dlclose FreeLibrary +#define dlsym GetProcAddress #endif #pragma GCC diagnostic push @@ -548,6 +548,8 @@ int openFile(char*, char*); bool closeFile(int); static inline void upCase(char*); uint8_t logictest(char*); +int loadExt(char*); +bool unloadExt(int); void cleanExit() { txtqunlock(); @@ -643,6 +645,7 @@ void cleanExit() { nfree(oldprogargs); */ clearGlobals(); + unloadExt(-1); #ifndef _WIN32 tcsetattr(0, TCSANOW, &initterm); #endif @@ -979,6 +982,9 @@ int main(int argc, char** argv) { skipscargv:; getCurPos(); if (curx != 1) putchar('\n'); + txtattrib.fgce = true; + txtattrib.fgc = 15; + txtattrib.truefgc = 0xFFFFFF; updateTxtAttrib(); if (!runfile) { if (info) printf("Command Line Interface BASIC version %s (%s %s-bit)\n", VER, OSVER, BVER); @@ -1034,7 +1040,9 @@ int main(int argc, char** argv) { cerr = 0; initBaseMem(); resetTimer(); - clearGlobals(); + if (inProg || runc) { + clearGlobals(); + } while (!pexit) { fchkint:; cp = 0; @@ -1663,51 +1671,51 @@ static inline void seterrstr(char* newstr) { void updateTxtAttrib() { #ifndef _WIN_NO_VT fputs("\e[0m", stdout); - if (txt_fgc) { - if (txt_truecolor) printf("\e[38;2;%u;%u;%um", (uint8_t)(truefgc >> 16), (uint8_t)(truefgc >> 8), (uint8_t)truefgc); - else printf("\e[38;5;%um", fgc); - } - if (txt_bgc) { - if (txt_truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(truebgc >> 16), (uint8_t)(truebgc >> 8), (uint8_t)truebgc); - else printf("\e[48;5;%um", bgc); - } - if (txt_bold) fputs("\e[1m", stdout); - if (txt_italic) fputs("\e[3m", stdout); - if (txt_underln) fputs("\e[4m", stdout); - if (txt_underlndbl) fputs("\e[21m", stdout); - if (txt_underlnsqg) fputs("\e[4:3m", stdout); - if (txt_strike) fputs("\e[9m", stdout); - if (txt_overln) fputs("\e[53m", stdout); - if (txt_dim) fputs("\e[2m", stdout); - if (txt_blink) fputs("\e[5m", stdout); - if (txt_hidden) fputs("\e[8m", stdout); - if (txt_reverse) fputs("\e[7m", stdout); - if (txt_underlncolor) printf("\e[58:5:%um", txt_underlncolor); + if (txtattrib.fgce) { + if (txtattrib.truecolor) printf("\e[38;2;%u;%u;%um", (uint8_t)(txtattrib.truefgc >> 16), (uint8_t)(txtattrib.truefgc >> 8), (uint8_t)txtattrib.truefgc); + else printf("\e[38;5;%um", txtattrib.fgc); + } + if (txtattrib.bgce) { + if (txtattrib.truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(txtattrib.truebgc >> 16), (uint8_t)(txtattrib.truebgc >> 8), (uint8_t)txtattrib.truebgc); + else printf("\e[48;5;%um", txtattrib.bgc); + } + if (txtattrib.bold) fputs("\e[1m", stdout); + if (txtattrib.italic) fputs("\e[3m", stdout); + if (txtattrib.underln) fputs("\e[4m", stdout); + if (txtattrib.underlndbl) fputs("\e[21m", stdout); + if (txtattrib.underlnsqg) fputs("\e[4:3m", stdout); + if (txtattrib.strike) fputs("\e[9m", stdout); + if (txtattrib.overln) fputs("\e[53m", stdout); + if (txtattrib.dim) fputs("\e[2m", stdout); + if (txtattrib.blink) fputs("\e[5m", stdout); + if (txtattrib.hidden) fputs("\e[8m", stdout); + if (txtattrib.reverse) fputs("\e[7m", stdout); + if (txtattrib.underlncolor) printf("\e[58:5:%um", txtattrib.underlncolor); #else uint8_t tmpfgc, tmpbgc; - if (txt_truecolor) { + if (txtattrib.truecolor) { uint8_t a; - tmpfgc = (((truefgc >> 23) & 1) << 2) | (((truefgc >> 15) & 1) << 1) | ((truefgc >> 7) & 1); - tmpfgc |= ((((truefgc >> 22) & 1) & ((truefgc >> 21) & 1)) << 2)\ - | ((((truefgc >> 14) & 1) & ((truefgc >> 13) & 1)) << 1)\ - | (((truefgc >> 6) & 1) & ((truefgc >> 5) & 1)); - a = ((((truefgc >> 16) & 0xFF) + ((truefgc >> 8) & 0xFF) + (truefgc & 0xFF)) / 3); + tmpfgc = (((txtattrib.truefgc >> 23) & 1) << 2) | (((txtattrib.truefgc >> 15) & 1) << 1) | ((txtattrib.truefgc >> 7) & 1); + tmpfgc |= ((((txtattrib.truefgc >> 22) & 1) & ((txtattrib.truefgc >> 21) & 1)) << 2)\ + | ((((txtattrib.truefgc >> 14) & 1) & ((txtattrib.truefgc >> 13) & 1)) << 1)\ + | (((txtattrib.truefgc >> 6) & 1) & ((txtattrib.truefgc >> 5) & 1)); + a = ((((txtattrib.truefgc >> 16) & 0xFF) + ((txtattrib.truefgc >> 8) & 0xFF) + (txtattrib.truefgc & 0xFF)) / 3); tmpfgc |= (8 * (a > 84)); - tmpbgc = (((truebgc >> 23) & 1) << 2) | (((truebgc >> 15) & 1) << 1) | ((truebgc >> 7) & 1); - tmpbgc |= ((((truebgc >> 22) & 1) & ((truebgc >> 21) & 1)) << 2)\ - | ((((truebgc >> 14) & 1) & ((truebgc >> 13) & 1)) << 1)\ - | (((truebgc >> 6) & 1) & ((truebgc >> 5) & 1)); - a = ((((truebgc >> 16) & 0xFF) + ((truebgc >> 8) & 0xFF) + (truebgc & 0xFF)) / 3); + tmpbgc = (((txtattrib.truebgc >> 23) & 1) << 2) | (((txtattrib.truebgc >> 15) & 1) << 1) | ((txtattrib.truebgc >> 7) & 1); + tmpbgc |= ((((txtattrib.truebgc >> 22) & 1) & ((txtattrib.truebgc >> 21) & 1)) << 2)\ + | ((((txtattrib.truebgc >> 14) & 1) & ((txtattrib.truebgc >> 13) & 1)) << 1)\ + | (((txtattrib.truebgc >> 6) & 1) & ((txtattrib.truebgc >> 5) & 1)); + a = ((((txtattrib.truebgc >> 16) & 0xFF) + ((txtattrib.truebgc >> 8) & 0xFF) + (txtattrib.truebgc & 0xFF)) / 3); tmpbgc |= (8 * (a > 84)); } else { uint8_t b1 = 0, b2 = 0; - b1 = fgc & 1; b2 = (fgc >> 2) & 1; tmpfgc = (b1 ^ b2); - tmpfgc = (tmpfgc) | (tmpfgc << 2); tmpfgc = fgc ^ tmpfgc; - b1 = bgc & 1; b2 = (bgc >> 2) & 1; tmpbgc = (b1 ^ b2); - tmpbgc = (tmpbgc) | (tmpbgc << 2); tmpbgc = bgc ^ tmpbgc; + b1 = txtattrib.fgc & 1; b2 = (txtattrib.fgc >> 2) & 1; tmpfgc = (b1 ^ b2); + tmpfgc = (tmpfgc) | (tmpfgc << 2); tmpfgc = txtattrib.fgc ^ tmpfgc; + b1 = txtattrib.bgc & 1; b2 = (txtattrib.bgc >> 2) & 1; tmpbgc = (b1 ^ b2); + tmpbgc = (tmpbgc) | (tmpbgc << 2); tmpbgc = txtattrib.bgc ^ tmpbgc; } - if (txt_dim) {tmpfgc %= 8; tmpbgc %= 8;} - if (txt_reverse) swap(tmpfgc, tmpbgc); + if (txtattrib.dim) {tmpfgc %= 8; tmpbgc %= 8;} + if (txtattrib.reverse) swap(tmpfgc, tmpbgc); SetConsoleTextAttribute(hConsole, (tmpfgc % 16) + ((tmpbgc % 16) << 4)); #endif fflush(stdout); @@ -1823,7 +1831,7 @@ uint8_t getFunc(char* inbuf, char* outbuf) { char** tmpfargs; char** farg; uint8_t* fargt; - int* flen; + int32_t* flen; int fargct; int ftmpct = 0; int ftype = 0; @@ -1837,6 +1845,7 @@ uint8_t getFunc(char* inbuf, char* outbuf) { gftmp[1] = getFunc_gftmp[1]; } ++getFuncIndex; + int extsas = -1; { int32_t i; bool invalName = false; @@ -1847,7 +1856,7 @@ uint8_t getFunc(char* inbuf, char* outbuf) { fargct = getArgCt(gftmp[0]); tmpfargs = malloc((fargct + 1) * sizeof(char*)); farg = malloc((fargct + 1) * sizeof(char*)); - flen = malloc((fargct + 1) * sizeof(int)); + flen = malloc((fargct + 1) * sizeof(int32_t)); fargt = malloc((fargct + 1) * sizeof(uint8_t)); for (int j = 0; j <= fargct; ++j) { farg[j] = NULL; @@ -1868,8 +1877,14 @@ uint8_t getFunc(char* inbuf, char* outbuf) { outbuf[0] = '0' + ret; outbuf[1] = 0; goto fexit; - } else if (farg[0][0] == 'E' && (!strcmp(farg[0] + 1, "XECA") || !strcmp(farg[0] + 1, "XECA$"))) { + } else if (!strcmp(farg[0], "EXECA") || !strcmp(farg[0], "EXECA$")) { skipfargsolve = true; + } else { + for (int i = extmaxct - 1; i > -1; --i) { + if (extdata[i].inuse && extdata[i].chkfuncsolve) { + if ((skipfargsolve = extdata[i].chkfuncsolve(farg[0]))) {extsas = i; break;} + } + } } } else { tmpfargs[j] = malloc(CB_BUF_SIZE); @@ -2709,6 +2724,7 @@ static inline uint8_t logictestexpr(char* inbuf) { ++logictestexpr_index; while (inbuf[p] == ' ') {++p;} if (p >= (int32_t)strlen(inbuf)) {cerr = 10; goto ltreturn;} + bool ltskip = false; for (int32_t i = p; inbuf[i]; ++i) { if (!inStr) { switch (inbuf[i]) { @@ -2719,7 +2735,7 @@ static inline uint8_t logictestexpr(char* inbuf) { } } if (inbuf[i] == '"') {inStr = !inStr;} - if (inbuf[i] == 0) {cerr = 1; goto ltreturn;} + if (inbuf[i + 1] == 0) {t2 = 255; copyStr("<>", lttmp[1]); ltskip = true;} if ((inbuf[i] == '<' || inbuf[i] == '=' || inbuf[i] == '>') && !inStr && pct == 0 && bct == 0) {p = i; break;} if (!inStr && pct == 0 && bct == 0) { if (inbuf[i] == ' ' && !sawSpChar) {lookingForSpChar = true;} @@ -2732,6 +2748,7 @@ static inline uint8_t logictestexpr(char* inbuf) { } } lttmp[0][tmpp] = 0; + if (ltskip) goto ltskipget; tmpp = 0; for (int32_t i = p; true; ++i) { if (tmpp > 2) {cerr = 1; goto ltreturn;} @@ -2768,12 +2785,17 @@ static inline uint8_t logictestexpr(char* inbuf) { } } lttmp[2][tmpp] = 0; - t1 = getVal(lttmp[0], lttmp[0]); - if (t1 == 0) goto ltreturn; - if (t1 == 255) {cerr = 1; goto ltreturn;} t2 = getVal(lttmp[2], lttmp[2]); if (t2 == 0) goto ltreturn; if (t2 == 255) {cerr = 1; goto ltreturn;} + ltskipget:; + t1 = getVal(lttmp[0], lttmp[0]); + if (t1 == 0) goto ltreturn; + if (t1 == 255) {cerr = 1; goto ltreturn;} + if (t2 == 255) { + t2 = t1; + if (t2 != 1) copyStr("0", lttmp[2]); + } if (t1 != t2) {cerr = 2; goto ltreturn;} if (!strcmp(lttmp[1], "=")) { ret = (uint8_t)(bool)!strcmp(lttmp[0], lttmp[2]); @@ -3061,7 +3083,7 @@ static inline void printError(int error) { fputs("Memory error", stdout); break; case 27:; - printf("Failed to open file '%s' (errno: [%d] %s)", errstr, errno, strerror(errno)); + printf("Failed to open file: '%s' (errno: [%d] %s)", errstr, errno, strerror(errno)); break; case 28:; fputs("Label is already defined", stdout); @@ -3078,6 +3100,21 @@ static inline void printError(int error) { case 32:; fputs("Reached GOSUB limit", stdout); break; + case 33:; + printf("Failed to open library: '%s'", errstr); + #ifndef _WIN32 + printf(" (%s)", dlerror()); + #endif + break; + case 34:; + printf("Not a CLIBASIC extension: '%s'", errstr); + break; + case 35:; + printf("Failed to initialize extension: '%s'", errstr); + break; + case 36:; + printf("Extension already loaded: '%s'", errstr); + break; case 125:; printf("Function only valid in program: '%s'", errstr); break; @@ -3100,6 +3137,103 @@ static inline void printError(int error) { putchar('\n'); } +int loadExt(char* path) { + seterrstr(path); + #ifndef _WIN32 + void* lib = dlopen(path, RTLD_LAZY); + #else + void* lib = LoadLibrary(path); + #endif + if (!lib) {cerr = 33; return -1;} + char* oextname = (void*)dlsym(lib, "cbext_name"); + bool (*cbext_init)(cb_extargs) = (void*)dlsym(lib, "cbext_init"); + if (!oextname | !cbext_init) {cerr = 34; goto loadfail;} + if (!oextname[0]) {cerr = 34; goto loadfail;} + int e = -1; + char* extname = (char*)malloc(strlen(oextname) + 1); + copyStr(oextname, extname); + upCase(extname); + for (register int i = 0; i < extmaxct; ++i) { + if (extdata[i].inuse && !strcmp(extname, extdata[i].name)) { + seterrstr(extname); + cerr = 36; + goto loadfail; + } + } + for (register int i = 0; i < extmaxct; ++i) { + if (!extdata[i].inuse) {e = i; break;} + } + cb_extargs extargs = { + VER, BVER, OSVER, + &varmaxct, vardata, + &retval, + &filemaxct, filedata, &fileerror, + &chkCmdPtr, + &txtattrib, + &curx, &cury, + getCurPos, + gethome, + seterrstr, + basefilename, pathfilename, + openFile, closeFile, cbrm, + usTime, timer, resetTimer, cb_wait, + randNum, + chkCmd, + isSpChar, isExSpChar, isValidVarChar, isValidHexChar, + updateTxtAttrib, + getStr, getType, + getVar, setVar, delVar, + getVal, + solvearg, + logictest, + printError + }; + if (!cbext_init(extargs)) {cerr = 35; goto loadfail;} + if (e == -1) { + e = extmaxct; + ++extmaxct; + extdata = (cb_ext*)realloc(extdata, extmaxct * sizeof(cb_ext)); + } + extdata[e].inuse = true; + extdata[e].name = extname; + extdata[e].lib = lib; + extdata[e].runcmd = (void*)dlsym(lib, "cbext_runcmd"); + extdata[e].runfunc = (void*)dlsym(lib, "cbext_runfunc"); + extdata[e].runlogic = (void*)dlsym(lib, "cbext_runlogic"); + extdata[e].chkfuncsolve = (void*)dlsym(lib, "cbext_chkfuncsolve"); + extdata[e].clearGlobals = (void*)dlsym(lib, "cbext_clearGlobals"); + extdata[e].deinit = (void*)dlsym(lib, "cbext_deinit"); + return e; + loadfail:; + dlclose(lib); + return -1; +} + +bool unloadExt(int e) { + if (e == -1) { + for (int i = extmaxct - 1; i > -1; --i) { + if (!extdata[i].inuse) continue; + extdata[i].inuse = false; + nfree(extdata[i].name); + dlclose(extdata[i].lib); + if (extdata[i].deinit) extdata[i].deinit(); + } + extmaxct = 0; + } else { + if (e < -1 || e >= extmaxct || !extdata[e].inuse) {cerr = 16; return false;} + extdata[e].inuse = false; + nfree(extdata[e].name); + dlclose(extdata[e].lib); + if (extdata[e].deinit) extdata[e].deinit(); + for (int i = extmaxct - 1; i > -1; --i) { + if (extdata[i].inuse) break; + --extmaxct; + } + extdata = (cb_ext*)realloc(extdata, extmaxct * sizeof(cb_ext)); + } + return true; +} + void runcmd() { if (cmd[0] == 0) return; cerr = 0; diff --git a/clibasic.h b/clibasic.h new file mode 100644 index 0000000..3ab0dd9 --- /dev/null +++ b/clibasic.h @@ -0,0 +1,140 @@ +/* -------------------------------------------------------------------------------------------- */ +/* | CLIBASIC header for extensions | */ +/* -------------------------------------------------------------------------------------------- */ +// +// Required: +// +// char cbext_name[] +// Name of the extension (cannot be empty) +// +// bool cbext_init(cb_extargs) +// Initialize pointers to internal CLIBASIC functions, set up the extension for use, and +// return true if successful, otherwise, return false +// +// Optional: +// +// int cbext_runcmd(int, char**, char**, uint8_t*, int32_t*) +// Run a command utilizing the arguments (argument count, raw arguments, solved arguments (a +// call to solvearg([n]) is needed), argument types, argument lengths) and return 255 if no +// match is found, an error code if an error is encountered, or 0 if execution succeeds +// +// cb_funcret cbext_runfunc(int, char**, char**, uint8_t*, int32_t*, char*); +// Run a function utilizing the arguments (argument count, raw arguments (will be pre-solved +// if cbext_chkfuncsolve does not exist or returns false when passed the function name) +// , solved arguments, argument types, argument lengths, output buffer) and return {255, 0} +// if no match is found, {error code, *} if an error is encountered, or {0, function type} +// if execution succeeds +// +// int cbext_runlogic(char*, char**, int32_t, int32_t) +// Run a logic command utilizing the arguments (raw input, split raw input ([0]) and empty +// buffer for use in copying raw input into and performing actions on the copied raw input +// ([1]), position of command in raw input, position of arguments in raw input) and return +// 255 if no match is found, an error code if an error is encountered, or 0 if execution is +// successful +// +// bool cbext_chkfuncsolve(char*) +// Return true if a function requires raw arguments, false otherwise +// +// void cbext_clearGlobals() +// Reset internal variables before displaying the prompt +// +// bool cbext_deinit() +// Clean up extension before exiting +// +// Notes: +// - It is a good idea to make all commands and functions adhere to '[Extension].[Cmd/Func]' +// (unless overriding internal CLIBASIC commands or functions) to avoid naming conflicts. +// This can be done with defining a macro as a string and passing 'MACRO".CMD/FUNC"' to +// chkCmd as so: +// #define EXTNAME "TEST" +// char cbext_name[] = EXTNAME; +// ... +// chkCmd(1, EXTNAME".TEST")... + +#include +#include +#include + +typedef struct { + bool inuse; // true if the spot is in use, false otherwise + char* name; // name of the variable + uint8_t type; // type of the variable, 1 = string, 2 = number + int32_t size; // max index of variable, -1 = normal variable, >= 0 = array, to get the size of the array, add 1 + char** data; // array of strings containing the value(s) +} cb_var; + +typedef struct { + FILE* fptr; // pointer to FILE* struct to read from and write to the file + int32_t size; // file size at open +} cb_file; + +typedef struct { + int cerr; // error to return + int ftype; // type of the function, 1 = string, 2 = number +} cb_funcret; + +typedef struct { + bool bold; // bold text attribute + bool italic; // italic text attribute + bool underln; // underline text attribute + bool underlndbl; // double underline text attribute + bool underlnsqg; // squiggly underline text attribute + bool strike; // strikethrough text attribute + bool overln; // overline text attribute + bool dim; // dim text attribute + bool blink; // blink text attribute + bool hidden; // hidden text attribute + bool reverse; // reverse colors text attribute + int underlncolor; // underline color (0-255) text attribute + bool fgce; // fgc enabled text attribute + bool bgce; // bgc enabled text attribute + bool truecolor; // truecolor enabled text attribute + uint8_t fgc; // 8-bit/standard fgc text attribute + uint8_t bgc; // 8-bit/standard bgc text attribute + uint32_t truefgc; // 24-bit/truecolor fgc text attribute + uint32_t truebgc; // 24-bit/truecolor bgc text attribute +} cb_txt; + +typedef struct { + char* VER; // CLIBASIC version + char* BVER; // bits ("64"/"32"/"?") + char* OSVER; // os name ("Linux", "Windows", ...) + int* varmaxct; // pointer to the amount of variable spots allocated + cb_var* vardata; // pointer to the variable spots + int* retval; // pointer to the variable read by the CLIBASIC function _RET() + int* filemaxct; // pointer to the amount of file spots allocated + cb_file* filedata; // pointer to the file spots + int* fileerror; // pointer to the variable read by the CLIBASIC function _FILEERROR() + char** chkCmdPtr; // pointer to a pointer of the char array to be compared by chkCmd() + cb_txt* txtattrib; // pointer to the struct that stores text attributes + int* curx; // pointer to the text cursor x position + int* cury; // pointer to the text cursor y position + void (*getCurPos)(void); // update the text cursor position + char* (*gethome)(void); // get the user's home directory + void (*seterrstr)(char*); // set the error string some errors require + char* (*basefilename)(char*); // gets the file name off of a file path + char* (*pathfilename)(char*); // gets the path name off of a file path + int (*openFile)(char*, char*); // opens a file in a new file spot and returns the position + bool (*closeFile)(int); // closes a file spot or all if supplied with -1 and returns true if successful, false otherwise + bool (*cbrm)(char*); // removes a file or directory and returns true if successful, false otherwise + uint64_t (*usTime)(void); // returns the time in nanoseconds + uint64_t (*timer)(void); // returns the timer ticks in nanoseconds + void (*resetTimer)(void); // resets the timer + void (*cb_wait)(uint64_t); // waits for a certain amount of nanoseconds + double (*randNum)(double, double); // returns a random double from within a range + bool (*chkCmd)(int, ...); // compares chkCmdPtr to strings supplied after the string count and returns true if a match is found, false otherwise + bool (*isSpChar)(char); // checks if a char is a special character ('+', '-', '*', '\', & '^') + bool (*isExSpChar)(char); // checks if a char is a special character but with more chars ('=', '>', '<', & ',') + bool (*isValidVarChar)(char); // checks if a char is valid in a variable name + bool (*isValidHexChar)(char); // checks if a char is valid in a hexadecimal number (0-F) + void (*updateTxtAttrib)(void); // reads the text attribute struct and applies the changes + void (*getStr)(char*, char*); // returns a string after evaluating any backslash escape codes + uint8_t (*getType)(char*); // gets the type of raw input ("\"test\"" returns 1 (string), "0" returns 2 (number), and "TEST" returns 255 (variable)) + uint8_t (*getVar)(char*, char*); // puts the value of the variable specified by argument 1 in argument 2 and returns the type (1 = string, 2 = number) + bool (*setVar)(char*, char*, uint8_t, int32_t); // sets the variable specified by argument 1 to the value specified by argument 2 and sets the type to argument 3 and size to argument 4 + bool (*delVar)(char*); // deletes a variable + uint8_t (*getVal)(char*, char*); // gets the value of the raw input supplied by argument and returns the type (1 = string, 2 = number, 255 = blank) or 0 on failure + bool (*solvearg)(int); // solves an argument for commands as some commands may want to read from raw input + uint8_t (*logictest)(char*); // takes raw input, tests it, and returns -1 on failure, 0 if false, and 1 if true + void (*printError)(int); // prints a built-in error string +} cb_extargs; diff --git a/commands.c b/commands.c index 4269801..8ce4502 100644 --- a/commands.c +++ b/commands.c @@ -1,3 +1,14 @@ +int extcerr = 255; +for (int i = extmaxct - 1; i > -1; --i) { + if (extdata[i].inuse && extdata[i].runcmd) { + extcerr = extdata[i].runcmd(argct, tmpargs, arg, argt, argl); + if (extcerr != 255) { + cerr = extcerr; + if (!extcerr) goto noerr; + else goto cmderr; + } + } +} if (chkCmdPtr[0] == '_') goto _cmd; if (chkCmd(2, "EXIT", "QUIT")) { if (argct > 1) {cerr = 3; goto cmderr;} @@ -282,17 +293,17 @@ if (chkCmd(1, "COLOR")) { if (argt[1] != 2) {cerr = 2; goto cmderr;} else { tmp = atoi(arg[1]); - if (txt_truecolor) { + if (txtattrib.truecolor) { if (tmp < 0 || tmp > 0xFFFFFF) {cerr = 16; goto cmderr;} - truefgc = tmp; + txtattrib.truefgc = tmp; } else { if (tmp < 0 || tmp > 255) {cerr = 16; goto cmderr;} - fgc = (uint8_t)tmp; + txtattrib.fgc = (uint8_t)tmp; } #ifndef _WIN_NO_VT - if (txt_fgc) { - if (txt_truecolor) printf("\e[38;2;%u;%u;%um", (uint8_t)(truefgc >> 16), (uint8_t)(truefgc >> 8), (uint8_t)truefgc); - else printf("\e[38;5;%um", fgc); + if (txtattrib.fgce) { + if (txtattrib.truecolor) printf("\e[38;2;%u;%u;%um", (uint8_t)(txtattrib.truefgc >> 16), (uint8_t)(txtattrib.truefgc >> 8), (uint8_t)txtattrib.truefgc); + else printf("\e[38;5;%um", txtattrib.fgc); } #else updateTxtAttrib(); @@ -304,17 +315,17 @@ if (chkCmd(1, "COLOR")) { if (argt[2] != 2) {cerr = 2; goto cmderr;} else { tmp = atoi(arg[2]); - if (txt_truecolor) { + if (txtattrib.truecolor) { if (tmp < 0 || tmp > 0xFFFFFF) {cerr = 16; goto cmderr;} - truebgc = tmp; + txtattrib.truebgc = tmp; } else { if (tmp < 0 || tmp > 255) {cerr = 16; goto cmderr;} - bgc = (uint8_t)tmp; + txtattrib.bgc = (uint8_t)tmp; } #ifndef _WIN_NO_VT - if (txt_bgc) { - if (txt_truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(truebgc >> 16), (uint8_t)(truebgc >> 8), (uint8_t)truebgc); - else printf("\e[48;5;%um", bgc); + if (txtattrib.bgce) { + if (txtattrib.truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(txtattrib.truebgc >> 16), (uint8_t)(txtattrib.truebgc >> 8), (uint8_t)txtattrib.truebgc); + else printf("\e[48;5;%um", txtattrib.bgc); } #else updateTxtAttrib(); @@ -339,7 +350,8 @@ if (chkCmd(1, "LOCATE")) { } if (argct > 1) { if (!solvearg(2)) goto cmderr; - if (argt[2] == 0) {} + if (argt[1] == 0 && argt[2] == 0) {cerr = 3; goto cmderr;} + else if (argt[2] == 0) {} else if (argt[2] != 2) {cerr = 2; goto cmderr;} else { tmp = atoi(arg[2]); @@ -381,7 +393,8 @@ if (chkCmd(1, "RLOCATE")) { } if (argct > 1) { if (!solvearg(2)) goto cmderr; - if (argt[2] == 0) {} + if (argt[1] == 0 && argt[2] == 0) {cerr = 3; goto cmderr;} + else if (argt[2] == 0) {} else if (argt[2] != 2) {cerr = 2; goto cmderr;} else { tmp = atoi(arg[2]); @@ -409,15 +422,15 @@ if (chkCmd(1, "RLOCATE")) { if (chkCmd(1, "CLS")) { if (argct > 1) {cerr = 3; goto cmderr;} cerr = 0; - uint8_t tbgc = bgc; + uint8_t tbgc = txtattrib.bgc; #ifndef _WIN_NO_VT - uint32_t ttbgc = truebgc; + uint32_t ttbgc = txtattrib.truebgc; #endif if (argct) { if (!solvearg(1)) goto cmderr; if (argt[1] != 2) {cerr = 2; goto cmderr;} #ifndef _WIN_NO_VT - if (txt_truecolor) { + if (txtattrib.truecolor) { ttbgc = (uint32_t)atoi(arg[1]); } else { tbgc = (uint8_t)atoi(arg[1]); @@ -428,14 +441,14 @@ if (chkCmd(1, "CLS")) { } #ifndef _WIN_NO_VT if (argct) { - if (txt_truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(ttbgc >> 16), (uint8_t)(ttbgc >> 8), (uint8_t)ttbgc); + if (txtattrib.truecolor) printf("\e[48;2;%u;%u;%um", (uint8_t)(ttbgc >> 16), (uint8_t)(ttbgc >> 8), (uint8_t)ttbgc); else printf("\e[48;5;%um", tbgc); } fputs("\e[H\e[2J\e[3J", stdout); updateTxtAttrib(); fflush(stdout); #else - SetConsoleTextAttribute(hConsole, (fgc % 16) + (tbgc % 16) * 16); + SetConsoleTextAttribute(hConsole, (txtattrib.fgc % 16) + (tbgc % 16) * 16); system("cls"); updateTxtAttrib(); #endif @@ -825,6 +838,14 @@ if (chkCmd(1, "FILES")) { (void)ret; goto noerr; } +if (chkCmd(1, "EXTENSIONS")) { + if (argct) {cerr = 3; goto cmderr;} + cerr = 0; + for (register int i = 0; i < extmaxct; ++i) { + if (extdata[i].inuse) {puts(extdata[i].name);} + } + goto noerr; +} if (chkCmd(2, "CHDIR", "CD")) { if (argct != 1) {cerr = 3; goto cmderr;} cerr = 0; @@ -939,6 +960,34 @@ if (chkCmd(4, "MV", "MOVE", "REN", "RENAME")) { fileerror = errno; goto noerr; } +if (chkCmd(1, "LOADEXT")) { + if (argct < 1) {cerr = 3; goto cmderr;} + cerr = 0; + for (int i = 1; i <= argct; i++) { + if (!solvearg(i) || argt[i] != 1) {cerr = 2; goto cmderr;} + if (loadExt(arg[i]) < 0) {goto cmderr;} + } + goto noerr; +} +if (chkCmd(1, "UNLOADEXT")) { + if (argct != 1) {cerr = 3; goto cmderr;} + cerr = 0; + if (!solvearg(1)) goto cmderr; + if (argt[1] == 1) { + upCase(arg[1]); + for (register int i = 0; i < extmaxct; ++i) { + if (extdata[i].inuse && !strcmp(arg[1], extdata[i].name)) { + unloadExt(i); + goto noerr; + } + } + cerr = 16; + goto cmderr; + } else { + if (!unloadExt(atoi(arg[1]))) goto cmderr; + } + goto noerr; +} goto cmderr; _cmd:; if (chkCmd(1, "_RESETTITLE")) { @@ -1140,21 +1189,8 @@ if (chkCmd(1, "_TXTATTRIB")) { int val = 0; if (attrib == 0) { if (argct == 2) {cerr = 3; goto cmderr;} - txt_bold = false; - txt_italic = false; - txt_underln = false; - txt_underlndbl = false; - txt_underlnsqg = false; - txt_strike = false; - txt_overln = false; - txt_dim = false; - txt_blink = false; - txt_hidden = false; - txt_reverse = false; - txt_underlncolor = 0; - txt_fgc = true; - txt_bgc = false; - txt_truecolor = false; + memset(&txtattrib, 0, sizeof(cb_txt)); + txtattrib.fgce = true; goto cmderr; } else if (argct != 2) { if (attrib == 12) {cerr = 16; goto cmderr;} @@ -1179,21 +1215,21 @@ if (chkCmd(1, "_TXTATTRIB")) { } } switch (attrib) { - case 1: txt_bold = (bool)val; break; - case 2: txt_italic = (bool)val; break; - case 3: txt_underln = (bool)val; break; - case 4: txt_underlndbl = (bool)val; break; - case 5: txt_underlnsqg = (bool)val; break; - case 6: txt_strike = (bool)val; break; - case 7: txt_overln = (bool)val; break; - case 8: txt_dim = (bool)val; break; - case 9: txt_blink = (bool)val; break; - case 10: txt_hidden = (bool)val; break; - case 11: txt_reverse = (bool)val; break; - case 12: txt_underlncolor = val; break; - case 13: txt_fgc = (bool)val; break; - case 14: txt_bgc = (bool)val; break; - case 15: txt_truecolor = (bool)val; break; + case 1: txtattrib.bold = (bool)val; break; + case 2: txtattrib.italic = (bool)val; break; + case 3: txtattrib.underln = (bool)val; break; + case 4: txtattrib.underlndbl = (bool)val; break; + case 5: txtattrib.underlnsqg = (bool)val; break; + case 6: txtattrib.strike = (bool)val; break; + case 7: txtattrib.overln = (bool)val; break; + case 8: txtattrib.dim = (bool)val; break; + case 9: txtattrib.blink = (bool)val; break; + case 10: txtattrib.hidden = (bool)val; break; + case 11: txtattrib.reverse = (bool)val; break; + case 12: txtattrib.underlncolor = val; break; + case 13: txtattrib.fgce = (bool)val; break; + case 14: txtattrib.bgce = (bool)val; break; + case 15: txtattrib.truecolor = (bool)val; break; } updateTxtAttrib(); goto noerr; diff --git a/commit.sh b/commit.sh index 510ea01..58d2749 100755 --- a/commit.sh +++ b/commit.sh @@ -35,8 +35,8 @@ cd .. echo $'\e[1m'"Adding files..."$'\e[0m' mv lib .lib -echo LICENSE Makefile *.c *.sh *.md .gitmodules .gitattributes */ -git add LICENSE Makefile *.c *.sh *.md .gitmodules .gitattributes */ +echo LICENSE Makefile *.c *.h *.sh *.md .gitmodules .gitattributes */ +git add LICENSE Makefile *.c *.h *.sh *.md .gitmodules .gitattributes */ e=$? mv .lib lib [ $e -ne 0 ] && exit 1 diff --git a/docs b/docs index 06ded4a..11d50e0 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 06ded4a0b7f89cf26035334c138dacf77687d92e +Subproject commit 11d50e0745245549b27e7f435f326d5ab7963d21 diff --git a/examples b/examples index d46e2bd..672f4f4 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit d46e2bd16e03d50d55ab87ba54ae66dd7621f4bd +Subproject commit 672f4f4287a8ded0bc20e89c48fdab9c6937d8d1 diff --git a/functions.c b/functions.c index f9e8770..60f3338 100644 --- a/functions.c +++ b/functions.c @@ -1,3 +1,25 @@ +cb_funcret extfr = {127, 0}; +for (int i = extmaxct - 1; i > -1; --i) { + if (extdata[i].inuse && extdata[i].runfunc) { + if (skipfargsolve && extsas != i) { + skipfargsolve = false; + for (int j = 1; j <= fargct; ++j) { + fargt[j] = getVal(tmpfargs[j], gftmp[1]); + if (fargt[j] == 0) goto fnoerrscan; + if (fargt[j] == 255) fargt[j] = 0; + flen[j] = strlen(gftmp[1]); + farg[j] = (char*)malloc(flen[j] + 1); + copyStr(gftmp[1], farg[j]); + } + } + extfr = extdata[i].runfunc(fargct, tmpfargs, farg, fargt, flen, outbuf); + if (extfr.cerr != 127) { + cerr = extfr.cerr; + ftype = extfr.ftype; + goto fexit; + } + } +} if (chkCmdPtr[0] == '_') goto _func; if (chkCmd(1, "CHR$")) { cerr = 0; @@ -649,6 +671,13 @@ if (chkCmd(1, "LEN")) { sprintf(outbuf, "%lu", (long unsigned)strlen(farg[1])); goto fexit; } +if (chkCmd(1, "TYPEOF")) { + if (fargct != 1) {cerr = 3; goto fexit;} + cerr = 0; + ftype = 2; + sprintf(outbuf, "%u", fargt[1]); + goto fexit; +} if (chkCmd(1, "SNIP$")) { if (fargct < 2 || fargct > 3) {cerr = 3; goto fexit;} cerr = 0; @@ -836,23 +865,23 @@ if (chkCmd(1, "FGC")) { cerr = 0; ftype = 2; if (fargct) {cerr = 3; goto fexit;} - if (txt_truecolor) sprintf(outbuf, "%u", truefgc); - else sprintf(outbuf, "%u", fgc); + if (txtattrib.truecolor) sprintf(outbuf, "%u", txtattrib.truefgc); + else sprintf(outbuf, "%u", txtattrib.fgc); goto fexit; } if (chkCmd(1, "BGC")) { cerr = 0; ftype = 2; if (fargct) {cerr = 3; goto fexit;} - if (txt_truecolor) sprintf(outbuf, "%u", truebgc); - else sprintf(outbuf, "%u", bgc); + if (txtattrib.truecolor) sprintf(outbuf, "%u", txtattrib.truebgc); + else sprintf(outbuf, "%u", txtattrib.bgc); goto fexit; } if (chkCmd(1, "TRUECOLOR")) { cerr = 0; ftype = 2; if (fargct) {cerr = 3; goto fexit;} - sprintf(outbuf, "%d", (int)txt_truecolor); + sprintf(outbuf, "%d", (int)txtattrib.truecolor); goto fexit; } if (chkCmd(1, "INPUT$")) { @@ -1302,6 +1331,100 @@ if (chkCmd(1, "ISFILE")) { outbuf[1] = 0; goto fexit; } +if (chkCmd(1, "LOADEXT")) { + ftype = 2; + if (fargct < 1) {cerr = 3; goto fexit;} + cerr = 0; + outbuf[0] = '1'; + for (int i = 1; i <= fargct; i++) { + if (fargt[i] != 1) {cerr = 2; goto fexit;} + if (loadExt(farg[i]) < 0) {outbuf[0] = '0'; break;} + } + outbuf[1] = 0; + cerr = 0; + goto fexit; +} +if (chkCmd(1, "UNLOADEXT")) { + ftype = 2; + if (fargct != 1) {cerr = 3; goto fexit;} + cerr = 0; + if (fargt[1] == 1) { + upCase(farg[1]); + for (register int i = 0; i < extmaxct; ++i) { + if (extdata[i].inuse && !strcmp(farg[1], extdata[i].name)) { + sprintf(outbuf, "%d", (int)unloadExt(i)); + } + } + } else { + sprintf(outbuf, "%d", (int)unloadExt(atoi(farg[1]))); + } + cerr = 0; + goto fexit; +} +if (chkCmd(1, "READEXTNAME$")) { + ftype = 1; + if (fargct != 1) {cerr = 3; goto fexit;} + cerr = 0; + if (fargt[1] != 1) {cerr = 2; goto fexit;} + #ifndef _WIN32 + void* lib = dlopen(farg[1], RTLD_LAZY); + #else + void* lib = LoadLibrary(farg[1]); + #endif + if (lib) { + char* extname = (void*)dlsym(lib, "cbext_name"); + if (!extname) {copyStr("", outbuf);} + else {copyStr(extname, outbuf); upCase(outbuf);} + dlclose(lib); + } else { + outbuf[0] = 0; + } + goto fexit; +} +if (chkCmd(1, "EXTNAME$")) { + ftype = 1; + if (fargct != 1) {cerr = 3; goto fexit;} + cerr = 0; + if (fargt[1] != 2) {cerr = 2; goto fexit;} + int e = atoi(farg[1]); + if (e >= 0 && e < extmaxct && extdata[e].inuse) { + copyStr(extdata[e].name, outbuf); + } else { + outbuf[0] = 0; + } + goto fexit; +} +if (chkCmd(1, "EXTLOADED")) { + ftype = 2; + if (fargct != 1) {cerr = 3; goto fexit;} + cerr = 0; + if (fargt[1] == 1) { + upCase(farg[1]); + outbuf[0] = '0'; + for (register int i = 0; i < extmaxct; ++i) { + if (extdata[i].inuse && !strcmp(farg[1], extdata[i].name)) { + outbuf[0] = '1'; + } + } + } else { + int e = atoi(farg[1]); + if (e >= 0 && e < extmaxct) { + outbuf[0] = '0' + extdata[e].inuse; + } + } + outbuf[1] = 0; + goto fexit; +} +if (chkCmd(1, "EXTENSIONS$")) { + ftype = 1; + if (fargct) {cerr = 3; goto fexit;} + cerr = 0; + for (register int i = 0; i < extmaxct; ++i) { + if (i < 0) strApndChar(outbuf, '\n'); + if (extdata[i].inuse) {copyStrApnd(extdata[i].name, outbuf);} + } + goto fexit; +} goto fexit; _func:; if (chkCmd(1, "_HOME$")) { diff --git a/logic.c b/logic.c index 4937dc7..5eae5ee 100644 --- a/logic.c +++ b/logic.c @@ -1,3 +1,22 @@ +int extcerr = 255; +for (int i = extmaxct - 1; i > -1; --i) { + if (dlstackp > ((progindex > -1) ? mindlstackp[progindex] : -1)) { + if (dldcmd[dlstackp]) break; + } + if (itstackp > ((progindex > -1) ? minitstackp[progindex] : -1)) { + if (itdcmd[itstackp]) break; + } + if (fnstackp > ((progindex > -1) ? minfnstackp[progindex] : -1)) { + if (fndcmd[fnstackp]) break; + } + if (extdata[i].inuse && extdata[i].runlogic) { + extcerr = extdata[i].runlogic(cmd, (char**)ltmp, i, j); + if (extcerr != 255) { + cerr = extcerr; + return true; + } + } +} if (chkCmd(1, "REM")) return true; if (chkCmd(2, "?", "PRINT") || cmd[i] == '?') { if (dlstackp > ((progindex > -1) ? mindlstackp[progindex] : -1)) {