From ba035696cf0927624b02db5573c617566cdd645f Mon Sep 17 00:00:00 2001 From: Haru Date: Mon, 16 May 2016 04:58:16 +0200 Subject: Re-implemented BUILDIN(sprintf) - The function now checks its arguments, rather than passing them to the system implementation (safer against arbitrary memory access or wrong variable type) - Implemented positional ('%1$d') specifiers (POSIX style) - See script_commands.txt for details about the supported format specifiers. Signed-off-by: Haru --- doc/script_commands.txt | 73 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 05d075ac1..f9a628953 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -8067,10 +8067,75 @@ Example: *sprintf({,param{,param{,...}}}) -C style sprintf. The resulting string is returned same as in PHP. All C -format specifiers are supported except %n. For more info check sprintf -function at www.cplusplus.com -Number of params is only limited by Hercules' script engine. +C style sprintf. The resulting string is returned. + +The format string can contain placeholders (format specifiers) using the +following structure: + + %[parameter][flags][width]type + +The following format specifier types are supported: + +%%: Prints a literal '%' (special case, doesn't support parameter, flag, width) +%d, %i: Formats the specified value as a decimal signed number +%u: Formats the specified value as a decimal unsigned number +%x: Formats the specified value as a hexadecimal (lowercase) unsigned number +%X: Formats the specified value as a hexadecimal (uppercase) unsigned number +%o: Formats the specified value as an octal unsigned number +%s: Formats the specified value as a string +%c: Formats the specified value as a character (only uses the first character + of strings) + +The following format specifier types are not supported: + +%n (not implemented due to safety concerns) +%f, %F, %e, %E, %g, %G (the script engine doesn't use floating point values) +%p (the script engine doesn't use pointers) +%a, %A (not supported, use 0x%x and 0x%X respectively instead) + +An ordinal parameter can be specified in the form 'x$' (where x is a number), +to reorder the output (this may be useful in translated strings, where the +sentence order may be different from the original order). Example: + + // Name, level, job name + mes(sprintf("Hello, I'm %s, a level %d %s", strcharinfo(PC_NAME), BaseLevel, jobname(Class))); + +When translating the sentence to other languages (for example Italian), +swapping some arguments may be appropriate, and it may be desirable to keep the +actual arguments in the same order (i.e. when translating through the HULD): + + // Job name is printed before the level, although they're specified in the opposite order. + // Name, job name, level + mes(sprintf("Ciao, io sono %1$s, un %3$s di livello %2$d", strcharinfo(PC_NAME), BaseLevel, jobname(Class))); + +The supported format specifier flags are: + +- (minus): Left-align the output of this format specifier. (the default is to + right-align the output). ++ (plus): Prepends a plus for positive signed-numeric types. positive = '+', + negative = '-'. +(space): Prepends a space for positive signed-numeric types. positive = ' ', + negative = '-'. This flag is ignored if the '+' flag exists. +0 (zero): When a field width option is specified, prepends zeros for numeric + types. (the default prepends spaces). +A field width can be specified. + + mes(sprintf("The temperature is %+d degrees Celsius", .@temperature)); // Keeps the '+' sign in front of positive values + .@map_name$ = sprintf("quiz_%02d", .@i); // Keeps the leading 0 in "quiz_00", etc + +A field width may be specified, to ensure that 'at least' that many characters +are printed. If a star ('*') is specified as width, then the width is read as +argument to the sprintf() function. This also supports positional arguments. + + sprintf("%04d", 10) // Returns "0010" + sprintf("%0*d", 5, 10) // Returns "00010" + sprintf("%5d", 10) // Returns " 10" + sprintf("%-5d", 10) // Returns "10 " + sprintf("%10s", "Hello") // Returns " Hello"; + sprintf("%-10s", "Hello") // Returns "Hello "; + +Precision ('.X') and length ('hh', 'h', 'l', 'll', 'L', 'z', 'j', 't') +specifiers are not implemented (not necessary for the script engine purposes) Example: .@format$ = "The %s contains %d monkeys"; -- cgit v1.2.3-70-g09d2 From be570dd88f744efcf837a69901ad60bb1c78fffe Mon Sep 17 00:00:00 2001 From: Haru Date: Sat, 18 Jun 2016 00:45:47 +0200 Subject: Removed the uncommon (and useless) multi-argument variant of mes() Signed-off-by: Haru --- doc/script_commands.txt | 10 +--------- src/map/script.c | 15 +++------------ 2 files changed, 4 insertions(+), 21 deletions(-) (limited to 'doc') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index f9a628953..fea7b3c3b 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -1188,7 +1188,7 @@ From here on, we will have the commands sorted as followed: //===================================== --------------------------------------- -*mes ""{,""...""}; +*mes ""; This command will displays a box on the screen for the invoking character, if no such box is displayed already, and will print the string specified @@ -1220,14 +1220,6 @@ non-English characters, the color codes might get screwed if they stick to letters with no intervening space. Separating them with spaces from the letters on either side solves the problem. -To display multiple lines of message while only using a single mes; -command, use the script command in the following format: - - mes "Line 1", "Line 2", "Line 3"; - -This will display 3 different lines while only consuming a single line in -the relevant script file. - If you're using a client from 2011-10-10aRagexe.exe onwards, you can also use automatic navigation and open URLs in browser by using some HTML-like labels. For example: diff --git a/src/map/script.c b/src/map/script.c index e4c973045..d9487de3b 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -5248,19 +5248,10 @@ const char *script_getfuncname(struct script_state *st) { BUILDIN(mes) { struct map_session_data *sd = script->rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return true; - if( !script_hasdata(st, 3) ) {// only a single line detected in the script - clif->scriptmes(sd, st->oid, script_getstr(st, 2)); - } else {// parse multiple lines as they exist - int i; - - for( i = 2; script_hasdata(st, i); i++ ) { - // send the message to the client - clif->scriptmes(sd, st->oid, script_getstr(st, i)); - } - } + clif->scriptmes(sd, st->oid, script_getstr(st, 2)); return true; } @@ -20354,7 +20345,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(__setr,"rv?"), // NPC interaction - BUILDIN_DEF(mes,"s*"), + BUILDIN_DEF(mes,"s"), BUILDIN_DEF(next,""), BUILDIN_DEF(close,""), BUILDIN_DEF(close2,""), -- cgit v1.2.3-70-g09d2 From 9164ea7ec938958245e1ceb75035f90500c8a7c8 Mon Sep 17 00:00:00 2001 From: Haru Date: Sat, 18 Jun 2016 00:19:39 +0200 Subject: Added mesf() command (combination of mes() and sprintf()) Signed-off-by: Haru --- doc/script_commands.txt | 17 +++++++++++++++++ src/map/script.c | 33 +++++++++++++++++++++++++++++++++ src/map/script.h | 1 + src/plugins/generate-translations.c | 4 +++- 4 files changed, 54 insertions(+), 1 deletion(-) (limited to 'doc') diff --git a/doc/script_commands.txt b/doc/script_commands.txt index fea7b3c3b..f0758de71 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -1235,6 +1235,23 @@ Clicking Google will open the browser and point to Google website. --------------------------------------- +*mesf(""{,{, {, ...}}}) + +This command will display a box on the screen for the invoking character, +if no such box is displayed already, and will print the string specified +into that box, after applying the same format-string replacements as sprintf(). + +Example: + + mesf("Hello, I'm %s, a level %d %s", strcharinfo(PC_NAME), BaseLevel, jobname(Class)); + // is equivalent to: + mes(sprintf("Hello, I'm %s, a level %d %s", strcharinfo(PC_NAME), BaseLevel, jobname(Class))); + +This command is a combination of mes() and sprintf(). See their documentation +for more details. + +--------------------------------------- + *next; This command will display a 'next' button in the message window for the diff --git a/src/map/script.c b/src/map/script.c index 926f19bf0..a6a9a9cf9 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -5487,6 +5487,37 @@ BUILDIN(mes) return true; } +/** + * Appends a message to the npc dialog, applying format string conversions (see + * sprintf). + * + * If a dialog doesn't exist yet, one is created. + * + * @code + * mes ""; + * @endcode + */ +BUILDIN(mesf) +{ + struct map_session_data *sd = script->rid2sd(st); + struct StringBuf buf; + + if (sd == NULL) + return true; + + StrBuf->Init(&buf); + + if (!script_sprintf(st, 2, &buf)) { + StrBuf->Destroy(&buf); + return false; + } + + clif->scriptmes(sd, st->oid, StrBuf->Value(&buf)); + StrBuf->Destroy(&buf); + + return true; +} + /// Displays the button 'next' in the npc dialog. /// The dialog text is cleared and the script continues when the button is pressed. /// @@ -20275,6 +20306,7 @@ bool script_add_builtin(const struct script_function *buildin, bool override) { else if( strcmp(buildin->name, "callfunc") == 0 ) script->buildin_callfunc_ref = n; else if( strcmp(buildin->name, "getelementofarray") == 0 ) script->buildin_getelementofarray_ref = n; else if( strcmp(buildin->name, "mes") == 0 ) script->buildin_mes_offset = script->buildin_count; + else if( strcmp(buildin->name, "mesf") == 0 ) script->buildin_mesf_offset = script->buildin_count; else if( strcmp(buildin->name, "select") == 0 ) script->buildin_select_offset = script->buildin_count; else if( strcmp(buildin->name, "_") == 0 ) script->buildin_lang_macro_offset = script->buildin_count; else if( strcmp(buildin->name, "_$") == 0 ) script->buildin_lang_macro_fmtstring_offset = script->buildin_count; @@ -20372,6 +20404,7 @@ void script_parse_builtin(void) { // NPC interaction BUILDIN_DEF(mes,"s"), + BUILDIN_DEF(mesf,"s*"), BUILDIN_DEF(next,""), BUILDIN_DEF(close,""), BUILDIN_DEF(close2,""), diff --git a/src/map/script.h b/src/map/script.h index 7811cd64e..61c6a4583 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -623,6 +623,7 @@ struct script_interface { const char *parser_current_npc_name; /* */ int buildin_mes_offset; + int buildin_mesf_offset; int buildin_select_offset; int buildin_lang_macro_offset; int buildin_lang_macro_fmtstring_offset; diff --git a/src/plugins/generate-translations.c b/src/plugins/generate-translations.c index 7928fec9c..0f69c69a1 100644 --- a/src/plugins/generate-translations.c +++ b/src/plugins/generate-translations.c @@ -116,7 +116,9 @@ void script_add_translatable_string_posthook(const struct script_string_buf *str || script->syntax.lang_macro_active ) { is_translatable_string = true; - } else if (script->syntax.lang_macro_fmtstring_active) { + } else if (script->syntax.last_func == script->buildin_mesf_offset + || script->syntax.lang_macro_fmtstring_active + ) { is_translatable_fmtstring = true; } } -- cgit v1.2.3-70-g09d2