sideband.con commit Merge branch 'sb/more-repo-in-api' (b99a579)
   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) &&
  99                    (len == n || !isalnum(src[len]))) {
 100                        strbuf_addstr(dest, p->color);
 101                        strbuf_add(dest, src, len);
 102                        strbuf_addstr(dest, GIT_COLOR_RESET);
 103                        n -= len;
 104                        src += len;
 105                        break;
 106                }
 107        }
 108
 109        strbuf_add(dest, src, n);
 110}
 111
 112
 113/*
 114 * Receive multiplexed output stream over git native protocol.
 115 * in_stream is the input stream from the remote, which carries data
 116 * in pkt_line format with band designator.  Demultiplex it into out
 117 * and err and return error appropriately.  Band #1 carries the
 118 * primary payload.  Things coming over band #2 is not necessarily
 119 * error; they are usually informative message on the standard error
 120 * stream, aka "verbose").  A message over band #3 is a signal that
 121 * the remote died unexpectedly.  A flush() concludes the stream.
 122 */
 123
 124#define DISPLAY_PREFIX "remote: "
 125
 126#define ANSI_SUFFIX "\033[K"
 127#define DUMB_SUFFIX "        "
 128
 129int recv_sideband(const char *me, int in_stream, int out)
 130{
 131        const char *suffix;
 132        char buf[LARGE_PACKET_MAX + 1];
 133        struct strbuf outbuf = STRBUF_INIT;
 134        int retval = 0;
 135
 136        if (isatty(2) && !is_terminal_dumb())
 137                suffix = ANSI_SUFFIX;
 138        else
 139                suffix = DUMB_SUFFIX;
 140
 141        while (!retval) {
 142                const char *b, *brk;
 143                int band, len;
 144                len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0);
 145                if (len == 0)
 146                        break;
 147                if (len < 1) {
 148                        strbuf_addf(&outbuf,
 149                                    "%s%s: protocol error: no band designator",
 150                                    outbuf.len ? "\n" : "", me);
 151                        retval = SIDEBAND_PROTOCOL_ERROR;
 152                        break;
 153                }
 154                band = buf[0] & 0xff;
 155                buf[len] = '\0';
 156                len--;
 157                switch (band) {
 158                case 3:
 159                        strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "",
 160                                    DISPLAY_PREFIX);
 161                        maybe_colorize_sideband(&outbuf, buf + 1, len);
 162
 163                        retval = SIDEBAND_REMOTE_ERROR;
 164                        break;
 165                case 2:
 166                        b = buf + 1;
 167
 168                        /*
 169                         * Append a suffix to each nonempty line to clear the
 170                         * end of the screen line.
 171                         *
 172                         * The output is accumulated in a buffer and
 173                         * each line is printed to stderr using
 174                         * write(2) to ensure inter-process atomicity.
 175                         */
 176                        while ((brk = strpbrk(b, "\n\r"))) {
 177                                int linelen = brk - b;
 178
 179                                if (!outbuf.len)
 180                                        strbuf_addstr(&outbuf, DISPLAY_PREFIX);
 181                                if (linelen > 0) {
 182                                        maybe_colorize_sideband(&outbuf, b, linelen);
 183                                        strbuf_addstr(&outbuf, suffix);
 184                                }
 185
 186                                strbuf_addch(&outbuf, *brk);
 187                                xwrite(2, outbuf.buf, outbuf.len);
 188                                strbuf_reset(&outbuf);
 189
 190                                b = brk + 1;
 191                        }
 192
 193                        if (*b) {
 194                                strbuf_addstr(&outbuf, outbuf.len ?
 195                                            "" : DISPLAY_PREFIX);
 196                                maybe_colorize_sideband(&outbuf, b, strlen(b));
 197                        }
 198                        break;
 199                case 1:
 200                        write_or_die(out, buf + 1, len);
 201                        break;
 202                default:
 203                        strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d",
 204                                    outbuf.len ? "\n" : "", me, band);
 205                        retval = SIDEBAND_PROTOCOL_ERROR;
 206                        break;
 207                }
 208        }
 209
 210        if (outbuf.len) {
 211                strbuf_addch(&outbuf, '\n');
 212                xwrite(2, outbuf.buf, outbuf.len);
 213        }
 214        strbuf_release(&outbuf);
 215        return retval;
 216}
 217
 218/*
 219 * fd is connected to the remote side; send the sideband data
 220 * over multiplexed packet stream.
 221 */
 222void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
 223{
 224        const char *p = data;
 225
 226        while (sz) {
 227                unsigned n;
 228                char hdr[5];
 229
 230                n = sz;
 231                if (packet_max - 5 < n)
 232                        n = packet_max - 5;
 233                if (0 <= band) {
 234                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
 235                        hdr[4] = band;
 236                        write_or_die(fd, hdr, 5);
 237                } else {
 238                        xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
 239                        write_or_die(fd, hdr, 4);
 240                }
 241                write_or_die(fd, p, n);
 242                p += n;
 243                sz -= n;
 244        }
 245}