]> git.proxmox.com Git - libgit2.git/blob - src/util.c
pkt-line: parse other-ref lines
[libgit2.git] / src / util.c
1 #define GIT__NO_HIDE_MALLOC
2 #include <git2.h>
3 #include "common.h"
4 #include "fnmatch.h"
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <ctype.h>
8
9 #ifdef _MSV_VER
10 # include <Shlwapi.h>
11 #else
12 # include <fnmatch.h>
13 #endif
14
15 void git_libgit2_version(int *major, int *minor, int *rev)
16 {
17 *major = LIBGIT2_VER_MAJOR;
18 *minor = LIBGIT2_VER_MINOR;
19 *rev = LIBGIT2_VER_REVISION;
20 }
21
22 void git_strarray_free(git_strarray *array)
23 {
24 size_t i;
25 for (i = 0; i < array->count; ++i)
26 free(array->strings[i]);
27
28 free(array->strings);
29 }
30
31 int git__fnmatch(const char *pattern, const char *name, int flags)
32 {
33 int ret;
34
35 ret = fnmatch(pattern, name, flags);
36 switch (ret) {
37 case 0:
38 return GIT_SUCCESS;
39 case FNM_NOMATCH:
40 return GIT_ENOMATCH;
41 default:
42 return git__throw(GIT_EOSERR, "Error trying to match path");
43 }
44 }
45
46 int git__strtol32(long *result, const char *nptr, const char **endptr, int base)
47 {
48 const char *p;
49 long n, nn;
50 int c, ovfl, v, neg, ndig;
51
52 p = nptr;
53 neg = 0;
54 n = 0;
55 ndig = 0;
56 ovfl = 0;
57
58 /*
59 * White space
60 */
61 while (isspace(*p))
62 p++;
63
64 /*
65 * Sign
66 */
67 if (*p == '-' || *p == '+')
68 if (*p++ == '-')
69 neg = 1;
70
71 /*
72 * Base
73 */
74 if (base == 0) {
75 if (*p != '0')
76 base = 10;
77 else {
78 base = 8;
79 if (p[1] == 'x' || p[1] == 'X') {
80 p += 2;
81 base = 16;
82 }
83 }
84 } else if (base == 16 && *p == '0') {
85 if (p[1] == 'x' || p[1] == 'X')
86 p += 2;
87 } else if (base < 0 || 36 < base)
88 goto Return;
89
90 /*
91 * Non-empty sequence of digits
92 */
93 for (;; p++,ndig++) {
94 c = *p;
95 v = base;
96 if ('0'<=c && c<='9')
97 v = c - '0';
98 else if ('a'<=c && c<='z')
99 v = c - 'a' + 10;
100 else if ('A'<=c && c<='Z')
101 v = c - 'A' + 10;
102 if (v >= base)
103 break;
104 nn = n*base + v;
105 if (nn < n)
106 ovfl = 1;
107 n = nn;
108 }
109
110 Return:
111 if (ndig == 0)
112 return git__throw(GIT_ENOTNUM, "Failed to convert string to long. Not a number");
113
114 if (endptr)
115 *endptr = p;
116
117 if (ovfl)
118 return git__throw(GIT_EOVERFLOW, "Failed to convert string to long. Overflow error");
119
120 *result = neg ? -n : n;
121 return GIT_SUCCESS;
122 }
123
124 int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...)
125 {
126 va_list va;
127 int r;
128
129 va_start(va, fmt);
130 r = vsnprintf(buf, buf_sz, fmt, va);
131 va_end(va);
132 if (r < 0 || ((size_t) r) >= buf_sz)
133 return git__throw(GIT_ERROR, "Failed to format string");
134 return r;
135 }
136
137 void git__strntolower(char *str, int len)
138 {
139 int i;
140
141 for (i = 0; i < len; ++i) {
142 str[i] = (char) tolower(str[i]);
143 }
144 }
145
146 void git__strtolower(char *str)
147 {
148 git__strntolower(str, strlen(str));
149 }
150
151 int git__prefixcmp(const char *str, const char *prefix)
152 {
153 for (;;) {
154 char p = *(prefix++), s;
155 if (!p)
156 return 0;
157 if ((s = *(str++)) != p)
158 return s - p;
159 }
160 }
161
162 int git__suffixcmp(const char *str, const char *suffix)
163 {
164 size_t a = strlen(str);
165 size_t b = strlen(suffix);
166 if (a < b)
167 return -1;
168 return strcmp(str + (a - b), suffix);
169 }
170
171 /*
172 * Based on the Android implementation, BSD licensed.
173 * Check http://android.git.kernel.org/
174 */
175 int git__basename_r(char *buffer, size_t bufflen, const char *path)
176 {
177 const char *endp, *startp;
178 int len, result;
179
180 /* Empty or NULL string gets treated as "." */
181 if (path == NULL || *path == '\0') {
182 startp = ".";
183 len = 1;
184 goto Exit;
185 }
186
187 /* Strip trailing slashes */
188 endp = path + strlen(path) - 1;
189 while (endp > path && *endp == '/')
190 endp--;
191
192 /* All slashes becomes "/" */
193 if (endp == path && *endp == '/') {
194 startp = "/";
195 len = 1;
196 goto Exit;
197 }
198
199 /* Find the start of the base */
200 startp = endp;
201 while (startp > path && *(startp - 1) != '/')
202 startp--;
203
204 len = endp - startp +1;
205
206 Exit:
207 result = len;
208 if (buffer == NULL) {
209 return result;
210 }
211 if (len > (int)bufflen-1) {
212 len = (int)bufflen-1;
213 result = GIT_ENOMEM;
214 }
215
216 if (len >= 0) {
217 memmove(buffer, startp, len);
218 buffer[len] = 0;
219 }
220 return result;
221 }
222
223 /*
224 * Based on the Android implementation, BSD licensed.
225 * Check http://android.git.kernel.org/
226 */
227 int git__dirname_r(char *buffer, size_t bufflen, const char *path)
228 {
229 const char *endp;
230 int result, len;
231
232 /* Empty or NULL string gets treated as "." */
233 if (path == NULL || *path == '\0') {
234 path = ".";
235 len = 1;
236 goto Exit;
237 }
238
239 /* Strip trailing slashes */
240 endp = path + strlen(path) - 1;
241 while (endp > path && *endp == '/')
242 endp--;
243
244 /* Find the start of the dir */
245 while (endp > path && *endp != '/')
246 endp--;
247
248 /* Either the dir is "/" or there are no slashes */
249 if (endp == path) {
250 path = (*endp == '/') ? "/" : ".";
251 len = 1;
252 goto Exit;
253 }
254
255 do {
256 endp--;
257 } while (endp > path && *endp == '/');
258
259 len = endp - path +1;
260
261 Exit:
262 result = len;
263 if (len+1 > GIT_PATH_MAX) {
264 return GIT_ENOMEM;
265 }
266 if (buffer == NULL)
267 return result;
268
269 if (len > (int)bufflen-1) {
270 len = (int)bufflen-1;
271 result = GIT_ENOMEM;
272 }
273
274 if (len >= 0) {
275 memmove(buffer, path, len);
276 buffer[len] = 0;
277 }
278 return result;
279 }
280
281
282 char *git__dirname(const char *path)
283 {
284 char *dname = NULL;
285 int len;
286
287 len = (path ? strlen(path) : 0) + 2;
288 dname = (char *)git__malloc(len);
289 if (dname == NULL)
290 return NULL;
291
292 if (git__dirname_r(dname, len, path) < GIT_SUCCESS) {
293 free(dname);
294 return NULL;
295 }
296
297 return dname;
298 }
299
300 char *git__basename(const char *path)
301 {
302 char *bname = NULL;
303 int len;
304
305 len = (path ? strlen(path) : 0) + 2;
306 bname = (char *)git__malloc(len);
307 if (bname == NULL)
308 return NULL;
309
310 if (git__basename_r(bname, len, path) < GIT_SUCCESS) {
311 free(bname);
312 return NULL;
313 }
314
315 return bname;
316 }
317
318
319 const char *git__topdir(const char *path)
320 {
321 size_t len;
322 int i;
323
324 assert(path);
325 len = strlen(path);
326
327 if (!len || path[len - 1] != '/')
328 return NULL;
329
330 for (i = len - 2; i >= 0; --i)
331 if (path[i] == '/')
332 break;
333
334 return &path[i + 1];
335 }
336
337 void git__joinpath_n(char *buffer_out, int count, ...)
338 {
339 va_list ap;
340 int i;
341 char *buffer_start = buffer_out;
342
343 va_start(ap, count);
344 for (i = 0; i < count; ++i) {
345 const char *path;
346 int len;
347
348 path = va_arg(ap, const char *);
349
350 assert((i == 0) || path != buffer_start);
351
352 if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/')
353 path++;
354
355 if (!*path)
356 continue;
357
358 len = strlen(path);
359 memmove(buffer_out, path, len);
360 buffer_out = buffer_out + len;
361
362 if (i < count - 1 && buffer_out[-1] != '/')
363 *buffer_out++ = '/';
364 }
365 va_end(ap);
366
367 *buffer_out = '\0';
368 }
369
370 char *git__strtok(char **end, const char *sep)
371 {
372 char *ptr = *end;
373
374 while (*ptr && strchr(sep, *ptr))
375 ++ptr;
376
377 if (*ptr) {
378 char *start = ptr;
379 *end = start + 1;
380
381 while (**end && !strchr(sep, **end))
382 ++*end;
383
384 if (**end) {
385 **end = '\0';
386 ++*end;
387 }
388
389 return start;
390 }
391
392 return NULL;
393 }
394
395 void git__hexdump(const char *buffer, size_t len)
396 {
397 static const size_t LINE_WIDTH = 16;
398
399 size_t line_count, last_line, i, j;
400 const char *line;
401
402 line_count = (len / LINE_WIDTH);
403 last_line = (len % LINE_WIDTH);
404
405 for (i = 0; i < line_count; ++i) {
406 line = buffer + (i * LINE_WIDTH);
407 for (j = 0; j < LINE_WIDTH; ++j, ++line)
408 printf("%02X ", (unsigned char)*line & 0xFF);
409
410 printf("| ");
411
412 line = buffer + (i * LINE_WIDTH);
413 for (j = 0; j < LINE_WIDTH; ++j, ++line)
414 printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
415
416 printf("\n");
417 }
418
419 if (last_line > 0) {
420
421 line = buffer + (line_count * LINE_WIDTH);
422 for (j = 0; j < last_line; ++j, ++line)
423 printf("%02X ", (unsigned char)*line & 0xFF);
424
425 for (j = 0; j < (LINE_WIDTH - last_line); ++j)
426 printf(" ");
427
428 printf("| ");
429
430 line = buffer + (line_count * LINE_WIDTH);
431 for (j = 0; j < last_line; ++j, ++line)
432 printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
433
434 printf("\n");
435 }
436
437 printf("\n");
438 }
439
440 #ifdef GIT_LEGACY_HASH
441 uint32_t git__hash(const void *key, int len, unsigned int seed)
442 {
443 const uint32_t m = 0x5bd1e995;
444 const int r = 24;
445 uint32_t h = seed ^ len;
446
447 const unsigned char *data = (const unsigned char *)key;
448
449 while(len >= 4) {
450 uint32_t k = *(uint32_t *)data;
451
452 k *= m;
453 k ^= k >> r;
454 k *= m;
455
456 h *= m;
457 h ^= k;
458
459 data += 4;
460 len -= 4;
461 }
462
463 switch(len) {
464 case 3: h ^= data[2] << 16;
465 case 2: h ^= data[1] << 8;
466 case 1: h ^= data[0];
467 h *= m;
468 };
469
470 h ^= h >> 13;
471 h *= m;
472 h ^= h >> 15;
473
474 return h;
475 }
476 #else
477 /*
478 Cross-platform version of Murmurhash3
479 http://code.google.com/p/smhasher/wiki/MurmurHash3
480 by Austin Appleby (aappleby@gmail.com)
481
482 This code is on the public domain.
483 */
484 uint32_t git__hash(const void *key, int len, uint32_t seed)
485 {
486
487 #define MURMUR_BLOCK() {\
488 k1 *= c1; \
489 k1 = git__rotl(k1,11);\
490 k1 *= c2;\
491 h1 ^= k1;\
492 h1 = h1*3 + 0x52dce729;\
493 c1 = c1*5 + 0x7b7d159c;\
494 c2 = c2*5 + 0x6bce6396;\
495 }
496
497 const uint8_t *data = (const uint8_t*)key;
498 const int nblocks = len / 4;
499
500 const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
501 const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
502
503 uint32_t h1 = 0x971e137b ^ seed;
504 uint32_t k1;
505
506 uint32_t c1 = 0x95543787;
507 uint32_t c2 = 0x2ad7eb25;
508
509 int i;
510
511 for (i = -nblocks; i; i++) {
512 k1 = blocks[i];
513 MURMUR_BLOCK();
514 }
515
516 k1 = 0;
517
518 switch(len & 3) {
519 case 3: k1 ^= tail[2] << 16;
520 case 2: k1 ^= tail[1] << 8;
521 case 1: k1 ^= tail[0];
522 MURMUR_BLOCK();
523 }
524
525 h1 ^= len;
526 h1 ^= h1 >> 16;
527 h1 *= 0x85ebca6b;
528 h1 ^= h1 >> 13;
529 h1 *= 0xc2b2ae35;
530 h1 ^= h1 >> 16;
531
532 return h1;
533 }
534 #endif