nntrac.c (43227B)
1 /* 2 * no-nonsense TRAC programming language implementation (T64 standard) 3 * 4 * Build with: cc [-static] -std=c99 -Os -s nntrac.c -o nntrac 5 * 6 * see README.md for details 7 * 8 * Created by Luxferre in 2023, released into public domain 9 */ 10 11 #define POSIX_SOURCE 12 #define POSIX_C_SOURCE 1 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <time.h> 17 18 #define uint unsigned int 19 #define uchar unsigned char 20 #ifndef NNT_SHARP 21 #define NNT_SHARP '#' /* can be redefined to : or other unused character */ 22 #endif 23 #ifndef NNT_SYMNAMELEN 24 #define NNT_SYMNAMELEN 32 /* max symbol name length, including null */ 25 #endif 26 27 #ifndef strrev 28 char *strrev(char *str) { 29 char *p1, *p2; 30 if(!str || !*str) return str; 31 for(p1=str,p2=str+strlen(str)-1;p2>p1;++p1,--p2) { 32 *p1 ^= *p2; *p2 ^= *p1; *p1 ^= *p2; 33 } 34 return str; 35 } 36 #endif 37 38 enum NNT_MARKERS { /* various markers: 248 to 255 never occur in UTF-8 */ 39 NNT_AFST=-8, /* active function start */ 40 NNT_NFST, /* neutral function start */ 41 NNT_EOF, /* end of function */ 42 NNT_ADEL, /* argument delimiter */ 43 NNT_SEGGAP /* segment gap character */ 44 }; 45 enum NNT_MODES {NNT_NORMAL=1, NNT_LEGACY, NNT_SECURE}; /* operation modes */ 46 static const char NNT_ADEL_S[2] = {NNT_ADEL, 0}; 47 static uchar nnt_meta = '\''; /* apostrophe by default */ 48 49 /* combined active + neutral string buffer and evaluation result buffer */ 50 static char *nnt_prog, *nnt_res, nnt_mode = NNT_NORMAL, nnt_mlock = 0; 51 /* scanning pointers and buffer lengths */ 52 static int nnt_ascan, nnt_alen, nnt_flen, nnt_reslen, nnt_trace = 0; 53 54 /* string to number */ 55 long long snum(char *str) { /* strtoll wrapper */ 56 char *endptr; 57 return strtoll(str, &endptr, 0); 58 } 59 60 /* number to string */ 61 char *n2s(long long num, char *s, int *slen) { 62 *slen = snprintf(NULL, 0, "%lld", num) + 1; 63 s = realloc(s, *slen); 64 memset(s, 0, *slen); /* fill it with zeroes*/ 65 snprintf(s, *slen, "%lld", num); 66 s = realloc(s, (*slen) = strlen(s)); 67 return s; 68 } 69 70 /* primitive function definition */ 71 72 typedef struct nnt_primitive_t { 73 char name[NNT_SYMNAMELEN]; /* primitive function name */ 74 /* registered function pointer */ 75 char* (*handler)(char *arglist, char *res, int *reslen); 76 } nnt_primitive; 77 78 static nnt_primitive *nnt_primitives; /* primitives table */ 79 static uint nnt_primitive_len = 0; /* primitives table size */ 80 81 /* find a primitive function index by name, -1 if not found */ 82 int nnt_findprimitive(const char *name) { 83 int i; 84 for(i=0;i<nnt_primitive_len;i++) 85 if(strncmp(nnt_primitives[i].name, name, NNT_SYMNAMELEN) == 0) 86 return i; /* found */ 87 return -1; /* nothing found */ 88 } 89 90 /* register a primitive function, overwrite if already exists */ 91 void nnt_regprimitive(const char *name, void *fptr) { 92 int pindex = nnt_findprimitive(name); 93 nnt_primitive newprim; /* create a new primitive template */ 94 strncpy(newprim.name, name, NNT_SYMNAMELEN); /* fill the name */ 95 newprim.handler = fptr; /* fill the handler pointer */ 96 if(pindex == -1) { /* create a new table entry */ 97 nnt_primitive_len++; 98 nnt_primitives = realloc(nnt_primitives, 99 nnt_primitive_len * sizeof(nnt_primitive)); 100 nnt_primitives[nnt_primitive_len - 1] = newprim; 101 } 102 else nnt_primitives[pindex] = newprim; /* replace the entry */ 103 } 104 105 /* forms definition */ 106 107 typedef struct nnt_form_t { 108 char name[NNT_SYMNAMELEN]; /* form (var/func) name */ 109 char *value; /* (reallocatable) value pointer (null-terminated) */ 110 uint len; /* form value length */ 111 int fptr; /* form character pointer */ 112 } nnt_form; 113 114 static nnt_form *nnt_forms; /* forms table */ 115 static uint nnt_forms_len = 0; /* forms table size */ 116 117 /* find a form index by name, -1 if not found */ 118 int nnt_findform(char *name) { 119 int i; 120 for(i=0;i<nnt_forms_len;i++) 121 if(strncmp(nnt_forms[i].name, name, NNT_SYMNAMELEN) == 0) 122 return i; /* found */ 123 return -1; /* nothing found */ 124 } 125 126 /* find a free form among previously deleted entries */ 127 int nnt_findfreeform() { 128 int i; 129 for(i=0;i<nnt_forms_len;i++) 130 if(nnt_forms[i].name[0] == NNT_EOF) return i; /* found */ 131 return -1; /* nothing found */ 132 } 133 134 /* assign a value to a form, creating it if doesn't exist */ 135 void nnt_assignform(char *name, char *value, uint len, int ptr) { 136 int findex = nnt_findform(name); 137 if(findex < 0) findex = nnt_findfreeform(); /* search among deleted */ 138 if(findex > -1) { /* existing form: just update the entry */ 139 strncpy(nnt_forms[findex].name, name, NNT_SYMNAMELEN); 140 nnt_forms[findex].value = realloc(nnt_forms[findex].value, len); 141 memcpy(nnt_forms[findex].value, value, len); 142 nnt_forms[findex].len = len; /* also update the length */ 143 nnt_forms[findex].fptr = ptr; /* reset the pointer */ 144 } else { /* create a new form and update the table */ 145 nnt_form newform; 146 strncpy(newform.name, name, NNT_SYMNAMELEN); /* fill the name */ 147 newform.value = calloc(len, 1); /* allocate the space */ 148 memcpy(newform.value, value, len); /* populate the value */ 149 newform.len = len; /* populate the length */ 150 newform.fptr = ptr; /* init the pointer */ 151 nnt_forms_len++; 152 nnt_forms = realloc(nnt_forms, nnt_forms_len * sizeof(nnt_form)); 153 nnt_forms[nnt_forms_len - 1] = newform; 154 } 155 } 156 157 /* delete a form by its name */ 158 /* doesn't fully delete the structure, just marks it as free to use */ 159 void nnt_delform(char *name) { 160 int findex = nnt_findform(name); 161 if(findex > -1) { 162 nnt_forms[findex].value = realloc(nnt_forms[findex].value, 0); 163 nnt_forms[findex].len = 0; 164 nnt_forms[findex].fptr = 0; 165 memset(nnt_forms[findex].name, 0, NNT_SYMNAMELEN); 166 /* null name is a valid form name in TRAC, so we use a marker char */ 167 nnt_forms[findex].name[0] = NNT_EOF; 168 } 169 } 170 171 /* form serialization/deserialization using netstrings */ 172 173 /* serialize the form and populate the buffer and length */ 174 char* nnt_serializeform(nnt_form *form, char *buf, int *len) { 175 *len = NNT_SYMNAMELEN + form->len + sizeof(int); 176 int lbl = snprintf(NULL, 0, "%d:", *len); /* length buffer length */ 177 char *lenbuf = calloc(lbl + 1, 1); 178 snprintf(lenbuf, lbl+1, "%d:", *len); /* fill in length buffer */ 179 *len += lbl; 180 buf = realloc(buf, *len); 181 memset(buf, 0, *len); /* zero out the buffer */ 182 memcpy(buf, lenbuf, lbl); /* fill the length buffer */ 183 memcpy(buf + lbl, form->name, NNT_SYMNAMELEN); /* fill name */ 184 memcpy(buf + lbl + NNT_SYMNAMELEN, &(form->fptr), sizeof(int)); /* fill fptr */ 185 /* fill value */ 186 memcpy(buf + lbl + NNT_SYMNAMELEN + sizeof(int), form->value, form->len); 187 buf[(*len) - 1] = ','; /* trailing comma */ 188 free(lenbuf); return buf; 189 } 190 191 /* deserialize the form given the netstring buffer 192 (push it into the form storage using nnt_assignform) */ 193 int nnt_deserializeform(char *buf) { 194 char lenbuf[11] = {0}; 195 int i, rdlen, fullen, rdptr, fid; 196 for(i=0;buf[i]!=':' && i<11;i++) lenbuf[i] = buf[i]; 197 rdlen = snum(lenbuf); /* practical read length */ 198 fullen = rdlen + strlen(lenbuf); 199 if(buf[fullen] != ',') return -1; /* invalid netstring detected */ 200 memcpy(&rdptr, &buf[i+1+NNT_SYMNAMELEN], sizeof(int)); /* fetch pointer */ 201 /* name is at i + 1, value is NNT_SYMNAMELEN + sizeof(int) away */ 202 nnt_assignform(&buf[i+1], &buf[i+1+NNT_SYMNAMELEN+sizeof(int)], 203 rdlen-NNT_SYMNAMELEN-sizeof(int)-1, rdptr); 204 /* return the total length of processed data */ 205 return fullen; 206 } 207 208 /* parser logic */ 209 210 /* find the matching character */ 211 int nnt_findmatch(char *str, uint len, int curpos, 212 uchar c1, uchar c2, int dir) { 213 int balance = 0, xpos = curpos; 214 if(xpos >= len) return -1; 215 for(;xpos >=0 && xpos < len ;xpos += dir) { 216 if(str[xpos] == c1) balance++; 217 if(str[xpos] == c2) balance--; 218 if(!balance) break; 219 } 220 return (xpos >= 0 && xpos < len) ? xpos : -1; 221 } 222 223 /* delete len chars from buf at pos, return new buf length */ 224 int nnt_delchars(char *buf, int buflen, int pos, int len) { 225 if(len < 1) len = 1; /* safeguard */ 226 if(pos < 0) pos = 0; /* safeguard */ 227 memmove(&buf[pos], &buf[pos+len], buflen - pos - len); 228 return buflen - len; 229 } 230 231 /* delete a single character under the pos in the active buffer */ 232 int nnt_delchar(int pos) { 233 nnt_flen = nnt_delchars(nnt_prog, nnt_flen, pos, 1); 234 nnt_prog[nnt_flen] = 0; 235 nnt_alen = nnt_flen - nnt_ascan; /* update active length */ 236 return nnt_flen; 237 } 238 239 /* insert len chars into buffer dest of length dlen from buffer src at pos */ 240 /* it reallocs dest, updates dlen and returns the resulting dest pointer */ 241 char* nnt_inschars(char *dest, int *dlen, char *src, int len, int pos) { 242 *dlen += len; /* increase the length */ 243 dest = realloc(dest, *dlen); /* reallocate the buffer */ 244 memmove(&dest[pos+len], &dest[pos], (*dlen)-pos-len); /* free up the space */ 245 memmove(&dest[pos], src, len); /* copy the source */ 246 return dest; 247 } 248 249 /* replace all occurrences of from with to in str (all null-terminated) */ 250 /* returns the new str pointer (str must be dynamically allocated before) */ 251 char *nnt_replace(char *str, char *from, char *to) { 252 int len = strlen(str), fromlen = strlen(from), tolen = strlen(to), i; 253 char *ptr = str; 254 while((ptr = strstr(ptr, from)) != NULL) { /* find the occurrence */ 255 len = nnt_delchars(str, len, (int) (ptr - str), fromlen); /* delete */ 256 str = nnt_inschars(str, &len, to, tolen, (int) (ptr - str)); /* add */ 257 ptr += tolen; /* move ptr */ 258 } 259 str[len] = 0; /* ensure null termination in the result */ 260 return str; 261 } 262 263 /* cl primitive: call string (form) */ 264 char* prim_cl(char *arglist, char *res, int *reslen) { 265 char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, snum = 0; 266 char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */ 267 int fid = -1; 268 /* skip the first one */ 269 name = strtok(NULL, NNT_ADEL_S); /* get the name */ 270 if(name != NULL && (fid = nnt_findform(name)) > -1) { 271 /* populate the result */ 272 *reslen = nnt_forms[fid].len; 273 res = realloc(res, *reslen); 274 memcpy(res, nnt_forms[fid].value, *reslen); 275 do { /* receive the segment ids */ 276 seg = strtok(NULL, NNT_ADEL_S); 277 if(seg != NULL) { 278 snum++; /* we start from 1 to avoid null bytes */ 279 idstr[1] = snum; /* update the segmentid byte */ 280 res = nnt_replace(res, idstr, seg); 281 *reslen = strlen(res) + 1; 282 } 283 } while(seg != NULL); 284 } 285 return res; 286 } 287 288 /* evaluate a TRAC function based on the neutral string 289 storing the results into nnt_res and nnt_reslen */ 290 void nnt_evalfunc(int startptr) { 291 char *arglist, *primname, *arg; 292 int i, l=0, primindex; 293 for(i=startptr;nnt_prog[i]!=NNT_EOF;i++,l++); 294 arglist = calloc(l, 1); 295 for(i=0;i<l;i++) arglist[i] = nnt_prog[startptr + i]; 296 /* now we have NNT_ADEL-delimited argument list to pass */ 297 nnt_reslen = 0; /* null value is by default */ 298 primname = strtok(arglist, NNT_ADEL_S); /* get primitive name */ 299 primindex = nnt_findprimitive(primname); 300 if(primindex > -1) { /* primitive found */ 301 if(nnt_trace) { /* trace mode */ 302 fprintf(stderr, "[nntrace] (%s", primname); 303 while((arg = strtok(NULL, NNT_ADEL_S)) != NULL) 304 fprintf(stderr, ",%s", arg); 305 fprintf(stderr, ")"); 306 } 307 for(i=0;i<l;i++) arglist[i] = nnt_prog[startptr + i]; /* restore */ 308 nnt_res = (*(nnt_primitives[primindex].handler))(arglist, nnt_res, &nnt_reslen); 309 } 310 /* now we have some core-level primitives */ 311 else if(strncmp(primname, "hl", 3) == 0) nnt_reslen = -1; /* halt */ 312 else if(strncmp(primname, "tn", 3) == 0) nnt_trace = 1; /* trace on */ 313 else if(strncmp(primname, "tf", 3) == 0) nnt_trace = 0; /* trace off */ 314 else { /* not found, run the default call logic */ 315 if(nnt_trace) { /* trace mode */ 316 fprintf(stderr, "[nntrace] (cl,%s",primname); 317 while((arg = strtok(NULL, NNT_ADEL_S)) != NULL) 318 fprintf(stderr, ",%s", arg); 319 fprintf(stderr, ")"); 320 } 321 /* expand the arglist */ 322 arglist = realloc(arglist, l + 2); 323 /* insert a dummy char and the argument delimiter at the start */ 324 arglist[0] = '0'; arglist[1] = NNT_ADEL; 325 for(i=0;i<l;i++) arglist[i+2] = nnt_prog[startptr + i]; /* restore */ 326 nnt_res = prim_cl(arglist, nnt_res, &nnt_reslen); 327 } 328 if(nnt_trace) { /* trace mode */ 329 if(nnt_reslen) 330 fprintf(stderr, " => %*.*s\r\n", nnt_reslen, nnt_reslen, nnt_res); 331 else fprintf(stderr, " => null\r\n"); 332 } 333 if(arglist) free(arglist); 334 } 335 336 /* main TRAC processing algorithm */ 337 void nnt_proc(char *prog, uint len) { 338 nnt_res = malloc(nnt_reslen = 0); /* init the result buffer */ 339 nnt_prog = calloc(len+1, 1); /* init the neutral+active string buffer */ 340 /* load the entire program into the active string */ 341 memcpy(nnt_prog, prog, len); 342 uchar cc; /* currently processed character */ 343 int bp, rl, j; /* position buffers for various needs */ 344 nnt_ascan = 0; /* init the pointer and NS length */ 345 nnt_alen = nnt_flen = len; /* init active length with the full length */ 346 while(nnt_alen >= 0) { /* processing loop start */ 347 switch((cc = nnt_prog[nnt_ascan])) { /* rule 1 */ 348 case '(': /* rule 2 */ 349 bp = nnt_findmatch(nnt_prog, nnt_flen, nnt_ascan, '(', ')', 1); 350 if(bp > -1) { /* copy the entire region into the neutral string */ 351 rl = bp - nnt_ascan; /* nested region length */ 352 nnt_delchar(nnt_ascan); /* delete this paren */ 353 nnt_ascan += rl - 1; /* increment active pointer */ 354 nnt_delchar(nnt_ascan); /* delete matching paren */ 355 } 356 break; 357 case '\r': case '\n': case '\t': /* rule 3 */ 358 nnt_delchar(nnt_ascan); 359 break; 360 case ',': /* rule 4 */ 361 nnt_prog[nnt_ascan++] = NNT_ADEL; /* argument delimiter */ 362 break; 363 case NNT_SHARP: /* rules 5 to 7 */ 364 switch(nnt_prog[nnt_ascan+1]) { 365 case '(': /* rule 5 - active function start */ 366 nnt_delchar(nnt_ascan); /* remove # character */ 367 nnt_prog[nnt_ascan++] = NNT_AFST; 368 break; 369 case NNT_SHARP: /* rule 6 */ 370 if(nnt_prog[nnt_ascan+2] == '(') { /* neutral function start */ 371 nnt_delchar(nnt_ascan); /* remove # character */ 372 nnt_delchar(nnt_ascan); /* remove another # character */ 373 nnt_prog[nnt_ascan++] = NNT_NFST; 374 } else nnt_ascan++; /* rule 7 */ 375 break; 376 default: nnt_ascan++; /* rule 7 */ 377 } 378 break; 379 case ')': /* rule 8 */ 380 nnt_prog[nnt_ascan] = NNT_EOF; 381 for(bp = nnt_ascan-1;bp>0;bp--) /* find function start */ 382 if(nnt_prog[bp] == NNT_AFST || nnt_prog[bp] == NNT_NFST) break; 383 /* evaluate the function: the result is stored into nnt_res, 384 its length into nnt_reslen */ 385 nnt_evalfunc(bp + 1); /* skip function start marker */ 386 if(nnt_reslen > 0) { 387 nnt_prog = nnt_inschars(nnt_prog, &nnt_flen, 388 nnt_res, nnt_reslen, nnt_ascan + 1); 389 if(nnt_prog[bp] == NNT_AFST) { /* rule 11 */ 390 nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); 391 nnt_ascan = bp; 392 } else if(nnt_prog[bp] == NNT_NFST) { /* rule 12 */ 393 nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); 394 nnt_ascan = bp + nnt_reslen; 395 } 396 } else if(nnt_reslen == 0) { /* rule 10 */ 397 nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1); 398 nnt_ascan = bp; 399 } else nnt_ascan = nnt_flen + 1; /* negative reslen causes the halt */ 400 break; 401 default: nnt_ascan++; /* rule 9 */ 402 } 403 nnt_alen = nnt_flen - nnt_ascan; /* update the length on every step */ 404 nnt_prog[nnt_flen] = 0; 405 } 406 /* free the buffers */ 407 free(nnt_prog); 408 free(nnt_res); 409 } 410 411 /* all other nntrac primitives */ 412 413 /* ps primitive: print string */ 414 char* prim_ps(char *arglist, char *res, int *reslen) { 415 char *arg = strtok(arglist, NNT_ADEL_S); 416 /* skip the first one (the ps string) */ 417 do { 418 arg = strtok(NULL, NNT_ADEL_S); 419 if(arg != NULL) printf("%s", arg); 420 } while(arg != NULL); 421 return res; 422 } 423 424 /* rc primitive: read char */ 425 char* prim_rc(char *arglist, char *res, int *reslen) { 426 res = realloc(res, 1); 427 res[0] = getchar(); 428 *reslen = 1; 429 return res; 430 } 431 432 /* rs primitive: read string */ 433 char* prim_rs(char *arglist, char *res, int *reslen) { 434 int c, i = 0; 435 res = realloc(res, (*reslen) = 0); 436 while((c = getchar()) > 0 && c != nnt_meta) { 437 (*reslen)++; 438 res = realloc(res, (*reslen) + 1); 439 res[(*reslen) - 1] = c&255; 440 res[*reslen] = 0; /* prefill with null terminator */ 441 } 442 return res = realloc(res, (*reslen) = strlen(res)); 443 } 444 445 /* cm primitive: change meta */ 446 char* prim_cm(char *arglist, char *res, int *reslen) { 447 char *arg = strtok(arglist, NNT_ADEL_S); 448 arg = strtok(NULL, NNT_ADEL_S); 449 if(arg != NULL) nnt_meta = arg[0]; 450 return res; 451 } 452 453 /* ds primitive: define string (form) */ 454 char* prim_ds(char *arglist, char *res, int *reslen) { 455 char *arg = strtok(arglist, NNT_ADEL_S), *name, *value; 456 name = strtok(NULL, NNT_ADEL_S); /* get the name */ 457 if(name != NULL) { 458 value = strtok(NULL, NNT_ADEL_S); /* get the value */ 459 if(value != NULL) nnt_assignform(name, value, strlen(value), 0); 460 } 461 return res; 462 } 463 464 /* ss primitive: segment string (form) */ 465 char* prim_ss(char *arglist, char *res, int *reslen) { 466 char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, sid = 0; 467 char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */ 468 int fid = -1; 469 /* skip the first one (the ps string) */ 470 name = strtok(NULL, NNT_ADEL_S); /* get the name */ 471 if(name != NULL && (fid = nnt_findform(name)) > -1) { 472 do { /* receive the segment ids */ 473 seg = strtok(NULL, NNT_ADEL_S); 474 if(seg != NULL) { 475 sid++; /* we start from 1 to avoid null bytes */ 476 idstr[1] = sid; /* update the segmentid byte */ 477 nnt_forms[fid].value = nnt_replace(nnt_forms[fid].value, seg, idstr); 478 nnt_forms[fid].len = strlen(nnt_forms[fid].value) + 1; 479 } 480 } while(seg != NULL); 481 } 482 nnt_forms[fid].fptr = 0; 483 return res; 484 } 485 486 /* cr primitive: call restore */ 487 char* prim_cr(char *arglist, char *res, int *reslen) { 488 char *arg = strtok(arglist, NNT_ADEL_S), *name; 489 int fid = -1; 490 name = strtok(NULL, NNT_ADEL_S); 491 if(name != NULL && (fid = nnt_findform(name)) > -1) 492 nnt_forms[fid].fptr = 0; 493 return res; 494 } 495 496 /* move form pointer helper */ 497 void nnt_moveptr(nnt_form *form, int offs) { 498 int i = 0; if(offs < 1) offs = 1; 499 for(i=0;i<offs;i++) { 500 form->fptr++; 501 while(form->value[form->fptr] == NNT_SEGGAP && form->fptr < form->len) 502 form->fptr += 2; /* skip the gap and the segment id */ 503 if(form->fptr > form->len) { 504 form->fptr = form->len; 505 break; 506 } 507 } 508 } 509 510 /* partial character fetch helper (returns -1 if unavailable) */ 511 char nnt_fetchformchar(nnt_form *form, char dir) { 512 if(dir >= 0) { /* move forward */ 513 while(form->value[form->fptr] == NNT_SEGGAP) { 514 form->fptr += 2; 515 if(form->fptr > form->len) { form->fptr = form->len; return -1; } 516 } 517 } else { /* move back */ 518 while(form->fptr >= 0 && form->value[form->fptr] == NNT_SEGGAP) { 519 form->fptr--; 520 if(form->fptr > 0 && form->value[form->fptr - 1] == NNT_SEGGAP) 521 form->fptr--; /* skip the segment id byte */ 522 if(form->fptr < 0) { form->fptr = 0; return -1; } 523 }; 524 } 525 if(form->fptr >= 0 && form->fptr < form->len) 526 return form->value[form->fptr]; 527 else return -1; 528 } 529 530 /* cs primitive: call segment */ 531 char* prim_cs(char *arglist, char *res, int *reslen) { 532 char *arg = strtok(arglist, NNT_ADEL_S), *name, *def; 533 int fid = -1; 534 name = strtok(NULL, NNT_ADEL_S); 535 def = strtok(NULL, NNT_ADEL_S); 536 if(name != NULL && (fid = nnt_findform(name)) > -1) { 537 int sl; /* segment length */ 538 for(sl=0;sl<nnt_forms[fid].len;sl++) 539 if(nnt_forms[fid].value[nnt_forms[fid].fptr+sl] == NNT_SEGGAP) break; 540 if(nnt_forms[fid].fptr + sl > nnt_forms[fid].len) 541 sl = nnt_forms[fid].len - nnt_forms[fid].fptr; 542 if(sl > 0) { 543 res = realloc(res, (*reslen) = sl); 544 memcpy(res, &nnt_forms[fid].value[nnt_forms[fid].fptr], sl); 545 nnt_moveptr(&nnt_forms[fid], sl); /* move the pointer */ 546 } else if(def != NULL) { /* set the default value */ 547 res = realloc(res, (*reslen) = strlen(def)); 548 memcpy(res, def, *reslen); 549 } 550 } 551 return res; 552 } 553 554 /* cc primitive: call character */ 555 char* prim_cc(char *arglist, char *res, int *reslen) { 556 char *arg = strtok(arglist, NNT_ADEL_S), *name, *def; 557 int fid = -1; 558 name = strtok(NULL, NNT_ADEL_S); 559 def = strtok(NULL, NNT_ADEL_S); 560 if(name != NULL && (fid = nnt_findform(name)) > -1) { 561 res = realloc(res, (*reslen) = 1); 562 res[0] = nnt_fetchformchar(&nnt_forms[fid], 1); 563 if(res[0] == -1) { /* set the default value */ 564 if(def != NULL) { 565 res = realloc(res, (*reslen) = strlen(def)); 566 memcpy(res, def, *reslen); 567 } else *reslen = 0; 568 } 569 nnt_moveptr(&nnt_forms[fid], 1); /* move the pointer */ 570 } 571 return res; 572 } 573 574 /* cn primitive: call N characters */ 575 char* prim_cn(char *arglist, char *res, int *reslen) { 576 char *arg = strtok(arglist, NNT_ADEL_S), *name, *offs, *def, c; 577 int fid = -1, i, roff, ccount = 0; 578 name = strtok(NULL, NNT_ADEL_S); 579 offs = strtok(NULL, NNT_ADEL_S); 580 def = strtok(NULL, NNT_ADEL_S); 581 if(name != NULL && (fid = nnt_findform(name)) > -1) { 582 roff = snum(offs); /* real offset */ 583 if(roff > 0) { /* positive offset */ 584 res = realloc(res, (*reslen) = roff + 1); 585 memset(res, 0, *reslen); 586 for(i=0;i<roff;i++) { 587 c = nnt_fetchformchar(&nnt_forms[fid], 1); 588 if(c < 0) break; else res[i] = c; 589 nnt_forms[fid].fptr++; ccount++; 590 } 591 if(nnt_forms[fid].fptr > nnt_forms[fid].len) 592 nnt_forms[fid].fptr = nnt_forms[fid].len; 593 *reslen = strlen(res); /* update the length */ 594 } else if(roff < 0) { /* negative offset */ 595 roff = -roff; /* get absolute value */ 596 res = realloc(res, (*reslen) = roff + 1); 597 memset(res, 0, *reslen); 598 nnt_forms[fid].fptr--; /* start counting from the previous char */ 599 for(i=0;i<roff;i++) { 600 c = nnt_fetchformchar(&nnt_forms[fid], -1); 601 if(c < 0) break; else res[i] = c; 602 nnt_forms[fid].fptr--; ccount++; 603 } 604 if(nnt_forms[fid].fptr < 0) nnt_forms[fid].fptr = 0; 605 *reslen = strlen(res); /* update the length */ 606 res = strrev(res); /* reverse the order */ 607 } 608 if(ccount && *reslen) /* finalize the allocation */ 609 res = realloc(res, *reslen); 610 else if(def != NULL) { /* return the default value */ 611 res = realloc(res, (*reslen) = strlen(def)); 612 memcpy(res, def, *reslen); 613 } else *reslen = 0; 614 } 615 return res; 616 } 617 618 /* in primitive: initial */ 619 char* prim_in(char *arglist, char *res, int *reslen) { 620 char *arg = strtok(arglist, NNT_ADEL_S), *name, *subs, *def; 621 name = strtok(NULL, NNT_ADEL_S); 622 subs = strtok(NULL, NNT_ADEL_S); 623 def = strtok(NULL, NNT_ADEL_S); 624 int fid, i, slen; 625 if(name != NULL && subs != NULL && (fid = nnt_findform(name)) > -1) { 626 char *ntv = calloc(nnt_forms[fid].len + 1, 1); /* null-terminated value */ 627 memcpy(ntv, nnt_forms[fid].value, nnt_forms[fid].len); 628 char *found = strstr(ntv, subs); 629 if(found != NULL && nnt_forms[fid].fptr < nnt_forms[fid].len) { 630 slen = ((long) (found - ntv)) - nnt_forms[fid].fptr; 631 if(slen > 0) { 632 res = realloc(res, slen + 1); 633 memset(res, 0, slen + 1); /* null-terminate */ 634 for(i=0;i<slen;i++) { /* copy ignoring the seggaps */ 635 res[i] = nnt_fetchformchar(&nnt_forms[fid], 1); 636 nnt_moveptr(&nnt_forms[fid], 1); 637 } 638 *reslen = strlen(res); /* update the length */ 639 res = realloc(res, *reslen); /* finalize */ 640 nnt_moveptr(&nnt_forms[fid], strlen(subs)); /* move after the fptr */ 641 } else found = NULL; 642 } else found = NULL; 643 if(found == NULL && def != NULL) { /* return the default value */ 644 res = realloc(res, (*reslen) = strlen(def)); 645 memcpy(res, def, *reslen); 646 } 647 free(ntv); 648 } 649 return res; 650 } 651 652 /* dd primitive: delete definition(s) */ 653 char* prim_dd(char *arglist, char *res, int *reslen) { 654 char *arg = strtok(arglist, NNT_ADEL_S); 655 do { 656 arg = strtok(NULL, NNT_ADEL_S); 657 if(arg != NULL) nnt_delform(arg); 658 } while(arg != NULL); 659 return res; 660 } 661 662 /* da primitive: delete all forms */ 663 char* prim_da(char *arglist, char *res, int *reslen) { 664 int i = 0; 665 for(i=0;i<nnt_forms_len;i++) free(nnt_forms[i].value); 666 nnt_forms_len = 0; 667 nnt_forms = realloc(nnt_forms, 0); 668 return res; 669 } 670 671 /* eq primitive: string equality */ 672 char* prim_eq(char *arglist, char *res, int *reslen) { 673 char *arg = strtok(arglist, NNT_ADEL_S), *x1, *x2, *x3, *x4; 674 x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S); 675 x3 = strtok(NULL, NNT_ADEL_S); x4 = strtok(NULL, NNT_ADEL_S); 676 if(x1 && x2 && x3 && x4) { 677 arg = strcmp(x1, x2) ? x4 : x3; 678 *reslen = strlen(arg); 679 res = realloc(res, *reslen); 680 memcpy(res, arg, *reslen); 681 } 682 return res; 683 } 684 685 /* gr primitive: number inequality */ 686 char* prim_gr(char *arglist, char *res, int *reslen) { 687 char *arg = strtok(arglist, NNT_ADEL_S), *d1, *d2, *x1, *x2; 688 d1 = strtok(NULL, NNT_ADEL_S); d2 = strtok(NULL, NNT_ADEL_S); 689 x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S); 690 if(x1 && x2 && d1 && d2) { 691 arg = (snum(d1) > snum(d2)) ? x1 : x2; 692 *reslen = strlen(arg); 693 res = realloc(res, *reslen); 694 memcpy(res, arg, *reslen); 695 } 696 return res; 697 } 698 699 /* theoretically we can safely operate from -2**62 to 2**62 */ 700 #define ARITH_LIMIT ((long long) (((unsigned long long) - 1LL) >> 2)) 701 #define BITWISE_BITS 32 702 #define BITWISE_LIMIT (unsigned long long) ((1LL<<BITWISE_BITS) - 1LL) 703 704 /* reusable arithmetic ops helper */ 705 char* handle_dec_result(long long nres, char *ovr, char *res, int *reslen) { 706 if(nres <= ARITH_LIMIT && nres > -ARITH_LIMIT) { /* valid result */ 707 res = n2s(nres, res, reslen); 708 } else if(ovr) { /* return the overflow/underflow value */ 709 *reslen = strlen(ovr); 710 res = realloc(res, *reslen); /* resize to actual length */ 711 memcpy(res, ovr, *reslen); /* fill the overflow value */ 712 } 713 return res; 714 } 715 716 /* ad primitive: addition (overflow arg is optional) */ 717 char* prim_ad(char *arglist, char *res, int *reslen) { 718 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; 719 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 720 ovr = strtok(NULL, NNT_ADEL_S); 721 if(as && bs) res = handle_dec_result(snum(as) + snum(bs), ovr, res, reslen); 722 return res; 723 } 724 725 /* su primitive: subtraction (overflow arg is optional) */ 726 char* prim_su(char *arglist, char *res, int *reslen) { 727 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; 728 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 729 ovr = strtok(NULL, NNT_ADEL_S); 730 if(as && bs) res = handle_dec_result(snum(as) - snum(bs), ovr, res, reslen); 731 return res; 732 } 733 734 /* ml primitive: multiplication (overflow arg is optional) */ 735 char* prim_ml(char *arglist, char *res, int *reslen) { 736 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; 737 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 738 ovr = strtok(NULL, NNT_ADEL_S); 739 if(as && bs) res = handle_dec_result(snum(as) * snum(bs), ovr, res, reslen); 740 return res; 741 } 742 743 /* dv primitive: division (overflow arg is optional) */ 744 char* prim_dv(char *arglist, char *res, int *reslen) { 745 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr; 746 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 747 ovr = strtok(NULL, NNT_ADEL_S); 748 if(as && bs) { 749 long long b = snum(bs); 750 if(b) res = handle_dec_result(snum(as) / b, ovr, res, reslen); 751 else if(ovr) { /* division by zero, return ovr */ 752 *reslen = strlen(ovr); 753 res = realloc(res, *reslen); /* resize to actual length */ 754 memcpy(res, ovr, *reslen); /* fill the overflow value */ 755 } 756 } 757 return res; 758 } 759 760 /* bu primitive: bitwise union (OR) */ 761 char* prim_bu(char *arglist, char *res, int *reslen) { 762 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; 763 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 764 if(as && bs) res = handle_dec_result((snum(as) | snum(bs)) & BITWISE_LIMIT, 765 NULL, res, reslen); 766 return res; 767 } 768 769 /* bi primitive: bitwise intersect (AND) */ 770 char* prim_bi(char *arglist, char *res, int *reslen) { 771 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; 772 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 773 if(as && bs) res = handle_dec_result((snum(as) & snum(bs)) & BITWISE_LIMIT, 774 NULL, res, reslen); 775 return res; 776 } 777 778 /* [new] bx primitive: bitwise exclusive or (XOR) */ 779 char* prim_bx(char *arglist, char *res, int *reslen) { 780 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 781 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; 782 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 783 if(as && bs) res = handle_dec_result((snum(as) ^ snum(bs)) & BITWISE_LIMIT, 784 NULL, res, reslen); 785 return res; 786 } 787 788 /* bc primitive: bitwise complement (NOT) */ 789 char* prim_bc(char *arglist, char *res, int *reslen) { 790 char *arg = strtok(arglist, NNT_ADEL_S), *as = strtok(NULL, NNT_ADEL_S); 791 if(as) res = handle_dec_result((~snum(as)) & BITWISE_LIMIT, 792 NULL, res, reslen); 793 return res; 794 } 795 796 /* br primitive: bitwise rotation */ 797 char* prim_br(char *arglist, char *res, int *reslen) { 798 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; 799 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 800 if(as && bs) { 801 long long a = snum(as), b = snum(bs), nres; 802 /* we rotate b according to the value of a */ 803 b &= BITWISE_LIMIT; /* bit-clip the value */ 804 if(a > 0) { /* rotate left */ 805 if(a > BITWISE_BITS) a %= BITWISE_BITS; 806 nres = (b << a) | (b >> (BITWISE_BITS - a)); 807 } else if(a < 0) { /* rotate right */ 808 a = -a; 809 if(a > BITWISE_BITS) a %= BITWISE_BITS; 810 nres = (b << (BITWISE_BITS - a)) | (b >> a); 811 } 812 else nres = b; /* no rotation */ 813 res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen); 814 } 815 return res; 816 } 817 818 /* bs primitive: bitwise shift */ 819 char* prim_bs(char *arglist, char *res, int *reslen) { 820 char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs; 821 as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S); 822 if(as && bs) { 823 long long a = snum(as), b = snum(bs), nres; 824 /* we shift b according to the value of a */ 825 if(a > 0) nres = b << a; /* shift left */ 826 else if(a < 0) nres = b >> (-a); /* shift right */ 827 else nres = b; /* no shift */ 828 res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen); 829 } 830 return res; 831 } 832 833 /* ln primitive: list names */ 834 char* prim_ln(char *arglist, char *res, int *reslen) { 835 char *arg = strtok(arglist, NNT_ADEL_S), *pref = strtok(NULL, NNT_ADEL_S); 836 int i; 837 for(i=0;i<nnt_forms_len;i++) { 838 if(nnt_forms[i].name[0] != NNT_EOF) { 839 if(pref) printf("%s", pref); 840 printf("%s", nnt_forms[i].name); 841 } 842 } 843 return res; 844 } 845 846 /* pf primitive: print form */ 847 char* prim_pf(char *arglist, char *res, int *reslen) { 848 char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S); 849 if(fname != NULL) { 850 int i = nnt_findform(fname), j; 851 if(i > -1) { /* form found */ 852 for(j=0;j<=nnt_forms[i].len;j++) { 853 if(j == nnt_forms[i].fptr) printf("<^>"); 854 if(nnt_forms[i].value[j] == NNT_SEGGAP) 855 printf("<%u>", (unsigned int) nnt_forms[i].value[++j]); 856 else printf("%c",nnt_forms[i].value[j]); 857 } 858 } 859 } 860 return res; 861 } 862 863 /* filesystem primitives */ 864 865 /* sb primitive: store block */ 866 char *prim_sb(char *arglist, char *res, int *reslen) { 867 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 868 char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; 869 fname = strtok(NULL, NNT_ADEL_S); 870 if(fname != NULL) { /* output file name */ 871 FILE *hnd = fopen(fname, "wb"); /* open the output file */ 872 if(hnd != NULL) { 873 char *vbuf = malloc(0); /* value buffer */ 874 int findex, vlen = 0; /* value len */ 875 do { 876 fmname = strtok(NULL, NNT_ADEL_S); /* form name */ 877 if(fmname != NULL) { 878 findex = nnt_findform(fmname); 879 if(findex > -1) { 880 vbuf = nnt_serializeform(&nnt_forms[findex], vbuf, &vlen); 881 fwrite(vbuf, vlen, 1, hnd); /* write the value out */ 882 nnt_delform(fmname); /* delete after exporting as per the spec */ 883 } 884 } 885 } while(fmname != NULL); 886 fclose(hnd); /* close the output file */ 887 free(vbuf); /* free value buffer */ 888 } 889 } 890 return res; 891 } 892 893 /* full file read helper */ 894 char* nnt_readfile(FILE *hnd, char *fbuf, int *buflen) { 895 fseek(hnd, 0, SEEK_END); 896 *buflen = ftell(hnd); 897 fseek(hnd, 0, SEEK_SET); 898 fbuf = realloc(fbuf, *buflen); /* allocate the full buffer */ 899 if(fbuf == NULL) return NULL; /* if allocation failed */ 900 fread(fbuf, 1, *buflen, hnd); /* read the entire file */ 901 return fbuf; 902 } 903 904 /* fb primitive: fetch block */ 905 char *prim_fb(char *arglist, char *res, int *reslen) { 906 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 907 char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S); 908 if(fname != NULL) { /* input file name */ 909 FILE *hnd = fopen(fname, "rb"); /* open the input file */ 910 if(hnd != NULL) { 911 char *fbuf = malloc(0); 912 int flen; 913 fbuf = nnt_readfile(hnd, fbuf, &flen); 914 fclose(hnd); 915 int i = 0, dlen; /* running index and length */ 916 while(i < flen) { /* form queue to deserialize */ 917 dlen = nnt_deserializeform(&fbuf[i]); 918 if(dlen < 0) break; 919 i += dlen + 1; 920 } 921 free(fbuf); /* free file buffer */ 922 } 923 } 924 return res; 925 } 926 927 /* [new] sf primitive: store raw file */ 928 char *prim_sf(char *arglist, char *res, int *reslen) { 929 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 930 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 931 char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; 932 fname = strtok(NULL, NNT_ADEL_S); /* file name */ 933 fmname = strtok(NULL, NNT_ADEL_S); /* form name */ 934 if(fname != NULL && fmname != NULL) { 935 int fid = nnt_findform(fmname); 936 if(fid > -1) { 937 FILE *hnd = fopen(fname, "wb"); /* open the output file */ 938 if(hnd != NULL) { /* write the value out */ 939 fwrite(nnt_forms[fid].value, nnt_forms[fid].len, 1, hnd); 940 fclose(hnd); 941 } 942 } 943 } 944 return res; 945 } 946 947 /* [new] ff primitive: fetch raw file */ 948 char *prim_ff(char *arglist, char *res, int *reslen) { 949 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 950 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 951 char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname; 952 fname = strtok(NULL, NNT_ADEL_S); /* file name */ 953 fmname = strtok(NULL, NNT_ADEL_S); /* form name */ 954 if(fname != NULL && fmname != NULL) { 955 FILE *hnd = fopen(fname, "rb"); /* open the input file */ 956 if(hnd != NULL) { 957 char *fbuf = malloc(0); 958 int flen; 959 fbuf = nnt_readfile(hnd, fbuf, &flen); 960 fclose(hnd); 961 nnt_assignform(fmname, fbuf, flen, 0); /* store it into the form */ 962 free(fbuf); /* and free the resources */ 963 } 964 } 965 return res; 966 } 967 968 /* eb primitive: erase block or raw file */ 969 char *prim_eb(char *arglist, char *res, int *reslen) { 970 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 971 char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S); 972 if(fname != NULL) remove(fname); /* delete the input file name */ 973 return res; 974 } 975 976 /* mode primitive: set operation mode */ 977 char *prim_mo(char *arglist, char *res, int *reslen) { 978 if(nnt_mlock == 1) return res; /* mode change is already locked */ 979 char *arg = strtok(arglist, NNT_ADEL_S), *modechar, *mlk; 980 modechar = strtok(NULL, NNT_ADEL_S); 981 if(modechar != NULL) { 982 switch(modechar[0]) { 983 case 'S': nnt_mode = NNT_SECURE; break; 984 case 'L': nnt_mode = NNT_LEGACY; break; 985 case 'E': nnt_mode = NNT_NORMAL; break; 986 } 987 mlk = strtok(NULL, NNT_ADEL_S); 988 if(mlk != NULL && mlk[0] == 'L') nnt_mlock = 1; /* set mode lock */ 989 } 990 return res; 991 } 992 993 /* [new] ac primitive: ASCII code */ 994 char *prim_ac(char *arglist, char *res, int *reslen) { 995 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 996 char *arg = strtok(arglist, NNT_ADEL_S), *cvs = strtok(NULL, NNT_ADEL_S); 997 if(cvs != NULL) res = n2s((uchar) cvs[0], res, reslen); 998 return res; 999 } 1000 1001 /* [new] av primitive: ASCII value */ 1002 char *prim_av(char *arglist, char *res, int *reslen) { 1003 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 1004 char *arg = strtok(arglist, NNT_ADEL_S), *cns = strtok(NULL, NNT_ADEL_S); 1005 long long rcode = 0; 1006 if(cns != NULL) rcode = snum(cns); 1007 res = realloc(res, (*reslen) = 1); 1008 res[0] = rcode&255; 1009 return res; 1010 } 1011 1012 /* [new] fn primitive: format number */ 1013 char *prim_fn(char *arglist, char *res, int *reslen) { 1014 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 1015 char *arg = strtok(arglist, NNT_ADEL_S), *fmt, *cns; 1016 fmt = strtok(NULL, NNT_ADEL_S); 1017 cns = strtok(NULL, NNT_ADEL_S); 1018 long long num = 0; 1019 if(fmt != NULL && cns != NULL) { 1020 num = snum(cns); 1021 *reslen = snprintf(NULL, 0, fmt, num); /* estimate the size */ 1022 res = realloc(res, 1 + (*reslen)); 1023 snprintf(res, 1 + (*reslen), fmt, num); /* actually format the number */ 1024 res = realloc(res, (*reslen) = strlen(res)); /* final reallocation */ 1025 } 1026 return res; 1027 } 1028 1029 #ifndef NNT_NO_EXTSHELL 1030 /* [new] os primitive: run external OS command */ 1031 char *prim_os(char *arglist, char *res, int *reslen) { 1032 if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */ 1033 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 1034 char *arg = strtok(arglist, NNT_ADEL_S), *cmd = strtok(NULL, NNT_ADEL_S); 1035 int rescode; 1036 if(cmd != NULL) { 1037 rescode = system(cmd); /* run the command and get the status code */ 1038 res = n2s(rescode, res, reslen); 1039 } 1040 return res; 1041 } 1042 #endif 1043 1044 /* [new] tm primitive: local/UTC/Epoch time */ 1045 char *prim_tm(char *arglist, char *res, int *reslen) { 1046 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 1047 char *arg = strtok(arglist, NNT_ADEL_S), *fmt = strtok(NULL, NNT_ADEL_S), 1048 *opt = strtok(NULL, NNT_ADEL_S), utc = !!(opt!=NULL && opt[0]=='U'); 1049 if(fmt != NULL) { /* format string exists */ 1050 time_t curtime = time(NULL); 1051 if(fmt[0] == 'E' && fmt[1] == 0) res = n2s(curtime, res, reslen); 1052 else { 1053 struct tm *tminfo = utc ? gmtime(&curtime) : localtime(&curtime); 1054 res = realloc(res, (*reslen) = 512); 1055 memset(res, 0, *reslen); 1056 *reslen = strftime(res, *reslen, fmt, tminfo); /* populate the time */ 1057 res = realloc(res, *reslen); /* shrink the result */ 1058 } 1059 } 1060 return res; 1061 } 1062 1063 static unsigned long long nnt_rngs; /* PRNG state*/ 1064 unsigned long long xorshift64s() { 1065 nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27); 1066 unsigned long long inter = (nnt_rngs * 0x2545F4914F6CDD1DULL) >> 32; 1067 nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27); 1068 return (inter << 32) | ((nnt_rngs * 0x2545F4914F6CDD1DULL) & 0xFFFFFFFF); 1069 } 1070 1071 /* [new] rn primitive: (pseudo)random number */ 1072 char *prim_rn(char *arglist, char *res, int *reslen) { 1073 if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */ 1074 char *arg = strtok(arglist, NNT_ADEL_S), *froms = strtok(NULL, NNT_ADEL_S), 1075 *tos = strtok(NULL, NNT_ADEL_S); 1076 long long a = (froms == NULL) ? 0 : snum(froms), 1077 b = (tos == NULL) ? ARITH_LIMIT : snum(tos); 1078 res = n2s(a == b ? a : (a + xorshift64s() % (b - a)), res, reslen); 1079 return res; 1080 } 1081 1082 /* init the resources and built-in primitives */ 1083 void nnt_init() { 1084 nnt_forms = malloc(0); /* init the forms table */ 1085 nnt_primitives = malloc(0); /* init the primitives table */ 1086 nnt_rngs = time(NULL); xorshift64s(); /* init the PRNG */ 1087 nnt_regprimitive("ps", &prim_ps); 1088 nnt_regprimitive("rc", &prim_rc); 1089 nnt_regprimitive("rs", &prim_rs); 1090 nnt_regprimitive("cm", &prim_cm); 1091 nnt_regprimitive("ds", &prim_ds); 1092 nnt_regprimitive("ss", &prim_ss); 1093 nnt_regprimitive("cl", &prim_cl); 1094 nnt_regprimitive("cr", &prim_cr); 1095 nnt_regprimitive("cc", &prim_cc); 1096 nnt_regprimitive("cs", &prim_cs); 1097 nnt_regprimitive("cn", &prim_cn); 1098 nnt_regprimitive("in", &prim_in); 1099 nnt_regprimitive("dd", &prim_dd); 1100 nnt_regprimitive("da", &prim_da); 1101 nnt_regprimitive("eq", &prim_eq); 1102 nnt_regprimitive("gr", &prim_gr); 1103 nnt_regprimitive("ad", &prim_ad); 1104 nnt_regprimitive("su", &prim_su); 1105 nnt_regprimitive("ml", &prim_ml); 1106 nnt_regprimitive("dv", &prim_dv); 1107 nnt_regprimitive("bu", &prim_bu); 1108 nnt_regprimitive("bi", &prim_bi); 1109 nnt_regprimitive("bx", &prim_bx); 1110 nnt_regprimitive("bc", &prim_bc); 1111 nnt_regprimitive("br", &prim_br); 1112 nnt_regprimitive("bs", &prim_bs); 1113 nnt_regprimitive("ln", &prim_ln); 1114 nnt_regprimitive("pf", &prim_pf); 1115 nnt_regprimitive("sb", &prim_sb); 1116 nnt_regprimitive("fb", &prim_fb); 1117 nnt_regprimitive("eb", &prim_eb); 1118 nnt_regprimitive("mo", &prim_mo); 1119 nnt_regprimitive("ac", &prim_ac); 1120 nnt_regprimitive("av", &prim_av); 1121 nnt_regprimitive("fn", &prim_fn); 1122 nnt_regprimitive("ff", &prim_ff); 1123 nnt_regprimitive("sf", &prim_sf); 1124 #ifndef NNT_NO_EXTSHELL 1125 nnt_regprimitive("os", &prim_os); 1126 #endif 1127 nnt_regprimitive("tm", &prim_tm); 1128 nnt_regprimitive("rn", &prim_rn); 1129 } 1130 1131 /* free the interpreter resources */ 1132 void nnt_finish() { 1133 prim_da(NULL, nnt_res, &nnt_reslen); /* free internal form resources */ 1134 if(nnt_primitives) free(nnt_primitives); 1135 if(nnt_forms) free(nnt_forms); 1136 } 1137 1138 /* non-embed entry point */ 1139 #ifndef NNT_EMBED 1140 int main(int argc, char *argv[]) { 1141 char *fname = "-", *prog, *buf; /* stdin by default */ 1142 int proglen, i, nlen; 1143 if(argc > 1) fname = argv[1]; 1144 FILE* fd = stdin; 1145 if(!(fname[0] == '-' && fname[1] == 0)) { 1146 fd = fopen(fname, "rb"); 1147 if(fd == NULL) {perror("Error");return 1;} 1148 } 1149 if(fd == stdin) { /* interactive session */ 1150 proglen = strlen(prog = "#(ps,#(rs))"); 1151 fprintf(stderr, "%s\n", "nntrac by Luxferre, 2023, public domain"); 1152 } else prog = nnt_readfile(fd, malloc(0), &proglen); 1153 nnt_init(); /* init processing resources */ 1154 if(argc > 1) { /* populate nnt-argc and nnt-argv forms */ 1155 nlen = snprintf(NULL, 0, "%d", argc - 1); 1156 buf = calloc(nlen+1, 1); 1157 snprintf(buf, nlen+1, "%d", argc - 1); 1158 nnt_assignform("nnt-argc", buf, nlen, 0); 1159 buf = realloc(buf, 1); /* reuse the buffer for nnt-argv */ 1160 buf[0] = 0; /* ensure it's an empty string */ 1161 char segsep[3] = {NNT_SEGGAP, 1, 0}; 1162 for(i=1,nlen=0;i<argc;i++) { 1163 nlen += strlen(argv[i]) + 2; 1164 buf = realloc(buf, nlen); 1165 strcat(buf, argv[i]); /* append the parameter */ 1166 strcat(buf, segsep); /* and the segment separator */ 1167 } 1168 nnt_assignform("nnt-argv", buf, nlen, 0); 1169 free(buf); 1170 } 1171 nnt_proc(prog, proglen); /* start main processor */ 1172 nnt_finish(); /* finalize processing resources */ 1173 fclose(fd); 1174 puts(""); /* just print a newline before exiting */ 1175 if(fd != stdin) free(prog); /* free the input buffer */ 1176 return 0; 1177 } 1178 #endif