/usr.bin/csup/misc.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 644 lines · 486 code · 84 blank · 74 comment · 130 complexity · 9b68cfe7c295a2614c58a00f29aecb08 MD5 · raw file
- /*-
- * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <assert.h>
- #include <err.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <pthread.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include "fattr.h"
- #include "main.h"
- #include "misc.h"
- struct pattlist {
- char **patterns;
- size_t size;
- size_t in;
- };
- struct backoff_timer {
- time_t min;
- time_t max;
- time_t interval;
- float backoff;
- float jitter;
- };
- static void bt_update(struct backoff_timer *);
- static void bt_addjitter(struct backoff_timer *);
- int
- asciitoint(const char *s, int *val, int base)
- {
- char *end;
- long longval;
- errno = 0;
- longval = strtol(s, &end, base);
- if (errno || *end != '\0')
- return (-1);
- if (longval > INT_MAX || longval < INT_MIN) {
- errno = ERANGE;
- return (-1);
- }
- *val = longval;
- return (0);
- }
- int
- lprintf(int level, const char *fmt, ...)
- {
- FILE *to;
- va_list ap;
- int ret;
- if (level > verbose)
- return (0);
- if (level == -1)
- to = stderr;
- else
- to = stdout;
- va_start(ap, fmt);
- ret = vfprintf(to, fmt, ap);
- va_end(ap);
- fflush(to);
- return (ret);
- }
- /*
- * Compute the MD5 checksum of a file. The md parameter must
- * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
- *
- * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
- * MD5_DIGEST_SIZE macro.
- */
- int
- MD5_File(char *path, char *md)
- {
- char buf[1024];
- MD5_CTX ctx;
- ssize_t n;
- int fd;
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return (-1);
- MD5_Init(&ctx);
- while ((n = read(fd, buf, sizeof(buf))) > 0)
- MD5_Update(&ctx, buf, n);
- close(fd);
- if (n == -1)
- return (-1);
- MD5_End(md, &ctx);
- return (0);
- }
- /*
- * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
- * to an ASCII string representing this value in hexadecimal.
- */
- void
- MD5_End(char *md, MD5_CTX *c)
- {
- unsigned char md5[MD5_DIGEST_LENGTH];
- const char hex[] = "0123456789abcdef";
- int i, j;
- MD5_Final(md5, c);
- j = 0;
- for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
- md[j++] = hex[md5[i] >> 4];
- md[j++] = hex[md5[i] & 0xf];
- }
- md[j] = '\0';
- }
- int
- pathcmp(const char *s1, const char *s2)
- {
- char c1, c2;
- do {
- c1 = *s1++;
- if (c1 == '/')
- c1 = 1;
- c2 = *s2++;
- if (c2 == '/')
- c2 = 1;
- } while (c1 == c2 && c1 != '\0');
- return (c1 - c2);
- }
- size_t
- commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
- {
- size_t i, minlen, lastslash;
- minlen = min(alen, blen);
- lastslash = 0;
- for (i = 0; i < minlen; i++) {
- if (a[i] != b[i])
- return (lastslash);
- if (a[i] == '/') {
- if (i == 0) /* Include the leading slash. */
- lastslash = 1;
- else
- lastslash = i;
- }
- }
- /* One path is a prefix of the other/ */
- if (alen > minlen) { /* Path "b" is a prefix of "a". */
- if (a[minlen] == '/')
- return (minlen);
- else
- return (lastslash);
- } else if (blen > minlen) { /* Path "a" is a prefix of "b". */
- if (b[minlen] == '/')
- return (minlen);
- else
- return (lastslash);
- }
- /* The paths are identical. */
- return (minlen);
- }
- const char *
- pathlast(const char *path)
- {
- const char *s;
- s = strrchr(path, '/');
- if (s == NULL)
- return (path);
- return (++s);
- }
- int
- rcsdatetotm(const char *revdate, struct tm *tm)
- {
- char *cp;
- size_t len;
- cp = strchr(revdate, '.');
- if (cp == NULL)
- return (-1);
- len = cp - revdate;
- if (len >= 4)
- cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
- else if (len == 2)
- cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
- else
- return (-1);
- if (cp == NULL || *cp != '\0')
- return (-1);
- return (0);
- }
- time_t
- rcsdatetotime(const char *revdate)
- {
- struct tm tm;
- time_t t;
- int error;
- error = rcsdatetotm(revdate, &tm);
- if (error)
- return (error);
- t = timegm(&tm);
- return (t);
- }
- /*
- * Checks if a file is an RCS file.
- */
- int
- isrcs(const char *file, size_t *len)
- {
- const char *cp;
- if (file[0] == '/')
- return (0);
- cp = file;
- while ((cp = strstr(cp, "..")) != NULL) {
- if (cp == file || cp[2] == '\0' ||
- (cp[-1] == '/' && cp[2] == '/'))
- return (0);
- cp += 2;
- }
- *len = strlen(file);
- if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
- return (0);
- }
- return (1);
- }
- /*
- * Returns a buffer allocated with malloc() containing the absolute
- * pathname to the checkout file made from the prefix and the path
- * of the corresponding RCS file relatively to the prefix. If the
- * filename is not an RCS filename, NULL will be returned.
- */
- char *
- checkoutpath(const char *prefix, const char *file)
- {
- char *path;
- size_t len;
- if (!isrcs(file, &len))
- return (NULL);
- xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
- return (path);
- }
- /*
- * Returns a cvs path allocated with malloc() containing absolute pathname to a
- * file in cvs mode which can reside in the attic. XXX: filename has really no
- * restrictions.
- */
- char *
- cvspath(const char *prefix, const char *file, int attic)
- {
- const char *last;
- char *path;
- last = pathlast(file);
- if (attic)
- xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
- file, last);
- else
- xasprintf(&path, "%s/%s", prefix, file);
- return (path);
- }
- /*
- * Regular or attic path if regular fails.
- * XXX: This should perhaps also check if the Attic file exists too, and return
- * NULL if not.
- */
- char *
- atticpath(const char *prefix, const char *file)
- {
- char *path;
- path = cvspath(prefix, file, 0);
- if (access(path, F_OK) != 0) {
- free(path);
- path = cvspath(prefix, file, 1);
- }
- return (path);
- }
- int
- mkdirhier(char *path, mode_t mask)
- {
- struct fattr *fa;
- size_t i, last, len;
- int error, finish, rv;
- finish = 0;
- last = 0;
- len = strlen(path);
- for (i = len - 1; i > 0; i--) {
- if (path[i] == '/') {
- path[i] = '\0';
- if (access(path, F_OK) == 0) {
- path[i] = '/';
- break;
- }
- if (errno != ENOENT) {
- path[i] = '/';
- if (last == 0)
- return (-1);
- finish = 1;
- break;
- }
- last = i;
- }
- }
- if (last == 0)
- return (0);
- i = strlen(path);
- fa = fattr_new(FT_DIRECTORY, -1);
- fattr_mergedefault(fa);
- fattr_umask(fa, mask);
- while (i < len) {
- if (!finish) {
- rv = 0;
- error = fattr_makenode(fa, path);
- if (!error)
- rv = fattr_install(fa, path, NULL);
- if (error || rv == -1)
- finish = 1;
- }
- path[i] = '/';
- i += strlen(path + i);
- }
- assert(i == len);
- if (finish)
- return (-1);
- return (0);
- }
- /*
- * Compute temporary pathnames.
- * This can look a bit like overkill but we mimic CVSup's behaviour.
- */
- #define TEMPNAME_PREFIX "#cvs.csup"
- static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
- static pid_t tempname_pid = -1;
- static int tempname_count;
- char *
- tempname(const char *path)
- {
- char *cp, *temp;
- int count, error;
- error = pthread_mutex_lock(&tempname_mtx);
- assert(!error);
- if (tempname_pid == -1) {
- tempname_pid = getpid();
- tempname_count = 0;
- }
- count = tempname_count++;
- error = pthread_mutex_unlock(&tempname_mtx);
- assert(!error);
- cp = strrchr(path, '/');
- if (cp == NULL)
- xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
- (long)tempname_pid, count);
- else
- xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
- TEMPNAME_PREFIX, (long)tempname_pid, count);
- return (temp);
- }
- void *
- xmalloc(size_t size)
- {
- void *buf;
- buf = malloc(size);
- if (buf == NULL)
- err(1, "malloc");
- return (buf);
- }
- void *
- xrealloc(void *buf, size_t size)
- {
- buf = realloc(buf, size);
- if (buf == NULL)
- err(1, "realloc");
- return (buf);
- }
- char *
- xstrdup(const char *str)
- {
- char *buf;
- buf = strdup(str);
- if (buf == NULL)
- err(1, "strdup");
- return (buf);
- }
- int
- xasprintf(char **ret, const char *format, ...)
- {
- va_list ap;
- int rv;
- va_start(ap, format);
- rv = vasprintf(ret, format, ap);
- va_end(ap);
- if (*ret == NULL)
- err(1, "asprintf");
- return (rv);
- }
- struct pattlist *
- pattlist_new(void)
- {
- struct pattlist *p;
- p = xmalloc(sizeof(struct pattlist));
- p->size = 4; /* Initial size. */
- p->patterns = xmalloc(p->size * sizeof(char *));
- p->in = 0;
- return (p);
- }
- void
- pattlist_add(struct pattlist *p, const char *pattern)
- {
- if (p->in == p->size) {
- p->size *= 2;
- p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
- }
- assert(p->in < p->size);
- p->patterns[p->in++] = xstrdup(pattern);
- }
- char *
- pattlist_get(struct pattlist *p, size_t i)
- {
- assert(i < p->in);
- return (p->patterns[i]);
- }
- size_t
- pattlist_size(struct pattlist *p)
- {
- return (p->in);
- }
- void
- pattlist_free(struct pattlist *p)
- {
- size_t i;
-
- for (i = 0; i < p->in; i++)
- free(p->patterns[i]);
- free(p->patterns);
- free(p);
- }
- /* Creates a backoff timer. */
- struct backoff_timer *
- bt_new(time_t min, time_t max, float backoff, float jitter)
- {
- struct backoff_timer *bt;
- bt = xmalloc(sizeof(struct backoff_timer));
- bt->min = min;
- bt->max = max;
- bt->backoff = backoff;
- bt->jitter = jitter;
- bt->interval = min;
- bt_addjitter(bt);
- srandom(time(0));
- return (bt);
- }
- /* Updates the backoff timer. */
- static void
- bt_update(struct backoff_timer *bt)
- {
- bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
- bt_addjitter(bt);
- }
- /* Adds some jitter. */
- static void
- bt_addjitter(struct backoff_timer *bt)
- {
- long mag;
- mag = (long)(bt->jitter * bt->interval);
- /* We want a random number between -mag and mag. */
- bt->interval += (time_t)(random() % (2 * mag) - mag);
- }
- /* Returns the current timer value. */
- time_t
- bt_get(struct backoff_timer *bt)
- {
- return (bt->interval);
- }
- /* Times out for bt->interval seconds. */
- void
- bt_pause(struct backoff_timer *bt)
- {
- sleep(bt->interval);
- bt_update(bt);
- }
- void
- bt_free(struct backoff_timer *bt)
- {
- free(bt);
- }
- /* Compare two revisions. */
- int
- rcsnum_cmp(char *revision1, char *revision2)
- {
- char *ptr1, *ptr2, *dot1, *dot2;
- int num1len, num2len, ret;
- ptr1 = revision1;
- ptr2 = revision2;
- while (*ptr1 != '\0' && *ptr2 != '\0') {
- dot1 = strchr(ptr1, '.');
- dot2 = strchr(ptr2, '.');
- if (dot1 == NULL)
- dot1 = strchr(ptr1, '\0');
- if (dot2 == NULL)
- dot2 = strchr(ptr2, '\0');
- num1len = dot1 - ptr1;
- num2len = dot2 - ptr2;
- /* Check the distance between each, showing how many digits */
- if (num1len > num2len)
- return (1);
- else if (num1len < num2len)
- return (-1);
- /* Equal distance means we must check each character. */
- ret = strncmp(ptr1, ptr2, num1len);
- if (ret != 0)
- return (ret);
- ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
- ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
- }
- if (*ptr1 != '\0' && *ptr2 == '\0')
- return (1);
- if (*ptr1 == '\0' && *ptr2 != '\0')
- return (-1);
- return (0);
- }
- /* Returns 0 if a rcsrev is not a trunk revision number. */
- int
- rcsrev_istrunk(char *revnum)
- {
- char *tmp;
- tmp = strchr(revnum, '.');
- tmp++;
- if (strchr(tmp, '.') != NULL)
- return (0);
- return (1);
- }
- /* Return prefix of rcsfile. */
- char *
- rcsrev_prefix(char *revnum)
- {
- char *modrev, *pos;
- modrev = xstrdup(revnum);
- pos = strrchr(modrev, '.');
- if (pos == NULL) {
- free(modrev);
- return (NULL);
- }
- *pos = '\0';
- return (modrev);
- }