sideband.con commit t1406: make hash-size independent (b1484ca)
   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 *
  69 * NEEDSWORK: use "size_t n" instead for clarity.
  70 */
  71static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
  72{
  73        int i;
  74
  75        if (!want_color_stderr(use_sideband_colors())) {
  76                strbuf_add(dest, src, n);
  77                return;
  78        }
  79
  80        while (0 < n && isspace(*src)) {
  81                strbuf_addch(dest, *src);
  82                src++;
  83                n--;
  84        }
  85
  86        for (i = 0; i < ARRAY_SIZE(keywords); i++) {
  87                struct keyword_entry *p = keywords + i;
  88                int len = strlen(p->keyword);
  89
  90                if (n <= len)
  91                        continue;
  92                /*
  93                 * Match case insensitively, so we colorize output from existing
  94                 * servers regardless of the case that they use for their
  95                 * messages. We only highlight the word precisely, so
  96                 * "successful" stays uncolored.
  97                 */
  98                if (!strncasecmp(p->keyword, src, len) && !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/*
 113 * Receive multiplexed output stream over git native protocol.
 114 * in_stream is the input stream from the remote, which carries data
 115 * in pkt_line format with band designator.  Demultiplex it into out
 116 * and err and return error appropriately.  Band #1 carries the
 117 * primary payload.  Things coming over band #2 is not necessarily
 118 * error; they are usually informative message on the standard error
 119 * stream, aka "verbose").  A message over band #3 is a signal that
 120 * the remote died unexpectedly.  A flush() concludes the stream.
 121 */
 122
 123#define DISPLAY_PREFIX "remote: "
 124
 125#define ANSI_SUFFIX "\033[K"
 126#define DUMB_SUFFIX "        "
 127
 128int recv_sideband(const char *me, int in_stream, int out)
 129{
 130        const char *suffix;
 131        char buf[LARGE_PACKET_MAX + 1];
 132        struct strbuf outbuf = STRBUF_INIT;
 133        int retval = 0;
 134
 135        if (isatty(2) && !is_terminal_dumb())
 136                suffix = ANSI_SUFFIX;
 137        else
 138                suffix = DUMB_SUFFIX;
 139
 140        while (!retval) {
 141                const char *b, *brk;
 142                int band, len;
 143                len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0);
 144                if (len == 0)
 145                        break;
 146                if (len < 1) {
 147                        strbuf_addf(&outbuf,
 148                                    "%s%s: protocol error: no band designator",
 149                                    outbuf.len ? "\n" : "", me);
 150                        retval = SIDEBAND_PROTOCOL_ERROR;
 151                        break;
 152                }
 153                band = buf[0] & 0xff;
 154                buf[len] = '\0';
 155                len--;
 156                switch (band) {
 157                case 3:
 158                        strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
 159                                    DISPLAY_PREFIX);
 160                        maybe_colorize_sideband(&outbuf, buf + 1, len);
 161
 162                        retval = SIDEBAND_REMOTE_ERROR;
 163                        break;
 164                case 2:
 165                        b = buf + 1;
 166
 167                        /*
 168                         * Append a suffix to each nonempty line to clear the
 169                         * end of the screen line.
 170                         *
 171                         * The output is accumulated in a buffer and
 172                         * each line is printed to stderr using
 173                         * write(2) to ensure inter-process atomicity.
 174                         */
 175                        while ((brk = strpbrk(b, "\n\r"))) {
 176                                int linelen = brk - b;
 177
 178                                if (!outbuf.len)
 179                                        strbuf_addstr(&outbuf, DISPLAY_PREFIX);
 180                                if (linelen > 0) {
 181                                        maybe_colorize_sideband(&outbuf, b, linelen);
 182                                        strbuf_addstr(&outbuf, suffix);
 183                                }
 184
 185                                strbuf_addch(&outbuf, *brk);
 186                                xwrite(2, outbuf.buf, outbuf.len);
 187                                strbuf_reset(&outbuf);
 188
 189                                b = brk + 1;
 190                        }
 191
 192                        if (*b) {
 193                                strbuf_addstr(&outbuf, outbuf.len ?
 194                                            "" : DISPLAY_PREFIX);
 195                                maybe_colorize_sideband(&outbuf, b, strlen(b));
 196                        }
 197                        break;
 198                case 1:
 199                        write_or_die(out, buf + 1, len);
 200                        break;
 201                default:
 202                        strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d",
 203                                    outbuf.len ? "\n" : "", me, band);
 204                        retval = SIDEBAND_PROTOCOL_ERROR;
 205                        break;
 206                }
 207        }
 208
 209        if (outbuf.len) {
 210                strbuf_addch(&outbuf, '\n');
 211                xwrite(2, outbuf.buf, outbuf.len);
 212        }
 213        strbuf_release(&outbuf);
 214        return retval;
 215}
 216
 217/*
 218 * fd is connected to the remote side; send the sideband data
 219 * over multiplexed packet stream.
 220 */
 221void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
 222{
 223        const char *p = data;
 224
 225        while (sz) {
 226                unsigned n;
 227                char hdr[5];
 228
 229                n = sz;
 230                if (packet_max - 5 < n)
 231                        n = packet_max - 5;
 232                if (0 <= band) {
 233                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
 234                        hdr[4] = band;
 235                        write_or_die(fd, hdr, 5);
 236                } else {
 237                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
 238                        write_or_die(fd, hdr, 4);
 239                }
 240                write_or_die(fd, p, n);
 241                p += n;
 242                sz -= n;
 243        }
 244}