Skip to content

[rtext] Fix TextToPascal()/TextToCamel() truncating text after a separator - #6132

Merged
raysan5 merged 1 commit into
raysan5:masterfrom
MaxFreedomPollard:fix-texttopascal-camel-separator
Sep 6, 2026
Merged

[rtext] Fix TextToPascal()/TextToCamel() truncating text after a separator#6132
raysan5 merged 1 commit into
raysan5:masterfrom
MaxFreedomPollard:fix-texttopascal-camel-separator

Conversation

@MaxFreedomPollard

Copy link
Copy Markdown
Contributor

TextToPascal() and TextToCamel() drop everything after an underscore unless the character right after it is one they explicitly handle. TextToPascal() (src/rtext.c:2186) handles a-z and 0-9, TextToCamel() (src/rtext.c:2270) handles only a-z. For anything else neither branch runs, buffer[i] keeps the zero from the memset() at the top of the function, and the returned string ends right there.

So TextToPascal("text_UTF8_load") returns "Text", and TextToCamel("sound_3d_mix") returns "sound". An upper case letter after the separator truncates both, a digit after the separator truncates TextToCamel(), and two underscores in a row truncate both.

The same code also reads one byte past the string. When _ is the last character, j++ lands on the \0, neither branch matches, and then the loop increment moves j one further before the condition reads text[j].

This skips the whole run of separators, stops when that reaches the end of the text, and copies a character that has no upper case form as it is. That last else also covers digits, so it replaces the 0-9 branch TextToPascal() had.

Verified at a2cc9b1 on macOS arm64 with Apple clang 17, CMake configured with -DBUILD_EXAMPLES=ON. The program below links against the built libraylib.a and opens no window. Before the change:

input            TextToPascal   expected            TextToCamel    expected       
-------------------------------------------------------------------------------------
hello_world      HelloWorld     HelloWorld     ok   helloWorld     helloWorld     ok
hello_World      Hello          HelloWorld     FAIL hello          helloWorld     FAIL
value_2          Value2         Value2         ok   value          value2         FAIL
sound_3d_mix     Sound3dMix     Sound3dMix     ok   sound          sound3dMix     FAIL
text_UTF8_load   Text           TextUTF8Load   FAIL text           textUTF8Load   FAIL
hello_           Hello          Hello          ok   hello          hello          ok
a__b             A              AB             FAIL a              aB             FAIL

FAILURES: 8 of 14

Guard page check, text "hello_" with its '\0' as the last readable byte:
  TextToPascal  CRASHED, read past the terminator
  TextToCamel   CRASHED, read past the terminator

RESULT: failures present

After:

input            TextToPascal   expected            TextToCamel    expected       
-------------------------------------------------------------------------------------
hello_world      HelloWorld     HelloWorld     ok   helloWorld     helloWorld     ok
hello_World      HelloWorld     HelloWorld     ok   helloWorld     helloWorld     ok
value_2          Value2         Value2         ok   value2         value2         ok
sound_3d_mix     Sound3dMix     Sound3dMix     ok   sound3dMix     sound3dMix     ok
text_UTF8_load   TextUTF8Load   TextUTF8Load   ok   textUTF8Load   textUTF8Load   ok
hello_           Hello          Hello          ok   hello          hello          ok
a__b             AB             AB             ok   aB             aB             ok

FAILURES: 0 of 14

Guard page check, text "hello_" with its '\0' as the last readable byte:
  TextToPascal  returned "Hello", no read past the terminator
  TextToCamel   returned "hello", no read past the terminator

RESULT: all checks passed

The last check writes "hello_" so its \0 is the final byte of a readable page with an unreadable page right behind it, which is why the read past the terminator shows up as a crash rather than as a wrong string.

TextToPascal("raylib_fun_videogames_programming") and TextToCamel("raylib_fun_videogames_programming") from examples/text/text_strings_management.c are unchanged, and TextToSnake() is not touched. The library and 225 examples build clean, and so do tools/rlparser and tools/rexm.

…rator

Both functions walk the input with two indexes and, on hitting '_', advance
j once and only write buffer[i] when the next character is one they know how
to handle. TextToPascal() (src/rtext.c:2186) covers 'a'-'z' and '0'-'9',
TextToCamel() (src/rtext.c:2270) covers only 'a'-'z'. Anything else leaves
buffer[i] at the zero it was memset to, so the returned string ends there
and the rest of the text is silently dropped: TextToPascal("text_UTF8_load")
returned "Text" and TextToCamel("sound_3d_mix") returned "sound".

The same path also reads past the end of the string. When '_' is the last
character, j++ lands on the '\0', neither branch matches, and then the loop
increment moves j one further before the condition reads text[j], one byte
beyond the terminator.

Skip the whole run of separators instead, stop when it reaches the end of
the text, and copy any character that has no upper case form as it is.
@raysan5
raysan5 merged commit 2b991b0 into raysan5:master Sep 6, 2026
@raysan5

raysan5 commented Sep 6, 2026

Copy link
Copy Markdown
Owner

@MaxFreedomPollard thanks for the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants