/* * no-nonsense TRAC programming language implementation (T64 standard) * * Build with: cc [-static] -std=c99 -Os -s nntrac.c -o nntrac * * see README.md for details * * Created by Luxferre in 2023, released into public domain */ #define POSIX_SOURCE #define POSIX_C_SOURCE 1 #include #include #include #include #define uint unsigned int #define uchar unsigned char #ifndef NNT_SHARP #define NNT_SHARP '#' /* can be redefined to : or other unused character */ #endif #ifndef NNT_SYMNAMELEN #define NNT_SYMNAMELEN 32 /* max symbol name length, including null */ #endif #ifndef strrev char *strrev(char *str) { char *p1, *p2; if(!str || !*str) return str; for(p1=str,p2=str+strlen(str)-1;p2>p1;++p1,--p2) { *p1 ^= *p2; *p2 ^= *p1; *p1 ^= *p2; } return str; } #endif enum NNT_MARKERS { /* various markers: 248 to 255 never occur in UTF-8 */ NNT_AFST=-8, /* active function start */ NNT_NFST, /* neutral function start */ NNT_EOF, /* end of function */ NNT_ADEL, /* argument delimiter */ NNT_SEGGAP /* segment gap character */ }; enum NNT_MODES {NNT_NORMAL=1, NNT_LEGACY, NNT_SECURE}; /* operation modes */ static const char NNT_ADEL_S[2] = {NNT_ADEL, 0}; static uchar nnt_meta = '\''; /* apostrophe by default */ /* combined active + neutral string buffer and evaluation result buffer */ static char *nnt_prog, *nnt_res, nnt_mode = NNT_NORMAL, nnt_mlock = 0; /* scanning pointers and buffer lengths */ static int nnt_ascan, nnt_alen, nnt_flen, nnt_reslen, nnt_trace = 0; /* string to number */ long long snum(char *str) { /* strtoll wrapper */ char *endptr; return strtoll(str, &endptr, 0); } /* number to string */ char *n2s(long long num, char *s, int *slen) { *slen = snprintf(NULL, 0, "%lld", num) + 1; s = realloc(s, *slen); memset(s, 0, *slen); /* fill it with zeroes*/ snprintf(s, *slen, "%lld", num); s = realloc(s, (*slen) = strlen(s)); return s; } /* primitive function definition */ typedef struct nnt_primitive_t { char name[NNT_SYMNAMELEN]; /* primitive function name */ /* registered function pointer */ char* (*handler)(char *arglist, char *res, int *reslen); } nnt_primitive; static nnt_primitive *nnt_primitives; /* primitives table */ static uint nnt_primitive_len = 0; /* primitives table size */ /* find a primitive function index by name, -1 if not found */ int nnt_findprimitive(const char *name) { int i; for(i=0;i -1) { /* existing form: just update the entry */ strncpy(nnt_forms[findex].name, name, NNT_SYMNAMELEN); nnt_forms[findex].value = realloc(nnt_forms[findex].value, len); memcpy(nnt_forms[findex].value, value, len); nnt_forms[findex].len = len; /* also update the length */ nnt_forms[findex].fptr = ptr; /* reset the pointer */ } else { /* create a new form and update the table */ nnt_form newform; strncpy(newform.name, name, NNT_SYMNAMELEN); /* fill the name */ newform.value = calloc(len, 1); /* allocate the space */ memcpy(newform.value, value, len); /* populate the value */ newform.len = len; /* populate the length */ newform.fptr = ptr; /* init the pointer */ nnt_forms_len++; nnt_forms = realloc(nnt_forms, nnt_forms_len * sizeof(nnt_form)); nnt_forms[nnt_forms_len - 1] = newform; } } /* delete a form by its name */ /* doesn't fully delete the structure, just marks it as free to use */ void nnt_delform(char *name) { int findex = nnt_findform(name); if(findex > -1) { nnt_forms[findex].value = realloc(nnt_forms[findex].value, 0); nnt_forms[findex].len = 0; nnt_forms[findex].fptr = 0; memset(nnt_forms[findex].name, 0, NNT_SYMNAMELEN); /* null name is a valid form name in TRAC, so we use a marker char */ nnt_forms[findex].name[0] = NNT_EOF; } } /* form serialization/deserialization using netstrings */ /* serialize the form and populate the buffer and length */ char* nnt_serializeform(nnt_form *form, char *buf, int *len) { *len = NNT_SYMNAMELEN + form->len + sizeof(int); int lbl = snprintf(NULL, 0, "%d:", *len); /* length buffer length */ char *lenbuf = calloc(lbl + 1, 1); snprintf(lenbuf, lbl+1, "%d:", *len); /* fill in length buffer */ *len += lbl; buf = realloc(buf, *len); memset(buf, 0, *len); /* zero out the buffer */ memcpy(buf, lenbuf, lbl); /* fill the length buffer */ memcpy(buf + lbl, form->name, NNT_SYMNAMELEN); /* fill name */ memcpy(buf + lbl + NNT_SYMNAMELEN, &(form->fptr), sizeof(int)); /* fill fptr */ /* fill value */ memcpy(buf + lbl + NNT_SYMNAMELEN + sizeof(int), form->value, form->len); buf[(*len) - 1] = ','; /* trailing comma */ free(lenbuf); return buf; } /* deserialize the form given the netstring buffer (push it into the form storage using nnt_assignform) */ int nnt_deserializeform(char *buf) { char lenbuf[11] = {0}; int i, rdlen, fullen, rdptr, fid; for(i=0;buf[i]!=':' && i<11;i++) lenbuf[i] = buf[i]; rdlen = snum(lenbuf); /* practical read length */ fullen = rdlen + strlen(lenbuf); if(buf[fullen] != ',') return -1; /* invalid netstring detected */ memcpy(&rdptr, &buf[i+1+NNT_SYMNAMELEN], sizeof(int)); /* fetch pointer */ /* name is at i + 1, value is NNT_SYMNAMELEN + sizeof(int) away */ nnt_assignform(&buf[i+1], &buf[i+1+NNT_SYMNAMELEN+sizeof(int)], rdlen-NNT_SYMNAMELEN-sizeof(int)-1, rdptr); /* return the total length of processed data */ return fullen; } /* parser logic */ /* find the matching character */ int nnt_findmatch(char *str, uint len, int curpos, uchar c1, uchar c2, int dir) { int balance = 0, xpos = curpos; if(xpos >= len) return -1; for(;xpos >=0 && xpos < len ;xpos += dir) { if(str[xpos] == c1) balance++; if(str[xpos] == c2) balance--; if(!balance) break; } return (xpos >= 0 && xpos < len) ? xpos : -1; } /* delete len chars from buf at pos, return new buf length */ int nnt_delchars(char *buf, int buflen, int pos, int len) { if(len < 1) len = 1; /* safeguard */ if(pos < 0) pos = 0; /* safeguard */ memmove(&buf[pos], &buf[pos+len], buflen - pos - len); return buflen - len; } /* delete a single character under the pos in the active buffer */ int nnt_delchar(int pos) { nnt_flen = nnt_delchars(nnt_prog, nnt_flen, pos, 1); nnt_prog[nnt_flen] = 0; nnt_alen = nnt_flen - nnt_ascan; /* update active length */ return nnt_flen; } /* insert len chars into buffer dest of length dlen from buffer src at pos */ /* it reallocs dest, updates dlen and returns the resulting dest pointer */ char* nnt_inschars(char *dest, int *dlen, char *src, int len, int pos) { *dlen += len; /* increase the length */ dest = realloc(dest, *dlen); /* reallocate the buffer */ memmove(&dest[pos+len], &dest[pos], (*dlen)-pos-len); /* free up the space */ memmove(&dest[pos], src, len); /* copy the source */ return dest; } /* replace all occurrences of from with to in str (all null-terminated) */ /* returns the new str pointer (str must be dynamically allocated before) */ char *nnt_replace(char *str, char *from, char *to) { int len = strlen(str), fromlen = strlen(from), tolen = strlen(to), i; char *ptr = str; while((ptr = strstr(ptr, from)) != NULL) { /* find the occurrence */ len = nnt_delchars(str, len, (int) (ptr - str), fromlen); /* delete */ str = nnt_inschars(str, &len, to, tolen, (int) (ptr - str)); /* add */ ptr += tolen; /* move ptr */ } str[len] = 0; /* ensure null termination in the result */ return str; } /* cl primitive: call string (form) */ char* prim_cl(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, snum = 0; char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */ int fid = -1; /* skip the first one */ name = strtok(NULL, NNT_ADEL_S); /* get the name */ if(name != NULL && (fid = nnt_findform(name)) > -1) { /* populate the result */ *reslen = nnt_forms[fid].len; res = realloc(res, *reslen); memcpy(res, nnt_forms[fid].value, *reslen); do { /* receive the segment ids */ seg = strtok(NULL, NNT_ADEL_S); if(seg != NULL) { snum++; /* we start from 1 to avoid null bytes */ idstr[1] = snum; /* update the segmentid byte */ res = nnt_replace(res, idstr, seg); *reslen = strlen(res) + 1; } } while(seg != NULL); } return res; } /* evaluate a TRAC function based on the neutral string storing the results into nnt_res and nnt_reslen */ void nnt_evalfunc(int startptr) { char *arglist, *primname, *arg; int i, l=0, primindex; for(i=startptr;nnt_prog[i]!=NNT_EOF;i++,l++); arglist = calloc(l, 1); for(i=0;i -1) { /* primitive found */ if(nnt_trace) { /* trace mode */ fprintf(stderr, "[nntrace] (%s", primname); while((arg = strtok(NULL, NNT_ADEL_S)) != NULL) fprintf(stderr, ",%s", arg); fprintf(stderr, ")"); } for(i=0;i %*.*s\r\n", nnt_reslen, nnt_reslen, nnt_res); else fprintf(stderr, " => null\r\n"); } if(arglist) free(arglist); } /* main TRAC processing algorithm */ void nnt_proc(char *prog, uint len) { nnt_res = malloc(nnt_reslen = 0); /* init the result buffer */ nnt_prog = calloc(len+1, 1); /* init the neutral+active string buffer */ /* load the entire program into the active string */ memcpy(nnt_prog, prog, len); uchar cc; /* currently processed character */ int bp, rl, j; /* position buffers for various needs */ nnt_ascan = 0; /* init the pointer and NS length */ nnt_alen = nnt_flen = len; /* init active length with the full length */ while(nnt_alen >= 0) { /* processing loop start */ switch((cc = nnt_prog[nnt_ascan])) { /* rule 1 */ case '(': /* rule 2 */ bp = nnt_findmatch(nnt_prog, nnt_flen, nnt_ascan, '(', ')', 1); if(bp > -1) { /* copy the entire region into the neutral string */ rl = bp - nnt_ascan; /* nested region length */ nnt_delchar(nnt_ascan); /* delete this paren */ nnt_ascan += rl - 1; /* increment active pointer */ nnt_delchar(nnt_ascan); /* delete matching paren */ } break; case '\r': case '\n': case '\t': /* rule 3 */ nnt_delchar(nnt_ascan); break; case ',': /* rule 4 */ nnt_prog[nnt_ascan++] = NNT_ADEL; /* argument delimiter */ break; case NNT_SHARP: /* rules 5 to 7 */ switch(nnt_prog[nnt_ascan+1]) { case '(': /* rule 5 - active function start */ nnt_delchar(nnt_ascan); /* remove # character */ nnt_prog[nnt_ascan++] = NNT_AFST; break; case NNT_SHARP: /* rule 6 */ if(nnt_prog[nnt_ascan+2] == '(') { /* neutral function start */ nnt_delchar(nnt_ascan); /* remove # character */ nnt_delchar(nnt_ascan); /* remove another # character */ nnt_prog[nnt_ascan++] = NNT_NFST; } else nnt_ascan++; /* rule 7 */ break; default: nnt_ascan++; /* rule 7 */ } break; case ')': /* rule 8 */ nnt_prog[nnt_ascan] = NNT_EOF; for(bp = nnt_ascan-1;bp>0;bp--) /* find function start */ if(nnt_prog[bp] == NNT_AFST || nnt_prog[bp] == NNT_NFST) break; /* evaluate the function: the result is stored into nnt_res, its length into nnt_reslen */ nnt_evalfunc(bp + 1); /* skip function start marker */ if(nnt_reslen > 0) { nnt_prog = nnt_inschars(nnt_prog, &nnt_flen, nnt_res, nnt_reslen, nnt_ascan + 1); if(nnt_prog[bp] == NNT_AFST) { /* rule 11 */ nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); nnt_ascan = bp; } else if(nnt_prog[bp] == NNT_NFST) { /* rule 12 */ nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); nnt_ascan = bp + nnt_reslen; } } else if(nnt_reslen == 0) { /* rule 10 */ nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); nnt_ascan = bp; } else nnt_ascan = nnt_flen + 1; /* negative reslen causes the halt */ break; default: nnt_ascan++; /* rule 9 */ } nnt_alen = nnt_flen - nnt_ascan; /* update the length on every step */ nnt_prog[nnt_flen] = 0; } /* free the buffers */ free(nnt_prog); free(nnt_res); } /* all other nntrac primitives */ /* ps primitive: print string */ char* prim_ps(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S); /* skip the first one (the ps string) */ do { arg = strtok(NULL, NNT_ADEL_S); if(arg != NULL) printf("%s", arg); } while(arg != NULL); return res; } /* rc primitive: read char */ char* prim_rc(char *arglist, char *res, int *reslen) { res = realloc(res, 1); res[0] = getchar(); *reslen = 1; return res; } /* rs primitive: read string */ char* prim_rs(char *arglist, char *res, int *reslen) { int c, i = 0; res = realloc(res, (*reslen) = 0); while((c = getchar()) > 0 && c != nnt_meta) { (*reslen)++; res = realloc(res, (*reslen) + 1); res[(*reslen) - 1] = c&255; res[*reslen] = 0; /* prefill with null terminator */ } return res = realloc(res, (*reslen) = strlen(res)); } /* cm primitive: change meta */ char* prim_cm(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S); arg = strtok(NULL, NNT_ADEL_S); if(arg != NULL) nnt_meta = arg[0]; return res; } /* ds primitive: define string (form) */ char* prim_ds(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *value; name = strtok(NULL, NNT_ADEL_S); /* get the name */ if(name != NULL) { value = strtok(NULL, NNT_ADEL_S); /* get the value */ if(value != NULL) nnt_assignform(name, value, strlen(value), 0); } return res; } /* ss primitive: segment string (form) */ char* prim_ss(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, sid = 0; char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */ int fid = -1; /* skip the first one (the ps string) */ name = strtok(NULL, NNT_ADEL_S); /* get the name */ if(name != NULL && (fid = nnt_findform(name)) > -1) { do { /* receive the segment ids */ seg = strtok(NULL, NNT_ADEL_S); if(seg != NULL) { sid++; /* we start from 1 to avoid null bytes */ idstr[1] = sid; /* update the segmentid byte */ nnt_forms[fid].value = nnt_replace(nnt_forms[fid].value, seg, idstr); nnt_forms[fid].len = strlen(nnt_forms[fid].value) + 1; } } while(seg != NULL); } nnt_forms[fid].fptr = 0; return res; } /* cr primitive: call restore */ char* prim_cr(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name; int fid = -1; name = strtok(NULL, NNT_ADEL_S); if(name != NULL && (fid = nnt_findform(name)) > -1) nnt_forms[fid].fptr = 0; return res; } /* move form pointer helper */ void nnt_moveptr(nnt_form *form, int offs) { int i = 0; if(offs < 1) offs = 1; for(i=0;ifptr++; while(form->value[form->fptr] == NNT_SEGGAP && form->fptr < form->len) form->fptr += 2; /* skip the gap and the segment id */ if(form->fptr > form->len) { form->fptr = form->len; break; } } } /* partial character fetch helper (returns -1 if unavailable) */ char nnt_fetchformchar(nnt_form *form, char dir) { if(dir >= 0) { /* move forward */ while(form->value[form->fptr] == NNT_SEGGAP) { form->fptr += 2; if(form->fptr > form->len) { form->fptr = form->len; return -1; } } } else { /* move back */ while(form->fptr >= 0 && form->value[form->fptr] == NNT_SEGGAP) { form->fptr--; if(form->fptr > 0 && form->value[form->fptr - 1] == NNT_SEGGAP) form->fptr--; /* skip the segment id byte */ if(form->fptr < 0) { form->fptr = 0; return -1; } }; } if(form->fptr >= 0 && form->fptr < form->len) return form->value[form->fptr]; else return -1; } /* cs primitive: call segment */ char* prim_cs(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *def; int fid = -1; name = strtok(NULL, NNT_ADEL_S); def = strtok(NULL, NNT_ADEL_S); if(name != NULL && (fid = nnt_findform(name)) > -1) { int sl; /* segment length */ for(sl=0;sl nnt_forms[fid].len) sl = nnt_forms[fid].len - nnt_forms[fid].fptr; if(sl > 0) { res = realloc(res, (*reslen) = sl); memcpy(res, &nnt_forms[fid].value[nnt_forms[fid].fptr], sl); nnt_moveptr(&nnt_forms[fid], sl); /* move the pointer */ } else if(def != NULL) { /* set the default value */ res = realloc(res, (*reslen) = strlen(def)); memcpy(res, def, *reslen); } } return res; } /* cc primitive: call character */ char* prim_cc(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *def; int fid = -1; name = strtok(NULL, NNT_ADEL_S); def = strtok(NULL, NNT_ADEL_S); if(name != NULL && (fid = nnt_findform(name)) > -1) { res = realloc(res, (*reslen) = 1); res[0] = nnt_fetchformchar(&nnt_forms[fid], 1); if(res[0] == -1) { /* set the default value */ if(def != NULL) { res = realloc(res, (*reslen) = strlen(def)); memcpy(res, def, *reslen); } else *reslen = 0; } nnt_moveptr(&nnt_forms[fid], 1); /* move the pointer */ } return res; } /* cn primitive: call N characters */ char* prim_cn(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *name, *offs, *def, c; int fid = -1, i, roff, ccount = 0; name = strtok(NULL, NNT_ADEL_S); offs = strtok(NULL, NNT_ADEL_S); def = strtok(NULL, NNT_ADEL_S); if(name != NULL && (fid = nnt_findform(name)) > -1) { roff = snum(offs); /* real offset */ if(roff > 0) { /* positive offset */ res = realloc(res, (*reslen) = roff + 1); memset(res, 0, *reslen); for(i=0;i nnt_forms[fid].len) nnt_forms[fid].fptr = nnt_forms[fid].len; *reslen = strlen(res); /* update the length */ } else if(roff < 0) { /* negative offset */ roff = -roff; /* get absolute value */ res = realloc(res, (*reslen) = roff + 1); memset(res, 0, *reslen); nnt_forms[fid].fptr--; /* start counting from the previous char */ for(i=0;i -1) { char *ntv = calloc(nnt_forms[fid].len + 1, 1); /* null-terminated value */ memcpy(ntv, nnt_forms[fid].value, nnt_forms[fid].len); char *found = strstr(ntv, subs); if(found != NULL && nnt_forms[fid].fptr < nnt_forms[fid].len) { slen = ((long) (found - ntv)) - nnt_forms[fid].fptr; if(slen > 0) { res = realloc(res, slen + 1); memset(res, 0, slen + 1); /* null-terminate */ for(i=0;i snum(d2)) ? x1 : x2; *reslen = strlen(arg); res = realloc(res, *reslen); memcpy(res, arg, *reslen); } return res; } /* theoretically we can safely operate from -2**62 to 2**62 */ #define ARITH_LIMIT ((long long) (((unsigned long long) - 1LL) >> 2)) #define BITWISE_BITS 32 #define BITWISE_LIMIT (unsigned long long) ((1LL< -ARITH_LIMIT) { /* valid result */ res = n2s(nres, res, reslen); } else if(ovr) { /* return the overflow/underflow value */ *reslen = strlen(ovr); res = realloc(res, *reslen); /* resize to actual length */ memcpy(res, ovr, *reslen); /* fill the overflow value */ } return res; } /* ad primitive: addition (overflow arg is optional) */ char* prim_ad(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); ovr = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result(snum(as) + snum(bs), ovr, res, reslen); return res; } /* su primitive: subtraction (overflow arg is optional) */ char* prim_su(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); ovr = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result(snum(as) - snum(bs), ovr, res, reslen); return res; } /* ml primitive: multiplication (overflow arg is optional) */ char* prim_ml(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); ovr = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result(snum(as) * snum(bs), ovr, res, reslen); return res; } /* dv primitive: division (overflow arg is optional) */ char* prim_dv(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); ovr = strtok(NULL, NNT_ADEL_S); if(as && bs) { long long b = snum(bs); if(b) res = handle_dec_result(snum(as) / b, ovr, res, reslen); else if(ovr) { /* division by zero, return ovr */ *reslen = strlen(ovr); res = realloc(res, *reslen); /* resize to actual length */ memcpy(res, ovr, *reslen); /* fill the overflow value */ } } return res; } /* bu primitive: bitwise union (OR) */ char* prim_bu(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result((snum(as) | snum(bs)) & BITWISE_LIMIT, NULL, res, reslen); return res; } /* bi primitive: bitwise intersect (AND) */ char* prim_bi(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result((snum(as) & snum(bs)) & BITWISE_LIMIT, NULL, res, reslen); return res; } /* [new] bx primitive: bitwise exclusive or (XOR) */ char* prim_bx(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); if(as && bs) res = handle_dec_result((snum(as) ^ snum(bs)) & BITWISE_LIMIT, NULL, res, reslen); return res; } /* bc primitive: bitwise complement (NOT) */ char* prim_bc(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as = strtok(NULL, NNT_ADEL_S); if(as) res = handle_dec_result((~snum(as)) & BITWISE_LIMIT, NULL, res, reslen); return res; } /* br primitive: bitwise rotation */ char* prim_br(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); if(as && bs) { long long a = snum(as), b = snum(bs), nres; /* we rotate b according to the value of a */ b &= BITWISE_LIMIT; /* bit-clip the value */ if(a > 0) { /* rotate left */ if(a > BITWISE_BITS) a %= BITWISE_BITS; nres = (b << a) | (b >> (BITWISE_BITS - a)); } else if(a < 0) { /* rotate right */ a = -a; if(a > BITWISE_BITS) a %= BITWISE_BITS; nres = (b << (BITWISE_BITS - a)) | (b >> a); } else nres = b; /* no rotation */ res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen); } return res; } /* bs primitive: bitwise shift */ char* prim_bs(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); if(as && bs) { long long a = snum(as), b = snum(bs), nres; /* we shift b according to the value of a */ if(a > 0) nres = b << a; /* shift left */ else if(a < 0) nres = b >> (-a); /* shift right */ else nres = b; /* no shift */ res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen); } return res; } /* ln primitive: list names */ char* prim_ln(char *arglist, char *res, int *reslen) { char *arg = strtok(arglist, NNT_ADEL_S), *pref = strtok(NULL, NNT_ADEL_S); int i; for(i=0;i -1) { /* form found */ for(j=0;j<=nnt_forms[i].len;j++) { if(j == nnt_forms[i].fptr) printf("<^>"); if(nnt_forms[i].value[j] == NNT_SEGGAP) printf("<%u>", (unsigned int) nnt_forms[i].value[++j]); else printf("%c",nnt_forms[i].value[j]); } } } return res; } /* filesystem primitives */ /* sb primitive: store block */ char *prim_sb(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; fname = strtok(NULL, NNT_ADEL_S); if(fname != NULL) { /* output file name */ FILE *hnd = fopen(fname, "wb"); /* open the output file */ if(hnd != NULL) { char *vbuf = malloc(0); /* value buffer */ int findex, vlen = 0; /* value len */ do { fmname = strtok(NULL, NNT_ADEL_S); /* form name */ if(fmname != NULL) { findex = nnt_findform(fmname); if(findex > -1) { vbuf = nnt_serializeform(&nnt_forms[findex], vbuf, &vlen); fwrite(vbuf, vlen, 1, hnd); /* write the value out */ nnt_delform(fmname); /* delete after exporting as per the spec */ } } } while(fmname != NULL); fclose(hnd); /* close the output file */ free(vbuf); /* free value buffer */ } } return res; } /* full file read helper */ char* nnt_readfile(FILE *hnd, char *fbuf, int *buflen) { fseek(hnd, 0, SEEK_END); *buflen = ftell(hnd); fseek(hnd, 0, SEEK_SET); fbuf = realloc(fbuf, *buflen); /* allocate the full buffer */ if(fbuf == NULL) return NULL; /* if allocation failed */ fread(fbuf, 1, *buflen, hnd); /* read the entire file */ return fbuf; } /* fb primitive: fetch block */ char *prim_fb(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S); if(fname != NULL) { /* input file name */ FILE *hnd = fopen(fname, "rb"); /* open the input file */ if(hnd != NULL) { char *fbuf = malloc(0); int flen; fbuf = nnt_readfile(hnd, fbuf, &flen); fclose(hnd); int i = 0, dlen; /* running index and length */ while(i < flen) { /* form queue to deserialize */ dlen = nnt_deserializeform(&fbuf[i]); if(dlen < 0) break; i += dlen + 1; } free(fbuf); /* free file buffer */ } } return res; } /* [new] sf primitive: store raw file */ char *prim_sf(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; fname = strtok(NULL, NNT_ADEL_S); /* file name */ fmname = strtok(NULL, NNT_ADEL_S); /* form name */ if(fname != NULL && fmname != NULL) { int fid = nnt_findform(fmname); if(fid > -1) { FILE *hnd = fopen(fname, "wb"); /* open the output file */ if(hnd != NULL) { /* write the value out */ fwrite(nnt_forms[fid].value, nnt_forms[fid].len, 1, hnd); fclose(hnd); } } } return res; } /* [new] ff primitive: fetch raw file */ char *prim_ff(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; fname = strtok(NULL, NNT_ADEL_S); /* file name */ fmname = strtok(NULL, NNT_ADEL_S); /* form name */ if(fname != NULL && fmname != NULL) { FILE *hnd = fopen(fname, "rb"); /* open the input file */ if(hnd != NULL) { char *fbuf = malloc(0); int flen; fbuf = nnt_readfile(hnd, fbuf, &flen); fclose(hnd); nnt_assignform(fmname, fbuf, flen, 0); /* store it into the form */ free(fbuf); /* and free the resources */ } } return res; } /* eb primitive: erase block or raw file */ char *prim_eb(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S); if(fname != NULL) remove(fname); /* delete the input file name */ return res; } /* mode primitive: set operation mode */ char *prim_mo(char *arglist, char *res, int *reslen) { if(nnt_mlock == 1) return res; /* mode change is already locked */ char *arg = strtok(arglist, NNT_ADEL_S), *modechar, *mlk; modechar = strtok(NULL, NNT_ADEL_S); if(modechar != NULL) { switch(modechar[0]) { case 'S': nnt_mode = NNT_SECURE; break; case 'L': nnt_mode = NNT_LEGACY; break; case 'E': nnt_mode = NNT_NORMAL; break; } mlk = strtok(NULL, NNT_ADEL_S); if(mlk != NULL && mlk[0] == 'L') nnt_mlock = 1; /* set mode lock */ } return res; } /* [new] ac primitive: ASCII code */ char *prim_ac(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *cvs = strtok(NULL, NNT_ADEL_S); if(cvs != NULL) res = n2s((uchar) cvs[0], res, reslen); return res; } /* [new] av primitive: ASCII value */ char *prim_av(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *cns = strtok(NULL, NNT_ADEL_S); long long rcode = 0; if(cns != NULL) rcode = snum(cns); res = realloc(res, (*reslen) = 1); res[0] = rcode&255; return res; } /* [new] fn primitive: format number */ char *prim_fn(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fmt, *cns; fmt = strtok(NULL, NNT_ADEL_S); cns = strtok(NULL, NNT_ADEL_S); long long num = 0; if(fmt != NULL && cns != NULL) { num = snum(cns); *reslen = snprintf(NULL, 0, fmt, num); /* estimate the size */ res = realloc(res, 1 + (*reslen)); snprintf(res, 1 + (*reslen), fmt, num); /* actually format the number */ res = realloc(res, (*reslen) = strlen(res)); /* final reallocation */ } return res; } #ifndef NNT_NO_EXTSHELL /* [new] os primitive: run external OS command */ char *prim_os(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *cmd = strtok(NULL, NNT_ADEL_S); int rescode; if(cmd != NULL) { rescode = system(cmd); /* run the command and get the status code */ res = n2s(rescode, res, reslen); } return res; } #endif /* [new] tm primitive: local/UTC/Epoch time */ char *prim_tm(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *fmt = strtok(NULL, NNT_ADEL_S), *opt = strtok(NULL, NNT_ADEL_S), utc = !!(opt!=NULL && opt[0]=='U'); if(fmt != NULL) { /* format string exists */ time_t curtime = time(NULL); if(fmt[0] == 'E' && fmt[1] == 0) res = n2s(curtime, res, reslen); else { struct tm *tminfo = utc ? gmtime(&curtime) : localtime(&curtime); res = realloc(res, (*reslen) = 512); memset(res, 0, *reslen); *reslen = strftime(res, *reslen, fmt, tminfo); /* populate the time */ res = realloc(res, *reslen); /* shrink the result */ } } return res; } static unsigned long long nnt_rngs; /* PRNG state*/ unsigned long long xorshift64s() { nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27); unsigned long long inter = (nnt_rngs * 0x2545F4914F6CDD1DULL) >> 32; nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27); return (inter << 32) | ((nnt_rngs * 0x2545F4914F6CDD1DULL) & 0xFFFFFFFF); } /* [new] rn primitive: (pseudo)random number */ char *prim_rn(char *arglist, char *res, int *reslen) { if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ char *arg = strtok(arglist, NNT_ADEL_S), *froms = strtok(NULL, NNT_ADEL_S), *tos = strtok(NULL, NNT_ADEL_S); long long a = (froms == NULL) ? 0 : snum(froms), b = (tos == NULL) ? ARITH_LIMIT : snum(tos); res = n2s(a == b ? a : (a + xorshift64s() % (b - a)), res, reslen); return res; } /* init the resources and built-in primitives */ void nnt_init() { nnt_forms = malloc(0); /* init the forms table */ nnt_primitives = malloc(0); /* init the primitives table */ nnt_rngs = time(NULL); xorshift64s(); /* init the PRNG */ nnt_regprimitive("ps", &prim_ps); nnt_regprimitive("rc", &prim_rc); nnt_regprimitive("rs", &prim_rs); nnt_regprimitive("cm", &prim_cm); nnt_regprimitive("ds", &prim_ds); nnt_regprimitive("ss", &prim_ss); nnt_regprimitive("cl", &prim_cl); nnt_regprimitive("cr", &prim_cr); nnt_regprimitive("cc", &prim_cc); nnt_regprimitive("cs", &prim_cs); nnt_regprimitive("cn", &prim_cn); nnt_regprimitive("in", &prim_in); nnt_regprimitive("dd", &prim_dd); nnt_regprimitive("da", &prim_da); nnt_regprimitive("eq", &prim_eq); nnt_regprimitive("gr", &prim_gr); nnt_regprimitive("ad", &prim_ad); nnt_regprimitive("su", &prim_su); nnt_regprimitive("ml", &prim_ml); nnt_regprimitive("dv", &prim_dv); nnt_regprimitive("bu", &prim_bu); nnt_regprimitive("bi", &prim_bi); nnt_regprimitive("bx", &prim_bx); nnt_regprimitive("bc", &prim_bc); nnt_regprimitive("br", &prim_br); nnt_regprimitive("bs", &prim_bs); nnt_regprimitive("ln", &prim_ln); nnt_regprimitive("pf", &prim_pf); nnt_regprimitive("sb", &prim_sb); nnt_regprimitive("fb", &prim_fb); nnt_regprimitive("eb", &prim_eb); nnt_regprimitive("mo", &prim_mo); nnt_regprimitive("ac", &prim_ac); nnt_regprimitive("av", &prim_av); nnt_regprimitive("fn", &prim_fn); nnt_regprimitive("ff", &prim_ff); nnt_regprimitive("sf", &prim_sf); #ifndef NNT_NO_EXTSHELL nnt_regprimitive("os", &prim_os); #endif nnt_regprimitive("tm", &prim_tm); nnt_regprimitive("rn", &prim_rn); } /* free the interpreter resources */ void nnt_finish() { prim_da(NULL, nnt_res, &nnt_reslen); /* free internal form resources */ if(nnt_primitives) free(nnt_primitives); if(nnt_forms) free(nnt_forms); } /* non-embed entry point */ #ifndef NNT_EMBED int main(int argc, char *argv[]) { char *fname = "-", *prog, *buf; /* stdin by default */ int proglen, i, nlen; if(argc > 1) fname = argv[1]; FILE* fd = stdin; if(!(fname[0] == '-' && fname[1] == 0)) { fd = fopen(fname, "rb"); if(fd == NULL) {perror("Error");return 1;} } if(fd == stdin) { /* interactive session */ proglen = strlen(prog = "#(ps,#(rs))"); fprintf(stderr, "%s\n", "nntrac by Luxferre, 2023, public domain"); } else prog = nnt_readfile(fd, malloc(0), &proglen); nnt_init(); /* init processing resources */ if(argc > 1) { /* populate nnt-argc and nnt-argv forms */ nlen = snprintf(NULL, 0, "%d", argc - 1); buf = calloc(nlen+1, 1); snprintf(buf, nlen+1, "%d", argc - 1); nnt_assignform("nnt-argc", buf, nlen, 0); buf = realloc(buf, 1); /* reuse the buffer for nnt-argv */ buf[0] = 0; /* ensure it's an empty string */ char segsep[3] = {NNT_SEGGAP, 1, 0}; for(i=1,nlen=0;i