1/*-
2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/types.h>
30#include <sys/stat.h>
31
32#include <assert.h>
33#include <err.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <limits.h>
37#include <pthread.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <time.h>
43#include <unistd.h>
44
45#include "fattr.h"
46#include "main.h"
47#include "misc.h"
48
49struct pattlist {
50	char **patterns;
51	size_t size;
52	size_t in;
53};
54
55struct backoff_timer {
56	time_t min;
57	time_t max;
58	time_t interval;
59	float backoff;
60	float jitter;
61};
62
63static void	bt_update(struct backoff_timer *);
64static void	bt_addjitter(struct backoff_timer *);
65
66int
67asciitoint(const char *s, int *val, int base)
68{
69	char *end;
70	long longval;
71
72	errno = 0;
73	longval = strtol(s, &end, base);
74	if (errno || *end != '\0')
75		return (-1);
76	if (longval > INT_MAX || longval < INT_MIN) {
77		errno = ERANGE;
78		return (-1);
79	}
80	*val = longval;
81	return (0);
82}
83
84int
85lprintf(int level, const char *fmt, ...)
86{
87	FILE *to;
88	va_list ap;
89	int ret;
90
91	if (level > verbose)
92		return (0);
93	if (level == -1)
94		to = stderr;
95	else
96		to = stdout;
97	va_start(ap, fmt);
98	ret = vfprintf(to, fmt, ap);
99	va_end(ap);
100	fflush(to);
101	return (ret);
102}
103
104/*
105 * Compute the MD5 checksum of a file.  The md parameter must
106 * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
107 *
108 * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
109 * MD5_DIGEST_SIZE macro.
110 */
111int
112MD5_File(char *path, char *md)
113{
114	char buf[1024];
115	MD5_CTX ctx;
116	ssize_t n;
117	int fd;
118
119	fd = open(path, O_RDONLY);
120	if (fd == -1)
121		return (-1);
122	MD5_Init(&ctx);
123	while ((n = read(fd, buf, sizeof(buf))) > 0)
124		MD5_Update(&ctx, buf, n);
125	close(fd);
126	if (n == -1)
127		return (-1);
128	MD5_End(md, &ctx);
129	return (0);
130}
131
132/*
133 * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
134 * to an ASCII string representing this value in hexadecimal.
135 */
136void
137MD5_End(char *md, MD5_CTX *c)
138{
139	unsigned char md5[MD5_DIGEST_LENGTH];
140	const char hex[] = "0123456789abcdef";
141	int i, j;
142
143	MD5_Final(md5, c);
144	j = 0;
145	for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
146		md[j++] = hex[md5[i] >> 4];
147		md[j++] = hex[md5[i] & 0xf];
148	}
149	md[j] = '\0';
150}
151
152int
153pathcmp(const char *s1, const char *s2)
154{
155	char c1, c2;
156
157	do {
158		c1 = *s1++;
159		if (c1 == '/')
160			c1 = 1;
161		c2 = *s2++;
162		if (c2 == '/')
163			c2 = 1;
164	} while (c1 == c2 && c1 != '\0');
165
166	return (c1 - c2);
167}
168
169size_t
170commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
171{
172	size_t i, minlen, lastslash;
173
174	minlen = min(alen, blen);
175	lastslash = 0;
176	for (i = 0; i < minlen; i++) {
177		if (a[i] != b[i])
178			return (lastslash);
179		if (a[i] == '/') {
180			if (i == 0)	/* Include the leading slash. */
181				lastslash = 1;
182			else
183				lastslash = i;
184		}
185	}
186
187	/* One path is a prefix of the other/ */
188	if (alen > minlen) {		/* Path "b" is a prefix of "a". */
189		if (a[minlen] == '/')
190			return (minlen);
191		else
192			return (lastslash);
193	} else if (blen > minlen) {	/* Path "a" is a prefix of "b". */
194		if (b[minlen] == '/')
195			return (minlen);
196		else
197			return (lastslash);
198	}
199
200	/* The paths are identical. */
201	return (minlen);
202}
203
204const char *
205pathlast(const char *path)
206{
207	const char *s;
208
209	s = strrchr(path, '/');
210	if (s == NULL)
211		return (path);
212	return (++s);
213}
214
215int
216rcsdatetotm(const char *revdate, struct tm *tm)
217{
218	char *cp;
219	size_t len;
220
221	cp = strchr(revdate, '.');
222	if (cp == NULL)
223		return (-1);
224	len = cp - revdate;
225	if (len >= 4)
226		cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
227	else if (len == 2)
228		cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
229	else
230		return (-1);
231	if (cp == NULL || *cp != '\0')
232		return (-1);
233	return (0);
234}
235
236time_t
237rcsdatetotime(const char *revdate)
238{
239	struct tm tm;
240	time_t t;
241	int error;
242
243	error = rcsdatetotm(revdate, &tm);
244	if (error)
245		return (error);
246	t = timegm(&tm);
247	return (t);
248}
249
250/*
251 * Checks if a file is an RCS file.
252 */
253int
254isrcs(const char *file, size_t *len)
255{
256	const char *cp;
257
258	if (file[0] == '/')
259		return (0);
260	cp = file;
261	while ((cp = strstr(cp, "..")) != NULL) {
262		if (cp == file || cp[2] == '\0' ||
263		    (cp[-1] == '/' && cp[2] == '/'))
264			return (0);
265		cp += 2;
266	}
267	*len = strlen(file);
268	if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
269		return (0);
270	}
271
272	return (1);
273}
274
275/*
276 * Returns a buffer allocated with malloc() containing the absolute
277 * pathname to the checkout file made from the prefix and the path
278 * of the corresponding RCS file relatively to the prefix.  If the
279 * filename is not an RCS filename, NULL will be returned.
280 */
281char *
282checkoutpath(const char *prefix, const char *file)
283{
284	char *path;
285	size_t len;
286
287	if (!isrcs(file, &len))
288		return (NULL);
289	xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
290	return (path);
291}
292
293/*
294 * Returns a cvs path allocated with malloc() containing absolute pathname to a
295 * file in cvs mode which can reside in the attic. XXX: filename has really no
296 * restrictions.
297 */
298char *
299cvspath(const char *prefix, const char *file, int attic)
300{
301	const char *last;
302	char *path;
303
304	last = pathlast(file);
305	if (attic)
306		xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
307		    file, last);
308	else
309		xasprintf(&path, "%s/%s", prefix, file);
310
311	return (path);
312}
313
314/*
315 * Regular or attic path if regular fails.
316 * XXX: This should perhaps also check if the Attic file exists too, and return
317 * NULL if not.
318 */
319char *
320atticpath(const char *prefix, const char *file)
321{
322	char *path;
323
324	path = cvspath(prefix, file, 0);
325	if (access(path, F_OK) != 0) {
326		free(path);
327		path = cvspath(prefix, file, 1);
328	}
329	return (path);
330}
331
332int
333mkdirhier(char *path, mode_t mask)
334{
335	struct fattr *fa;
336	size_t i, last, len;
337	int error, finish, rv;
338
339	finish = 0;
340	last = 0;
341	len = strlen(path);
342	for (i = len - 1; i > 0; i--) {
343		if (path[i] == '/') {
344			path[i] = '\0';
345			if (access(path, F_OK) == 0) {
346				path[i] = '/';
347				break;
348			}
349			if (errno != ENOENT) {
350				path[i] = '/';
351				if (last == 0)
352					return (-1);
353				finish = 1;
354				break;
355			}
356			last = i;
357		}
358	}
359	if (last == 0)
360		return (0);
361
362	i = strlen(path);
363	fa = fattr_new(FT_DIRECTORY, -1);
364	fattr_mergedefault(fa);
365	fattr_umask(fa, mask);
366	while (i < len) {
367		if (!finish) {
368			rv = 0;
369			error = fattr_makenode(fa, path);
370			if (!error)
371				rv = fattr_install(fa, path, NULL);
372			if (error || rv == -1)
373				finish = 1;
374		}
375		path[i] = '/';
376		i += strlen(path + i);
377        }
378	assert(i == len);
379	if (finish)
380		return (-1);
381        return (0);
382}
383
384/*
385 * Compute temporary pathnames.
386 * This can look a bit like overkill but we mimic CVSup's behaviour.
387 */
388#define	TEMPNAME_PREFIX		"#cvs.csup"
389
390static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
391static pid_t tempname_pid = -1;
392static int tempname_count;
393
394char *
395tempname(const char *path)
396{
397	char *cp, *temp;
398	int count, error;
399
400	error = pthread_mutex_lock(&tempname_mtx);
401	assert(!error);
402	if (tempname_pid == -1) {
403		tempname_pid = getpid();
404		tempname_count = 0;
405	}
406	count = tempname_count++;
407	error = pthread_mutex_unlock(&tempname_mtx);
408	assert(!error);
409	cp = strrchr(path, '/');
410	if (cp == NULL)
411		xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
412		    (long)tempname_pid, count);
413	else
414		xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
415		    TEMPNAME_PREFIX, (long)tempname_pid, count);
416	return (temp);
417}
418
419void *
420xmalloc(size_t size)
421{
422	void *buf;
423
424	buf = malloc(size);
425	if (buf == NULL)
426		err(1, "malloc");
427	return (buf);
428}
429
430void *
431xrealloc(void *buf, size_t size)
432{
433
434	buf = realloc(buf, size);
435	if (buf == NULL)
436		err(1, "realloc");
437	return (buf);
438}
439
440char *
441xstrdup(const char *str)
442{
443	char *buf;
444
445	buf = strdup(str);
446	if (buf == NULL)
447		err(1, "strdup");
448	return (buf);
449}
450
451int
452xasprintf(char **ret, const char *format, ...)
453{
454	va_list ap;
455	int rv;
456
457	va_start(ap, format);
458	rv = vasprintf(ret, format, ap);
459	va_end(ap);
460	if (*ret == NULL)
461		err(1, "asprintf");
462	return (rv);
463}
464
465struct pattlist *
466pattlist_new(void)
467{
468	struct pattlist *p;
469
470	p = xmalloc(sizeof(struct pattlist));
471	p->size = 4;		/* Initial size. */
472	p->patterns = xmalloc(p->size * sizeof(char *));
473	p->in = 0;
474	return (p);
475}
476
477void
478pattlist_add(struct pattlist *p, const char *pattern)
479{
480
481	if (p->in == p->size) {
482		p->size *= 2;
483		p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
484	}
485	assert(p->in < p->size);
486	p->patterns[p->in++] = xstrdup(pattern);
487}
488
489char *
490pattlist_get(struct pattlist *p, size_t i)
491{
492
493	assert(i < p->in);
494	return (p->patterns[i]);
495}
496
497size_t
498pattlist_size(struct pattlist *p)
499{
500
501	return (p->in);
502}
503
504void
505pattlist_free(struct pattlist *p)
506{
507	size_t i;
508
509	for (i = 0; i < p->in; i++)
510		free(p->patterns[i]);
511	free(p->patterns);
512	free(p);
513}
514
515/* Creates a backoff timer. */
516struct backoff_timer *
517bt_new(time_t min, time_t max, float backoff, float jitter)
518{
519	struct backoff_timer *bt;
520
521	bt = xmalloc(sizeof(struct backoff_timer));
522	bt->min = min;
523	bt->max = max;
524	bt->backoff = backoff;
525	bt->jitter = jitter;
526	bt->interval = min;
527	bt_addjitter(bt);
528	srandom(time(0));
529	return (bt);
530}
531
532/* Updates the backoff timer. */
533static void
534bt_update(struct backoff_timer *bt)
535{
536
537	bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
538	bt_addjitter(bt);
539}
540
541/* Adds some jitter. */
542static void
543bt_addjitter(struct backoff_timer *bt)
544{
545	long mag;
546
547	mag = (long)(bt->jitter * bt->interval);
548	/* We want a random number between -mag and mag. */
549	bt->interval += (time_t)(random() % (2 * mag) - mag);
550}
551
552/* Returns the current timer value. */
553time_t
554bt_get(struct backoff_timer *bt)
555{
556
557	return (bt->interval);
558}
559
560/* Times out for bt->interval seconds. */
561void
562bt_pause(struct backoff_timer *bt)
563{
564
565	sleep(bt->interval);
566	bt_update(bt);
567}
568
569void
570bt_free(struct backoff_timer *bt)
571{
572
573	free(bt);
574}
575
576/* Compare two revisions. */
577int
578rcsnum_cmp(char *revision1, char *revision2)
579{
580        char *ptr1, *ptr2, *dot1, *dot2;
581	int num1len, num2len, ret;
582
583        ptr1 = revision1;
584        ptr2 = revision2;
585        while (*ptr1 != '\0' && *ptr2 != '\0') {
586                dot1 = strchr(ptr1, '.');
587                dot2 = strchr(ptr2, '.');
588                if (dot1 == NULL)
589                        dot1 = strchr(ptr1, '\0');
590                if (dot2 == NULL)
591                        dot2 = strchr(ptr2, '\0');
592
593		num1len = dot1 - ptr1;
594		num2len = dot2 - ptr2;
595                /* Check the distance between each, showing how many digits */
596                if (num1len > num2len)
597                        return (1);
598                else if (num1len < num2len)
599                        return (-1);
600
601                /* Equal distance means we must check each character. */
602		ret = strncmp(ptr1, ptr2, num1len);
603		if (ret != 0)
604			return (ret);
605		ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
606		ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
607        }
608
609        if (*ptr1 != '\0' && *ptr2 == '\0')
610                return (1);
611        if (*ptr1 == '\0' && *ptr2 != '\0')
612                return (-1);
613        return (0);
614
615}
616
617/* Returns 0 if a rcsrev is not a trunk revision number. */
618int
619rcsrev_istrunk(char *revnum)
620{
621	char *tmp;
622
623	tmp = strchr(revnum, '.');
624	tmp++;
625	if (strchr(tmp, '.') != NULL)
626		return (0);
627	return (1);
628}
629
630/* Return prefix of rcsfile. */
631char *
632rcsrev_prefix(char *revnum)
633{
634	char *modrev, *pos;
635
636	modrev = xstrdup(revnum);
637	pos = strrchr(modrev, '.');
638	if (pos == NULL) {
639		free(modrev);
640		return (NULL);
641	}
642	*pos = '\0';
643	return (modrev);
644}
645