cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

color-scheme.c (8882B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20
     21#include "terminal/color-scheme.h"
     22#include "terminal/palette.h"
     23#include "terminal/xparsecolor.h"
     24
     25#include <ctype.h>
     26#include <stdio.h>
     27#include <stdlib.h>
     28#include <string.h>
     29
     30#include <guacamole/client.h>
     31
     32/**
     33 * Compare a non-null-terminated string to a null-terminated literal, in the
     34 * same manner as strcmp().
     35 *
     36 * @param str_start
     37 *     Start of the non-null-terminated string.
     38 *
     39 * @param str_end
     40 *     End of the non-null-terminated string, after the last character.
     41 *
     42 * @param literal
     43 *     The null-terminated literal to compare against.
     44 *
     45 * @return
     46 *     Zero if the two strings are equal and non-zero otherwise.
     47 */
     48static int guac_terminal_color_scheme_compare_token(const char* str_start,
     49        const char* str_end, const char* literal) {
     50
     51    const int result = strncmp(literal, str_start, str_end - str_start);
     52    if (result != 0)
     53        return result;
     54
     55    /* At this point, literal is same length or longer than
     56     * | str_end - str_start |, so if the two are equal, literal should
     57     * have its null-terminator at | str_end - str_start |. */
     58    return (int) (unsigned char) literal[str_end - str_start];
     59}
     60
     61/**
     62 * Strip the leading and trailing spaces of a bounded string.
     63 *
     64 * @param[in,out] str_start
     65 *     Address of a pointer to the start of the string. On return, the pointer
     66 *     is advanced to after any leading spaces.
     67 *
     68 * @param[in,out] str_end
     69 *     Address of a pointer to the end of the string, after the last character.
     70 *     On return, the pointer is moved back to before any trailing spaces.
     71 */
     72static void guac_terminal_color_scheme_strip_spaces(const char** str_start,
     73        const char** str_end) {
     74
     75    /* Strip leading spaces. */
     76    while (*str_start < *str_end && isspace(**str_start))
     77        (*str_start)++;
     78
     79    /* Strip trailing spaces. */
     80    while (*str_end > *str_start && isspace(*(*str_end - 1)))
     81        (*str_end)--;
     82}
     83
     84/**
     85 * Parse the name part of the name-value pair within the color-scheme
     86 * configuration.
     87 *
     88 * @param client
     89 *     The client that the terminal is connected to.
     90 *
     91 * @param name_start
     92 *     Start of the name string.
     93 *
     94 * @param name_end
     95 *     End of the name string, after the last character.
     96 *
     97 * @param foreground
     98 *     Pointer to the foreground color.
     99 *
    100 * @param background
    101 *     Pointer to the background color.
    102 *
    103 * @param palette
    104 *     Pointer to the palette array.
    105 *
    106 * @param[out] target
    107 *     On return, pointer to the color struct that corresponds to the name.
    108 *
    109 * @return
    110 *     Zero if successful or non-zero otherwise.
    111 */
    112static int guac_terminal_parse_color_scheme_name(guac_client* client,
    113        const char* name_start, const char* name_end,
    114        guac_terminal_color* foreground, guac_terminal_color* background,
    115        guac_terminal_color (*palette)[256],
    116        guac_terminal_color** target) {
    117
    118    guac_terminal_color_scheme_strip_spaces(&name_start, &name_end);
    119
    120    if (!guac_terminal_color_scheme_compare_token(
    121            name_start, name_end, GUAC_TERMINAL_SCHEME_FOREGROUND)) {
    122        *target = foreground;
    123        return 0;
    124    }
    125
    126    if (!guac_terminal_color_scheme_compare_token(
    127            name_start, name_end, GUAC_TERMINAL_SCHEME_BACKGROUND)) {
    128        *target = background;
    129        return 0;
    130    }
    131
    132    /* Parse color<n> value. */
    133    int index = -1;
    134    if (sscanf(name_start, GUAC_TERMINAL_SCHEME_NUMBERED "%d", &index) &&
    135            index >= 0 && index <= 255) {
    136        *target = &(*palette)[index];
    137        return 0;
    138    }
    139
    140    guac_client_log(client, GUAC_LOG_WARNING,
    141                    "Unknown color name: \"%.*s\".",
    142                    name_end - name_start, name_start);
    143    return 1;
    144}
    145
    146/**
    147 * Parse the value part of the name-value pair within the color-scheme
    148 * configuration.
    149 *
    150 * @param client
    151 *     The client that the terminal is connected to.
    152 *
    153 * @param value_start
    154 *     Start of the value string.
    155 *
    156 * @param value_end
    157 *     End of the value string, after the last character.
    158 *
    159 * @param palette
    160 *     The current color palette.
    161 *
    162 * @param[out] target
    163 *     On return, the parsed color.
    164 *
    165 * @return
    166 *     Zero if successful or non-zero otherwise.
    167 */
    168static int guac_terminal_parse_color_scheme_value(guac_client* client,
    169        const char* value_start, const char* value_end,
    170        const guac_terminal_color (*palette)[256],
    171        guac_terminal_color* target) {
    172
    173    guac_terminal_color_scheme_strip_spaces(&value_start, &value_end);
    174
    175    /* Parse color<n> value. */
    176    int index = -1;
    177    if (sscanf(value_start, GUAC_TERMINAL_SCHEME_NUMBERED "%d", &index) &&
    178            index >= 0 && index <= 255) {
    179        *target = (*palette)[index];
    180        return 0;
    181    }
    182
    183    /* Parse X11 value. */
    184    if (!guac_terminal_xparsecolor(value_start, target))
    185        return 0;
    186
    187    guac_client_log(client, GUAC_LOG_WARNING,
    188                    "Invalid color value: \"%.*s\".",
    189                    value_end - value_start, value_start);
    190    return 1;
    191}
    192
    193void guac_terminal_parse_color_scheme(guac_client* client,
    194        const char* color_scheme, guac_terminal_color* foreground,
    195        guac_terminal_color* background,
    196        guac_terminal_color (*palette)[256]) {
    197
    198    /* Special cases. */
    199    if (color_scheme[0] == '\0') {
    200        /* guac_terminal_parse_color_scheme defaults to gray-black */
    201    }
    202    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GRAY_BLACK) == 0) {
    203        color_scheme = "foreground:color7;background:color0";
    204    }
    205    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_BLACK_WHITE) == 0) {
    206        color_scheme = "foreground:color0;background:color15";
    207    }
    208    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GREEN_BLACK) == 0) {
    209        color_scheme = "foreground:color2;background:color0";
    210    }
    211    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_WHITE_BLACK) == 0) {
    212        color_scheme = "foreground:color15;background:color0";
    213    }
    214
    215    /* Set default gray-black color scheme and initial palette. */
    216    *foreground = GUAC_TERMINAL_INITIAL_PALETTE[GUAC_TERMINAL_COLOR_GRAY];
    217    *background = GUAC_TERMINAL_INITIAL_PALETTE[GUAC_TERMINAL_COLOR_BLACK];
    218    memcpy(palette, GUAC_TERMINAL_INITIAL_PALETTE,
    219            sizeof(GUAC_TERMINAL_INITIAL_PALETTE));
    220
    221    /* Current char being parsed, or NULL if at end of parsing. */
    222    const char* cursor = color_scheme;
    223
    224    while (cursor) {
    225        /* Start of the current "name: value" pair. */
    226        const char* pair_start = cursor;
    227
    228        /* End of the current name-value pair. */
    229        const char* pair_end = strchr(pair_start, ';');
    230        if (pair_end) {
    231            cursor = pair_end + 1;
    232        }
    233        else {
    234            pair_end = pair_start + strlen(pair_start);
    235            cursor = NULL;
    236        }
    237
    238        guac_terminal_color_scheme_strip_spaces(&pair_start, &pair_end);
    239        if (pair_start >= pair_end)
    240            /* Allow empty pairs, which happens, e.g., when the configuration
    241             * string ends in a semi-colon. */
    242            continue;
    243
    244        /* End of the name part of the pair. */
    245        const char* name_end = memchr(pair_start, ':', pair_end - pair_start);
    246        if (name_end == NULL) {
    247            guac_client_log(client, GUAC_LOG_WARNING,
    248                            "Expecting colon: \"%.*s\".",
    249                            pair_end - pair_start, pair_start);
    250            return;
    251        }
    252
    253        /* The color that the name corresponds to. */
    254        guac_terminal_color* color_target = NULL;
    255
    256        if (guac_terminal_parse_color_scheme_name(
    257                client, pair_start, name_end, foreground, background,
    258                palette, &color_target))
    259            return; /* Parsing failed. */
    260
    261        if (guac_terminal_parse_color_scheme_value(
    262                client, name_end + 1, pair_end,
    263                (const guac_terminal_color(*)[256]) palette, color_target))
    264            return; /* Parsing failed. */
    265    }
    266
    267    /* Persist pseudo-index for foreground/background colors */
    268    foreground->palette_index = GUAC_TERMINAL_COLOR_FOREGROUND;
    269    background->palette_index = GUAC_TERMINAL_COLOR_BACKGROUND;
    270
    271}
    272