Coverage Report

Created: 2024-09-08 06:23

/src/git/sideband.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "color.h"
5
#include "config.h"
6
#include "editor.h"
7
#include "gettext.h"
8
#include "sideband.h"
9
#include "help.h"
10
#include "pkt-line.h"
11
#include "write-or-die.h"
12
13
struct keyword_entry {
14
  /*
15
   * We use keyword as config key so it should be a single alphanumeric word.
16
   */
17
  const char *keyword;
18
  char color[COLOR_MAXLEN];
19
};
20
21
static struct keyword_entry keywords[] = {
22
  { "hint", GIT_COLOR_YELLOW },
23
  { "warning",  GIT_COLOR_BOLD_YELLOW },
24
  { "success",  GIT_COLOR_BOLD_GREEN },
25
  { "error",  GIT_COLOR_BOLD_RED },
26
};
27
28
/* Returns a color setting (GIT_COLOR_NEVER, etc). */
29
static int use_sideband_colors(void)
30
0
{
31
0
  static int use_sideband_colors_cached = -1;
32
33
0
  const char *key = "color.remote";
34
0
  struct strbuf sb = STRBUF_INIT;
35
0
  const char *value;
36
0
  int i;
37
38
0
  if (use_sideband_colors_cached >= 0)
39
0
    return use_sideband_colors_cached;
40
41
0
  if (!git_config_get_string_tmp(key, &value))
42
0
    use_sideband_colors_cached = git_config_colorbool(key, value);
43
0
  else if (!git_config_get_string_tmp("color.ui", &value))
44
0
    use_sideband_colors_cached = git_config_colorbool("color.ui", value);
45
0
  else
46
0
    use_sideband_colors_cached = GIT_COLOR_AUTO;
47
48
0
  for (i = 0; i < ARRAY_SIZE(keywords); i++) {
49
0
    strbuf_reset(&sb);
50
0
    strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword);
51
0
    if (git_config_get_string_tmp(sb.buf, &value))
52
0
      continue;
53
0
    color_parse(value, keywords[i].color);
54
0
  }
55
56
0
  strbuf_release(&sb);
57
0
  return use_sideband_colors_cached;
58
0
}
59
60
void list_config_color_sideband_slots(struct string_list *list, const char *prefix)
61
0
{
62
0
  int i;
63
64
0
  for (i = 0; i < ARRAY_SIZE(keywords); i++)
65
0
    list_config_item(list, prefix, keywords[i].keyword);
66
0
}
67
68
/*
69
 * Optionally highlight one keyword in remote output if it appears at the start
70
 * of the line. This should be called for a single line only, which is
71
 * passed as the first N characters of the SRC array.
72
 *
73
 * It is fine to use "int n" here instead of "size_t n" as all calls to this
74
 * function pass an 'int' parameter. Additionally, the buffer involved in
75
 * storing these 'int' values takes input from a packet via the pkt-line
76
 * interface, which is capable of transferring only 64kB at a time.
77
 */
78
static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
79
0
{
80
0
  int i;
81
82
0
  if (!want_color_stderr(use_sideband_colors())) {
83
0
    strbuf_add(dest, src, n);
84
0
    return;
85
0
  }
86
87
0
  while (0 < n && isspace(*src)) {
88
0
    strbuf_addch(dest, *src);
89
0
    src++;
90
0
    n--;
91
0
  }
92
93
0
  for (i = 0; i < ARRAY_SIZE(keywords); i++) {
94
0
    struct keyword_entry *p = keywords + i;
95
0
    int len = strlen(p->keyword);
96
97
0
    if (n < len)
98
0
      continue;
99
    /*
100
     * Match case insensitively, so we colorize output from existing
101
     * servers regardless of the case that they use for their
102
     * messages. We only highlight the word precisely, so
103
     * "successful" stays uncolored.
104
     */
105
0
    if (!strncasecmp(p->keyword, src, len) &&
106
0
        (len == n || !isalnum(src[len]))) {
107
0
      strbuf_addstr(dest, p->color);
108
0
      strbuf_add(dest, src, len);
109
0
      strbuf_addstr(dest, GIT_COLOR_RESET);
110
0
      n -= len;
111
0
      src += len;
112
0
      break;
113
0
    }
114
0
  }
115
116
0
  strbuf_add(dest, src, n);
117
0
}
118
119
120
0
#define DISPLAY_PREFIX "remote: "
121
122
0
#define ANSI_SUFFIX "\033[K"
123
0
#define DUMB_SUFFIX "        "
124
125
int demultiplex_sideband(const char *me, int status,
126
       char *buf, int len,
127
       int die_on_error,
128
       struct strbuf *scratch,
129
       enum sideband_type *sideband_type)
