rugged 0.21.2 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@
7
7
  #include "common.h"
8
8
  #include "path.h"
9
9
  #include "posix.h"
10
+ #include "repository.h"
10
11
  #ifdef GIT_WIN32
11
12
  #include "win32/posix.h"
12
13
  #include "win32/w32_util.h"
@@ -1155,3 +1156,258 @@ int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
1155
1156
 
1156
1157
  return 0;
1157
1158
  }
1159
+
1160
+ /* Reject paths like AUX or COM1, or those versions that end in a dot or
1161
+ * colon. ("AUX." or "AUX:")
1162
+ */
1163
+ GIT_INLINE(bool) verify_dospath(
1164
+ const char *component,
1165
+ size_t len,
1166
+ const char dospath[3],
1167
+ bool trailing_num)
1168
+ {
1169
+ size_t last = trailing_num ? 4 : 3;
1170
+
1171
+ if (len < last || git__strncasecmp(component, dospath, 3) != 0)
1172
+ return true;
1173
+
1174
+ if (trailing_num && !git__isdigit(component[3]))
1175
+ return true;
1176
+
1177
+ return (len > last &&
1178
+ component[last] != '.' &&
1179
+ component[last] != ':');
1180
+ }
1181
+
1182
+ static int32_t next_hfs_char(const char **in, size_t *len)
1183
+ {
1184
+ while (*len) {
1185
+ int32_t codepoint;
1186
+ int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
1187
+ if (cp_len < 0)
1188
+ return -1;
1189
+
1190
+ (*in) += cp_len;
1191
+ (*len) -= cp_len;
1192
+
1193
+ /* these code points are ignored completely */
1194
+ switch (codepoint) {
1195
+ case 0x200c: /* ZERO WIDTH NON-JOINER */
1196
+ case 0x200d: /* ZERO WIDTH JOINER */
1197
+ case 0x200e: /* LEFT-TO-RIGHT MARK */
1198
+ case 0x200f: /* RIGHT-TO-LEFT MARK */
1199
+ case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
1200
+ case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
1201
+ case 0x202c: /* POP DIRECTIONAL FORMATTING */
1202
+ case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
1203
+ case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
1204
+ case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
1205
+ case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
1206
+ case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
1207
+ case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
1208
+ case 0x206e: /* NATIONAL DIGIT SHAPES */
1209
+ case 0x206f: /* NOMINAL DIGIT SHAPES */
1210
+ case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
1211
+ continue;
1212
+ }
1213
+
1214
+ /* fold into lowercase -- this will only fold characters in
1215
+ * the ASCII range, which is perfectly fine, because the
1216
+ * git folder name can only be composed of ascii characters
1217
+ */
1218
+ return tolower(codepoint);
1219
+ }
1220
+ return 0; /* NULL byte -- end of string */
1221
+ }
1222
+
1223
+ static bool verify_dotgit_hfs(const char *path, size_t len)
1224
+ {
1225
+ if (next_hfs_char(&path, &len) != '.' ||
1226
+ next_hfs_char(&path, &len) != 'g' ||
1227
+ next_hfs_char(&path, &len) != 'i' ||
1228
+ next_hfs_char(&path, &len) != 't' ||
1229
+ next_hfs_char(&path, &len) != 0)
1230
+ return true;
1231
+
1232
+ return false;
1233
+ }
1234
+
1235
+ GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
1236
+ {
1237
+ const char *shortname = NULL;
1238
+ size_t i, start, shortname_len = 0;
1239
+
1240
+ /* See if the repo has a custom shortname (not "GIT~1") */
1241
+ if (repo &&
1242
+ (shortname = git_repository__8dot3_name(repo)) &&
1243
+ shortname != git_repository__8dot3_default)
1244
+ shortname_len = strlen(shortname);
1245
+
1246
+ if (len >= 4 && strncasecmp(path, ".git", 4) == 0)
1247
+ start = 4;
1248
+ else if (len >= git_repository__8dot3_default_len &&
1249
+ strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0)
1250
+ start = git_repository__8dot3_default_len;
1251
+ else if (shortname_len && len >= shortname_len &&
1252
+ strncasecmp(path, shortname, shortname_len) == 0)
1253
+ start = shortname_len;
1254
+ else
1255
+ return true;
1256
+
1257
+ /* Reject paths beginning with ".git\" */
1258
+ if (path[start] == '\\')
1259
+ return false;
1260
+
1261
+ for (i = start; i < len; i++) {
1262
+ if (path[i] != ' ' && path[i] != '.')
1263
+ return true;
1264
+ }
1265
+
1266
+ return false;
1267
+ }
1268
+
1269
+ GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
1270
+ {
1271
+ if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
1272
+ return false;
1273
+
1274
+ if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
1275
+ return false;
1276
+
1277
+ if (flags & GIT_PATH_REJECT_NT_CHARS) {
1278
+ if (c < 32)
1279
+ return false;
1280
+
1281
+ switch (c) {
1282
+ case '<':
1283
+ case '>':
1284
+ case ':':
1285
+ case '"':
1286
+ case '|':
1287
+ case '?':
1288
+ case '*':
1289
+ return false;
1290
+ }
1291
+ }
1292
+
1293
+ return true;
1294
+ }
1295
+
1296
+ /*
1297
+ * We fundamentally don't like some paths when dealing with user-inputted
1298
+ * strings (in checkout or ref names): we don't want dot or dot-dot
1299
+ * anywhere, we want to avoid writing weird paths on Windows that can't
1300
+ * be handled by tools that use the non-\\?\ APIs, we don't want slashes
1301
+ * or double slashes at the end of paths that can make them ambiguous.
1302
+ *
1303
+ * For checkout, we don't want to recurse into ".git" either.
1304
+ */
1305
+ static bool verify_component(
1306
+ git_repository *repo,
1307
+ const char *component,
1308
+ size_t len,
1309
+ unsigned int flags)
1310
+ {
1311
+ if (len == 0)
1312
+ return false;
1313
+
1314
+ if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1315
+ len == 1 && component[0] == '.')
1316
+ return false;
1317
+
1318
+ if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1319
+ len == 2 && component[0] == '.' && component[1] == '.')
1320
+ return false;
1321
+
1322
+ if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
1323
+ return false;
1324
+
1325
+ if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
1326
+ return false;
1327
+
1328
+ if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
1329
+ return false;
1330
+
1331
+ if (flags & GIT_PATH_REJECT_DOS_PATHS) {
1332
+ if (!verify_dospath(component, len, "CON", false) ||
1333
+ !verify_dospath(component, len, "PRN", false) ||
1334
+ !verify_dospath(component, len, "AUX", false) ||
1335
+ !verify_dospath(component, len, "NUL", false) ||
1336
+ !verify_dospath(component, len, "COM", true) ||
1337
+ !verify_dospath(component, len, "LPT", true))
1338
+ return false;
1339
+ }
1340
+
1341
+ if (flags & GIT_PATH_REJECT_DOT_GIT_HFS &&
1342
+ !verify_dotgit_hfs(component, len))
1343
+ return false;
1344
+
1345
+ if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS &&
1346
+ !verify_dotgit_ntfs(repo, component, len))
1347
+ return false;
1348
+
1349
+ if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
1350
+ (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
1351
+ (flags & GIT_PATH_REJECT_DOT_GIT) &&
1352
+ len == 4 &&
1353
+ component[0] == '.' &&
1354
+ (component[1] == 'g' || component[1] == 'G') &&
1355
+ (component[2] == 'i' || component[2] == 'I') &&
1356
+ (component[3] == 't' || component[3] == 'T'))
1357
+ return false;
1358
+
1359
+ return true;
1360
+ }
1361
+
1362
+ GIT_INLINE(unsigned int) dotgit_flags(
1363
+ git_repository *repo,
1364
+ unsigned int flags)
1365
+ {
1366
+ int protectHFS = 0, protectNTFS = 0;
1367
+
1368
+ #ifdef __APPLE__
1369
+ protectHFS = 1;
1370
+ #endif
1371
+
1372
+ #ifdef GIT_WIN32
1373
+ protectNTFS = 1;
1374
+ #endif
1375
+
1376
+ if (repo && !protectHFS)
1377
+ git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
1378
+ if (protectHFS)
1379
+ flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
1380
+
1381
+ if (repo && !protectNTFS)
1382
+ git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
1383
+ if (protectNTFS)
1384
+ flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
1385
+
1386
+ return flags;
1387
+ }
1388
+
1389
+ bool git_path_isvalid(
1390
+ git_repository *repo,
1391
+ const char *path,
1392
+ unsigned int flags)
1393
+ {
1394
+ const char *start, *c;
1395
+
1396
+ /* Upgrade the ".git" checks based on platform */
1397
+ if ((flags & GIT_PATH_REJECT_DOT_GIT))
1398
+ flags = dotgit_flags(repo, flags);
1399
+
1400
+ for (start = c = path; *c; c++) {
1401
+ if (!verify_char(*c, flags))
1402
+ return false;
1403
+
1404
+ if (*c == '/') {
1405
+ if (!verify_component(repo, start, (c - start), flags))
1406
+ return false;
1407
+
1408
+ start = c+1;
1409
+ }
1410
+ }
1411
+
1412
+ return verify_component(repo, start, (c - start), flags);
1413
+ }
@@ -8,6 +8,7 @@
8
8
  #define INCLUDE_path_h__
