ref: cc8faa989c678a7b76688def831f5fed996bcd8c
parent: 2ecf987df183e01f2e73db001542cfc56a84c6ce
author: Ioan Chera <[email protected]>
date: Sun Mar 18 08:08:35 EDT 2018
Clean-up of AppleScript-generating C string processing * Functions which return something that must be freed now have a Create or Generate in their name * Fix a buffer overflow risk in CreateEscapedString * Ensure const-correctness * Verify malloc result * Updated guitest with file extensions which would have crashed the previous code.
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -30,7 +30,10 @@
RADIO_VALUE_MUSHROOM,
RADIO_VALUE_SNAKE,
};
-char *extensions[] = { "wad", "lmp", "txt", NULL };
+
+// also put some crazy extensions to test the escape function. a"b"c"""dd
+char *extensions[] = { "wad", "lmp", "txt", "a\"b\"c\"\"\"dd", "",
+ "\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"", NULL };
char *radio_values[] = { "Badger", "Mushroom", "Snake" };
char *textbox_value = NULL;
int numbox_value = 0;
--- a/textscreen/txt_fileselect.c
+++ b/textscreen/txt_fileselect.c
@@ -333,31 +333,48 @@
// Printf format string for the "wrapper" portion of the AppleScript:
#define APPLESCRIPT_WRAPPER "copy POSIX path of (%s) to stdout"
-static char *EscapedString(char *s)
+static char *CreateEscapedString(const char *original)
{
char *result;
- char *in, *out;
+ const char *in;
+ char *out;
- result = malloc(strlen(s) + 3);
+ // We need to take care not to overflow the buffer, so count exactly.
+#define ESCAPED_CHARS "\"\\"
+ size_t count_extras = 2; // start counting the two quotes
+ for (in = original; *in; ++in)
+ {
+ if (strchr(ESCAPED_CHARS, *in))
+ {
+ ++count_extras;
+ }
+ }
+
+ result = malloc(strlen(original) + count_extras + 1);
+ if (!result)
+ {
+ return NULL;
+ }
out = result;
- *out++ = '\"';
- for (in = s; *in != '\0'; ++in)
+ *out++ = '"';
+ for (in = original; *in; ++in)
{
- if (*in == '\"' || *in == '\\')
+ if (strchr(ESCAPED_CHARS, *in))
{
*out++ = '\\';
}
*out++ = *in;
}
- *out++ = '\"';
- *out = '\0';
+ *out++ = '"';
+ *out = 0;
return result;
+#undef ESCAPED_CHARS
}
// Build list of extensions, like: {"wad","lmp","txt"}
-static char *ExtensionsList(char **extensions)
+static char *CreateExtensionsList(char **extensions)
{
char *result, *escaped;
unsigned int result_len;
@@ -375,16 +392,27 @@
}
result = malloc(result_len);
+ if (!result)
+ {
+ return NULL;
+ }
TXT_StringCopy(result, "{", result_len);
for (i = 0; extensions[i] != NULL; ++i)
{
- escaped = EscapedString(extensions[i]);
+ escaped = CreateEscapedString(extensions[i]);
+ if (!escaped)
+ {
+ free(result);
+ return NULL;
+ }
TXT_StringConcat(result, escaped, result_len);
free(escaped);
if (extensions[i + 1] != NULL)
+ {
TXT_StringConcat(result, ",", result_len);
+ }
}
TXT_StringConcat(result, "}", result_len);
@@ -392,22 +420,26 @@
return result;
}
-static char *GenerateSelector(char *window_title, char **extensions)
+static char *GenerateSelector(const char *const window_title, char **extensions)
{
- char *chooser, *ext_list, *result;
- unsigned int result_len;
+ const char *chooser;
+ char *ext_list = NULL;
+ char *window_title_escaped = NULL;
+ char *result = NULL;
+ unsigned int result_len = 64;
- result_len = 64;
-
if (extensions == TXT_DIRECTORY)
{
chooser = "choose folder";
- ext_list = NULL;
}
else
{
chooser = "choose file";
- ext_list = ExtensionsList(extensions);
+ ext_list = CreateExtensionsList(extensions);
+ if (!ext_list)
+ {
+ return NULL;
+ }
}
// Calculate size.
@@ -414,8 +446,13 @@
if (window_title != NULL)
{
- window_title = EscapedString(window_title);
- result_len += strlen(window_title);
+ window_title_escaped = CreateEscapedString(window_title);
+ if (!window_title_escaped)
+ {
+ free(ext_list);
+ return NULL;
+ }
+ result_len += strlen(window_title_escaped);
}
if (ext_list != NULL)
{
@@ -423,35 +460,51 @@
}
result = malloc(result_len);
+ if (!result)
+ {
+ free(window_title_escaped);
+ free(ext_list);
+ return NULL;
+ }
TXT_StringCopy(result, chooser, result_len);
- if (window_title != NULL)
+ if (window_title_escaped != NULL)
{
TXT_StringConcat(result, " with prompt ", result_len);
- TXT_StringConcat(result, window_title, result_len);
- free(window_title);
+ TXT_StringConcat(result, window_title_escaped, result_len);
}
if (ext_list != NULL)
{
- TXT_StringConcat(result, "of type ", result_len);
+ TXT_StringConcat(result, " of type ", result_len);
TXT_StringConcat(result, ext_list, result_len);
- free(ext_list);
}
+ free(window_title_escaped);
+ free(ext_list);
return result;
}
-static char *GenerateAppleScript(char *window_title, char **extensions)
+static char *GenerateAppleScript(const char *window_title, char **extensions)
{
char *selector, *result;
size_t result_len;
selector = GenerateSelector(window_title, extensions);
+ if (!selector)
+ {
+ return NULL;
+ }
result_len = strlen(APPLESCRIPT_WRAPPER) + strlen(selector);
result = malloc(result_len);
+ if (!result)
+ {
+ free(selector);
+ return NULL;
+ }
+
TXT_snprintf(result, result_len, APPLESCRIPT_WRAPPER, selector);
free(selector);
@@ -468,16 +521,12 @@
char *argv[4];
char *result, *applescript;
- // We need to create a temporary copy of window_title because GenerateApple-
- // Script works with non-const string.
- char *window_title_copy = strdup(window_title);
- if (!window_title_copy)
+ applescript = GenerateAppleScript(window_title, extensions);
+ if (!applescript)
{
return NULL;
}
- applescript = GenerateAppleScript(window_title_copy, extensions);
-
argv[0] = "/usr/bin/osascript";
argv[1] = "-e";
argv[2] = applescript;
@@ -486,7 +535,6 @@
result = ExecReadOutput(argv);
free(applescript);
- free(window_title_copy);
return result;
}