2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/types.h>
31 #include <openssl/md5.h>
56 struct backoff_timer {
64 static void bt_update(struct backoff_timer *);
65 static void bt_addjitter(struct backoff_timer *);
68 asciitoint(const char *s, int *val, int base)
74 longval = strtol(s, &end, base);
75 if (errno || *end != '\0')
77 if (longval > INT_MAX || longval < INT_MIN) {
86 lprintf(int level, const char *fmt, ...)
99 ret = vfprintf(to, fmt, ap);
106 * Compute the MD5 checksum of a file. The md parameter must
107 * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
109 * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
110 * MD5_DIGEST_SIZE macro.
113 MD5_File(char *path, char *md)
120 fd = open(path, O_RDONLY);
124 while ((n = read(fd, buf, sizeof(buf))) > 0)
125 MD5_Update(&ctx, buf, n);
134 * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
135 * to an ASCII string representing this value in hexadecimal.
138 MD5_End(char *md, MD5_CTX *c)
140 unsigned char md5[MD5_DIGEST_LENGTH];
141 const char hex[] = "0123456789abcdef";
146 for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
147 md[j++] = hex[md5[i] >> 4];
148 md[j++] = hex[md5[i] & 0xf];
154 pathcmp(const char *s1, const char *s2)
165 } while (c1 == c2 && c1 != '\0');
171 commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
173 size_t i, minlen, lastslash;
175 minlen = min(alen, blen);
177 for (i = 0; i < minlen; i++) {
181 if (i == 0) /* Include the leading slash. */
188 /* One path is a prefix of the other/ */
189 if (alen > minlen) { /* Path "b" is a prefix of "a". */
190 if (a[minlen] == '/')
194 } else if (blen > minlen) { /* Path "a" is a prefix of "b". */
195 if (b[minlen] == '/')
201 /* The paths are identical. */
206 pathlast(const char *path)
210 s = strrchr(path, '/');
217 rcsdatetotm(const char *revdate, struct tm *tm)
222 cp = strchr(revdate, '.');
227 cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
229 cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
232 if (cp == NULL || *cp != '\0')
238 rcsdatetotime(const char *revdate)
244 error = rcsdatetotm(revdate, &tm);
252 * Checks if a file is an RCS file.
255 isrcs(const char *file, size_t *len)
262 while ((cp = strstr(cp, "..")) != NULL) {
263 if (cp == file || cp[2] == '\0' ||
264 (cp[-1] == '/' && cp[2] == '/'))
269 if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
277 * Returns a buffer allocated with malloc() containing the absolute
278 * pathname to the checkout file made from the prefix and the path
279 * of the corresponding RCS file relatively to the prefix. If the
280 * filename is not an RCS filename, NULL will be returned.
283 checkoutpath(const char *prefix, const char *file)
288 if (!isrcs(file, &len))
290 xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
295 * Returns a cvs path allocated with malloc() containing absolute pathname to a
296 * file in cvs mode which can reside in the attic. XXX: filename has really no
300 cvspath(const char *prefix, const char *file, int attic)
305 last = pathlast(file);
307 xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
310 xasprintf(&path, "%s/%s", prefix, file);
316 * Regular or attic path if regular fails.
317 * XXX: This should perhaps also check if the Attic file exists too, and return
321 atticpath(const char *prefix, const char *file)
325 path = cvspath(prefix, file, 0);
326 if (access(path, F_OK) != 0) {
328 path = cvspath(prefix, file, 1);
334 mkdirhier(char *path, mode_t mask)
338 int error, finish, rv;
343 for (i = len - 1; i > 0; i--) {
344 if (path[i] == '/') {
346 if (access(path, F_OK) == 0) {
350 if (errno != ENOENT) {
364 fa = fattr_new(FT_DIRECTORY, -1);
365 fattr_mergedefault(fa);
366 fattr_umask(fa, mask);
370 error = fattr_makenode(fa, path);
372 rv = fattr_install(fa, path, NULL);
373 if (error || rv == -1)
377 i += strlen(path + i);
386 * Compute temporary pathnames.
387 * This can look a bit like overkill but we mimic CVSup's behaviour.
389 #define TEMPNAME_PREFIX "#cvs.csup"
391 static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
392 static pid_t tempname_pid = -1;
393 static int tempname_count;
396 tempname(const char *path)
401 error = pthread_mutex_lock(&tempname_mtx);
403 if (tempname_pid == -1) {
404 tempname_pid = getpid();
407 count = tempname_count++;
408 error = pthread_mutex_unlock(&tempname_mtx);
410 cp = strrchr(path, '/');
412 xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
413 (long)tempname_pid, count);
415 xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
416 TEMPNAME_PREFIX, (long)tempname_pid, count);
432 xrealloc(void *buf, size_t size)
435 buf = realloc(buf, size);
442 xstrdup(const char *str)
453 xasprintf(char **ret, const char *format, ...)
458 va_start(ap, format);
459 rv = vasprintf(ret, format, ap);
471 p = xmalloc(sizeof(struct pattlist));
472 p->size = 4; /* Initial size. */
473 p->patterns = xmalloc(p->size * sizeof(char *));
479 pattlist_add(struct pattlist *p, const char *pattern)
482 if (p->in == p->size) {
484 p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
486 assert(p->in < p->size);
487 p->patterns[p->in++] = xstrdup(pattern);
491 pattlist_get(struct pattlist *p, size_t i)
495 return (p->patterns[i]);
499 pattlist_size(struct pattlist *p)
506 pattlist_free(struct pattlist *p)
510 for (i = 0; i < p->in; i++)
511 free(p->patterns[i]);
516 /* Creates a backoff timer. */
517 struct backoff_timer *
518 bt_new(time_t min, time_t max, float backoff, float jitter)
520 struct backoff_timer *bt;
522 bt = xmalloc(sizeof(struct backoff_timer));
525 bt->backoff = backoff;
533 /* Updates the backoff timer. */
535 bt_update(struct backoff_timer *bt)
538 bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
542 /* Adds some jitter. */
544 bt_addjitter(struct backoff_timer *bt)
548 mag = (long)(bt->jitter * bt->interval);
549 /* We want a random number between -mag and mag. */
550 bt->interval += (time_t)(random() % (2 * mag) - mag);
553 /* Returns the current timer value. */
555 bt_get(struct backoff_timer *bt)
558 return (bt->interval);
561 /* Times out for bt->interval seconds. */
563 bt_pause(struct backoff_timer *bt)
571 bt_free(struct backoff_timer *bt)
577 /* Compare two revisions. */
579 rcsnum_cmp(char *revision1, char *revision2)
581 char *ptr1, *ptr2, *dot1, *dot2;
582 int num1len, num2len, ret;
586 while (*ptr1 != '\0' && *ptr2 != '\0') {
587 dot1 = strchr(ptr1, '.');
588 dot2 = strchr(ptr2, '.');
590 dot1 = strchr(ptr1, '\0');
592 dot2 = strchr(ptr2, '\0');
594 num1len = dot1 - ptr1;
595 num2len = dot2 - ptr2;
596 /* Check the distance between each, showing how many digits */
597 if (num1len > num2len)
599 else if (num1len < num2len)
602 /* Equal distance means we must check each character. */
603 ret = strncmp(ptr1, ptr2, num1len);
606 ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
607 ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
610 if (*ptr1 != '\0' && *ptr2 == '\0')
612 if (*ptr1 == '\0' && *ptr2 != '\0')
618 /* Returns 0 if a rcsrev is not a trunk revision number. */
620 rcsrev_istrunk(char *revnum)
624 tmp = strchr(revnum, '.');
626 if (strchr(tmp, '.') != NULL)
631 /* Return prefix of rcsfile. */
633 rcsrev_prefix(char *revnum)
637 modrev = xstrdup(revnum);
638 pos = strrchr(modrev, '.');