9
9
 
10
10
  #include "common.h"
11
+ #include "posix.h"
11
12
  #include "buffer.h"
12
13
  #include "vector.h"
13
14
 
@@ -441,4 +442,47 @@ extern bool git_path_does_fs_decompose_unicode(const char *root);
441
442
  /* Used for paths to repositories on the filesystem */
442
443
  extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
443
444
 
445
+ /* Flags to determine path validity in `git_path_isvalid` */
446
+ #define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
447
+ #define GIT_PATH_REJECT_DOT_GIT (1 << 1)
448
+ #define GIT_PATH_REJECT_SLASH (1 << 2)
449
+ #define GIT_PATH_REJECT_BACKSLASH (1 << 3)
450
+ #define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
451
+ #define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
452
+ #define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
453
+ #define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
454
+ #define GIT_PATH_REJECT_NT_CHARS (1 << 8)
455
+ #define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
456
+ #define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
457
+
458
+ /* Default path safety for writing files to disk: since we use the
459
+ * Win32 "File Namespace" APIs ("\\?\") we need to protect from
460
+ * paths that the normal Win32 APIs would not write.
461
+ */
462
+ #ifdef GIT_WIN32
463
+ # define GIT_PATH_REJECT_DEFAULTS \
464
+ GIT_PATH_REJECT_TRAVERSAL | \
465
+ GIT_PATH_REJECT_BACKSLASH | \
466
+ GIT_PATH_REJECT_TRAILING_DOT | \
467
+ GIT_PATH_REJECT_TRAILING_SPACE | \
468
+ GIT_PATH_REJECT_TRAILING_COLON | \
469
+ GIT_PATH_REJECT_DOS_PATHS | \
470
+ GIT_PATH_REJECT_NT_CHARS
471
+ #else
472
+ # define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
473
+ #endif
474
+
475
+ /*
476
+ * Determine whether a path is a valid git path or not - this must not contain
477
+ * a '.' or '..' component, or a component that is ".git" (in any case).
478
+ *
479
+ * `repo` is optional. If specified, it will be used to determine the short
480
+ * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
481
+ * in addition to the default of "git~1".
482
+ */
483
+ extern bool git_path_isvalid(
484
+ git_repository *repo,
485
+ const char *path,
486
+ unsigned int flags);
487
+
444
488
  #endif