130
0
{
131
0
  static const char *suffix;
132
0
  const char *b, *brk;
133
0
  int band;
134
135
0
  if (!suffix) {
136
0
    if (isatty(2) && !is_terminal_dumb())
137
0
      suffix = ANSI_SUFFIX;
138
0
    else
139
0
      suffix = DUMB_SUFFIX;
140
0
  }
141
142
0
  if (status == PACKET_READ_EOF) {
143
0
    strbuf_addf(scratch,
144
0
          "%s%s: unexpected disconnect while reading sideband packet",
145
0
          scratch->len ? "\n" : "", me);
146
0
    *sideband_type = SIDEBAND_PROTOCOL_ERROR;
147
0
    goto cleanup;
148
0
  }
149
150
0
  if (len < 0)
151
0
    BUG("negative length on non-eof packet read");
152
153
0
  if (len == 0) {
154
0
    if (status == PACKET_READ_NORMAL) {
155
0
      strbuf_addf(scratch,
156
0
            "%s%s: protocol error: missing sideband designator",
157
0
            scratch->len ? "\n" : "", me);
158
0
      *sideband_type = SIDEBAND_PROTOCOL_ERROR;
159
0
    } else {
160
      /* covers flush, delim, etc */
161
0
      *sideband_type = SIDEBAND_FLUSH;
162
0
    }
163
0
    goto cleanup;
164
0
  }
165
166
0
  band = buf[0] & 0xff;
167
0
  buf[len] = '\0';
168
0
  len--;
169
0
  switch (band) {
170
0
  case 3:
171
0
    if (die_on_error)
172
0
      die(_("remote error: %s"), buf + 1);
173
0
    strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
174
0
          DISPLAY_PREFIX);
175
0
    maybe_colorize_sideband(scratch, buf + 1, len);
176
177
0
    *sideband_type = SIDEBAND_REMOTE_ERROR;
178
0
    break;
179
0
  case 2:
180
0
    b = buf + 1;
181
182
    /*
183
     * Append a suffix to each nonempty line to clear the
184
     * end of the screen line.
185
     *
186
     * The output is accumulated in a buffer and
187
     * each line is printed to stderr using
188
     * write(2) to ensure inter-process atomicity.
189
     */
190
0
    while ((brk = strpbrk(b, "\n\r"))) {
191
0
      int linelen = brk - b;
192
193
      /*
194
       * For message accross packet boundary, there would have
195
       * a nonempty "scratch" buffer from last call of this
196
       * function, and there may have a leading CR/LF in "buf".
197
       * For this case we should add a clear-to-eol suffix to
198
       * clean leftover letters we previously have written on
199
       * the same line.
200
       */
201
0
      if (scratch->len && !linelen)
202
0
        strbuf_addstr(scratch, suffix);
203
204
0
      if (!scratch->len)
205
0
        strbuf_addstr(scratch, DISPLAY_PREFIX);
206
207
      /*
208
       * A use case that we should not add clear-to-eol suffix
209
       * to empty lines:
210
       *
211
       * For progress reporting we may receive a bunch of
212
       * percentage updates followed by '\r' to remain on the
213
       * same line, and at the end receive a single '\n' to
214
       * move to the next line. We should preserve the final
215
       * status report line by not appending clear-to-eol
216
       * suffix to this single line break.
217
       */
218
0
      if (linelen > 0) {
219
0
        maybe_colorize_sideband(scratch, b, linelen);
220
0
        strbuf_addstr(scratch, suffix);
221
0
      }
222
223
0
      strbuf_addch(scratch, *brk);
224
0
      write_in_full(2, scratch->buf, scratch->len);
225
0
      strbuf_reset(scratch);
226
227
0
      b = brk + 1;
228
0
    }
229
230
0
    if (*b) {
231
0
      strbuf_addstr(scratch, scratch->len ?
232
0
            "" : DISPLAY_PREFIX);
233
0
      maybe_colorize_sideband(scratch, b, strlen(b));
234
0
    }
235
0
    return 0;
236
0
  case 1:
237
0
    *sideband_type = SIDEBAND_PRIMARY;
238
0
    return 1;
239
0
  default:
240
0
    strbuf_addf(scratch, "%s%s: protocol error: bad band #%d",
241
0
          scratch->len ? "\n" : "", me, band);
242
0
    *sideband_type = SIDEBAND_PROTOCOL_ERROR;
243
0
    break;
244
0
  }
245
246
0
cleanup:
247
0
  if (die_on_error && *sideband_type == SIDEBAND_PROTOCOL_ERROR)
248
0
    die("%s", scratch->buf);
249
0
  if (scratch->len) {
250
0
    strbuf_addch(scratch, '\n');
251
0
    write_in_full(2, scratch->buf, scratch->len);
252
0
  }
253
0
  strbuf_release(scratch);
254
0
  return 1;
255
0
}
256
257
/*
258
 * fd is connected to the remote side; send the sideband data
259
 * over multiplexed packet stream.
260
 */
261
void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
262
0
{
263
0
  const char *p = data;
264
265
0
  while (sz) {
266
0
    unsigned n;
267
0
    char hdr[5];
268
269
0
    n = sz;
270
0
    if (packet_max - 5 < n)
271
0
      n = packet_max - 5;
272
0
    if (0 <= band) {
273
0
      xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
274
0
      hdr[4] = band;
275
0
      write_or_die(fd, hdr, 5);
276
0
    } else {
277
0
      xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
278
0
      write_or_die(fd, hdr, 4);
279
0
    }
280
0
    write_or_die(fd, p, n);
281
0
    p += n;
282
0
    sz -= n;
283
0
  }
284
0
}