Coverage Report

Created: 2026-02-26 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/url.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "hex-ll.h"
3
#include "strbuf.h"
4
#include "url.h"
5
6
int is_rfc3986_unreserved(char ch)
7
0
{
8
0
  return isalnum(ch) ||
9
0
    ch == '-' || ch == '_' || ch == '.' || ch == '~';
10
0
}
11
12
int is_casefolding_rfc3986_unreserved(char c)
13
0
{
14
0
  return (c >= 'a' && c <= 'z') ||
15
0
         (c >= '0' && c <= '9') ||
16
0
         c == '-' || c == '.' || c == '_' || c == '~';
17
0
}
18
19
int is_urlschemechar(int first_flag, int ch)
20
0
{
21
  /*
22
   * The set of valid URL schemes, as per STD66 (RFC3986) is
23
   * '[A-Za-z][A-Za-z0-9+.-]*'. But use slightly looser check
24
   * of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
25
   * of check used '[A-Za-z0-9]+' so not to break any remote
26
   * helpers.
27
   */
28
0
  int alphanumeric, special;
29
0
  alphanumeric = ch > 0 && isalnum(ch);
30
0
  special = ch == '+' || ch == '-' || ch == '.';
31
0
  return alphanumeric || (!first_flag && special);
32
0
}
33
34
int is_url(const char *url)
35
0
{
36
  /* Is "scheme" part reasonable? */
37
0
  if (!url || !is_urlschemechar(1, *url++))
38
0
    return 0;
39
0
  while (*url && *url != ':') {
40
0
    if (!is_urlschemechar(0, *url++))
41
0
      return 0;
42
0
  }
43
  /* We've seen "scheme"; we want colon-slash-slash */
44
0
  return (url[0] == ':' && url[1] == '/' && url[2] == '/');
45
0
}
46
47
static char *url_decode_internal(const char **query, int len,
48
         const char *stop_at, struct strbuf *out,
49
         int decode_plus)
50
0
{
51
0
  const char *q = *query;
52
53
0
  while (len) {
54
0
    unsigned char c = *q;
55
56
0
    if (!c)
57
0
      break;
58
0
    if (stop_at && strchr(stop_at, c)) {
59
0
      q++;
60
0
      len--;
61
0
      break;
62
0
    }
63
64
0
    if (c == '%' && (len < 0 || len >= 3)) {
65
0
      int val = hex2chr(q + 1);
66
0
      if (0 < val) {
67
0
        strbuf_addch(out, val);
68
0
        q += 3;
69
0
        len -= 3;
70
0
        continue;
71
0
      }
72
0
    }
73
74
0
    if (decode_plus && c == '+')
75
0
      strbuf_addch(out, ' ');
76
0
    else
77
0
      strbuf_addch(out, c);
78
0
    q++;
79
0
    len--;
80
0
  }
81
0
  *query = q;
82
0
  return strbuf_detach(out, NULL);
83
0
}
84
85
char *url_decode(const char *url)
86
0
{
87
0
  return url_decode_mem(url, strlen(url));
88
0
}
89
90
char *url_decode_mem(const char *url, int len)
91
0
{
92
0
  struct strbuf out = STRBUF_INIT;
93
0
  const char *colon = memchr(url, ':', len);
94
95
  /* Skip protocol part if present */
96
0
  if (colon && url < colon) {
97
0
    strbuf_add(&out, url, colon - url);
98
0
    len -= colon - url;
99
0
    url = colon;
100
0
  }
101
0
  return url_decode_internal(&url, len, NULL, &out, 0);
102
0
}
103
104
char *url_percent_decode(const char *encoded)
105
0
{
106
0
  struct strbuf out = STRBUF_INIT;
107
0
  return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0);
108
0
}
109
110
char *url_decode_parameter_name(const char **query)
111
0
{
112
0
  struct strbuf out = STRBUF_INIT;
113
0
  return url_decode_internal(query, -1, "&=", &out, 1);
114
0
}
115
116
char *url_decode_parameter_value(const char **query)
117
0
{
118
0
  struct strbuf out = STRBUF_INIT;
119
0
  return url_decode_internal(query, -1, "&", &out, 1);
120
0
}
121
122
void end_url_with_slash(struct strbuf *buf, const char *url)
123
0
{
124
0
  strbuf_addstr(buf, url);
125
0
  strbuf_complete(buf, '/');
126
0
}
127
128
void str_end_url_with_slash(const char *url, char **dest)
129
0
{
130
0
  struct strbuf buf = STRBUF_INIT;
131
0
  end_url_with_slash(&buf, url);
132
0
  free(*dest);
133
  *dest = strbuf_detach(&buf, NULL);
134
0
}