summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/map/script.c448
1 files changed, 234 insertions, 214 deletions
diff --git a/src/map/script.c b/src/map/script.c
index d9487de3b..40dafd2a5 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -5233,6 +5233,232 @@ const char *script_getfuncname(struct script_state *st) {
return NULL;
}
+/**
+ * Writes a string to a StringBuf by combining a format string and a set of
+ * arguments taken from the current script state (caller script function
+ * arguments).
+ *
+ * @param[in] st Script state (must have at least a string at index
+ * 'start').
+ * @param[in] start Index of the format string argument.
+ * @param[out] out Output string buffer (managed by the caller, must be
+ * already initialized)
+ * @retval false if an error occurs.
+ */
+bool script_sprintf(struct script_state *st, int start, struct StringBuf *out)
+{
+ const char *format = NULL;
+ const char *p = NULL, *np = NULL;
+ char *buf = NULL;
+ int buf_len = 0;
+ int lastarg = start;
+ int argc = script_lastdata(st) + 1;
+
+ Assert_retr(-1, start >= 2 && start <= argc);
+ Assert_retr(-1, script_hasdata(st, start));
+
+ p = format = script_getstr(st, start);
+
+ /*
+ * format-string = "" / *(text / placeholder)
+ * placeholder = "%%" / "%n" / std-placeholder
+ * std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type
+ * pos-parameter = number "$"
+ * flags = *("-" / "+" / "0" / SP)
+ * width = number / ("*" [pos-parameter])
+ * precision = "." (number / ("*" [pos-parameter]))
+ * length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t"
+ * type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A"
+ * number = digit-nonzero *DIGIT
+ * digit-nonzero = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
+ */
+
+ while ((np = strchr(p, '%')) != NULL) {
+ bool flag_plus = false, flag_minus = false, flag_zero = false, flag_space = false;
+ bool positional_arg = false;
+ int width = 0, nextarg = lastarg + 1, thisarg = nextarg;
+
+ if (p != np) {
+ int len = (int)(np - p + 1);
+ if (buf_len < len) {
+ RECREATE(buf, char, len);
+ buf_len = len;
+ }
+ safestrncpy(buf, p, len);
+ StrBuf->AppendStr(out, buf);
+ }
+
+ p = np;
+ np++;
+
+ // placeholder = "%%" ; (special case)
+ if (*np == '%') {
+ StrBuf->AppendStr(out, "%");
+ p = np + 1;
+ continue;
+ }
+ // placeholder = "%n" ; (ignored)
+ if (*np == 'n') {
+ ShowWarning("script_sprintf: Format %%n not supported! Skipping...\n");
+ script->reportsrc(st);
+ lastarg = nextarg;
+ p = np + 1;
+ continue;
+ }
+
+ // std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type
+
+ // pos-parameter = number "$"
+ if (ISDIGIT(*np) && *np != '0') {
+ const char *pp = np;
+ while (ISDIGIT(*pp))
+ pp++;
+ if (*pp == '$') {
+ thisarg = atoi(np) + start;
+ positional_arg = true;
+ np = pp + 1;
+ }
+ }
+
+ if (thisarg >= argc) {
+ ShowError("buildin_sprintf: Not enough arguments passed!\n");
+ if (buf != NULL)
+ aFree(buf);
+ return false;
+ }
+
+ // flags = *("-" / "+" / "0" / SP)
+ while (true) {
+ if (*np == '-') {
+ flag_minus = true;
+ } else if (*np == '+') {
+ flag_plus = true;
+ } else if (*np == ' ') {
+ flag_space = true;
+ } else if (*np == '0') {
+ flag_zero = true;
+ } else {
+ break;
+ }
+ np++;
+ }
+
+ // width = number / ("*" [pos-parameter])
+ if (ISDIGIT(*np)) {
+ width = atoi(np);
+ while (ISDIGIT(*np))
+ np++;
+ } else if (*np == '*') {
+ bool positional_widtharg = false;
+ int width_arg;
+ np++;
+ // pos-parameter = number "$"
+ if (ISDIGIT(*np) && *np != '0') {
+ const char *pp = np;
+ while (ISDIGIT(*pp))
+ pp++;
+ if (*pp == '$') {
+ width_arg = atoi(np) + start;
+ positional_widtharg = true;
+ np = pp + 1;
+ }
+ }
+ if (!positional_widtharg) {
+ width_arg = nextarg;
+ nextarg++;
+ if (!positional_arg)
+ thisarg++;
+ }
+
+ if (width_arg >= argc || thisarg >= argc) {
+ ShowError("buildin_sprintf: Not enough arguments passed!\n");
+ if (buf != NULL)
+ aFree(buf);
+ return false;
+ }
+ width = script_getnum(st, width_arg);
+ }
+
+ // precision = "." (number / ("*" [pos-parameter])) ; (not needed/implemented)
+
+ // length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t" ; (not needed/implemented)
+
+ // type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A"
+ if (buf_len < 16) {
+ RECREATE(buf, char, 16);
+ buf_len = 16;
+ }
+ {
+ int i = 0;
+ memset(buf, '\0', buf_len);
+ buf[i++] = '%';
+ if (flag_minus)
+ buf[i++] = '-';
+ if (flag_plus)
+ buf[i++] = '+';
+ else if (flag_space) // ignored if '+' is specified
+ buf[i++] = ' ';
+ if (flag_zero)
+ buf[i++] = '0';
+ if (width > 0)
+ safesnprintf(buf + i, buf_len - i - 1, "%d", width);
+ }
+ buf[(int)strlen(buf)] = *np;
+ switch (*np) {
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'o':
+ // Piggyback printf
+ StrBuf->Printf(out, buf, script_getnum(st, thisarg));
+ break;
+ case 's':
+ // Piggyback printf
+ StrBuf->Printf(out, buf, script_getstr(st, thisarg));
+ break;
+ case 'c':
+ {
+ const char *str = script_getstr(st, thisarg);
+ // Piggyback printf
+ StrBuf->Printf(out, buf, str[0]);
+ }
+ break;
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ case 'p':
+ case 'a':
+ case 'A':
+ ShowWarning("buildin_sprintf: Format %%%c not supported! Skipping...\n", *np);
+ script->reportsrc(st);
+ lastarg = nextarg;
+ p = np + 1;
+ continue;
+ default:
+ ShowError("buildin_sprintf: Invalid format string.\n");
+ if (buf != NULL)
+ aFree(buf);
+ return false;
+ }
+ lastarg = nextarg;
+ p = np + 1;
+ }
+
+ // Append the remaining part
+ if (p != NULL)
+ StrBuf->AppendStr(out, p);
+
+ if (buf != NULL)
+ aFree(buf);
+
+ return true;
+}
+
//-----------------------------------------------------------------------------
// buildin functions
//
@@ -15185,223 +15411,17 @@ BUILDIN(implode)
//-------------------------------------------------------
BUILDIN(sprintf)
{
- const char *format = script_getstr(st, 2);
- const char *p = NULL, *np = NULL;
- StringBuf final_buf;
- char *buf = NULL;
- int buf_len = 0;
- int lastarg = 2;
- int argc = script_lastdata(st) + 1;
-
- StrBuf->Init(&final_buf);
-
- p = format;
-
- /*
- * format-string = "" / *(text / placeholder)
- * placeholder = "%%" / "%n" / std-placeholder
- * std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type
- * pos-parameter = number "$"
- * flags = *("-" / "+" / "0" / SP)
- * width = number / ("*" [pos-parameter])
- * precision = "." (number / ("*" [pos-parameter]))
- * length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t"
- * type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A"
- * number = digit-nonzero *DIGIT
- * digit-nonzero = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
- */
-
- while ((np = strchr(p, '%')) != NULL) {
- bool flag_plus = false, flag_minus = false, flag_zero = false, flag_space = false;
- bool positional_arg = false;
- int width = 0, nextarg = lastarg + 1, thisarg = nextarg;
-
- if (p != np) {
- int len = (int)(np - p + 1);
- if (buf_len < len) {
- RECREATE(buf, char, len);
- buf_len = len;
- }
- safestrncpy(buf, p, len);
- StrBuf->AppendStr(&final_buf, buf);
- }
-
- p = np;
- np++;
-
- // placeholder = "%%" ; (special case)
- if (*np == '%') {
- StrBuf->AppendStr(&final_buf, "%");
- p = np + 1;
- continue;
- }
- // placeholder = "%n" ; (ignored)
- if (*np == 'n') {
- ShowWarning("buildin_sprintf: Format %%n not supported! Skipping...\n");
- script->reportsrc(st);
- lastarg = nextarg;
- p = np + 1;
- continue;
- }
-
- // std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type
-
- // pos-parameter = number "$"
- if (ISDIGIT(*np) && *np != '0') {
- const char *pp = np;
- while (ISDIGIT(*pp))
- pp++;
- if (*pp == '$') {
- thisarg = atoi(np) + 2;
- positional_arg = true;
- np = pp + 1;
- }
- }
-
- if (thisarg >= argc) {
- ShowError("buildin_sprintf: Not enough arguments passed!\n");
- if (buf != NULL)
- aFree(buf);
- StrBuf->Destroy(&final_buf);
- script_pushconststr(st,"");
- return false;
- }
-
- // flags = *("-" / "+" / "0" / SP)
- while (true) {
- if (*np == '-') {
- flag_minus = true;
- } else if (*np == '+') {
- flag_plus = true;
- } else if (*np == ' ') {
- flag_space = true;
- } else if (*np == '0') {
- flag_zero = true;
- } else {
- break;
- }
- np++;
- }
-
- // width = number / ("*" [pos-parameter])
- if (ISDIGIT(*np)) {
- width = atoi(np);
- while (ISDIGIT(*np))
- np++;
- } else if (*np == '*') {
- bool positional_widtharg = false;
- int width_arg;
- np++;
- // pos-parameter = number "$"
- if (ISDIGIT(*np) && *np != '0') {
- const char *pp = np;
- while (ISDIGIT(*pp))
- pp++;
- if (*pp == '$') {
- width_arg = atoi(np) + 2;
- positional_widtharg = true;
- np = pp + 1;
- }
- }
- if (!positional_widtharg) {
- width_arg = nextarg;
- nextarg++;
- if (!positional_arg)
- thisarg++;
- }
-
- if (width_arg >= argc || thisarg >= argc) {
- ShowError("buildin_sprintf: Not enough arguments passed!\n");
- if (buf != NULL)
- aFree(buf);
- StrBuf->Destroy(&final_buf);
- script_pushconststr(st,"");
- return false;
- }
- width = script_getnum(st, width_arg);
- }
-
- // precision = "." (number / ("*" [pos-parameter])) ; (not needed/implemented)
-
- // length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t" ; (not needed/implemented)
+ struct StringBuf buf;
+ StrBuf->Init(&buf);
- // type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A"
- if (buf_len < 16) {
- RECREATE(buf, char, 16);
- buf_len = 16;
- }
- {
- int i = 0;
- memset(buf, '\0', buf_len);
- buf[i++] = '%';
- if (flag_minus)
- buf[i++] = '-';
- if (flag_plus)
- buf[i++] = '+';
- else if (flag_space) // ignored if '+' is specified
- buf[i++] = ' ';
- if (flag_zero)
- buf[i++] = '0';
- if (width > 0)
- safesnprintf(buf + i, buf_len - i - 1, "%d", width);
- }
- buf[(int)strlen(buf)] = *np;
- switch (*np) {
- case 'd':
- case 'i':
- case 'u':
- case 'x':
- case 'X':
- case 'o':
- // Piggyback printf
- StrBuf->Printf(&final_buf, buf, script_getnum(st, thisarg));
- break;
- case 's':
- // Piggyback printf
- StrBuf->Printf(&final_buf, buf, script_getstr(st, thisarg));
- break;
- case 'c':
- {
- const char *str = script_getstr(st, thisarg);
- // Piggyback printf
- StrBuf->Printf(&final_buf, buf, str[0]);
- }
- break;
- case 'f':
- case 'F':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- case 'p':
- case 'a':
- case 'A':
- ShowWarning("buildin_sprintf: Format %%%c not supported! Skipping...\n", *np);
- script->reportsrc(st);
- lastarg = nextarg;
- p = np + 1;
- continue;
- default:
- ShowError("buildin_sprintf: Invalid format string.\n");
- if (buf != NULL)
- aFree(buf);
- StrBuf->Destroy(&final_buf);
- script_pushconststr(st,"");
- return false;
- }
- lastarg = nextarg;
- p = np + 1;
+ if (!script_sprintf(st, 2, &buf)) {
+ StrBuf->Destroy(&buf);
+ script_pushconststr(st, "");
+ return false;
}
- // Append the remaining part
- if (p != NULL)
- StrBuf->AppendStr(&final_buf, p);
-
- script_pushstrcopy(st, StrBuf->Value(&final_buf));
-
- if (buf != NULL)
- aFree(buf);
- StrBuf->Destroy(&final_buf);
+ script_pushstrcopy(st, StrBuf->Value(&buf));
+ StrBuf->Destroy(&buf);
return true;
}