@@ -707,11 +707,16 @@ static int reference_path_available(
707
707
 
708
708
  static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
709
709
  {
710
- int error;
710
+ int error;
711
711
  git_buf ref_path = GIT_BUF_INIT;
712
712
 
713
713
  assert(file && backend && name);
714
714
 
715
+ if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) {
716
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
717
+ return GIT_EINVALIDSPEC;
718
+ }
719
+
715
720
  /* Remove a possibly existing empty directory hierarchy
716
721
  * which name would collide with the reference name
717
722
  */
@@ -1573,6 +1578,11 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char
1573
1578
 
1574
1579
  repo = backend->repo;
1575
1580
 
1581
+ if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) {
1582
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
1583
+ return GIT_EINVALIDSPEC;
1584
+ }
1585
+
1576
1586
  if (retrieve_reflog_path(&log_path, repo, refname) < 0)
1577
1587
  return -1;
1578
1588
 
@@ -37,6 +37,9 @@
37
37
 
38
38
  #define GIT_REPO_VERSION 0
39
39
 
40
+ const char *git_repository__8dot3_default = "GIT~1";
41
+ size_t git_repository__8dot3_default_len = 5;
42
+
40
43
  static void set_odb(git_repository *repo, git_odb *odb)
41
44
  {
42
45
  if (odb) {
@@ -120,6 +123,7 @@ void git_repository_free(git_repository *repo)
120
123
  git__free(repo->path_repository);
121
124
  git__free(repo->workdir);
122
125
  git__free(repo->namespace);
126
+ git__free(repo->name_8dot3);
123
127
 
124
128
  git__memzero(repo, sizeof(*repo));
125
129
  git__free(repo);
@@ -791,6 +795,27 @@ const char *git_repository_get_namespace(git_repository *repo)
791
795
  return repo->namespace;
792
796
  }
793
797
 
798
+ const char *git_repository__8dot3_name(git_repository *repo)
799
+ {
800
+ if (!repo->has_8dot3) {
801
+ repo->has_8dot3 = 1;
802
+
803
+ #ifdef GIT_WIN32
804
+ if (!repo->is_bare) {
805
+ repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository);
806
+
807
+ /* We anticipate the 8.3 name is "GIT~1", so use a static for
808
+ * easy testing in the common case */
809
+ if (strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
810
+ repo->has_8dot3_default = 1;
811
+ }
812
+ #endif
813
+ }
814
+
815
+ return repo->has_8dot3_default ?
816
+ git_repository__8dot3_default : repo->name_8dot3;
817
+ }
818
+
794
819
  static int check_repositoryformatversion(git_config *config)
795
820
  {
796
821
  int version;
@@ -40,6 +40,8 @@ typedef enum {
40
40
  GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
41
41
  GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
42
42
  GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
43
+ GIT_CVAR_PROTECTHFS, /* core.protectHFS */
44
+ GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */
43
45
  GIT_CVAR_CACHE_MAX
44
46
  } git_cvar_cached;
45
47
 
@@ -96,6 +98,10 @@ typedef enum {
96
98
  /* core.logallrefupdates */
97
99
  GIT_LOGALLREFUPDATES_UNSET = 2,
98
100
  GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
101
+ /* core.protectHFS */
102
+ GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
103
+ /* core.protectNTFS */
104
+ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
99
105
  } git_cvar_value;
100
106
 
101
107
  /* internal repository init flags */
@@ -120,8 +126,11 @@ struct git_repository {
120
126
  char *path_repository;
121
127
  char *workdir;
122
128
  char *namespace;
129
+ char *name_8dot3;
123
130
 
124
- unsigned is_bare:1;
131
+ unsigned is_bare:1,
132
+ has_8dot3:1,
133
+ has_8dot3_default:1;
125
134
  unsigned int lru_counter;
126
135
 
127
136
  git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
@@ -172,4 +181,19 @@ GIT_INLINE(int) git_repository__ensure_not_bare(
172
181
 
173
182
  int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
174
183
 
184
+ /*
185
+ * Gets the DOS-compatible 8.3 "short name". This will return only the
186
+ * short name for the repository directory (ie, "git~1" for ".git"). This
187
+ * will always return a pointer to `git_repository__8dot3_default` when
188
+ * "GIT~1" is the short name. This will return NULL for bare repositories,
189
+ * and systems that do not have a short name.
190
+ */
191
+ const char *git_repository__8dot3_name(git_repository *repo);
192
+
193
+ /* The default DOS-compatible 8.3 "short name" for a git repository,
194
+ * "GIT~1".
195
+ */
196
+ extern const char *git_repository__8dot3_default;
197
+ extern size_t git_repository__8dot3_default_len;
198
+
175
199
  #endif
@@ -592,7 +592,9 @@ int git_smart__download_pack(
592
592
  }
593
593
  } else if (pkt->type == GIT_PKT_DATA) {
594
594
  git_pkt_data *p = (git_pkt_data *) pkt;
595
- error = writepack->append(writepack, p->data, p->len, stats);
595
+
596
+ if (p->len)
597
+ error = writepack->append(writepack, p->data, p->len, stats);
596
598
  } else if (pkt->type == GIT_PKT_FLUSH) {
597
599
  /* A flush indicates the end of the packfile */
598
600
  git__free(pkt);
@@ -55,7 +55,7 @@ static int valid_entry_name(const char *filename)
55
55
  (*filename != '.' ||
56
56
  (strcmp(filename, ".") != 0 &&
57
57
  strcmp(filename, "..") != 0 &&
58
- strcmp(filename, DOT_GIT) != 0));
58
+ strcasecmp(filename, DOT_GIT) != 0));
59
59
  }
60
60
 
61
61
  static int entry_sort_cmp(const void *a, const void *b)
@@ -20,7 +20,7 @@ long git__page_size(void)
20
20
 
21
21
  int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
22
22
  {
23
- int mprot = 0;
23
+ int mprot = PROT_READ;
24
24
  int mflag = 0;
25
25
 
26
26
  GIT_MMAP_VALIDATE(out, len, prot, flags);
@@ -29,9 +29,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
29
29
  out->len = 0;
30
30
 
31
31
  if (prot & GIT_PROT_WRITE)
32
- mprot = PROT_WRITE;
33
- else if (prot & GIT_PROT_READ)
34
- mprot = PROT_READ;
32
+ mprot |= PROT_WRITE;
35
33
 
36
34
  if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
37
35
  mflag = MAP_SHARED;
@@ -250,6 +250,21 @@ int git__prefixcmp_icase(const char *str, const char *prefix)
250
250
  return strncasecmp(str, prefix, strlen(prefix));
251
251
  }
252
252
 
253
+ int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
254
+ {
255
+ int s, p;
256
+
257
+ while(str_n--) {
258
+ s = (unsigned char)tolower(*str++);
259
+ p = (unsigned char)tolower(*prefix++);
260
+
261
+ if (s != p)
262
+ return s - p;
263
+ }
264
+
265
+ return (0 - *prefix);
266
+ }
267
+
253
268
  int git__suffixcmp(const char *str, const char *suffix)
254
269
  {
255
270
  size_t a = strlen(str);
@@ -648,3 +663,79 @@ void git__insertsort_r(
648
663
  if (freeswap)
649
664
  git__free(swapel);
650
665
  }
666
+
667
+ static const int8_t utf8proc_utf8class[256] = {
668
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
669
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
670
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
671
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
672
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
673
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
674
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
675
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
676
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
678
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
679
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
680
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
681
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
682
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
683
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
684
+ };
685
+
686
+ int git__utf8_charlen(const uint8_t *str, int str_len)
687
+ {
688
+ int length, i;
689
+
690
+ length = utf8proc_utf8class[str[0]];
691
+ if (!length)
692
+ return -1;
693
+
694
+ if (str_len >= 0 && length > str_len)
695
+ return -str_len;
696
+
697
+ for (i = 1; i < length; i++) {
698
+ if ((str[i] & 0xC0) != 0x80)
699
+ return -i;
700
+ }
701
+
702
+ return length;
703
+ }
704
+
705
+ int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
706
+ {
707
+ int length;
708
+ int32_t uc = -1;
709
+
710
+ *dst = -1;
711
+ length = git__utf8_charlen(str, str_len);
712
+ if (length < 0)
713
+ return -1;
714
+
715
+ switch (length) {
716
+ case 1:
717
+ uc = str[0];
718
+ break;
719
+ case 2:
720
+ uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
721
+ if (uc < 0x80) uc = -1;
722
+ break;
723
+ case 3:
724
+ uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
725
+ + (str[2] & 0x3F);
726
+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
727
+ (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
728
+ break;
729
+ case 4:
730
+ uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
731
+ + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
732
+ if (uc < 0x10000 || uc >= 0x110000) uc = -1;
733
+ break;
734
+ }
735
+
736
+ if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
737
+ return -1;
738
+
739
+ *dst = uc;
740
+ return length;
741
+ }
@@ -106,6 +106,7 @@ GIT_INLINE(void) git__free(void *ptr)
106
106
 
107
107
  extern int git__prefixcmp(const char *str, const char *prefix);
108
108
  extern int git__prefixcmp_icase(const char *str, const char *prefix);
109
+ extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
109
110
  extern int git__suffixcmp(const char *str, const char *suffix);
110
111
 
111
112
  GIT_INLINE(int) git__signum(int val)
@@ -366,6 +367,17 @@ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
366
367
  */
367
368
  extern size_t git__unescape(char *str);
368
369
 
370
+ /*
371
+ * Iterate through an UTF-8 string, yielding one
372
+ * codepoint at a time.
373
+ *
374
+ * @param str current position in the string
375
+ * @param str_len size left in the string; -1 if the string is NULL-terminated
376
+ * @param dst pointer where to store the current codepoint
377
+ * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
378
+ */
379
+ extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
380
+
369
381
  /*
370
382
  * Safely zero-out memory, making sure that the compiler
371
383
  * doesn't optimize away the operation.
@@ -5,6 +5,7 @@
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
7
 
8
+ #include "path_w32.h"
8
9
  #include "utf-conv.h"
9
10
  #include "path.h"
10
11
  #include "findfile.h"