void Glulxe::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, uint subaddress, int subpassin) { const char *cx; int ix, argx; int gargnum, numwanted; void *opref; gluniversal_t *garglist; uint *varglist; garglist = splot->garglist; varglist = splot->varglist; gargnum = *argnumptr; cx = *proto; numwanted = 0; while (*cx >= '0' && *cx <= '9') { numwanted = 10 * numwanted + (*cx - '0'); cx++; } for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { char typeclass; int skipval; int isref, passin, passout, nullok, isarray, isretained, isreturn; cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, &isretained, &isreturn); typeclass = *cx; cx++; skipval = false; if (isref) { if (!isreturn && varglist[ix] == 0) { if (!nullok) error("Zero passed invalidly to Glk function."); garglist[gargnum]._ptrflag = false; gargnum++; skipval = true; } else { garglist[gargnum]._ptrflag = true; gargnum++; } } if (!skipval) { uint thisval; if (typeclass == '[') { parse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passin); } else if (isarray) { /* definitely isref */ switch (typeclass) { case 'C': /* This test checks for a giant array length, which is deprecated. It displays a warning and cuts it down to something reasonable. Future releases of this interpreter may remove this test and go on to verify_array_addresses(), which treats this case as a fatal error. */ if (varglist[ix + 1] > endmem || varglist[ix] + varglist[ix + 1] > endmem) { nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]); varglist[ix + 1] = endmem - varglist[ix]; } verify_array_addresses(varglist[ix], varglist[ix + 1], 1); garglist[gargnum]._array = CaptureCArray(varglist[ix], varglist[ix + 1], passin); gargnum++; ix++; garglist[gargnum]._uint = varglist[ix]; gargnum++; cx++; break; case 'I': /* See comment above. */ if (varglist[ix + 1] > endmem / 4 || varglist[ix + 1] > (endmem - varglist[ix]) / 4) { nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]); varglist[ix + 1] = (endmem - varglist[ix]) / 4; } verify_array_addresses(varglist[ix], varglist[ix + 1], 4); garglist[gargnum]._array = CaptureIArray(varglist[ix], varglist[ix + 1], passin); gargnum++; ix++; garglist[gargnum]._uint = varglist[ix]; gargnum++; cx++; break; case 'Q': /* This case was added after the giant arrays were deprecated, so we don't bother to allow for that case. We just verify the length. */ verify_array_addresses(varglist[ix], varglist[ix + 1], 4); garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix + 1], (*cx - 'a'), passin); gargnum++; ix++; garglist[gargnum]._uint = varglist[ix]; gargnum++; cx++; break; default: error("Illegal format string."); break; } } else { /* a plain value or a reference to one. */ if (isreturn) { thisval = 0; } else if (depth > 0) { /* Definitely not isref or isarray. */ if (subpassin) thisval = ReadStructField(subaddress, ix); else thisval = 0; } else if (isref) { if (passin) thisval = ReadMemory(varglist[ix]); else thisval = 0; } else { thisval = varglist[ix]; } switch (typeclass) { case 'I': if (*cx == 'u') garglist[gargnum]._uint = (uint)(thisval); else if (*cx == 's') garglist[gargnum]._sint = (int)(thisval); else error("Illegal format string."); gargnum++; cx++; break; case 'Q': if (thisval) { opref = classes_get(*cx - 'a', thisval); if (!opref) { error("Reference to nonexistent Glk object."); } } else { opref = nullptr; } garglist[gargnum]._opaqueref = opref; gargnum++; cx++; break; case 'C': if (*cx == 'u') garglist[gargnum]._uch = (unsigned char)(thisval); else if (*cx == 's') garglist[gargnum]._sch = (signed char)(thisval); else if (*cx == 'n') garglist[gargnum]._ch = (char)(thisval); else error("Illegal format string."); gargnum++; cx++; break; case 'S': garglist[gargnum]._charstr = DecodeVMString(thisval); gargnum++; break; #ifdef GLK_MODULE_UNICODE case 'U': garglist[gargnum]._unicharstr = DecodeVMUstring(thisval); gargnum++; break; #endif /* GLK_MODULE_UNICODE */ default: error("Illegal format string."); break; } } } else { /* We got a null reference, so we have to skip the format element. */ if (typeclass == '[') { int numsubwanted, refdepth; numsubwanted = 0; while (*cx >= '0' && *cx <= '9') { numsubwanted = 10 * numsubwanted + (*cx - '0'); cx++; } refdepth = 1; while (refdepth > 0) { if (*cx == '[') refdepth++; else if (*cx == ']') refdepth--; cx++; } } else if (typeclass == 'S' || typeclass == 'U') { /* leave it */ } else { cx++; if (isarray) ix++; } } } if (depth > 0) { if (*cx != ']') error("Illegal format string."); cx++; } else { if (*cx != ':' && *cx != '\0') error("Illegal format string."); } *proto = cx; *argnumptr = gargnum; }
/* parse_glk_args(): This long and unpleasant function translates a set of Floo objects into a gluniversal_t array. It's recursive, too, to deal with structures. */ static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth, int *argnumptr, glui32 subaddress, int subpassin) { char *cx; int ix, argx; int gargnum, numwanted; void *opref; gluniversal_t *garglist; glui32 *varglist; garglist = splot->garglist; varglist = splot->varglist; gargnum = *argnumptr; cx = *proto; numwanted = 0; while (*cx >= '0' && *cx <= '9') { numwanted = 10 * numwanted + (*cx - '0'); cx++; } for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) { char typeclass; int skipval; int isref, passin, passout, nullok, isarray, isretained, isreturn; cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok, &isretained, &isreturn); typeclass = *cx; cx++; skipval = FALSE; if (isref) { if (!isreturn && varglist[ix] == 0) { if (!nullok) fatalError("Zero passed invalidly to Glk function."); garglist[gargnum].ptrflag = FALSE; gargnum++; skipval = TRUE; } else { garglist[gargnum].ptrflag = TRUE; gargnum++; } } if (!skipval) { glui32 thisval; if (typeclass == '[') { parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin); } else if (isarray) { /* definitely isref */ switch (typeclass) { case 'C': /* This test checks for a giant array length, and cuts it down to something reasonable. Future releases of this interpreter may treat this case as a fatal error. */ if (varglist[ix+1] > gEndMem || varglist[ix]+varglist[ix+1] > gEndMem) varglist[ix+1] = gEndMem - varglist[ix]; garglist[gargnum].array = (void*) CaptureCArray(varglist[ix], varglist[ix+1], passin); gargnum++; ix++; garglist[gargnum].uint = varglist[ix]; gargnum++; cx++; break; case 'I': /* See comment above. */ if (varglist[ix+1] > gEndMem/4 || varglist[ix+1] > (gEndMem-varglist[ix])/4) varglist[ix+1] = (gEndMem - varglist[ix]) / 4; garglist[gargnum].array = CaptureIArray(varglist[ix], varglist[ix+1], passin); gargnum++; ix++; garglist[gargnum].uint = varglist[ix]; gargnum++; cx++; break; case 'Q': garglist[gargnum].array = CapturePtrArray(varglist[ix], varglist[ix+1], (*cx-'a'), passin); gargnum++; ix++; garglist[gargnum].uint = varglist[ix]; gargnum++; cx++; break; default: fatalError("Illegal format string."); break; } } else { /* a plain value or a reference to one. */ if (isreturn) { thisval = 0; } else if (depth > 0) { /* Definitely not isref or isarray. */ if (subpassin) thisval = ReadStructField(subaddress, ix); else thisval = 0; } else if (isref) { if (passin) thisval = ReadMemory(varglist[ix]); else thisval = 0; } else { thisval = varglist[ix]; } switch (typeclass) { case 'I': if (*cx == 'u') garglist[gargnum].uint = (glui32)(thisval); else if (*cx == 's') garglist[gargnum].sint = (glsi32)(thisval); else fatalError("Illegal format string."); gargnum++; cx++; break; case 'Q': if (thisval) { opref = classes_get(*cx-'a', thisval); if (!opref) { fatalError("Reference to nonexistent Glk object."); } } else { opref = NULL; } garglist[gargnum].opaqueref = opref; gargnum++; cx++; break; case 'C': if (*cx == 'u') garglist[gargnum].uch = (unsigned char)(thisval); else if (*cx == 's') garglist[gargnum].sch = (signed char)(thisval); else if (*cx == 'n') garglist[gargnum].ch = (char)(thisval); else fatalError("Illegal format string."); gargnum++; cx++; break; case 'S': garglist[gargnum].charstr = DecodeVMString(thisval); gargnum++; break; #ifdef GLK_MODULE_UNICODE case 'U': garglist[gargnum].unicharstr = DecodeVMUstring(thisval); gargnum++; break; #endif /* GLK_MODULE_UNICODE */ default: fatalError("Illegal format string."); break; } } } else { /* We got a null reference, so we have to skip the format element. */ if (typeclass == '[') { int numsubwanted, refdepth; numsubwanted = 0; while (*cx >= '0' && *cx <= '9') { numsubwanted = 10 * numsubwanted + (*cx - '0'); cx++; } refdepth = 1; while (refdepth > 0) { if (*cx == '[') refdepth++; else if (*cx == ']') refdepth--; cx++; } } else if (typeclass == 'S' || typeclass == 'U') { /* leave it */ } else { cx++; if (isarray) ix++; } } } if (depth > 0) { if (*cx != ']') fatalError("Illegal format string."); cx++; } else { if (*cx != ':' && *cx != '\0') fatalError("Illegal format string."); } *proto = cx; *argnumptr = gargnum; }