Files

1178 lines
42 KiB
C
Raw Permalink Normal View History

2023-08-09 21:41:34 +03:00
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#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<nnt_primitive_len;i++)
if(strncmp(nnt_primitives[i].name, name, NNT_SYMNAMELEN) == 0)
return i; /* found */
return -1; /* nothing found */
}
/* register a primitive function, overwrite if already exists */
void nnt_regprimitive(const char *name, void *fptr) {
int pindex = nnt_findprimitive(name);
nnt_primitive newprim; /* create a new primitive template */
strncpy(newprim.name, name, NNT_SYMNAMELEN); /* fill the name */
newprim.handler = fptr; /* fill the handler pointer */
if(pindex == -1) { /* create a new table entry */
nnt_primitive_len++;
nnt_primitives = realloc(nnt_primitives,
nnt_primitive_len * sizeof(nnt_primitive));
nnt_primitives[nnt_primitive_len - 1] = newprim;
}
else nnt_primitives[pindex] = newprim; /* replace the entry */
}
/* forms definition */
typedef struct nnt_form_t {
char name[NNT_SYMNAMELEN]; /* form (var/func) name */
char *value; /* (reallocatable) value pointer (null-terminated) */
uint len; /* form value length */
int fptr; /* form character pointer */
} nnt_form;
static nnt_form *nnt_forms; /* forms table */
static uint nnt_forms_len = 0; /* forms table size */
/* find a form index by name, -1 if not found */
int nnt_findform(char *name) {
int i;
for(i=0;i<nnt_forms_len;i++)
if(strncmp(nnt_forms[i].name, name, NNT_SYMNAMELEN) == 0)
return i; /* found */
return -1; /* nothing found */
}
/* find a free form among previously deleted entries */
int nnt_findfreeform() {
int i;
for(i=0;i<nnt_forms_len;i++)
if(nnt_forms[i].name[0] == NNT_EOF) return i; /* found */
return -1; /* nothing found */
}
/* assign a value to a form, creating it if doesn't exist */
void nnt_assignform(char *name, char *value, uint len, int ptr) {
int findex = nnt_findform(name);
if(findex < 0) findex = nnt_findfreeform(); /* search among deleted */
if(findex > -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<l;i++) arglist[i] = nnt_prog[startptr + i];
/* now we have NNT_ADEL-delimited argument list to pass */
nnt_reslen = 0; /* null value is by default */
primname = strtok(arglist, NNT_ADEL_S); /* get primitive name */
primindex = nnt_findprimitive(primname);
if(primindex > -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<l;i++) arglist[i] = nnt_prog[startptr + i]; /* restore */
nnt_res = (*(nnt_primitives[primindex].handler))(arglist, nnt_res, &nnt_reslen);
}
/* now we have some core-level primitives */
else if(strncmp(primname, "hl", 3) == 0) nnt_reslen = -1; /* halt */
else if(strncmp(primname, "tn", 3) == 0) nnt_trace = 1; /* trace on */
else if(strncmp(primname, "tf", 3) == 0) nnt_trace = 0; /* trace off */
else { /* not found, run the default call logic */
if(nnt_trace) { /* trace mode */
fprintf(stderr, "[nntrace] (cl,%s",primname);
while((arg = strtok(NULL, NNT_ADEL_S)) != NULL)
fprintf(stderr, ",%s", arg);
fprintf(stderr, ")");
}
/* expand the arglist */
arglist = realloc(arglist, l + 2);
/* insert a dummy char and the argument delimiter at the start */
arglist[0] = '0'; arglist[1] = NNT_ADEL;
for(i=0;i<l;i++) arglist[i+2] = nnt_prog[startptr + i]; /* restore */
nnt_res = prim_cl(arglist, nnt_res, &nnt_reslen);
}
if(nnt_trace) { /* trace mode */
if(nnt_reslen)
fprintf(stderr, " => %*.*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;i<offs;i++) {
form->fptr++;
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++)
if(nnt_forms[fid].value[nnt_forms[fid].fptr+sl] == NNT_SEGGAP) break;
if(nnt_forms[fid].fptr + 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<roff;i++) {
c = nnt_fetchformchar(&nnt_forms[fid], 1);
if(c < 0) break; else res[i] = c;
nnt_forms[fid].fptr++; ccount++;
}
if(nnt_forms[fid].fptr > 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<roff;i++) {
c = nnt_fetchformchar(&nnt_forms[fid], -1);
if(c < 0) break; else res[i] = c;
nnt_forms[fid].fptr--; ccount++;
}
if(nnt_forms[fid].fptr < 0) nnt_forms[fid].fptr = 0;
*reslen = strlen(res); /* update the length */
res = strrev(res); /* reverse the order */
}
if(ccount && *reslen) /* finalize the allocation */
res = realloc(res, *reslen);
else if(def != NULL) { /* return the default value */
res = realloc(res, (*reslen) = strlen(def));
memcpy(res, def, *reslen);
} else *reslen = 0;
}
return res;
}
/* in primitive: initial */
char* prim_in(char *arglist, char *res, int *reslen) {
char *arg = strtok(arglist, NNT_ADEL_S), *name, *subs, *def;
name = strtok(NULL, NNT_ADEL_S);
subs = strtok(NULL, NNT_ADEL_S);
def = strtok(NULL, NNT_ADEL_S);
int fid, i, slen;
if(name != NULL && subs != NULL && (fid = nnt_findform(name)) > -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<slen;i++) { /* copy ignoring the seggaps */
res[i] = nnt_fetchformchar(&nnt_forms[fid], 1);
nnt_moveptr(&nnt_forms[fid], 1);
}
*reslen = strlen(res); /* update the length */
res = realloc(res, *reslen); /* finalize */
nnt_moveptr(&nnt_forms[fid], strlen(subs)); /* move after the fptr */
} else found = NULL;
} else found = NULL;
if(found == NULL && def != NULL) { /* return the default value */
res = realloc(res, (*reslen) = strlen(def));
memcpy(res, def, *reslen);
}
free(ntv);
}
return res;
}
/* dd primitive: delete definition(s) */
char* prim_dd(char *arglist, char *res, int *reslen) {
char *arg = strtok(arglist, NNT_ADEL_S);
do {
arg = strtok(NULL, NNT_ADEL_S);
if(arg != NULL) nnt_delform(arg);
} while(arg != NULL);
return res;
}
/* da primitive: delete all forms */
char* prim_da(char *arglist, char *res, int *reslen) {
int i = 0;
for(i=0;i<nnt_forms_len;i++) free(nnt_forms[i].value);
nnt_forms_len = 0;
nnt_forms = realloc(nnt_forms, 0);
return res;
}
/* eq primitive: string equality */
char* prim_eq(char *arglist, char *res, int *reslen) {
char *arg = strtok(arglist, NNT_ADEL_S), *x1, *x2, *x3, *x4;
x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S);
x3 = strtok(NULL, NNT_ADEL_S); x4 = strtok(NULL, NNT_ADEL_S);
if(x1 && x2 && x3 && x4) {
arg = strcmp(x1, x2) ? x4 : x3;
*reslen = strlen(arg);
res = realloc(res, *reslen);
memcpy(res, arg, *reslen);
}
return res;
}
/* gr primitive: number inequality */
char* prim_gr(char *arglist, char *res, int *reslen) {
char *arg = strtok(arglist, NNT_ADEL_S), *d1, *d2, *x1, *x2;
d1 = strtok(NULL, NNT_ADEL_S); d2 = strtok(NULL, NNT_ADEL_S);
x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S);
if(x1 && x2 && d1 && d2) {
arg = (snum(d1) > 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<<BITWISE_BITS) - 1LL)
/* reusable arithmetic ops helper */
char* handle_dec_result(long long nres, char *ovr, char *res, int *reslen) {
if(nres <= ARITH_LIMIT && nres > -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<nnt_forms_len;i++) {
if(nnt_forms[i].name[0] != NNT_EOF) {
if(pref) printf("%s", pref);
printf("%s", nnt_forms[i].name);
}
}
return res;
}
/* pf primitive: print form */
char* prim_pf(char *arglist, char *res, int *reslen) {
char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S);
if(fname != NULL) {
int i = nnt_findform(fname), j;
if(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*/
2023-08-10 09:11:43 +03:00
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);
2023-08-09 21:41:34 +03:00
}
/* [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);
2023-08-10 09:11:43 +03:00
res = n2s(a == b ? a : (a + xorshift64s() % (b - a)), res, reslen);
2023-08-09 21:41:34 +03:00
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 */
2023-08-10 09:11:43 +03:00
nnt_rngs = time(NULL); xorshift64s(); /* init the PRNG */
2023-08-09 21:41:34 +03:00
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<argc;i++) {
nlen += strlen(argv[i]) + 2;
buf = realloc(buf, nlen);
strcat(buf, argv[i]); /* append the parameter */
strcat(buf, segsep); /* and the segment separator */
}
nnt_assignform("nnt-argv", buf, nlen, 0);
free(buf);
}
nnt_proc(prog, proglen); /* start main processor */
nnt_finish(); /* finalize processing resources */
fclose(fd);
puts(""); /* just print a newline before exiting */
if(fd != stdin) free(prog); /* free the input buffer */
return 0;
}
#endif