virtual void invoke(int player, EventSequenceArg* args, int numArgs) { if(!strchr(Str_Text(&commandTemplate), '%')) { DD_Execute(true/*silent*/, Str_Text(&commandTemplate)); return; } // Compose the command from the template, inserting values for named arguments. /// @todo This logic should be extracted and made into an Str method. AutoStr* cmd = AutoStr_NewStd(); // Reserve an estimate of the composed command length. Str_Reserve(cmd, Str_Length(&commandTemplate) + numArgs + 1); int len = Str_Length(&commandTemplate); const char* start = Str_Text(&commandTemplate); const char* ch = start, *substart = start; while(ch + 1 < start + len) { if(ch[0] == '%' && ch[1] && ch[1] != '%') { Str_PartAppend(cmd, substart, 0, ch - substart); if(ch[1] == 'p') { Str_AppendChar(cmd, '0' + player); } else { int arg = ch[1] - '0' - 1; DENG_ASSERT(arg >= 0 && arg < 9); Str_AppendChar(cmd, char(args[arg])); } ch += 2; substart = ch; } else { ch++; } } // Add anything remaining. Str_Append(cmd, substart); DD_Execute(true/*silent*/, Str_Text(cmd)); }
void Str_Write(const ddstring_t *str, Writer *writer) { size_t len = Str_Length(str); DENG_ASSERT(str); Writer_WriteUInt32(writer, len); Writer_Write(writer, Str_Text(str), len); }
void BGMerger_Commit_IMP(BackgroundMerger *self) { BackgroundMergerIVARS *const ivars = BGMerger_IVARS(self); // Safety check. if (!ivars->merge_lock) { THROW(ERR, "Can't call commit() more than once"); } if (!ivars->prepared) { BGMerger_Prepare_Commit(self); } if (ivars->needs_commit) { bool success = false; String *temp_snapfile = ivars->snapfile; // Rename temp snapshot file. size_t ext_len = sizeof(".temp") - 1; size_t snapfile_len = Str_Length(temp_snapfile); if (snapfile_len <= ext_len) { THROW(ERR, "Invalid snapfile name: %o", temp_snapfile); } ivars->snapfile = Str_SubString(temp_snapfile, 0, snapfile_len - ext_len); success = Folder_Hard_Link(ivars->folder, temp_snapfile, ivars->snapfile); Snapshot_Set_Path(ivars->snapshot, ivars->snapfile); if (!success) { String *mess = Str_newf("Can't create hard link from %o to %o", temp_snapfile, ivars->snapfile); DECREF(temp_snapfile); Err_throw_mess(ERR, mess); } if (!Folder_Delete(ivars->folder, temp_snapfile)) { String *mess = Str_newf("Can't delete %o", temp_snapfile); DECREF(temp_snapfile); Err_throw_mess(ERR, mess); } DECREF(temp_snapfile); } // Release the merge lock and remove the merge data file. S_release_merge_lock(self); IxManager_Remove_Merge_Data(ivars->manager); if (ivars->needs_commit) { // Purge obsolete files. FilePurger_Purge(ivars->file_purger); } // Release the write lock. S_release_write_lock(self); }
void Str_Truncate(ddstring_t *str, int position) { DENG_ASSERT(str); if(!str) return; if(position < 0) position = 0; if(!(position < Str_Length(str))) return; str->length = position; str->str[str->length] = '\0'; }
/// @note Derived from Qt's QByteArray q_fromPercentEncoding ddstring_t *Str_PercentDecode(ddstring_t *str) { int i, len, outlen, a, b; char const *inputPtr; char *data; char c; DENG_ASSERT(str); if(!str) return 0; if(Str_IsEmpty(str)) return str; data = str->str; inputPtr = data; i = 0; len = Str_Length(str); outlen = 0; while(i < len) { c = inputPtr[i]; if(c == '%' && i + 2 < len) { a = inputPtr[++i]; b = inputPtr[++i]; if(a >= '0' && a <= '9') a -= '0'; else if(a >= 'a' && a <= 'f') a = a - 'a' + 10; else if(a >= 'A' && a <= 'F') a = a - 'A' + 10; if(b >= '0' && b <= '9') b -= '0'; else if(b >= 'a' && b <= 'f') b = b - 'a' + 10; else if(b >= 'A' && b <= 'F') b = b - 'A' + 10; *data++ = (char)((a << 4) | b); } else { *data++ = c; } ++i; ++outlen; } if(outlen != len) Str_Truncate(str, outlen); return str; }
/// @return @c true= @a the sequence was completed. bool complete(event_t* ev, int player, bool* eat) { DENG_ASSERT(ev && ev->type == EV_KEY && ev->state == EVS_DOWN); const int key = ev->data1; if(Str_At(&sequence, pos ) == '%' && pos+1 < Str_Length(&sequence) && Str_At(&sequence, pos+1) >= '0' && Str_At(&sequence, pos+1) <= '9') { const int arg = Str_At(&sequence, pos+1) - '0' - 1; DENG_ASSERT(arg >= 0 && arg < numArgs); EventSequenceArg& currentArg = args[arg]; currentArg = key; pos += 2; if(eat) *eat = true; } else if(key == Str_At(&sequence, pos)) { pos++; // Not eating partial matches. if(eat) *eat = false; } else { // Rewind the sequence. rewind(); } if(pos < Str_Length(&sequence)) return false; // Sequence completed. handler.invoke(player, args, numArgs); rewind(); return true; }
Str *Str_ReplaceAll(Str *ds, char from, char to) { size_t i; size_t len = Str_Length(ds); if(!ds || !ds->str) return ds; for(i = 0; i < len; ++i) { if(ds->str[i] == from) ds->str[i] = to; } return ds; }
Lock* IxManager_Make_Snapshot_Read_Lock_IMP(IndexManager *self, String *filename) { LockFactory *lock_factory = S_obtain_lock_factory(self); if (!Str_Starts_With_Utf8(filename, "snapshot_", 9) || !Str_Ends_With_Utf8(filename, ".json", 5) ) { THROW(ERR, "Not a snapshot filename: %o", filename); } // Truncate ".json" from end of snapshot file name. size_t lock_name_len = Str_Length(filename) - (sizeof(".json") - 1); String *lock_name = Str_SubString(filename, 0, lock_name_len); Lock *lock = LockFact_Make_Shared_Lock(lock_factory, lock_name, 1000, 100); DECREF(lock_name); return lock; }
Lock* IxManager_Make_Snapshot_Lock_IMP(IndexManager *self, String *filename) { IndexManagerIVARS *const ivars = IxManager_IVARS(self); if (!Str_Starts_With_Utf8(filename, "snapshot_", 9) || !Str_Ends_With_Utf8(filename, ".json", 5) ) { THROW(ERR, "Not a snapshot filename: %o", filename); } // Truncate ".json" from end of snapshot file name. size_t lock_name_len = Str_Length(filename) - (sizeof(".json") - 1); String *lock_name = Str_SubString(filename, 0, lock_name_len); Lock *lock = (Lock*)LFLock_new(ivars->folder, lock_name, ivars->host, 1000, 100, false); DECREF(lock_name); return lock; }
static void readArchivedUri(Uri &uri, int version, reader_s &reader) { if(version >= 4) { // A serialized, percent encoded URI. Uri_Read(reinterpret_cast<uri_s *>(&uri), &reader); } else if(version == 3) { // A percent encoded textual URI. ddstring_t *_uri = Str_NewFromReader(&reader); uri.setUri(Str_Text(_uri), RC_NULL); Str_Delete(_uri); } else if(version == 2) { // An unencoded textual URI. ddstring_t *_uri = Str_NewFromReader(&reader); uri.setUri(QString(QByteArray(Str_Text(_uri), Str_Length(_uri)).toPercentEncoding()), RC_NULL); Str_Delete(_uri); } else // ver 1 { // A short textual path (unencoded). uri.setPath(readArchivedPath(reader)); // Plus a legacy scheme id. int oldSchemeId = Reader_ReadByte(&reader); switch(oldSchemeId) { case 0: uri.setScheme("Textures"); break; case 1: uri.setScheme("Flats"); break; case 2: uri.setScheme("Sprites"); break; case 3: uri.setScheme("System"); break; default: throw Error("readArchiveUri", QString("Unknown old-scheme id #%1, expected [0..3)").arg(oldSchemeId)); } } }
void Indexer_Commit_IMP(Indexer *self) { IndexerIVARS *const ivars = Indexer_IVARS(self); // Safety check. if (!ivars->write_lock) { THROW(ERR, "Can't call commit() more than once"); } if (!ivars->prepared) { Indexer_Prepare_Commit(self); } if (ivars->needs_commit) { bool success; // Rename temp snapshot file. String *temp_snapfile = ivars->snapfile; size_t ext_len = sizeof(".temp") - 1; size_t snapfile_len = Str_Length(temp_snapfile); if (snapfile_len <= ext_len) { THROW(ERR, "Invalid snapfile name: %o", temp_snapfile); } ivars->snapfile = Str_SubString(temp_snapfile, 0, snapfile_len - ext_len); Snapshot_Set_Path(ivars->snapshot, ivars->snapfile); success = Folder_Rename(ivars->folder, temp_snapfile, ivars->snapfile); DECREF(temp_snapfile); if (!success) { RETHROW(INCREF(Err_get_error())); } // Purge obsolete files. FilePurger_Purge(ivars->file_purger); } // Release locks, invalidating the Indexer. S_release_merge_lock(self); S_release_write_lock(self); }
/// @note Derived from Qt's QByteArray q_toPercentEncoding ddstring_t *Str_PercentEncode2(ddstring_t *str, char const *excludeChars, char const *includeChars) { dd_bool didEncode = false; int i, span, begin, len; ddstring_t buf; DENG_ASSERT(str); if(!str) return 0; if(Str_IsEmpty(str)) return str; len = Str_Length(str); begin = span = 0; for(i = 0; i < len; ++i) { char ch = str->str[i]; // Are we encoding this? if(((ch >= 0x61 && ch <= 0x7A) // ALPHA || (ch >= 0x41 && ch <= 0x5A) // ALPHA || (ch >= 0x30 && ch <= 0x39) // DIGIT || ch == 0x2D // - || ch == 0x2E // . || ch == 0x5F // _ || ch == 0x7E // ~ || (excludeChars && strchr(excludeChars, ch))) && !(includeChars && strchr(includeChars, ch))) { // Not an encodeable. Span grows. span++; } else { // Found an encodeable. if(!didEncode) { Str_InitStd(&buf); Str_Reserve(&buf, len*3); // Worst case. didEncode = true; } Str_PartAppend(&buf, str->str, begin, span); Str_Appendf(&buf, "%%%X", (uint)ch); // Start a new span. begin += span + 1; span = 0; } } if(didEncode) { // Copy anything remaining. if(span) { Str_PartAppend(&buf, str->str, begin, span); } Str_Set(str, Str_Text(&buf)); Str_Free(&buf); } return str; }
dd_bool Str_IsEmpty(const ddstring_t *str) { DENG_ASSERT(str); return Str_Length(str) == 0; }
static void test_Length(TestBatchRunner *runner) { String *string = Str_newf("a%s%sb%sc", smiley, smiley, smiley); TEST_INT_EQ(runner, Str_Length(string), 6, "Length"); DECREF(string); }
CompoundFileReader* CFReader_do_open(CompoundFileReader *self, Folder *folder) { CompoundFileReaderIVARS *const ivars = CFReader_IVARS(self); String *cfmeta_file = (String*)SSTR_WRAP_UTF8("cfmeta.json", 11); Hash *metadata = (Hash*)Json_slurp_json((Folder*)folder, cfmeta_file); Err *error = NULL; Folder_init((Folder*)self, Folder_Get_Path(folder)); // Parse metadata file. if (!metadata || !Hash_Is_A(metadata, HASH)) { error = Err_new(Str_newf("Can't read '%o' in '%o'", cfmeta_file, Folder_Get_Path(folder))); } else { Obj *format = Hash_Fetch_Utf8(metadata, "format", 6); ivars->format = format ? (int32_t)Obj_To_I64(format) : 0; ivars->records = (Hash*)INCREF(Hash_Fetch_Utf8(metadata, "files", 5)); if (ivars->format < 1) { error = Err_new(Str_newf("Corrupt %o file: Missing or invalid 'format'", cfmeta_file)); } else if (ivars->format > CFWriter_current_file_format) { error = Err_new(Str_newf("Unsupported compound file format: %i32 " "(current = %i32", ivars->format, CFWriter_current_file_format)); } else if (!ivars->records) { error = Err_new(Str_newf("Corrupt %o file: missing 'files' key", cfmeta_file)); } } DECREF(metadata); if (error) { Err_set_error(error); DECREF(self); return NULL; } // Open an instream which we'll clone over and over. String *cf_file = (String*)SSTR_WRAP_UTF8("cf.dat", 6); ivars->instream = Folder_Open_In(folder, cf_file); if (!ivars->instream) { ERR_ADD_FRAME(Err_get_error()); DECREF(self); return NULL; } // Assign. ivars->real_folder = (Folder*)INCREF(folder); // Strip directory name from filepaths for old format. if (ivars->format == 1) { Vector *files = Hash_Keys(ivars->records); String *folder_name = IxFileNames_local_part(Folder_Get_Path(folder)); size_t folder_name_len = Str_Length(folder_name); for (uint32_t i = 0, max = Vec_Get_Size(files); i < max; i++) { String *orig = (String*)Vec_Fetch(files, i); if (Str_Starts_With(orig, folder_name)) { Obj *record = Hash_Delete(ivars->records, orig); size_t offset = folder_name_len + sizeof(CHY_DIR_SEP) - 1; size_t len = Str_Length(orig) - offset; String *filename = Str_SubString(orig, offset, len); Hash_Store(ivars->records, filename, (Obj*)record); DECREF(filename); } } DECREF(folder_name); DECREF(files); } return self; }
String* Highlighter_Highlight_Excerpt_IMP(Highlighter *self, VArray *spans, String *raw_excerpt, int32_t top) { int32_t hl_start = 0; int32_t hl_end = 0; StringIterator *iter = Str_Top(raw_excerpt); StringIterator *temp = Str_Top(raw_excerpt); CharBuf *buf = CB_new(Str_Get_Size(raw_excerpt) + 32); CharBuf *encode_buf = NULL; int32_t raw_excerpt_end = top + Str_Length(raw_excerpt); for (uint32_t i = 0, max = VA_Get_Size(spans); i < max; i++) { Span *span = (Span*)VA_Fetch(spans, i); int32_t offset = Span_Get_Offset(span); if (offset < top) { continue; } else if (offset >= raw_excerpt_end) { break; } else { int32_t relative_start = offset - top; int32_t relative_end = relative_start + Span_Get_Length(span); if (relative_start <= hl_end) { if (relative_end > hl_end) { hl_end = relative_end; } } else { if (hl_start < hl_end) { // Highlight previous section int32_t highlighted_len = hl_end - hl_start; StrIter_Assign(temp, iter); StrIter_Advance(iter, highlighted_len); String *to_cat = StrIter_substring(temp, iter); String *encoded = S_do_encode(self, to_cat, &encode_buf); String *hl_frag = Highlighter_Highlight(self, encoded); CB_Cat(buf, hl_frag); DECREF(hl_frag); DECREF(encoded); DECREF(to_cat); } int32_t non_highlighted_len = relative_start - hl_end; StrIter_Assign(temp, iter); StrIter_Advance(iter, non_highlighted_len); String *to_cat = StrIter_substring(temp, iter); String *encoded = S_do_encode(self, to_cat, &encode_buf); CB_Cat(buf, (String*)encoded); DECREF(encoded); DECREF(to_cat); hl_start = relative_start; hl_end = relative_end; } } } if (hl_start < hl_end) { // Highlight final section int32_t highlighted_len = hl_end - hl_start; StrIter_Assign(temp, iter); StrIter_Advance(iter, highlighted_len); String *to_cat = StrIter_substring(temp, iter); String *encoded = S_do_encode(self, to_cat, &encode_buf); String *hl_frag = Highlighter_Highlight(self, encoded); CB_Cat(buf, hl_frag); DECREF(hl_frag); DECREF(encoded); DECREF(to_cat); } // Last text, beyond last highlight span. if (StrIter_Has_Next(iter)) { String *to_cat = StrIter_substring(iter, NULL); String *encoded = S_do_encode(self, to_cat, &encode_buf); CB_Cat(buf, encoded); DECREF(encoded); DECREF(to_cat); } String *highlighted = CB_Yield_String(buf); DECREF(encode_buf); DECREF(buf); DECREF(temp); DECREF(iter); return highlighted; }