aboutsummaryrefslogtreecommitdiffstats
path: root/sideband.c
diff options
context:
space:
mode:
Diffstat (limited to 'sideband.c')
-rw-r--r--sideband.c133
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;
+ }
+}