Github user mike-jumper commented on a diff in the pull request:
https://github.com/apache/guacamole-server/pull/132#discussion_r161149994
--- Diff: src/terminal/terminal.c ---
@@ -255,52 +257,203 @@ void* guac_terminal_thread(void* data) {
}
-guac_terminal* guac_terminal_create(guac_client* client,
- const char* font_name, int font_size, int dpi,
- int width, int height, const char* color_scheme) {
+/**
+ * Compare a non-null-terminated string spanning token_start to token_end,
with
+ * a null-terminating string at literal.
+ */
+static int guac_terminal_color_scheme_compare_token(const char*
token_start,
+ const char* token_end, const char* literal) {
- int default_foreground;
- int default_background;
+ const int result = strncmp(literal, token_start, token_end -
token_start);
+ if (result != 0)
+ return result;
- /* Default to "gray-black" color scheme if no scheme provided */
- if (color_scheme == NULL || color_scheme[0] == '\0') {
- default_foreground = GUAC_TERMINAL_COLOR_GRAY;
- default_background = GUAC_TERMINAL_COLOR_BLACK;
- }
+ /* At this point, literal is same length or longer than
+ * | token_end - token_start |, so if the two are equal, literal should
+ * have its null-terminator at | token_end - token_start |. */
+ return (int) (unsigned char) literal[token_end - token_start];
+}
- /* Otherwise, parse color scheme */
- else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GRAY_BLACK) == 0) {
- default_foreground = GUAC_TERMINAL_COLOR_GRAY;
- default_background = GUAC_TERMINAL_COLOR_BLACK;
- }
- else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_BLACK_WHITE) == 0) {
- default_foreground = GUAC_TERMINAL_COLOR_BLACK;
- default_background = GUAC_TERMINAL_COLOR_WHITE;
- }
- else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GREEN_BLACK) == 0) {
- default_foreground = GUAC_TERMINAL_COLOR_DARK_GREEN;
- default_background = GUAC_TERMINAL_COLOR_BLACK;
- }
- else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_WHITE_BLACK) == 0) {
- default_foreground = GUAC_TERMINAL_COLOR_WHITE;
- default_background = GUAC_TERMINAL_COLOR_BLACK;
- }
+/**
+ * Parse a color-scheme configuration string, and return specified
+ * foreground/background colors and color palette.
+ *
+ * @param client
+ *
+ * @param color_scheme
+ * A semicolon-separated list of name-value pairs, i.e.
+ * "<name>: <value> [; <name>: <value> [; ...]]".
+ * For example, "color2: rgb:cc/33/22; background: color5".
+ *
+ * @param foreground
+ * (out) Parsed foreground color.
+ *
+ * @param background
+ * (out) Parsed background color.
+ *
+ * @param palette
+ * (out) Parsed color palette. Null is returned if color_scheme does
not
+ * require a custom palette. Otherwise, malloc() is used to allocate a
new
+ * palette, which must be freed manually by the calling code.
+ */
+static void guac_terminal_parse_color_scheme(guac_client* client,
+ const char* color_scheme, guac_terminal_color* foreground,
+ guac_terminal_color* background,
+ const guac_terminal_color (**palette)[256]) {
+
+ /* Set default gray-black color scheme and initial palette. */
+ *foreground = GUAC_TERMINAL_INITIAL_PALETTE[GUAC_TERMINAL_COLOR_GRAY];
+ *background = GUAC_TERMINAL_INITIAL_PALETTE[GUAC_TERMINAL_COLOR_BLACK];
+ *palette = NULL;
+
+ const guac_terminal_color (*ref_palette)[256] =
&GUAC_TERMINAL_INITIAL_PALETTE;
+ guac_terminal_color (*new_palette)[256] = NULL;
+
+ /* Pointer to the next token to parse. */
+ const char* next_token = color_scheme;
+
+ /* True if expecting the current token to be a value; false if
expecting a name. */
+ bool expect_value = false;
+
+ /* True if current token should be skipped. */
+ bool skip_token = false;
+
+ /* Target to place the parsed color value into. */
+ guac_terminal_color* value_target = NULL;
+
+ /* Terminator for the current token. */
+ char token_terminator = ';';
+
+ while (next_token) {
+ /* Use token_start and token_end to denote the current token. */
+ const char* token_start = next_token;
+ /* Strip leading spaces in current token. */
+ while (isspace(*token_start))
+ token_start++;
+
+ /* Find where this token ends */
+ const char* token_end = strpbrk(token_start, ":;");
+ if (token_end) {
+ next_token = token_end + 1;
+ token_terminator = *token_end;
+ }
+ else {
+ /* We've reached the end of the string. */
+ next_token = NULL;
+ token_end = token_start + strlen(token_start);
+ token_terminator = ';';
+ }
+ /* Strip trailing spaces in current token. */
+ while (token_end > token_start && isspace(*(token_end - 1)))
+ token_end--;
+
+ if (token_start == token_end) {
+ if (expect_value) {
+ guac_client_log(client, GUAC_LOG_WARNING,
+ "Empty color value at position %d.",
+ token_start - color_scheme);
+ return;
+ }
+ }
- /* If invalid, default to "gray-black" */
- else {
- guac_client_log(client, GUAC_LOG_WARNING,
- "Invalid color scheme: \"%s\". Defaulting to
\"gray-black\".",
- color_scheme);
- default_foreground = GUAC_TERMINAL_COLOR_GRAY;
- default_background = GUAC_TERMINAL_COLOR_BLACK;
+ else if (skip_token) {
+ expect_value = false;
+ skip_token = false;
+ }
+
+ else if (expect_value) {
+ expect_value = false;
+
+ /* Parse color<n> value. */
+ int index = -1;
+ if (sscanf(token_start, GUAC_TERMINAL_SCHEME_NUMBERED "%d",
&index) &&
+ index >= 0 && index <= 255) {
+ *value_target = (*ref_palette)[index];
+ }
+
+ /* Parse X11 value. */
+ else if (!guac_terminal_xparsecolor(token_start,
value_target)) {
+ if (!guac_terminal_color_scheme_compare_token(token_start,
+ token_end,
"rgb")) {
+ /* This value was a "rgb:R/G/B" value, so the next
token
+ * will be the "R/G/B" part. Skip processing that
part. */
+ expect_value = true;
+ skip_token = true;
+ }
+ }
+
+ else {
+ guac_client_log(client, GUAC_LOG_WARNING,
+ "Invalid color value: \"%.*s\".",
+ token_end - token_start, token_start);
+ return;
+ }
+ }
+
+ /* Expecting a name. */
+ else if (!guac_terminal_color_scheme_compare_token(
+ token_start, token_end, GUAC_TERMINAL_SCHEME_FOREGROUND)) {
+ value_target = foreground;
+ expect_value = true;
+ }
+
+ else if (!guac_terminal_color_scheme_compare_token(
+ token_start, token_end, GUAC_TERMINAL_SCHEME_BACKGROUND)) {
+ value_target = background;
+ expect_value = true;
+ }
+
+ else {
+ /* Parse color<n> value. */
+ int index = -1;
+ if (sscanf(token_start, GUAC_TERMINAL_SCHEME_NUMBERED "%d",
&index) &&
+ index >= 0 && index <= 255) {
+
+ /* Allocate a new palette now that we are mutating it. */
+ if (!new_palette) {
+ const size_t size = sizeof(guac_terminal_color[256]);
+ new_palette = (guac_terminal_color(*)[256])
malloc(size);
--- End diff --
Why conditionally allocate space, copy, etc.? It seems like the caller
should provide initialized space (presumably a copy of
`GUAC_TERMINAL_INITIAL_PALETTE`), with the scheme parser mutating that provided
space as specified, no?
---