Coverage Report

Created: 2026-09-28 06:15

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
2.37k
{
51
2.37k
  const char *q = *query;
52
53
92.5M
  while (len) {
54
92.5M
    unsigned char c = *q;
55
56
92.5M
    if (!c)
57
530
      break;
58
92.5M
    if (stop_at && strchr(stop_at, c)) {
59
296
      q++;
60
296
      len--;
61
296
      break;
62
296
    }
63
64
92.5M
    if (c == '%' && (len < 0 || len >= 3)) {
65
9.98M
      int val = hex2chr(q + 1);
66
9.98M
      if (0 < val) {
67
24.8k
        strbuf_addch(out, val);
68
24.8k
        q += 3;
69
24.8k
        len -= 3;
70
24.8k
        continue;
71
24.8k
      }
72
9.98M
    }
73
74
92.5M
    if (decode_plus && c == '+')
75
3.24M
      strbuf_addch(out, ' ');
76
89.2M
    else
77
89.2M
      strbuf_addch(out, c);
78
92.5M
    q++;
79
92.5M
    len--;
80
92.5M
  }
81
2.37k
  *query = q;
82
2.37k
  return strbuf_detach(out, NULL);
83
2.37k
}
84
85
char *url_decode(const char *url)
86
631
{
87
631
  return url_decode_mem(url, strlen(url));
88
631
}
89
90
char *url_decode_mem(const char *url, int len)
91
1.13k
{
92
1.13k
  struct strbuf out = STRBUF_INIT;
93
1.13k
  const char *colon = memchr(url, ':', len);
94
95
  /* Skip protocol part if present */
96
1.13k
  if (colon && url < colon) {
97
487
    strbuf_add(&out, url, colon - url);
98
487
    len -= colon - url;
99
487
    url = colon;
100
487
  }
101
1.13k
  return url_decode_internal(&url, len, NULL, &out, 0);
102
1.13k
}
103
104
char *url_percent_decode(const char *encoded)
105
413
{
106
413
  struct strbuf out = STRBUF_INIT;
107
413
  return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0);
108
413
}
109
110
char *url_decode_parameter_name(const char **query)
111
413
{
112
413
  struct strbuf out = STRBUF_INIT;
113
413
  return url_decode_internal(query, -1, "&=", &out, 1);
114
413
}
115
116
char *url_decode_parameter_value(const char **query)
117
413
{
118
413
  struct strbuf out = STRBUF_INIT;
119
413
  return url_decode_internal(query, -1, "&", &out, 1);
120
413
}
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
0
  *dest = strbuf_detach(&buf, NULL);
134
0
}
135
136
int url_is_local_not_ssh(const char *url)
137
0
{
138
0
  const char *colon = strchr(url, ':');
139
0
  const char *slash = strchr(url, '/');
140
0
  return !colon || (slash && slash < colon) ||
141
0
    (has_dos_drive_prefix(url) && is_valid_path(url));
142
0
}
143
144
enum url_scheme url_get_scheme(const char *name)
145
0
{
146
0
  if (!strcmp(name, "ssh"))
147
0
    return URL_SCHEME_SSH;
148
0
  if (!strcmp(name, "git"))
149
0
    return URL_SCHEME_GIT;
150
0
  if (!strcmp(name, "git+ssh")) /* deprecated - do not use */
151
0
    return URL_SCHEME_SSH;
152
0
  if (!strcmp(name, "ssh+git")) /* deprecated - do not use */
153
0
    return URL_SCHEME_SSH;
154
0
  if (!strcmp(name, "file"))
155
0
    return URL_SCHEME_FILE;
156
0
  return URL_SCHEME_UNKNOWN;
157
0
}