]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
-printdefs for qcvm
[xonotic/gmqcc.git] / exec.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdarg.h>
5
6 #include "gmqcc.h"
7
8 MEM_VEC_FUNCTIONS(qc_program,   prog_section_statement, code)
9 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       defs)
10 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       fields)
11 MEM_VEC_FUNCTIONS(qc_program,   prog_section_function,  functions)
12 MEM_VEC_FUNCTIONS(qc_program,   char,                   strings)
13 MEM_VEC_FUN_APPEND(qc_program,  char,                   strings)
14 MEM_VEC_FUN_RESIZE(qc_program,  char,                   strings)
15 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  globals)
16 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  entitydata)
17 MEM_VEC_FUNCTIONS(qc_program,   bool,                   entitypool)
18
19 MEM_VEC_FUNCTIONS(qc_program,   qcint,         localstack)
20 MEM_VEC_FUN_APPEND(qc_program,  qcint,         localstack)
21 MEM_VEC_FUN_RESIZE(qc_program,  qcint,         localstack)
22 MEM_VEC_FUNCTIONS(qc_program,   qc_exec_stack, stack)
23
24 MEM_VEC_FUNCTIONS(qc_program,   size_t, profile)
25 MEM_VEC_FUN_RESIZE(qc_program,  size_t, profile)
26
27 MEM_VEC_FUNCTIONS(qc_program,   prog_builtin, builtins)
28
29 static void loaderror(const char *fmt, ...)
30 {
31     int     err = errno;
32     va_list ap;
33     va_start(ap, fmt);
34     vprintf(fmt, ap);
35     va_end(ap);
36     printf(": %s\n", strerror(err));
37 }
38
39 static void qcvmerror(qc_program *prog, const char *fmt, ...)
40 {
41     va_list ap;
42
43     prog->vmerror++;
44
45     va_start(ap, fmt);
46     vprintf(fmt, ap);
47     va_end(ap);
48     putchar('\n');
49 }
50
51 qc_program* prog_load(const char *filename)
52 {
53     qc_program *prog;
54     prog_header header;
55     size_t      i;
56     FILE *file;
57
58     file = util_fopen(filename, "rb");
59     if (!file)
60         return NULL;
61
62     if (fread(&header, sizeof(header), 1, file) != 1) {
63         loaderror("failed to read header from '%s'", filename);
64         fclose(file);
65         return NULL;
66     }
67
68     if (header.version != 6) {
69         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
70         fclose(file);
71         return NULL;
72     }
73
74     prog = (qc_program*)mem_a(sizeof(qc_program));
75     if (!prog) {
76         fclose(file);
77         printf("failed to allocate program data\n");
78         return NULL;
79     }
80     memset(prog, 0, sizeof(*prog));
81
82     prog->entityfields = header.entfield;
83     prog->crc16 = header.crc16;
84
85     prog->filename = util_strdup(filename);
86     if (!prog->filename) {
87         loaderror("failed to store program name");
88         goto error;
89     }
90
91 #define read_data(hdrvar, progvar, type)                                         \
92     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
93         loaderror("seek failed");                                                \
94         goto error;                                                              \
95     }                                                                            \
96     prog->progvar##_alloc = header.hdrvar.length;                                \
97     prog->progvar##_count = header.hdrvar.length;                                \
98     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
99     if (!prog->progvar)                                                          \
100         goto error;                                                              \
101     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
102         != header.hdrvar.length) {                                               \
103         loaderror("read failed");                                                \
104         goto error;                                                              \
105     }
106 #define read_data1(x, y) read_data(x, x, y)
107
108     read_data (statements, code, prog_section_statement);
109     read_data1(defs,             prog_section_def);
110     read_data1(fields,           prog_section_def);
111     read_data1(functions,        prog_section_function);
112     read_data1(strings,          char);
113     read_data1(globals,          qcint);
114
115     fclose(file);
116
117     /* profile counters */
118     if (!qc_program_profile_resize(prog, prog->code_count))
119         goto error;
120
121     /* Add tempstring area */
122     prog->tempstring_start = prog->strings_count;
123     prog->tempstring_at    = prog->strings_count;
124     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
125         goto error;
126
127     /* spawn the world entity */
128     if (!qc_program_entitypool_add(prog, true)) {
129         loaderror("failed to allocate world entity\n");
130         goto error;
131     }
132     for (i = 0; i < prog->entityfields; ++i) {
133         if (!qc_program_entitydata_add(prog, 0)) {
134             loaderror("failed to allocate world data\n");
135             goto error;
136         }
137     }
138     prog->entities = 1;
139
140     return prog;
141
142 error:
143     if (prog->filename)   mem_d(prog->filename);
144     if (prog->code)       mem_d(prog->code);
145     if (prog->defs)       mem_d(prog->defs);
146     if (prog->fields)     mem_d(prog->fields);
147     if (prog->functions)  mem_d(prog->functions);
148     if (prog->strings)    mem_d(prog->strings);
149     if (prog->globals)    mem_d(prog->globals);
150     if (prog->entitydata) mem_d(prog->entitydata);
151     if (prog->entitypool) mem_d(prog->entitypool);
152     mem_d(prog);
153     return NULL;
154 }
155
156 void prog_delete(qc_program *prog)
157 {
158     if (prog->filename) mem_d(prog->filename);
159     MEM_VECTOR_CLEAR(prog, code);
160     MEM_VECTOR_CLEAR(prog, defs);
161     MEM_VECTOR_CLEAR(prog, fields);
162     MEM_VECTOR_CLEAR(prog, functions);
163     MEM_VECTOR_CLEAR(prog, strings);
164     MEM_VECTOR_CLEAR(prog, globals);
165     MEM_VECTOR_CLEAR(prog, entitydata);
166     MEM_VECTOR_CLEAR(prog, entitypool);
167     MEM_VECTOR_CLEAR(prog, localstack);
168     MEM_VECTOR_CLEAR(prog, stack);
169     MEM_VECTOR_CLEAR(prog, profile);
170
171     if (prog->builtins_alloc) {
172         MEM_VECTOR_CLEAR(prog, builtins);
173     }
174     /* otherwise the builtins were statically allocated */
175     mem_d(prog);
176 }
177
178 /***********************************************************************
179  * VM code
180  */
181
182 char* prog_getstring(qc_program *prog, qcint str)
183 {
184     if (str < 0 || str >= prog->strings_count)
185         return "<<<invalid string>>>";
186     return prog->strings + str;
187 }
188
189 prog_section_def* prog_entfield(qc_program *prog, qcint off)
190 {
191     size_t i;
192     for (i = 0; i < prog->fields_count; ++i) {
193         if (prog->fields[i].offset == off)
194             return (prog->fields + i);
195     }
196     return NULL;
197 }
198
199 prog_section_def* prog_getdef(qc_program *prog, qcint off)
200 {
201     size_t i;
202     for (i = 0; i < prog->defs_count; ++i) {
203         if (prog->defs[i].offset == off)
204             return (prog->defs + i);
205     }
206     return NULL;
207 }
208
209 qcany* prog_getedict(qc_program *prog, qcint e)
210 {
211     if (e >= prog->entitypool_count) {
212         prog->vmerror++;
213         printf("Accessing out of bounds edict %i\n", (int)e);
214         e = 0;
215     }
216     return (qcany*)(prog->entitydata + (prog->entityfields * e));
217 }
218
219 qcint prog_spawn_entity(qc_program *prog)
220 {
221     size_t i;
222     qcint  e;
223     for (e = 0; e < (qcint)prog->entitypool_count; ++e) {
224         if (!prog->entitypool[e]) {
225             char *data = (char*)(prog->entitydata + (prog->entityfields * e));
226             memset(data, 0, prog->entityfields * sizeof(qcint));
227             return e;
228         }
229     }
230     if (!qc_program_entitypool_add(prog, true)) {
231         prog->vmerror++;
232         printf("Failed to allocate entity\n");
233         return 0;
234     }
235     prog->entities++;
236     for (i = 0; i < prog->entityfields; ++i) {
237         if (!qc_program_entitydata_add(prog, 0)) {
238             printf("Failed to allocate entity\n");
239             return 0;
240         }
241     }
242     return e;
243 }
244
245 void prog_free_entity(qc_program *prog, qcint e)
246 {
247     if (!e) {
248         prog->vmerror++;
249         printf("Trying to free world entity\n");
250         return;
251     }
252     if (e >= prog->entitypool_count) {
253         prog->vmerror++;
254         printf("Trying to free out of bounds entity\n");
255         return;
256     }
257     if (!prog->entitypool[e]) {
258         prog->vmerror++;
259         printf("Double free on entity\n");
260         return;
261     }
262     prog->entitypool[e] = false;
263 }
264
265 qcint prog_tempstring(qc_program *prog, const char *_str)
266 {
267     /* we don't access it, but the macro-generated functions don't use
268      * const
269      */
270     char *str = (char*)_str;
271
272     size_t len = strlen(str);
273     size_t at = prog->tempstring_at;
274
275     /* when we reach the end we start over */
276     if (at + len >= prog->strings_count)
277         at = prog->tempstring_start;
278
279     /* when it doesn't fit, reallocate */
280     if (at + len >= prog->strings_count)
281     {
282         prog->strings_count = at;
283         if (!qc_program_strings_append(prog, str, len+1)) {
284             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
285             return 0;
286         }
287         return at;
288     }
289
290     /* when it fits, just copy */
291     memcpy(prog->strings + at, str, len+1);
292     prog->tempstring_at += len+1;
293     return at;
294 }
295
296 static int print_escaped_string(const char *str)
297 {
298     int len = 2;
299     putchar('"');
300     while (*str) {
301         switch (*str) {
302             case '\a': len += 2; putchar('\\'); putchar('a'); break;
303             case '\b': len += 2; putchar('\\'); putchar('b'); break;
304             case '\r': len += 2; putchar('\\'); putchar('r'); break;
305             case '\n': len += 2; putchar('\\'); putchar('n'); break;
306             case '\t': len += 2; putchar('\\'); putchar('t'); break;
307             case '\f': len += 2; putchar('\\'); putchar('f'); break;
308             case '\v': len += 2; putchar('\\'); putchar('v'); break;
309             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
310             case '"':  len += 2; putchar('\\'); putchar('"'); break;
311             default:
312                 ++len;
313                 putchar(*str);
314                 break;
315         }
316         ++str;
317     }
318     putchar('"');
319     return len;
320 }
321
322 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
323 {
324     static char spaces[40+1] = "                                        ";
325     prog_section_def *def;
326     qcany    *value;
327     int       len;
328
329     if (!glob) {
330         len = printf("<null>,");
331         goto done;
332     }
333
334     def = prog_getdef(prog, glob);
335     value = (qcany*)(&prog->globals[glob]);
336
337     if (def) {
338         len = printf("[%s] ", prog_getstring(prog, def->name));
339         vtype = def->type;
340     }
341     else
342         len = printf("[@%u] ", glob);
343
344     switch (vtype) {
345         case TYPE_VOID:
346         case TYPE_ENTITY:
347         case TYPE_FIELD:
348         case TYPE_FUNCTION:
349         case TYPE_POINTER:
350             len += printf("%i,", value->_int);
351             break;
352         case TYPE_VECTOR:
353             len += printf("'%g %g %g',", value->vector[0],
354                                          value->vector[1],
355                                          value->vector[2]);
356             break;
357         case TYPE_STRING:
358             len += print_escaped_string(prog_getstring(prog, value->string));
359             len += printf(",");
360             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
361             break;
362         case TYPE_FLOAT:
363         default:
364             len += printf("%g,", value->_float);
365             break;
366     }
367 done:
368     if (len < sizeof(spaces)-1) {
369         spaces[sizeof(spaces)-1-len] = 0;
370         printf(spaces);
371         spaces[sizeof(spaces)-1-len] = ' ';
372     }
373 }
374
375 static void prog_print_statement(qc_program *prog, prog_section_statement *st)
376 {
377     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
378         printf("<illegal instruction %d>\n", st->opcode);
379         return;
380     }
381     printf(" <> %-12s", asm_instr[st->opcode].m);
382     if (st->opcode >= INSTR_IF &&
383         st->opcode <= INSTR_IFNOT)
384     {
385         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
386         printf("%d\n", st->o2.s1);
387     }
388     else if (st->opcode >= INSTR_CALL0 &&
389              st->opcode <= INSTR_CALL8)
390     {
391         printf("\n");
392     }
393     else if (st->opcode == INSTR_GOTO)
394     {
395         printf("%i\n", st->o1.s1);
396     }
397     else
398     {
399         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
400         switch (st->opcode)
401         {
402             case INSTR_MUL_FV:
403                 t[1] = t[2] = TYPE_VECTOR;
404                 break;
405             case INSTR_MUL_VF:
406                 t[0] = t[2] = TYPE_VECTOR;
407                 break;
408             case INSTR_MUL_V:
409                 t[0] = t[1] = TYPE_VECTOR;
410                 break;
411             case INSTR_ADD_V:
412             case INSTR_SUB_V:
413             case INSTR_EQ_V:
414             case INSTR_NE_V:
415                 t[0] = t[1] = t[2] = TYPE_VECTOR;
416                 break;
417             case INSTR_EQ_S:
418             case INSTR_NE_S:
419                 t[0] = t[1] = TYPE_STRING;
420                 break;
421             case INSTR_STORE_F:
422             case INSTR_STOREP_F:
423                 t[2] = -1;
424                 break;
425             case INSTR_STORE_V:
426                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
427                 break;
428             case INSTR_STORE_S:
429                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
430                 break;
431             case INSTR_STORE_ENT:
432                 t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
433                 break;
434             case INSTR_STORE_FLD:
435                 t[0] = t[1] = TYPE_FIELD; t[2] = -1;
436                 break;
437             case INSTR_STORE_FNC:
438                 t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
439                 break;
440             case INSTR_STOREP_V:
441                 t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
442                 break;
443             case INSTR_STOREP_S:
444                 t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
445                 break;
446             case INSTR_STOREP_ENT:
447                 t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
448                 break;
449             case INSTR_STOREP_FLD:
450                 t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
451                 break;
452             case INSTR_STOREP_FNC:
453                 t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
454                 break;
455         }
456         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
457         else           printf("(none),          ");
458         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
459         else           printf("(none),          ");
460         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
461         else           printf("(none)");
462         printf("\n");
463     }
464     fflush(stdout);
465 }
466
467 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
468 {
469     qc_exec_stack st;
470     size_t p, parampos;
471
472     /* back up locals */
473     st.localsp  = prog->localstack_count;
474     st.stmt     = prog->statement;
475     st.function = func;
476
477 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
478     if (prog->stack_count)
479     {
480         prog_section_function *cur;
481         cur = prog->stack[prog->stack_count-1].function;
482         if (cur)
483         {
484             qcint *globals = prog->globals + cur->firstlocal;
485             if (!qc_program_localstack_append(prog, globals, cur->locals))
486             {
487                 printf("out of memory\n");
488                 exit(1);
489             }
490         }
491     }
492 #else
493     {
494         qcint *globals = prog->globals + func->firstlocal;
495         if (!qc_program_localstack_append(prog, globals, func->locals))
496         {
497             printf("out of memory\n");
498             exit(1);
499         }
500     }
501 #endif
502
503     /* copy parameters */
504     parampos = func->firstlocal;
505     for (p = 0; p < func->nargs; ++p)
506     {
507         size_t s;
508         for (s = 0; s < func->argsize[p]; ++s) {
509             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
510             ++parampos;
511         }
512     }
513
514     if (!qc_program_stack_add(prog, st)) {
515         printf("out of memory\n");
516         exit(1);
517     }
518
519     return func->entry;
520 }
521
522 static qcint prog_leavefunction(qc_program *prog)
523 {
524     prog_section_function *prev = NULL;
525     size_t oldsp;
526
527     qc_exec_stack st = prog->stack[prog->stack_count-1];
528
529 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
530     if (prog->stack_count > 1) {
531         prev  = prog->stack[prog->stack_count-2].function;
532         oldsp = prog->stack[prog->stack_count-2].localsp;
533     }
534 #else
535     prev  = prog->stack[prog->stack_count-1].function;
536     oldsp = prog->stack[prog->stack_count-1].localsp;
537 #endif
538     if (prev) {
539         qcint *globals = prog->globals + prev->firstlocal;
540         memcpy(globals, prog->localstack + oldsp, prev->locals);
541         if (!qc_program_localstack_resize(prog, oldsp)) {
542             printf("out of memory\n");
543             exit(1);
544         }
545     }
546
547     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
548         printf("out of memory\n");
549         exit(1);
550     }
551
552     return st.stmt - 1; /* offset the ++st */
553 }
554
555 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
556 {
557     long jumpcount = 0;
558     prog_section_statement *st;
559
560     prog->vmerror = 0;
561
562     st = prog->code + prog_enterfunction(prog, func);
563     --st;
564     switch (flags)
565     {
566         default:
567         case 0:
568         {
569 #define QCVM_PROFILE 0
570 #define QCVM_TRACE   0
571 #           include "execloop.h"
572             break;
573         }
574         case (VMXF_TRACE):
575         {
576 #define QCVM_PROFILE 0
577 #define QCVM_TRACE   1
578 #           include "execloop.h"
579             break;
580         }
581         case (VMXF_PROFILE):
582         {
583 #define QCVM_PROFILE 1
584 #define QCVM_TRACE   0
585 #           include "execloop.h"
586             break;
587         }
588         case (VMXF_TRACE|VMXF_PROFILE):
589         {
590 #define QCVM_PROFILE 1
591 #define QCVM_TRACE   1
592 #           include "execloop.h"
593             break;
594         }
595     };
596
597 cleanup:
598     prog->localstack_count = 0;
599     prog->stack_count = 0;
600     if (prog->vmerror)
601         return false;
602     return true;
603 }
604
605 /***********************************************************************
606  * main for when building the standalone executor
607  */
608
609 #if defined(QCVM_EXECUTOR)
610 const char *type_name[TYPE_COUNT] = {
611     "void",
612     "string",
613     "float",
614     "vector",
615     "entity",
616     "field",
617     "function",
618     "pointer",
619 #if 0
620     "integer",
621 #endif
622     "variant"
623 };
624
625 bool        opts_debug    = false;
626 bool        opts_memchk   = false;
627
628 #define CheckArgs(num) do {                                                    \
629     if (prog->argc != (num)) {                                                 \
630         prog->vmerror++;                                                       \
631         printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
632         __FUNCTION__, prog->argc, (num));                                      \
633         return -1;                                                             \
634     }                                                                          \
635 } while (0)
636
637 #define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
638 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
639 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
640
641 static int qc_print(qc_program *prog)
642 {
643     size_t i;
644     for (i = 0; i < prog->argc; ++i) {
645         qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
646         printf("%s", prog_getstring(prog, str->string));
647     }
648     return 0;
649 }
650
651 static int qc_ftos(qc_program *prog)
652 {
653     char buffer[512];
654     qcany *num;
655     qcany str;
656     CheckArgs(1);
657     num = GetArg(0);
658     snprintf(buffer, sizeof(buffer), "%g", num->_float);
659     str.string = prog_tempstring(prog, buffer);
660     Return(str);
661     return 0;
662 }
663
664 static int qc_spawn(qc_program *prog)
665 {
666     qcany ent;
667     CheckArgs(0);
668     ent.edict = prog_spawn_entity(prog);
669     Return(ent);
670     return (ent.edict ? 0 : -1);
671 }
672
673 static int qc_kill(qc_program *prog)
674 {
675     qcany *ent;
676     CheckArgs(1);
677     ent = GetArg(0);
678     prog_free_entity(prog, ent->edict);
679     return 0;
680 }
681
682 static prog_builtin qc_builtins[] = {
683     NULL,
684     &qc_print,
685     &qc_ftos,
686     &qc_spawn,
687     &qc_kill
688 };
689 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
690
691 static const char *arg0 = NULL;
692
693 void usage()
694 {
695     printf("usage: [-debug] %s file\n", arg0);
696     exit(1);
697 }
698
699 int main(int argc, char **argv)
700 {
701     size_t      i;
702     qcint       fnmain = -1;
703     qc_program *prog;
704     size_t      xflags = VMXF_DEFAULT;
705     bool        opts_printfields = false;
706     bool        opts_printdefs   = false;
707     bool        opts_info  = false;
708
709     arg0 = argv[0];
710
711     if (argc < 2)
712         usage();
713
714     while (argc > 2) {
715         if (!strcmp(argv[1], "-trace")) {
716             --argc;
717             ++argv;
718             xflags |= VMXF_TRACE;
719         }
720         else if (!strcmp(argv[1], "-profile")) {
721             --argc;
722             ++argv;
723             xflags |= VMXF_PROFILE;
724         }
725         else if (!strcmp(argv[1], "-info")) {
726             --argc;
727             ++argv;
728             opts_info = true;
729         }
730         else if (!strcmp(argv[1], "-printdefs")) {
731             --argc;
732             ++argv;
733             opts_printdefs = true;
734         }
735         else if (!strcmp(argv[1], "-printfields")) {
736             --argc;
737             ++argv;
738             opts_printfields = true;
739         }
740         else
741             usage();
742     }
743
744
745     prog = prog_load(argv[1]);
746     if (!prog) {
747         printf("failed to load program '%s'\n", argv[1]);
748         exit(1);
749     }
750
751     prog->builtins       = qc_builtins;
752     prog->builtins_count = qc_builtins_count;
753     prog->builtins_alloc = 0;
754
755     printf("Program's system-checksum = 0x%04x\n", (int)prog->crc16);
756
757     for (i = 1; i < prog->functions_count; ++i) {
758         const char *name = prog_getstring(prog, prog->functions[i].name);
759         /* printf("Found function: %s\n", name); */
760         if (!strcmp(name, "main"))
761             fnmain = (qcint)i;
762     }
763     printf("Entity field space: %i\n", (int)prog->entityfields);
764     if (opts_info) {
765         prog_delete(prog);
766         return 0;
767     }
768     if (opts_printdefs) {
769         for (i = 0; i < prog->defs_count; ++i) {
770             printf("Global: %8s %-16s at %u\n",
771                    type_name[prog->defs[i].type],
772                    prog_getstring(prog, prog->defs[i].name),
773                    (unsigned int)prog->defs[i].offset);
774         }
775     }
776     else if (opts_printfields) {
777         for (i = 0; i < prog->fields_count; ++i) {
778             printf("Field: %8s %-16s at %u\n",
779                    type_name[prog->fields[i].type],
780                    prog_getstring(prog, prog->fields[i].name),
781                    (unsigned int)prog->fields[i].offset);
782         }
783     }
784     else
785     {
786         if (fnmain > 0)
787         {
788             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
789         }
790         else
791             printf("No main function found\n");
792     }
793
794     prog_delete(prog);
795     return 0;
796 }
797 #endif