]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/command/rpn.qc
Merge branch 'master' into Mario/ons_updates
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / command / rpn.qc
1 #include "rpn.qh"
2
3 // ========================================
4 //  RPN command code, written by divVerent
5 //  Last updated: December 28th, 2011
6 // ========================================
7
8 string rpn_pop()
9 {
10         if(rpn_sp > 0) {
11                 --rpn_sp;
12                 return rpn_stack[rpn_sp];
13         } else {
14                 print("rpn: stack underflow\n");
15                 rpn_error = true;
16                 return "";
17         }
18 }
19 void rpn_push(string s)
20 {
21         if(rpn_sp < MAX_RPN_STACK) {
22                 rpn_stack[rpn_sp] = s;
23                 ++rpn_sp;
24         } else {
25                 print("rpn: stack overflow\n");
26                 rpn_error = true;
27         }
28 }
29 string rpn_get()
30 {
31         if(rpn_sp > 0) {
32                 return rpn_stack[rpn_sp - 1];
33         } else {
34                 print("rpn: empty stack\n");
35                 rpn_error = true;
36                 return "";
37         }
38 }
39 void rpn_set(string s)
40 {
41         if(rpn_sp > 0) {
42                 rpn_stack[rpn_sp - 1] = s;
43         } else {
44                 print("rpn: empty stack\n");
45                 rpn_error = true;
46         }
47 }
48
49 float rpn_getf() { return stof(rpn_get()); }
50 float rpn_popf() { return stof(rpn_pop()); }
51 void rpn_pushf(float f) { return rpn_push(sprintf("%.9g", f)); }
52 void rpn_setf(float f) { return rpn_set(sprintf("%.9g", f)); }
53
54 void GenericCommand_rpn(float request, float argc, string command)
55 {
56         switch(request)
57         {
58                 case CMD_REQUEST_COMMAND:
59                 {
60                         float i, j, f, f2, f3, rpnpos;
61                         //vector rgb;
62                         string s, s2, rpncmd;
63
64                         if(!rpn_db)
65                         {
66                                 rpn_db = db_create();
67                                 db_put(rpn_db, "stack.pointer", "0");
68                                 db_put(rpn_db, "stack.pos", "-1");
69                         }
70
71                         if(argc >= 2)
72                         {
73                                 rpn_sp = 0;
74                                 rpn_error = false;
75                                 for(rpnpos = 1; rpnpos < argc; ++rpnpos)
76                                 {
77                                         rpncmd = argv(rpnpos);
78                                         f = strlen(rpncmd);
79                                         if(rpncmd == "") {
80                                         } else if(stof(substring(rpncmd, 0, 1)) > 0) {
81                                                 rpn_push(rpncmd);
82                                         } else if(substring(rpncmd, 0, 1) == "0") {
83                                                 rpn_push(rpncmd);
84                                         } else if(f >= 2 && substring(rpncmd, 0, 1) == "+") {
85                                                 rpn_push(rpncmd);
86                                         } else if(f >= 2 && substring(rpncmd, 0, 1) == "-") {
87                                                 rpn_push(rpncmd);
88                                         } else if(f >= 2 && substring(rpncmd, 0, 1) == "/") {
89                                                 rpn_push(substring(rpncmd, 1, strlen(rpncmd) - 1));
90                                         } else if(rpncmd == "clear") {
91                                                 rpn_sp = 0;
92                                         } else if(rpncmd == "def" || rpncmd == "=") {
93                                                 s = rpn_pop();
94                                                 s2 = rpn_pop();
95
96                                                 if(s2 != "")
97                                                 {
98                                                         #ifdef MENUQC
99                                                         registercvar(s2, "", 0);
100                                                         #else
101                                                         registercvar(s2, "");
102                                                         #endif
103                                                         if(!rpn_error) // don't change cvars if a stack error had happened!
104                                                                 cvar_set(s2, s);
105                                                 }
106                                                 else
107                                                 {
108                                                         print("rpn: empty cvar name for 'def'\n");
109                                                         rpn_error = true;
110                                                 }
111                                         } else if(rpncmd == "defs" || rpncmd == "@") {
112                                                 s = "";
113                                                 i = rpn_popf();
114                                                 j = (i == 0);
115                                                 while(rpn_sp > 1 && (j || i > 0))
116                                                 {
117                                                         s = strcat("/", rpn_pop(), " ", s);
118                                                         --i;
119                                                 }
120                                                 s2 = rpn_pop();
121                                                 if(s2 != "")
122                                                 {
123                                                         #ifdef MENUQC
124                                                         registercvar(s2, "", 0);
125                                                         #else
126                                                         registercvar(s2, "");
127                                                         #endif
128                                                         if(!rpn_error) // don't change cvars if a stack error had happened!
129                                                                 cvar_set(s2, s);
130                                                 }
131                                                 else
132                                                 {
133                                                         print("rpn: empty cvar name for 'defs'\n");
134                                                         rpn_error = true;
135                                                 }
136                                         } else if(rpncmd == "load") {
137                                                 rpn_set(cvar_string(rpn_get()));
138                                         } else if(rpncmd == "exch") {
139                                                 s = rpn_pop();
140                                                 s2 = rpn_get();
141                                                 rpn_set(s);
142                                                 rpn_push(s2);
143                                         } else if(rpncmd == "dup") {
144                                                 rpn_push(rpn_get());
145                                         } else if(rpncmd == "pop") {
146                                                 rpn_pop();
147                                         } else if(rpncmd == "add" || rpncmd == "+") {
148                                                 f = rpn_popf();
149                                                 rpn_setf(rpn_getf() + f);
150                                         } else if(rpncmd == "sub" || rpncmd == "-") {
151                                                 f = rpn_popf();
152                                                 rpn_setf(rpn_getf() - f);
153                                         } else if(rpncmd == "mul" || rpncmd == "*") {
154                                                 f = rpn_popf();
155                                                 rpn_setf(rpn_getf() * f);
156                                         } else if(rpncmd == "div" || rpncmd == "/") {
157                                                 f = rpn_popf();
158                                                 rpn_setf(rpn_getf() / f);
159                                         } else if(rpncmd == "mod" || rpncmd == "%") {
160                                                 f = rpn_popf();
161                                                 f2 = rpn_getf();
162                                                 rpn_setf(f2 - f * floor(f2 / f));
163                                         } else if(rpncmd == "pow" || rpncmd == "**") {
164                                                 f = rpn_popf();
165                                                 rpn_setf(pow(rpn_getf(), f));
166                                         } else if(rpncmd == "bitand" || rpncmd == "&") {
167                                                 f = rpn_popf();
168                                                 rpn_setf(rpn_getf() & f);
169                                         } else if(rpncmd == "bitor" || rpncmd == "|") {
170                                                 f = rpn_popf();
171                                                 rpn_setf(rpn_getf() | f);
172                                         } else if(rpncmd == "bitxor" || rpncmd == "^") {
173                                                 f = rpn_popf();
174                                                 rpn_setf(rpn_getf() ^ f);
175                                         } else if(rpncmd == "and" || rpncmd == "&&") {
176                                                 f = rpn_popf();
177                                                 rpn_setf(rpn_getf() && f);
178                                         } else if(rpncmd == "or" || rpncmd == "||") {
179                                                 f = rpn_popf();
180                                                 rpn_setf(rpn_getf() || f);
181                                         } else if(rpncmd == "xor" || rpncmd == "^^") {
182                                                 f = rpn_popf();
183                                                 rpn_setf(!rpn_getf() != !f);
184                                         } else if(rpncmd == "bitnot") {
185                                                 rpn_setf(~rpn_popf());
186                                         } else if(rpncmd == "not") {
187                                                 rpn_setf(!rpn_popf());
188                                         } else if(rpncmd == "abs") {
189                                                 rpn_setf(fabs(rpn_getf()));
190                                         } else if(rpncmd == "sgn") {
191                                                 f = rpn_getf();
192                                                 if(f < 0)
193                                                         rpn_set("-1");
194                                                 else if(f > 0)
195                                                         rpn_set("1");
196                                                 else
197                                                         rpn_set("0");
198                                         } else if(rpncmd == "neg" || rpncmd == "~") {
199                                                 rpn_setf(-rpn_getf());
200                                         } else if(rpncmd == "floor" || rpncmd == "f") {
201                                                 rpn_setf(floor(rpn_getf()));
202                                         } else if(rpncmd == "ceil" || rpncmd == "c") {
203                                                 rpn_setf(ceil(rpn_getf()));
204                                         } else if(rpncmd == "exp") {
205                                                 rpn_setf(exp(rpn_getf()));
206                                         } else if(rpncmd == "log") {
207                                                 rpn_setf(exp(rpn_getf()));
208                                         } else if(rpncmd == "sin") {
209                                                 rpn_setf(sin(rpn_getf()));
210                                         } else if(rpncmd == "cos") {
211                                                 rpn_setf(cos(rpn_getf()));
212                                         } else if(rpncmd == "max") {
213                                                 f = rpn_popf();
214                                                 f2 = rpn_getf();
215                                                 rpn_setf(max(f2, f));
216                                         } else if(rpncmd == "min") {
217                                                 f = rpn_popf();
218                                                 f2 = rpn_getf();
219                                                 rpn_setf(min(f2, f));
220                                         } else if(rpncmd == "bound") {
221                                                 f = rpn_popf();
222                                                 f2 = rpn_popf();
223                                                 f3 = rpn_getf();
224                                                 rpn_setf(bound(f3, f2, f));
225                                         } else if(rpncmd == "when") {
226                                                 f = rpn_popf();
227                                                 s = rpn_pop();
228                                                 s2 = rpn_get();
229                                                 if(f)
230                                                         rpn_set(s2);
231                                                 else
232                                                         rpn_set(s);
233                                         } else if(rpncmd == ">" || rpncmd == "gt") {
234                                                 f = rpn_popf();
235                                                 rpn_setf(rpn_getf() > f);
236                                         } else if(rpncmd == "<" || rpncmd == "lt") {
237                                                 f = rpn_popf();
238                                                 rpn_setf(rpn_getf() < f);
239                                         } else if(rpncmd == "==" || rpncmd == "eq") {
240                                                 f = rpn_popf();
241                                                 rpn_setf(rpn_getf() == f);
242                                         } else if(rpncmd == ">=" || rpncmd == "ge") {
243                                                 f = rpn_popf();
244                                                 rpn_setf(rpn_getf() >= f);
245                                         } else if(rpncmd == "<=" || rpncmd == "le") {
246                                                 f = rpn_popf();
247                                                 rpn_setf(rpn_getf() <= f);
248                                         } else if(rpncmd == "!=" || rpncmd == "ne") {
249                                                 f = rpn_popf();
250                                                 rpn_setf(rpn_getf() != f);
251                                         } else if(rpncmd == "rand") {
252                                                 rpn_setf(ceil(random() * rpn_getf()) - 1);
253                                         } else if(rpncmd == "crc16") {
254                                                 rpn_setf(crc16(false, rpn_get()));
255                                         } else if(rpncmd == "put") {
256                                                 s2 = rpn_pop();
257                                                 if (!rpn_error)
258                                                 {
259                                                         s = rpn_pop();
260                                                         if (!rpn_error)
261                                                                 db_put(rpn_db, s, s2);
262                                                 }
263                                         } else if(rpncmd == "get") {
264                                                 s = rpn_pop();
265                                                 if (!rpn_error)
266                                                         rpn_push(db_get(rpn_db, s));
267                                         } else if(rpncmd == "dbpush") {
268                                                 s = rpn_pop();
269                                                 if(!rpn_error)
270                                                 {
271                                                         i = stof(db_get(rpn_db, "stack.pointer"));
272                                                         db_put(rpn_db, "stack.pointer", ftos(i+1));
273                                                         db_put(rpn_db, strcat("stack.", ftos(i)), s);
274                                                         if(!i)
275                                                                 db_put(rpn_db, "stack.pos", "0");
276                                                 }
277                                         } else if(rpncmd == "dbpop") {
278                                                 i = stof(db_get(rpn_db, "stack.pointer"));
279                                                 if(i)
280                                                 {
281                                                         s = ftos(i-1);
282                                                         db_put(rpn_db, "stack.pointer", s);
283                                                         rpn_push(db_get(rpn_db, strcat("stack.", s)));
284                                                         j = stof(db_get(rpn_db, "stack.pos"));
285                                                         if(j >= i)
286                                                                 db_put(rpn_db, "stack.pos", ftos(i-2));
287                                                 } else {
288                                                         rpn_error = 1;
289                                                         print("rpn: database underflow\n");
290                                                 }
291                                         } else if(rpncmd == "dbget") {
292
293                                                 i = stof(db_get(rpn_db, "stack.pointer"));
294                                                 if(i)
295                                                 {
296                                                         rpn_push(db_get(rpn_db, strcat("stack.", ftos(i-1))));
297                                                 } else {
298                                                         rpn_error = 1;
299                                                         print("rpn: database empty\n");
300                                                 }
301                                         } else if(rpncmd == "dblen") {
302                                                 rpn_push(db_get(rpn_db, "stack.pointer"));
303                                         } else if(rpncmd == "dbclr") {
304                                                 db_close(rpn_db);
305                                                 rpn_db = db_create();
306                                                 db_put(rpn_db, "stack.pointer", "0");
307                                                 db_put(rpn_db, "stack.pos", "-1");
308                                         } else if(rpncmd == "dbsave") {
309                                                 s = rpn_pop();
310                                                 if(!rpn_error)
311                                                         db_save(rpn_db, s);
312                                         } else if(rpncmd == "dbload") {
313                                                 s = rpn_pop();
314                                                 if(!rpn_error)
315                                                 {
316                                                         db_close(rpn_db);
317                                                         rpn_db = db_load(s);
318                                                 }
319                                         } else if(rpncmd == "dbins") {
320                                                 s = rpn_pop();
321                                                 if(!rpn_error)
322                                                         //if(rpn_sp > 0)
323                                                 {
324                                                         j = stof(db_get(rpn_db, "stack.pointer"));
325                                                         i = stof(db_get(rpn_db, "stack.pos"));
326
327                                                         if(i < 0)
328                                                         {
329                                                                 i = 0;
330                                                                 db_put(rpn_db, "stack.pos", "0");
331                                                         }
332
333                                                         db_put(rpn_db, "stack.pointer", ftos(j+1));
334                                                         for(--j; j >= i; --j)
335                                                         {
336                                                                 db_put(rpn_db, strcat("stack.", ftos(j+1)),
337                                                                                          db_get(rpn_db, (strcat("stack.", ftos(j))))
338                                                                         );
339                                                         }
340                                                         db_put(rpn_db, strcat("stack.", ftos(i)), s);
341                                                 }
342                                         } else if(rpncmd == "dbext") {
343                                                 j = stof(db_get(rpn_db, "stack.pointer"));
344                                                 i = stof(db_get(rpn_db, "stack.pos"));
345                                                 if(!j)
346                                                 {
347                                                         rpn_error = true;
348                                                         print("rpn: empty database\n");
349                                                 } else {
350                                                         --j;
351                                                         rpn_push(db_get(rpn_db, strcat("stack.", ftos(i))));
352                                                         db_put(rpn_db, "stack.pointer", ftos(j));
353                                                         if(i == j)
354                                                         {
355                                                                 db_put(rpn_db, "stack.pos", ftos(j-1));
356                                                         } else {
357                                                                 while(i < j)
358                                                                 {
359                                                                         db_put(rpn_db, strcat("stack.", ftos(i)),
360                                                                                                  db_get(rpn_db, (strcat("stack.", ftos(i+1))))
361                                                                                 );
362                                                                         ++i;
363                                                                 }
364                                                         }
365                                                 }
366                                         } else if(rpncmd == "dbread") {
367                                                 s = db_get(rpn_db, "stack.pos");
368                                                 if(stof(s) >= 0)
369                                                 {
370                                                         rpn_push(db_get(rpn_db, strcat("stack.", s)));
371                                                 } else {
372                                                         rpn_error = 1;
373                                                         print("rpn: empty database\n");
374                                                 }
375                                         } else if(rpncmd == "dbat") {
376                                                 rpn_push(db_get(rpn_db, "stack.pos"));
377                                         } else if(rpncmd == "dbmov") {
378                                                 j = stof(db_get(rpn_db, "stack.pointer"));
379                                                 i = stof(db_get(rpn_db, "stack.pos"));
380                                                 i += rpn_popf();
381                                                 if(!rpn_error)
382                                                 {
383                                                         if(i < 0 || i >= j)
384                                                         {
385                                                                 print("rpn: database cursor out of bounds\n");
386                                                                 rpn_error = true;
387                                                         }
388                                                         if(!rpn_error)
389                                                         {
390                                                                 db_put(rpn_db, "stack.pos", ftos(i));
391                                                         }
392                                                 }
393                                         } else if(rpncmd == "dbgoto") {
394                                                 s = rpn_pop();
395                                                 j = stof(db_get(rpn_db, "stack.pointer"));
396                                                 if(!j)
397                                                 {
398                                                         rpn_error = true;
399                                                         print("rpn: empty database, cannot move cursor\n");
400                                                 }
401                                                 if(!rpn_error)
402                                                 {
403                                                         if(s == "end")
404                                                                 i = stof(db_get(rpn_db, "stack.pointer"))-1;
405                                                         else if(s == "beg")
406                                                                 i = 0;
407                                                         else
408                                                                 i = stof(s);
409
410                                                         j = stof(db_get(rpn_db, "stack.pointer"));
411                                                         if(i < 0 || i >= j)
412                                                         {
413                                                                 print("rpn: database cursor destination out of bounds\n");
414                                                                 rpn_error = true;
415                                                         }
416                                                         if(!rpn_error)
417                                                         {
418                                                                 db_put(rpn_db, "stack.pos", ftos(i));
419                                                         }
420                                                 }
421                                         } else if(rpncmd == "union") {
422                                                 // s s2 union
423                                                 s2 = rpn_pop();
424                                                 s = rpn_get();
425                                                 f = tokenize_console(s);
426                                                 f2 = tokenize_console(strcat(s, " ", s2));
427                                                 // tokens 0..(f-1) represent s
428                                                 // tokens f..f2 represent s2
429                                                 // UNION: add all tokens to s that are in s2 but not in s
430                                                 s = "";
431                                                 for(i = 0; i < f; ++i)
432                                                         s = strcat(s, " ", argv(i));
433                                                 for(i = f; i < f2; ++i) {
434                                                         for(j = 0; j < f; ++j)
435                                                                 if(argv(i) == argv(j))
436                                                                         goto skip_union;
437                                                         s = strcat(s, " ", argv(i));
438                                                         :skip_union
439                                                 }
440                                                 if(substring(s, 0, 1) == " ")
441                                                         s = substring(s, 1, 99999);
442                                                 rpn_set(s);
443                                                 tokenize_console(command);
444                                         } else if(rpncmd == "intersection") {
445                                                 // s s2 intersection
446                                                 s2 = rpn_pop();
447                                                 s = rpn_get();
448                                                 f = tokenize_console(s);
449                                                 f2 = tokenize_console(strcat(s, " ", s2));
450                                                 // tokens 0..(f-1) represent s
451                                                 // tokens f..f2 represent s2
452                                                 // INTERSECTION: keep only the tokens from s that are also in s2
453                                                 s = "";
454                                                 for(i = 0; i < f; ++i) {
455                                                         for(j = f; j < f2; ++j)
456                                                                 if(argv(i) == argv(j))
457                                                                 {
458                                                                         s = strcat(s, " ", argv(i));
459                                                                         break;
460                                                                 }
461                                                 }
462                                                 if(substring(s, 0, 1) == " ")
463                                                         s = substring(s, 1, 99999);
464                                                 rpn_set(s);
465                                                 tokenize_console(command);
466                                         } else if(rpncmd == "difference") {
467                                                 // s s2 difference
468                                                 s2 = rpn_pop();
469                                                 s = rpn_get();
470                                                 f = tokenize_console(s);
471                                                 f2 = tokenize_console(strcat(s, " ", s2));
472                                                 // tokens 0..(f-1) represent s
473                                                 // tokens f..f2 represent s2
474                                                 // DIFFERENCE: keep only the tokens from s that are not in s2
475                                                 s = "";
476                                                 for(i = 0; i < f; ++i) {
477                                                         for(j = f; j < f2; ++j)
478                                                                 if(argv(i) == argv(j))
479                                                                         goto skip_difference;
480                                                         s = strcat(s, " ", argv(i));
481                                                         :skip_difference
482                                                 }
483                                                 if(substring(s, 0, 1) == " ")
484                                                         s = substring(s, 1, 99999);
485                                                 rpn_set(s);
486                                                 tokenize_console(command);
487                                         } else if(rpncmd == "shuffle") {
488                                                 // s shuffle
489                                                 s = rpn_get();
490                                                 f = tokenize_console(s);
491
492                                                 for(i = 0; i < f - 1; ++i) {
493                                                         // move a random item from i..f-1 to position i
494                                                         s = "";
495                                                         f2 = floor(random() * (f - i) + i);
496                                                         for(j = 0; j < i; ++j)
497                                                                 s = strcat(s, " ", argv(j));
498                                                         s = strcat(s, " ", argv(f2));
499                                                         for(j = i; j < f; ++j)
500                                                                 if(j != f2)
501                                                                         s = strcat(s, " ", argv(j));
502                                                         f = tokenize_console(s);
503                                                 }
504
505                                                 if(substring(s, 0, 1) == " ")
506                                                         s = substring(s, 1, 99999);
507                                                 rpn_set(s);
508                                                 tokenize_console(command);
509                                         } else if(rpncmd == "fexists_assert") {
510                                                 s = rpn_pop();
511                                                 if(!rpn_error)
512                                                 {
513                                                         if (!fexists(s))
514                                                         {
515                                                                 print("rpn: ERROR: ", s, " does not exist!\n");
516                                                                 rpn_error = true;
517                                                         }
518                                                 }
519                                         } else if(rpncmd == "fexists") {
520                                                 s = rpn_get();
521                                                 if(!rpn_error)
522                                                 {
523                                                         if (fexists(s))
524                                                                 rpn_setf(1);
525                                                         else
526                                                                 rpn_setf(0);
527                                                 }
528                                         } else if(rpncmd == "localtime") {
529                                                 rpn_set(strftime(true, rpn_get()));
530                                         } else if(rpncmd == "gmtime") {
531                                                 rpn_set(strftime(false, rpn_get()));
532                                         } else if(rpncmd == "time") {
533                                                 rpn_pushf(time);
534                                         } else if(rpncmd == "digest") {
535                                                 s = rpn_pop();
536                                                 rpn_set(digest_hex(s, rpn_get()));
537                                         } else if(rpncmd == "sprintf1s") {
538                                                 s = rpn_pop();
539                                                 rpn_set(sprintf(s, rpn_get()));
540                                         } else if(rpncmd == "eval") {
541                                                 s = rpn_pop();
542                                                 command = strcat(s, substring(command, argv_end_index(rpnpos), -1));
543                                                 argc = tokenize_console(command);
544                                                 rpnpos = -1;
545                                         } else {
546                                                 rpn_push(cvar_string(rpncmd));
547                                         }
548                                         if(rpn_error)
549                                                 break;
550                                 }
551                                 while(rpn_sp > 0)
552                                 {
553                                         s = rpn_pop();
554                                         print("rpn: still on stack: ", s, "\n");
555                                 }
556                         }
557
558                         return;
559                 }
560
561                 default:
562                 case CMD_REQUEST_USAGE:
563                 {
564                         print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " rpn EXPRESSION...\n"));
565                         print("    Operator description (x: string, s: set, f: float):\n");
566                         print("    x pop ----------------------------->     : removes the top\n");
567                         print("    x dup -----------------------------> x x : duplicates the top\n");
568                         print("    x x exch --------------------------> x x : swap the top two\n");
569                         print("    /cvarname load --------------------> x   : loads a cvar\n");
570                         print("    /cvarname x def ------------------->     : writes to a cvar\n");
571                         print("    f f add|sub|mul|div|mod|pow -------> f   : adds/... two numbers\n");
572                         print("    f f and|or|xor|bitand|bitor|bitxor > f   : logical and bitwise operations\n");
573                         print("    f f eq|ne|gt|ge|lt|le|max|min -----> f   : compares two numbers\n");
574                         print("    f neg|abs|sgn|rand|floor|ceil------> f   : negates/... a number\n");
575                         print("    f not|bitnot ----------------------> f   : logical and bitwise negation\n");
576                         print("    f exp|log|sin|cos -----------------> f   : exponential function & Co.\n");
577                         print("    f f f bound -----------------------> f   : bounds the middle number\n");
578                         print("    f1 f2 b when ----------------------> f   : f1 if b, f2 otherwise\n");
579                         print("    s s union|intersection|difference -> s   : set operations\n");
580                         print("    s shuffle -------------------------> s   : randomly arrange elements\n");
581                         print("    /key /value put ------------------->     : set a database key\n");
582                         print("    /key get --------------------------> s   : get a database value\n");
583                         print("    x dbpush -------------------------->     : pushes the top onto the database\n");
584                         print("    dbpop|dbget -----------------------> x   : removes/reads DB's top\n");
585                         print("    dblen|dbat ------------------------> f   : gets the DB's size/cursor pos\n");
586                         print("    dbclr ----------------------------->     : clear the DB\n");
587                         print("    s dbsave|dbload-------------------->     : save/load the DB to/from a file\n");
588                         print("    x dbins --------------------------->     : moves the top into the DB\n");
589                         print("    dbext|dbread ----------------------> x   : extract/get from the DB's cursor\n");
590                         print("    f dbmov|dbgoto -------------------->     : move or set the DB's cursor\n");
591                         print("    s localtime -----------------------> s   : formats the current local time\n");
592                         print("    s gmtime --------------------------> s   : formats the current UTC time\n");
593                         print("    time ------------------------------> f   : seconds since VM start\n");
594                         print("    s /MD4 digest ---------------------> s   : MD4 digest\n");
595                         print("    s /SHA256 digest ------------------> s   : SHA256 digest\n");
596                         print("    s /formatstring sprintf1s ---------> s   : sprintf with 1 string (pad, cut)\n");
597                         print("    s eval ---------------------------->     : does something eval\n");
598                         print("    Set operations operate on 'such''strings'.\n");
599                         print("    Unknown tokens insert their cvar value.\n");
600                         return;
601                 }
602         }
603 }