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