git/sideband.c
Lukas Fleischer 5e5be9e257 sideband.c: refactor recv_sideband()
We used character buffer manipulations to split messages from the
sideband at line breaks and insert "remote: " at the beginning of
each line, using the packet size to determine the end of a message.

However, since it is safe to assume that diagnostic messages from
the sideband never contain NUL characters, we can also NUL-terminate
the buffer, use strpbrk() for splitting lines and use format strings
to insert the prefix, to make the code easier to read and maintain.

A strbuf is used for accumulating the output which is then printed
using a single write(2) call to ensure the atomicity of the output.
See 9ac13ec (atomic write for sideband remote messages, 2006-10-11)
for details.

Helped-by: Jeff King <peff@peff.net>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Lukas Fleischer <lfleischer@lfos.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 13:09:37 -07:00

137 lines
3.1 KiB
C

#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 *term, *suffix;
char buf[LARGE_PACKET_MAX + 1];
struct strbuf outbuf = STRBUF_INIT;
int retval = 0;
term = getenv("TERM");
if (isatty(2) && term && strcmp(term, "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_addf(&outbuf, "%s", PREFIX);
if (linelen > 0) {
strbuf_addf(&outbuf, "%.*s%s%c",
linelen, b, suffix, *brk);
} else {
strbuf_addf(&outbuf, "%c", *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_addf(&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.
*/
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
{
ssize_t ssz = sz;
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) {
sprintf(hdr, "%04x", n + 5);
hdr[4] = band;
write_or_die(fd, hdr, 5);
} else {
sprintf(hdr, "%04x", n + 4);
write_or_die(fd, hdr, 4);
}
write_or_die(fd, p, n);
p += n;
sz -= n;
}
return ssz;
}