+ // close file
+ FS_Close(file);
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+/*
+========================
+VM_buf_writefile
+writes stringbuffer to a file, returns 0 or 1
+float buf_writefile(float filehandle, float bufhandle, [, float startpos, float numstrings]) = #468;
+========================
+*/
+
+void VM_buf_writefile(prvm_prog_t *prog)
+{
+ int filenum, strindex, strnum, strlength;
+ prvm_stringbuffer_t *stringbuffer;
+
+ VM_SAFEPARMCOUNTRANGE(2, 4, VM_buf_writefile);
+
+ // get file
+ filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
+ if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
+ {
+ VM_Warning(prog, "VM_buf_writefile: invalid file handle %i used in %s\n", filenum, prog->name);
+ return;
+ }
+ if (prog->openfiles[filenum] == NULL)
+ {
+ VM_Warning(prog, "VM_buf_writefile: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
+ return;
+ }
+
+ // get string buffer
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
+ if(!stringbuffer)
+ {
+ VM_Warning(prog, "VM_buf_writefile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ // get start and end parms
+ if (prog->argc > 3)
+ {
+ strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+ strnum = (int)PRVM_G_FLOAT(OFS_PARM3);
+ }
+ else if (prog->argc > 2)
+ {
+ strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+ strnum = stringbuffer->num_strings - strindex;
+ }
+ else
+ {
+ strindex = 0;
+ strnum = stringbuffer->num_strings;
+ }
+ if (strindex < 0 || strindex >= stringbuffer->num_strings)
+ {
+ VM_Warning(prog, "VM_buf_writefile: wrong start string index %i used in %s\n", strindex, prog->name);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+ if (strnum < 0)
+ {
+ VM_Warning(prog, "VM_buf_writefile: wrong strings count %i used in %s\n", strnum, prog->name);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ // write
+ while(strindex < stringbuffer->num_strings && strnum)
+ {
+ if (stringbuffer->strings[strindex])
+ {
+ if ((strlength = (int)strlen(stringbuffer->strings[strindex])))
+ FS_Write(prog->openfiles[filenum], stringbuffer->strings[strindex], strlength);
+ FS_Write(prog->openfiles[filenum], "\n", 1);
+ }
+ strindex++;
+ strnum--;
+ }
+
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+#define MATCH_AUTO 0
+#define MATCH_WHOLE 1
+#define MATCH_LEFT 2
+#define MATCH_RIGHT 3
+#define MATCH_MIDDLE 4
+#define MATCH_PATTERN 5
+
+static const char *detect_match_rule(char *pattern, int *matchrule)
+{
+ char *ppos, *qpos;
+ int patternlength;
+
+ patternlength = (int)strlen(pattern);
+ ppos = strchr(pattern, '*');
+ qpos = strchr(pattern, '?');
+ // has ? - pattern
+ if (qpos)
+ {
+ *matchrule = MATCH_PATTERN;
+ return pattern;
+ }
+ // has * - left, mid, right or pattern
+ if (ppos)
+ {
+ // starts with * - may be right/mid or pattern
+ if ((ppos - pattern) == 0)
+ {
+ ppos = strchr(pattern+1, '*');
+ // *something
+ if (!ppos)
+ {
+ *matchrule = MATCH_RIGHT;
+ return pattern+1;
+ }
+ // *something*
+ if ((ppos - pattern) == patternlength)
+ {
+ *matchrule = MATCH_MIDDLE;
+ *ppos = 0;
+ return pattern+1;
+ }
+ // *som*thing
+ *matchrule = MATCH_PATTERN;
+ return pattern;
+ }
+ // end with * - left
+ if ((ppos - pattern) == patternlength)
+ {
+ *matchrule = MATCH_LEFT;
+ *ppos = 0;
+ return pattern;
+ }
+ // som*thing
+ *matchrule = MATCH_PATTERN;
+ return pattern;
+ }
+ // have no wildcards - whole string
+ *matchrule = MATCH_WHOLE;
+ return pattern;
+}
+
+// todo: support UTF8
+static qboolean match_rule(const char *string, int max_string, const char *pattern, int patternlength, int rule)
+{
+ const char *mid;
+
+ if (rule == 1)
+ return !strncmp(string, pattern, max_string) ? true : false;
+ if (rule == 2)
+ return !strncmp(string, pattern, patternlength) ? true : false;
+ if (rule == 3)
+ {
+ mid = strstr(string, pattern);
+ return mid && !*(mid+patternlength);
+ }
+ if (rule == 4)
+ return strstr(string, pattern) ? true : false;
+ // pattern
+ return matchpattern_with_separator(string, pattern, false, "", false) ? true : false;
+}
+
+/*
+========================
+VM_bufstr_find
+find an index of bufstring matching rule
+float bufstr_find(float bufhandle, string match, float matchrule, float startpos, float step) = #468;
+========================
+*/
+
+void VM_bufstr_find(prvm_prog_t *prog)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ char string[VM_STRINGTEMP_LENGTH];
+ int matchrule, matchlen, i, step;
+ const char *match;
+
+ VM_SAFEPARMCOUNTRANGE(3, 5, VM_bufstr_find);
+
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+
+ // get string buffer
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ VM_Warning(prog, "VM_bufstr_find: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
+ return;
+ }
+
+ // get pattern/rule
+ matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if (matchrule < 0 && matchrule > 5)
+ {
+ VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
+ return;
+ }
+ if (matchrule)
+ match = PRVM_G_STRING(OFS_PARM1);
+ else
+ {
+ strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+ match = detect_match_rule(string, &matchrule);
+ }
+ matchlen = (int)strlen(match);
+
+ // find
+ i = (prog->argc > 3) ? (int)PRVM_G_FLOAT(OFS_PARM3) : 0;
+ step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
+ while(i < stringbuffer->num_strings)
+ {
+ if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+ break;
+ }
+ i += step;
+ }
+}
+
+/*
+========================
+VM_matchpattern
+float matchpattern(string s, string pattern, float matchrule, float startpos) = #468;
+========================
+*/
+void VM_matchpattern(prvm_prog_t *prog)
+{
+ const char *s, *match;
+ char string[VM_STRINGTEMP_LENGTH];
+ int matchrule, l;
+
+ VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
+
+ s = PRVM_G_STRING(OFS_PARM0);
+
+ // get pattern/rule
+ matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if (matchrule < 0 && matchrule > 5)
+ {
+ VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
+ return;
+ }
+ if (matchrule)
+ match = PRVM_G_STRING(OFS_PARM1);
+ else
+ {
+ strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+ match = detect_match_rule(string, &matchrule);
+ }
+
+ // offset
+ l = (int)strlen(match);
+ if (prog->argc > 3)
+ s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
+
+ // match
+ PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
+}
+
+/*
+========================
+VM_buf_cvarlist
+========================
+*/