diff options
Diffstat (limited to 'sideband.c')
| -rw-r--r-- | sideband.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/sideband.c b/sideband.c new file mode 100644 index 0000000000..6d7f943e43 --- /dev/null +++ b/sideband.c @@ -0,0 +1,133 @@ +#include "cache.h" +#include "pkt-line.h" +#include "sideband.h" + +/* + * Receive multiplexed output stream over git native protocol. + * in_stream is the input stream from the remote, which carries data + * in pkt_line format with band designator. Demultiplex it into out + * and err and return error appropriately. Band #1 carries the + * primary payload. Things coming over band #2 is not necessarily + * error; they are usually informative message on the standard error + * stream, aka "verbose"). A message over band #3 is a signal that + * the remote died unexpectedly. A flush() concludes the stream. + */ + +#define PREFIX "remote: " + +#define ANSI_SUFFIX "\033[K" +#define DUMB_SUFFIX " " + +int recv_sideband(const char *me, int in_stream, int out) +{ + const char *suffix; + char buf[LARGE_PACKET_MAX + 1]; + struct strbuf outbuf = STRBUF_INIT; + int retval = 0; + + if (isatty(2) && !is_terminal_dumb()) + suffix = ANSI_SUFFIX; + else + suffix = DUMB_SUFFIX; + + while (!retval) { + const char *b, *brk; + int band, len; + len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0); + if (len == 0) + break; + if (len < 1) { + strbuf_addf(&outbuf, + "%s%s: protocol error: no band designator", + outbuf.len ? "\n" : "", me); + retval = SIDEBAND_PROTOCOL_ERROR; + break; + } + band = buf[0] & 0xff; + buf[len] = '\0'; + len--; + switch (band) { + case 3: + strbuf_addf(&outbuf, "%s%s%s", outbuf.len ? "\n" : "", + PREFIX, buf + 1); + retval = SIDEBAND_REMOTE_ERROR; + break; + case 2: + b = buf + 1; + + /* + * Append a suffix to each nonempty line to clear the + * end of the screen line. + * + * The output is accumulated in a buffer and + * each line is printed to stderr using + * write(2) to ensure inter-process atomicity. + */ + while ((brk = strpbrk(b, "\n\r"))) { + int linelen = brk - b; + + if (!outbuf.len) + strbuf_addstr(&outbuf, PREFIX); + if (linelen > 0) { + strbuf_addf(&outbuf, "%.*s%s%c", + linelen, b, suffix, *brk); + } else { + strbuf_addch(&outbuf, *brk); + } + xwrite(2, outbuf.buf, outbuf.len); + strbuf_reset(&outbuf); + + b = brk + 1; + } + + if (*b) + strbuf_addf(&outbuf, "%s%s", + outbuf.len ? "" : PREFIX, b); + break; + case 1: + write_or_die(out, buf + 1, len); + break; + default: + strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d", + outbuf.len ? "\n" : "", me, band); + retval = SIDEBAND_PROTOCOL_ERROR; + break; + } + } + + if (outbuf.len) { + strbuf_addch(&outbuf, '\n'); + xwrite(2, outbuf.buf, outbuf.len); + } + strbuf_release(&outbuf); + return retval; +} + +/* + * fd is connected to the remote side; send the sideband data + * over multiplexed packet stream. + */ +void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max) +{ + const char *p = data; + + while (sz) { + unsigned n; + char hdr[5]; + + n = sz; + if (packet_max - 5 < n) + n = packet_max - 5; + if (0 <= band) { + xsnprintf(hdr, sizeof(hdr), "%04x", n + 5); + hdr[4] = band; + write_or_die(fd, hdr, 5); + } else { + xsnprintf(hdr, sizeof(hdr), "%04x", n + 4); + write_or_die(fd, hdr, 4); + } + write_or_die(fd, p, n); + p += n; + sz -= n; + } +} |
