/** * Handle a plain string (e.g., 'text'). * * @param[in, out] state */ static void sqltfn_normalize_pg_handle_string(tfn_state_t *state, int delimiter) { // Process input until the end of string is encountered while (state->slen > 0) { if (MATCH_TWO_BYTES('\\', '\\')) { // Backslash-escaped backslash COPY_BYTE; COPY_BYTE; } else if (MATCH_TWO_BYTES('\\', delimiter)) { // Backslash-escaped delimiter COPY_BYTE; COPY_BYTE; } else if (MATCH_BYTE(delimiter)) { // Delimiter COPY_BYTE; // End of string return; } else { // Copy the current byte COPY_BYTE; } } return; }
/** * Handle a Unicode-escaped string (e.g., U&'unicode-text'). * * @param[in, out] state */ static void sqltfn_normalize_pg_handle_string_unicode(tfn_state_t *state, int delimiter) { // Process input until the end of string is encountered while (state->slen > 0) { if (MATCH_BYTE(delimiter)) { // Copy the terminating single quote COPY_BYTE; return; } else { // Copy the current byte COPY_BYTE; } } return; }
/* * Loads all resources from the stream, except images, binaries and sprites. */ bool Syscall::loadResources(Stream& file, const char* aFilename) { bool hasResources = true; if(!file.isOpen()) hasResources = false; else { int len, pos; TEST(file.length(len)); TEST(file.tell(pos)); if(len == pos) hasResources = false; } if(!hasResources/* && aFilename != NULL*/) { resources.init(0); return true; } #define MATCH_BYTE(c) { DAR_UBYTE(b); if(b != c) { FAIL; } } MATCH_BYTE('M'); MATCH_BYTE('A'); MATCH_BYTE('R'); MATCH_BYTE('S'); DAR_UVINT(nResources); DAR_UVINT(rSize); resources.init(nResources); resourcesCount = nResources; resourceOffset = new int[nResources]; resourceSize = new int[nResources]; resourceType = new int[nResources]; resourcesFilename = new char[strlen(aFilename) + 1]; strcpy(resourcesFilename, aFilename); // rI is the resource index. int rI = 1; while(true) { DAR_UBYTE(type); if(type == 0) break; //dispose flag DAR_UVINT(size); LOG_RES("Type %i, size %i\n", type, size); int index = rI - 1; TEST(file.tell(resourceOffset[index])); resourceSize[index] = size; resourceType[index] = type; switch(type) { case RT_UBIN: { int pos; MYASSERT(aFilename, ERR_RES_LOAD_UBIN); TEST(file.tell(pos)); #ifndef _android ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream(aFilename, pos, size))); #else // Android loads ubins by using JNI. loadUBinary(rI, pos, size); ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream( aFilename, pos, size, getJNIEnvironment(), getJNIThis()))); #endif TEST(file.seek(Seek::Current, size)); } break; case RT_PLACEHOLDER: ROOM(resources.dadd_RT_PLACEHOLDER(rI, NULL)); break; case RT_LABEL: { MemStream b(size); TEST(file.readFully(b)); ROOM(resources.dadd_RT_LABEL(rI, new Label((const char*)b.ptr(), rI))); } break; #ifdef LOGGING_ENABLED case 99: //testtype #define DUMP_UVI { DAR_UVINT(u); LOG_RES("u %i\n", u); } #define DUMP_SVI { DAR_SVINT(s); LOG_RES("s %i\n", s); } DUMP_UVI; DUMP_UVI; DUMP_UVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; break; #endif default: TEST(file.seek(Seek::Current, size)); } rI++; } if(rI != nResources + 1) { LOG("rI %i, nR %i\n", rI, nResources); BIG_PHAT_ERROR(ERR_RES_FILE_INCONSISTENT); } LOG_RES("ResLoad complete\n"); return true; }
/* * Loads all resources from the given buffer. */ bool Syscall::loadResourcesFromBuffer(Stream& file, const char* aFilename) { bool hasResources = true; if(!file.isOpen()) hasResources = false; else { int len, pos; TEST(file.length(len)); TEST(file.tell(pos)); if(len == pos) hasResources = false; } if(!hasResources/* && aFilename != NULL*/) { resources.init(0); return true; } #define MATCH_BYTE(c) { DAR_UBYTE(b); if(b != c) { FAIL; } } MATCH_BYTE('M'); MATCH_BYTE('A'); MATCH_BYTE('R'); MATCH_BYTE('S'); DAR_UVINT(nResources); DAR_UVINT(rSize); resources.init(nResources); // rI is the resource index. int rI = 1; while(true) { DAR_UBYTE(type); if(type == 0) break; //dispose flag DAR_UVINT(size); LOG_RES("Type %i, size %i\n", type, size); switch(type) { case RT_BINARY: { #ifndef _android MemStream* ms = new MemStream(size); #else char* b = loadBinary(rI, size); MemStream* ms = new MemStream(b, size); #endif TEST(file.readFully(*ms)); ROOM(resources.dadd_RT_BINARY(rI, ms)); #ifdef _android checkAndStoreAudioResource(rI); #endif } break; case RT_UBIN: { int pos; MYASSERT(aFilename, ERR_RES_LOAD_UBIN); TEST(file.tell(pos)); #ifndef _android ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream(aFilename, pos, size))); #else // Android loads ubins by using JNI. loadUBinary(rI, pos, size); ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream( aFilename, pos, size, getJNIEnvironment(), getJNIThis()))); #endif TEST(file.seek(Seek::Current, size)); } break; case RT_PLACEHOLDER: ROOM(resources.dadd_RT_PLACEHOLDER(rI, NULL)); break; case RT_IMAGE: { MemStream b(size); TEST(file.readFully(b)); #ifndef _android // On all platforms except Android, we load and add // the image data. "dadd" means "delete and add", // and is defined in runtimes\cpp\base\ResourceArray.h RT_IMAGE_Type* image = loadImage(b); if(!image) BIG_PHAT_ERROR(ERR_IMAGE_LOAD_FAILED); ROOM(resources.dadd_RT_IMAGE(rI, image)); #else // On Android images are stored on the Java side. // Here we allocate a dummy array (real image is // in a table in Java) so that the resource handling, // like deleting resources, will work also on Android. // The actual image will be garbage collected on // Android when a resource is replaced in the Java table. ROOM(resources.dadd_RT_IMAGE(rI, new int[1])); int pos; file.tell(pos); loadImage( rI, pos - size, size, Base::gSyscall->getReloadHandle()); #endif } break; case RT_SPRITE: { DAR_USHORT(indexSource); DAR_USHORT(left); DAR_USHORT(top); DAR_USHORT(width); DAR_USHORT(height); DAR_SHORT(cx); DAR_SHORT(cy); #ifndef _android ROOM(resources.dadd_RT_IMAGE(rI, loadSprite(resources.get_RT_IMAGE(indexSource), left, top, width, height, cx, cy))); #endif } break; case RT_LABEL: { MemStream b(size); TEST(file.readFully(b)); ROOM(resources.dadd_RT_LABEL(rI, new Label((const char*)b.ptr(), rI))); } break; #ifdef LOGGING_ENABLED case 99: //testtype #define DUMP_UVI { DAR_UVINT(u); LOG_RES("u %i\n", u); } #define DUMP_SVI { DAR_SVINT(s); LOG_RES("s %i\n", s); } DUMP_UVI; DUMP_UVI; DUMP_UVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; break; #endif default: TEST(file.seek(Seek::Current, size)); } rI++; } if(rI != nResources + 1) { LOG("rI %i, nR %i\n", rI, nResources); BIG_PHAT_ERROR(ERR_RES_FILE_INCONSISTENT); } LOG_RES("ResLoad complete\n"); return true; }
/** * Handle a dollar-escaped string (e.g., $$text$$ or $tag$text$tag$). * * @param[in, out] state * @return -1 in case of memory allocation errorr */ static int sqltfn_normalize_pg_handle_dollar_string(tfn_state_t *state) { char *tag = NULL; size_t tag_len = 0; // copy $ COPY_BYTE; // Have we reached the end of input? if (state->slen == 0) { return 1; } // The first character cannot be a digit if (isdigit(*state->s)) { return 1; } // Is there a tag? if (!MATCH_BYTE('$')) { // Extract tag char *tag_start = state->s; size_t tag_len = 0; while (((state->slen - tag_len) > 0) && (*(state->s + tag_len) != '$')) { // Check that the byte is a valid tag character if (!TAG_CHAR(*(state->s + tag_len))) { return 1; } tag_len++; } // Have we reached the end of input without // finding the second $ character? if (state->slen - tag_len == 0) { return 1; } // Now that we know that we have a valid tag, make // a copy of it, and also copy it into output // Determine the tag length, and copy it // TODO Why copy when the tag is always available in input? tag = malloc(tag_len); if (tag == NULL) return -1; memcpy(tag, tag_start, tag_len); while(tag_len--) { COPY_BYTE; } } // Copy the second $ COPY_BYTE; // Loop until the end of the string while (state->slen > 0) { if (MATCH_BYTE('$')) { // Possible end of string // Match the tag, if any size_t i = 0; while (((state->slen - i) > 0) && (i < tag_len) && (state->s[i + 1] == tag[i])) { i++; } // If we've matched the entire tag, and the next character // is a $, then we've reached the end of the string if ((i == tag_len) && (state->slen - i - 1 > 0) && (*(state->s + i + 1) == '$')) { // Copy the first $ COPY_BYTE; // Copy tag characters while (i--) { COPY_BYTE; } // Copy the second $ COPY_BYTE; if (tag != NULL) { free(tag); } return 1; } } // Copy the current byte COPY_BYTE; } if (tag != NULL) { free(tag); } return 1; }
static int _sqltfn_normalize_pg(const char *input, size_t input_len, char **output, size_t *output_len) { tfn_state_t _state; tfn_state_t *state = &_state; // Parameter sanity check if (input == NULL) return -1; if (output == NULL) return -1; if (*output == NULL) return -1; if (output_len == NULL) return -1; // Setup transformation state state->s = (char *) input; state->d = *output; state->slen = input_len; state->dlen = 0; state->last_byte = -1; int comment_depth = 0; // Loop until there is input data to process while (state->slen > 0) { // Are we in a multi-line comment? if (comment_depth > 0) { // In a multi-line comment; ignoring bytes // until the comments unwrap. // Is it a beginning of a sub-comment? if (MATCH_TWO_BYTES('/', '*')) { // Sub-comment comment_depth++; // Go over /* SKIP_BYTE; SKIP_BYTE; } if (MATCH_TWO_BYTES('*', '/')) { // End of an existing comment comment_depth--; // Go over */ SKIP_BYTE; SKIP_BYTE; // If we have unwrapped the entire comment, determine // if we need to replace it with a space. if (comment_depth == 0) { // If the last byte we sent to output was // not a whitespace, send one whitespace // instead of the entire comment. if (state->last_byte != SP) { WRITE_BYTE(SP); } } } else { // Go over one byte of input data SKIP_BYTE; } } else { // Not in a multi-line comment // Determine the next token if (MATCH_BYTE('\'')) { // 'text' // String COPY_BYTE; sqltfn_normalize_pg_handle_string(state, '\''); } else if (MATCH_BYTE('"')) { // "text" // String COPY_BYTE; sqltfn_normalize_pg_handle_string(state, '"'); } else /* "A dollar sign (cash) followed by digits is used to represent a * positional parameter in the body of a function definition or a * prepared statement. In other contexts the dollar sign can be * part of an identifier or a dollar-quoted string constant." */ if ((MATCH_BYTE('$')) && (!isalpha(state->last_byte))) { // $$text$$ or $tag$text$tag$, but not a$b$ // $ string if (sqltfn_normalize_pg_handle_dollar_string(state) < 0) { free(*output); return -1; } } else if (MATCH_E_STRING) { // E'text' // E string COPY_BYTE; COPY_BYTE; sqltfn_normalize_pg_handle_string(state, '\''); } else if (MATCH_U_STRING) { // U&'text' // U string COPY_BYTE; COPY_BYTE; COPY_BYTE; sqltfn_normalize_pg_handle_string_unicode(state, state->last_byte); } else if (IS_WHITESPACE(*state->s)) { // Handle a whitespace character // Was the previous character also a whitespace? if (state->last_byte != SP) { // The previous character was not a whitespace // Go over the whitespace character SKIP_BYTE; // Convert whitespace to SP WRITE_BYTE(SP); } else { // The previous character was also a whitespace, // so we're going to ignore this one. SKIP_BYTE; } } else if (MATCH_TWO_BYTES('/', '*')) { // Handle the beginning of a multi-line comment comment_depth++; // Go over /* SKIP_BYTE; SKIP_BYTE; } else if (MATCH_TWO_BYTES('-', '-')) { // Handle a dash comment // Go over -- SKIP_BYTE; SKIP_BYTE; // Find end of line or end of input while ((state->slen > 0) && (*state->s != CR) && (*state->s != LF)) { SKIP_BYTE; } // If we stopped because we encountered a newline, go over it if (state->slen > 0) { SKIP_BYTE; // Replace comment with a SP, but only if // the previous character was not a SP if (state->last_byte != SP) { WRITE_BYTE(SP); } } } else { // Handle a non-significant byte // Copy byte COPY_BYTE; } } } *output_len = state->dlen; return 1; }