sideband.con commit Fourth batch (bc12974)
   1#include "cache.h"
   2#include "color.h"
   3#include "config.h"
   4#include "sideband.h"
   5#include "help.h"
   6
   7struct keyword_entry {
   8        /*
   9         * We use keyword as config key so it should be a single alphanumeric word.
  10         */
  11        const char *keyword;
  12        char color[COLOR_MAXLEN];
  13};
  14
  15static struct keyword_entry keywords[] = {
  16        { "hint",       GIT_COLOR_YELLOW },
  17        { "warning",    GIT_COLOR_BOLD_YELLOW },
  18        { "success",    GIT_COLOR_BOLD_GREEN },
  19        { "error",      GIT_COLOR_BOLD_RED },
  20};
  21
  22/* Returns a color setting (GIT_COLOR_NEVER, etc). */
  23static int use_sideband_colors(void)
  24{
  25        static int use_sideband_colors_cached = -1;
  26
  27        const char *key = "color.remote";
  28        struct strbuf sb = STRBUF_INIT;
  29        char *value;
  30        int i;
  31
  32        if (use_sideband_colors_cached >= 0)
  33                return use_sideband_colors_cached;
  34
  35        if (!git_config_get_string(key, &value)) {
  36                use_sideband_colors_cached = git_config_colorbool(key, value);
  37        } else if (!git_config_get_string("color.ui", &value)) {
  38                use_sideband_colors_cached = git_config_colorbool("color.ui", value);
  39        } else {
  40                use_sideband_colors_cached = GIT_COLOR_AUTO;
  41        }
  42
  43        for (i = 0; i < ARRAY_SIZE(keywords); i++) {
  44                strbuf_reset(&sb);
  45                strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);
  46                if (git_config_get_string(sb.buf, &value))
  47                        continue;
  48                if (color_parse(value, keywords[i].color))
  49                        continue;
  50        }
  51        strbuf_release(&sb);
  52        return use_sideband_colors_cached;
  53}
  54
  55void list_config_color_sideband_slots(struct string_list *list, const char *prefix)
  56{
  57        int i;
  58
  59        for (i = 0; i < ARRAY_SIZE(keywords); i++)
  60                list_config_item(list, prefix, keywords[i].keyword);
  61}
  62
  63/*
  64 * Optionally highlight one keyword in remote output if it appears at the start
  65 * of the line. This should be called for a single line only, which is
  66 * passed as the first N characters of the SRC array.
  67 *
  68 * NEEDSWORK: use "size_t n" instead for clarity.
  69 */
  70static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
  71{
  72        int i;
  73
  74        if (!want_color_stderr(use_sideband_colors())) {
  75                strbuf_add(dest, src, n);
  76                return;
  77        }
  78
  79        while (0 < n && isspace(*src)) {
  80                strbuf_addch(dest, *src);
  81                src++;
  82                n--;
  83        }
  84
  85        for (i = 0; i < ARRAY_SIZE(keywords); i++) {
  86                struct keyword_entry *p = keywords + i;
  87                int len = strlen(p->keyword);
  88
  89                if (n < len)
  90                        continue;
  91                /*
  92                 * Match case insensitively, so we colorize output from existing
  93                 * servers regardless of the case that they use for their
  94                 * messages. We only highlight the word precisely, so
  95                 * "successful" stays uncolored.
  96                 */
  97                if (!strncasecmp(p->keyword, src, len) &&
  98                    (len == n || !isalnum(src[len]))) {
  99                        strbuf_addstr(dest, p->color);
 100                        strbuf_add(dest, src, len);
 101                        strbuf_addstr(dest, GIT_COLOR_RESET);
 102                        n -= len;
 103                        src += len;
 104                        break;
 105                }
 106        }
 107
 108        strbuf_add(dest, src, n);
 109}
 110
 111
 112#define DISPLAY_PREFIX "remote: "
 113
 114#define ANSI_SUFFIX "\033[K"
 115#define DUMB_SUFFIX "        "
 116
 117int demultiplex_sideband(const char *me, char *buf, int len,
 118                         int die_on_error,
 119                         struct strbuf *scratch,
 120                         enum sideband_type *sideband_type)
 121{
 122        static const char *suffix;
 123        const char *b, *brk;
 124        int band;
 125
 126        if (!suffix) {
 127                if (isatty(2) && !is_terminal_dumb())
 128                        suffix = ANSI_SUFFIX;
 129                else
 130                        suffix = DUMB_SUFFIX;
 131        }
 132
 133        if (len == 0) {
 134                *sideband_type = SIDEBAND_FLUSH;
 135                goto cleanup;
 136        }
 137        if (len < 1) {
 138                strbuf_addf(scratch,
 139                            "%s%s: protocol error: no band designator",
 140                            scratch->len ? "\n" : "", me);
 141                *sideband_type = SIDEBAND_PROTOCOL_ERROR;
 142                goto cleanup;
 143        }
 144        band = buf[0] & 0xff;
 145        buf[len] = '\0';
 146        len--;
 147        switch (band) {
 148        case 3:
 149                if (die_on_error)
 150                        die("remote error: %s", buf + 1);
 151                strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
 152                            DISPLAY_PREFIX);
 153                maybe_colorize_sideband(scratch, buf + 1, len);
 154
 155                *sideband_type = SIDEBAND_REMOTE_ERROR;
 156                break;
 157        case 2:
 158                b = buf + 1;
 159
 160                /*
 161                 * Append a suffix to each nonempty line to clear the
 162                 * end of the screen line.
 163                 *
 164                 * The output is accumulated in a buffer and
 165                 * each line is printed to stderr using
 166                 * write(2) to ensure inter-process atomicity.
 167                 */
 168                while ((brk = strpbrk(b, "\n\r"))) {
 169                        int linelen = brk - b;
 170
 171                        if (!scratch->len)
 172                                strbuf_addstr(scratch, DISPLAY_PREFIX);
 173                        if (linelen > 0) {
 174                                maybe_colorize_sideband(scratch, b, linelen);
 175                                strbuf_addstr(scratch, suffix);
 176                        }
 177
 178                        strbuf_addch(scratch, *brk);
 179                        xwrite(2, scratch->buf, scratch->len);
 180                        strbuf_reset(scratch);
 181
 182                        b = brk + 1;
 183                }
 184
 185                if (*b) {
 186                        strbuf_addstr(scratch, scratch->len ?
 187                                    "" : DISPLAY_PREFIX);
 188                        maybe_colorize_sideband(scratch, b, strlen(b));
 189                }
 190                return 0;
 191        case 1:
 192                *sideband_type = SIDEBAND_PRIMARY;
 193                break;
 194        default:
 195                strbuf_addf(scratch, "%s%s: protocol error: bad band #%d",
 196                            scratch->len ? "\n" : "", me, band);
 197                *sideband_type = SIDEBAND_PROTOCOL_ERROR;
 198                break;
 199        }
 200
 201cleanup:
 202        if (die_on_error && *sideband_type == SIDEBAND_PROTOCOL_ERROR)
 203                die("%s", scratch->buf);
 204        if (scratch->len) {
 205                strbuf_addch(scratch, '\n');
 206                xwrite(2, scratch->buf, scratch->len);
 207        }
 208        strbuf_release(scratch);
 209        return 1;
 210}
 211
 212/*
 213 * fd is connected to the remote side; send the sideband data
 214 * over multiplexed packet stream.
 215 */
 216void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
 217{
 218        const char *p = data;
 219
 220        while (sz) {
 221                unsigned n;
 222                char hdr[5];
 223
 224                n = sz;
 225                if (packet_max - 5 < n)
 226                        n = packet_max - 5;
 227                if (0 <= band) {
 228                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
 229                        hdr[4] = band;
 230                        write_or_die(fd, hdr, 5);
 231                } else {
 232                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
 233                        write_or_die(fd, hdr, 4);
 234                }
 235                write_or_die(fd, p, n);
 236                p += n;
 237                sz -= n;
 238        }
 239}