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));
    }
Example #2
0
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);
}
Example #3
0
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);
}
Example #4
0
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';
}
Example #5
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;
    }
Example #7
0
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;
}
Example #8
0
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;
}
Example #9
0
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));
        }
    }
}
Example #11
0
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);
}
Example #12
0
/// @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;
}
Example #13
0
dd_bool Str_IsEmpty(const ddstring_t *str)
{
    DENG_ASSERT(str);
    return Str_Length(str) == 0;
}
Example #14
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);
}
Example #15
0
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;
}
Example #16
0
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;
}