GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/util.c Lines: 0 391 0.0 %
Date: 2017-11-13 Branches: 0 320 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: util.c,v 1.161 2017/08/28 19:33:20 otto Exp $	*/
2
/*
3
 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4
 * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
5
 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. The name of the author may not be used to endorse or promote products
15
 *    derived from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
#include <sys/stat.h>
30
#include <sys/types.h>
31
#include <sys/wait.h>
32
33
#include <atomicio.h>
34
#include <errno.h>
35
#include <fcntl.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <paths.h>
39
#include <unistd.h>
40
41
#include "cvs.h"
42
#include "remote.h"
43
#include "hash.h"
44
45
extern int print_stdout;
46
extern int build_dirs;
47
extern int disable_fast_checkout;
48
49
/* letter -> mode type map */
50
static const int cvs_modetypes[26] = {
51
	-1, -1, -1, -1, -1, -1,  1, -1, -1, -1, -1, -1, -1,
52
	-1,  2, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1, -1,
53
};
54
55
/* letter -> mode map */
56
static const mode_t cvs_modes[3][26] = {
57
	{
58
		0,  0,       0,       0,       0,  0,  0,    /* a - g */
59
		0,  0,       0,       0,       0,  0,  0,    /* h - m */
60
		0,  0,       0,       S_IRUSR, 0,  0,  0,    /* n - u */
61
		0,  S_IWUSR, S_IXUSR, 0,       0             /* v - z */
62
	},
63
	{
64
		0,  0,       0,       0,       0,  0,  0,    /* a - g */
65
		0,  0,       0,       0,       0,  0,  0,    /* h - m */
66
		0,  0,       0,       S_IRGRP, 0,  0,  0,    /* n - u */
67
		0,  S_IWGRP, S_IXGRP, 0,       0             /* v - z */
68
	},
69
	{
70
		0,  0,       0,       0,       0,  0,  0,    /* a - g */
71
		0,  0,       0,       0,       0,  0,  0,    /* h - m */
72
		0,  0,       0,       S_IROTH, 0,  0,  0,    /* n - u */
73
		0,  S_IWOTH, S_IXOTH, 0,       0             /* v - z */
74
	}
75
};
76
77
78
/* octal -> string */
79
static const char *cvs_modestr[8] = {
80
	"", "x", "w", "wx", "r", "rx", "rw", "rwx"
81
};
82
83
/*
84
 * cvs_strtomode()
85
 *
86
 * Read the contents of the string <str> and generate a permission mode from
87
 * the contents of <str>, which is assumed to have the mode format of CVS.
88
 * The CVS protocol specification states that any modes or mode types that are
89
 * not recognized should be silently ignored.  This function does not return
90
 * an error in such cases, but will issue warnings.
91
 */
92
void
93
cvs_strtomode(const char *str, mode_t *mode)
94
{
95
	char type;
96
	size_t l;
97
	mode_t m;
98
	char buf[32], ms[4], *sp, *ep;
99
100
	m = 0;
101
	l = strlcpy(buf, str, sizeof(buf));
102
	if (l >= sizeof(buf))
103
		fatal("cvs_strtomode: string truncation");
104
105
	sp = buf;
106
	ep = sp;
107
108
	for (sp = buf; ep != NULL; sp = ep + 1) {
109
		ep = strchr(sp, ',');
110
		if (ep != NULL)
111
			*ep = '\0';
112
113
		memset(ms, 0, sizeof ms);
114
		if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
115
			sscanf(sp, "%c=", &type) != 1) {
116
			fatal("failed to scan mode string `%s'", sp);
117
		}
118
119
		if (type <= 'a' || type >= 'z' ||
120
		    cvs_modetypes[type - 'a'] == -1) {
121
			cvs_log(LP_ERR,
122
			    "invalid mode type `%c'"
123
			    " (`u', `g' or `o' expected), ignoring", type);
124
			continue;
125
		}
126
127
		/* make type contain the actual mode index */
128
		type = cvs_modetypes[type - 'a'];
129
130
		for (sp = ms; *sp != '\0'; sp++) {
131
			if (*sp <= 'a' || *sp >= 'z' ||
132
			    cvs_modes[(int)type][*sp - 'a'] == 0) {
133
				fatal("invalid permission bit `%c'", *sp);
134
			} else
135
				m |= cvs_modes[(int)type][*sp - 'a'];
136
		}
137
	}
138
139
	*mode = m;
140
}
141
142
/*
143
 * cvs_modetostr()
144
 *
145
 * Generate a CVS-format string to represent the permissions mask on a file
146
 * from the mode <mode> and store the result in <buf>, which can accept up to
147
 * <len> bytes (including the terminating NUL byte).  The result is guaranteed
148
 * to be NUL-terminated.
149
 */
150
void
151
cvs_modetostr(mode_t mode, char *buf, size_t len)
152
{
153
	char tmp[16], *bp;
154
	mode_t um, gm, om;
155
156
	um = (mode & S_IRWXU) >> 6;
157
	gm = (mode & S_IRWXG) >> 3;
158
	om = mode & S_IRWXO;
159
160
	bp = buf;
161
	*bp = '\0';
162
163
	if (um) {
164
		if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
165
		    strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
166
			fatal("cvs_modetostr: overflow for user mode");
167
168
		if (strlcat(buf, tmp, len) >= len)
169
			fatal("cvs_modetostr: string truncation");
170
	}
171
172
	if (gm) {
173
		if (um) {
174
			if (strlcat(buf, ",", len) >= len)
175
				fatal("cvs_modetostr: string truncation");
176
		}
177
178
		if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
179
		    strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
180
			fatal("cvs_modetostr: overflow for group mode");
181
182
		if (strlcat(buf, tmp, len) >= len)
183
			fatal("cvs_modetostr: string truncation");
184
	}
185
186
	if (om) {
187
		if (um || gm) {
188
			if (strlcat(buf, ",", len) >= len)
189
				fatal("cvs_modetostr: string truncation");
190
		}
191
192
		if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
193
		    strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
194
			fatal("cvs_modetostr: overflow for others mode");
195
196
		if (strlcat(buf, tmp, len) >= len)
197
			fatal("cvs_modetostr: string truncation");
198
	}
199
}
200
201
/*
202
 * cvs_getargv()
203
 *
204
 * Parse a line contained in <line> and generate an argument vector by
205
 * splitting the line on spaces and tabs.  The resulting vector is stored in
206
 * <argv>, which can accept up to <argvlen> entries.
207
 * Returns the number of arguments in the vector, or -1 if an error occurred.
208
 */
209
int
210
cvs_getargv(const char *line, char **argv, int argvlen)
211
{
212
	u_int i;
213
	int argc, error;
214
	char *linebuf, *lp, *cp;
215
216
	linebuf = xstrdup(line);
217
218
	memset(argv, 0, argvlen * sizeof(char *));
219
	argc = 0;
220
221
	/* build the argument vector */
222
	error = 0;
223
	for (lp = linebuf; lp != NULL;) {
224
		cp = strsep(&lp, " \t");
225
		if (cp == NULL)
226
			break;
227
		else if (*cp == '\0')
228
			continue;
229
230
		if (argc == argvlen) {
231
			error++;
232
			break;
233
		}
234
235
		argv[argc] = xstrdup(cp);
236
		argc++;
237
	}
238
239
	if (error != 0) {
240
		/* ditch the argument vector */
241
		for (i = 0; i < (u_int)argc; i++)
242
			free(argv[i]);
243
		argc = -1;
244
	}
245
246
	free(linebuf);
247
	return (argc);
248
}
249
250
/*
251
 * cvs_makeargv()
252
 *
253
 * Allocate an argument vector large enough to accommodate for all the
254
 * arguments found in <line> and return it.
255
 */
256
char **
257
cvs_makeargv(const char *line, int *argc)
258
{
259
	int i, ret;
260
	char *argv[1024], **copy;
261
262
	ret = cvs_getargv(line, argv, 1024);
263
	if (ret == -1)
264
		return (NULL);
265
266
	copy = xcalloc(ret + 1, sizeof(char *));
267
268
	for (i = 0; i < ret; i++)
269
		copy[i] = argv[i];
270
	copy[ret] = NULL;
271
272
	*argc = ret;
273
	return (copy);
274
}
275
276
/*
277
 * cvs_freeargv()
278
 *
279
 * Free an argument vector previously generated by cvs_getargv().
280
 */
281
void
282
cvs_freeargv(char **argv, int argc)
283
{
284
	int i;
285
286
	for (i = 0; i < argc; i++)
287
		free(argv[i]);
288
}
289
290
/*
291
 * cvs_chdir()
292
 *
293
 * Change to directory <path>.
294
 * If <rm> is equal to `1', <path> is removed if chdir() fails so we
295
 * do not have temporary directories leftovers.
296
 * Returns 0 on success.
297
 */
298
int
299
cvs_chdir(const char *path, int rm)
300
{
301
	if (chdir(path) == -1) {
302
		if (rm == 1)
303
			cvs_unlink(path);
304
		fatal("cvs_chdir: `%s': %s", path, strerror(errno));
305
	}
306
307
	return (0);
308
}
309
310
/*
311
 * cvs_rename()
312
 * Change the name of a file.
313
 * rename() wrapper with an error message.
314
 * Returns 0 on success.
315
 */
316
int
317
cvs_rename(const char *from, const char *to)
318
{
319
	if (cvs_server_active == 0)
320
		cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
321
322
	if (cvs_noexec == 1)
323
		return (0);
324
325
	if (rename(from, to) == -1)
326
		fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
327
328
	return (0);
329
}
330
331
/*
332
 * cvs_unlink()
333
 *
334
 * Removes the link named by <path>.
335
 * unlink() wrapper with an error message.
336
 * Returns 0 on success, or -1 on failure.
337
 */
338
int
339
cvs_unlink(const char *path)
340
{
341
	if (cvs_server_active == 0)
342
		cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
343
344
	if (cvs_noexec == 1 && disable_fast_checkout != 0)
345
		return (0);
346
347
	if (unlink(path) == -1 && errno != ENOENT) {
348
		cvs_log(LP_ERRNO, "%s", path);
349
		return (-1);
350
	}
351
352
	return (0);
353
}
354
355
/*
356
 * cvs_rmdir()
357
 *
358
 * Remove a directory tree from disk.
359
 * Returns 0 on success, or -1 on failure.
360
 */
361
int
362
cvs_rmdir(const char *path)
363
{
364
	int type, ret = -1;
365
	DIR *dirp;
366
	struct dirent *ent;
367
	struct stat st;
368
	char fpath[PATH_MAX];
369
370
	if (cvs_server_active == 0)
371
		cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
372
373
	if (cvs_noexec == 1 && disable_fast_checkout != 0)
374
		return (0);
375
376
	if ((dirp = opendir(path)) == NULL) {
377
		cvs_log(LP_ERR, "failed to open '%s'", path);
378
		return (-1);
379
	}
380
381
	while ((ent = readdir(dirp)) != NULL) {
382
		if (!strcmp(ent->d_name, ".") ||
383
		    !strcmp(ent->d_name, ".."))
384
			continue;
385
386
		(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
387
		    path, ent->d_name);
388
389
		if (ent->d_type == DT_UNKNOWN) {
390
			if (lstat(fpath, &st) == -1)
391
				fatal("'%s': %s", fpath, strerror(errno));
392
393
			switch (st.st_mode & S_IFMT) {
394
			case S_IFDIR:
395
				type = CVS_DIR;
396
				break;
397
			case S_IFREG:
398
				type = CVS_FILE;
399
				break;
400
			default:
401
				fatal("'%s': Unknown file type in copy",
402
				    fpath);
403
			}
404
		} else {
405
			switch (ent->d_type) {
406
			case DT_DIR:
407
				type = CVS_DIR;
408
				break;
409
			case DT_REG:
410
				type = CVS_FILE;
411
				break;
412
			default:
413
				fatal("'%s': Unknown file type in copy",
414
				    fpath);
415
			}
416
		}
417
		switch (type) {
418
		case CVS_DIR:
419
			if (cvs_rmdir(fpath) == -1)
420
				goto done;
421
			break;
422
		case CVS_FILE:
423
			if (cvs_unlink(fpath) == -1 && errno != ENOENT)
424
				goto done;
425
			break;
426
		default:
427
			fatal("type %d unknown, shouldn't happen", type);
428
		}
429
	}
430
431
432
	if (rmdir(path) == -1 && errno != ENOENT) {
433
		cvs_log(LP_ERRNO, "%s", path);
434
		goto done;
435
	}
436
437
	ret = 0;
438
done:
439
	closedir(dirp);
440
	return (ret);
441
}
442
443
void
444
cvs_get_repository_path(const char *dir, char *dst, size_t len)
445
{
446
	char buf[PATH_MAX];
447
448
	cvs_get_repository_name(dir, buf, sizeof(buf));
449
	(void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
450
	cvs_validate_directory(dst);
451
}
452
453
void
454
cvs_get_repository_name(const char *dir, char *dst, size_t len)
455
{
456
	FILE *fp;
457
	char fpath[PATH_MAX];
458
459
	dst[0] = '\0';
460
461
	if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
462
		if (strlcpy(dst, dir, len) >= len)
463
			fatal("cvs_get_repository_name: truncation");
464
		return;
465
	}
466
467
	switch (cvs_cmdop) {
468
	case CVS_OP_EXPORT:
469
		if (strcmp(dir, "."))
470
			if (strlcpy(dst, dir, len) >= len)
471
				fatal("cvs_get_repository_name: truncation");
472
		break;
473
	case CVS_OP_IMPORT:
474
		if (strlcpy(dst, import_repository, len) >= len)
475
			fatal("cvs_get_repository_name: truncation");
476
		if (strlcat(dst, "/", len) >= len)
477
			fatal("cvs_get_repository_name: truncation");
478
479
		if (strcmp(dir, "."))
480
			if (strlcat(dst, dir, len) >= len)
481
				fatal("cvs_get_repository_name: truncation");
482
		break;
483
	default:
484
		(void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
485
		    dir, CVS_PATH_REPOSITORY);
486
		if ((fp = fopen(fpath, "r")) != NULL) {
487
			if ((fgets(dst, len, fp)) == NULL)
488
				fatal("%s: bad repository file", fpath);
489
			dst[strcspn(dst, "\n")] = '\0';
490
			(void)fclose(fp);
491
		} else if (cvs_cmdop != CVS_OP_CHECKOUT)
492
			fatal("%s is missing", fpath);
493
		break;
494
	}
495
}
496
497
void
498
cvs_mkadmin(const char *path, const char *root, const char *repo,
499
    char *tag, char *date)
500
{
501
	FILE *fp;
502
	int fd;
503
	char buf[PATH_MAX];
504
	struct hash_data *hdata, hd;
505
506
	hdata = hash_table_find(&created_cvs_directories, path, strlen(path));
507
	if (hdata != NULL)
508
		return;
509
510
	hd.h_key = xstrdup(path);
511
	hd.h_data = NULL;
512
	hash_table_enter(&created_cvs_directories, &hd);
513
514
	if (cvs_server_active == 0)
515
		cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
516
		    path, root, repo, (tag != NULL) ? tag : "",
517
		    (date != NULL) ? date : "");
518
519
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
520
521
	if (mkdir(buf, 0755) == -1 && errno != EEXIST)
522
		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
523
524
	if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
525
	    (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
526
		(void)xsnprintf(buf, sizeof(buf), "%s/%s",
527
		    path, CVS_PATH_ROOTSPEC);
528
529
		if ((fp = fopen(buf, "w")) == NULL)
530
			fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
531
532
		fprintf(fp, "%s\n", root);
533
		(void)fclose(fp);
534
	}
535
536
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
537
538
	if ((fp = fopen(buf, "w")) == NULL)
539
		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
540
541
	fprintf(fp, "%s\n", repo);
542
	(void)fclose(fp);
543
544
	cvs_write_tagfile(path, tag, date);
545
546
	(void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
547
548
	if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask))
549
	    == -1) {
550
		if (errno == EEXIST)
551
			return;
552
		fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
553
	}
554
555
	if (atomicio(vwrite, fd, "D\n", 2) != 2)
556
		fatal("cvs_mkadmin: %s", strerror(errno));
557
	close(fd);
558
}
559
560
void
561
cvs_mkpath(const char *path, char *tag)
562
{
563
	CVSENTRIES *ent;
564
	FILE *fp;
565
	size_t len;
566
	struct hash_data *hdata, hd;
567
	char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX];
568
569
	hdata = hash_table_find(&created_directories, path, strlen(path));
570
	if (hdata != NULL)
571
		return;
572
573
	hd.h_key = xstrdup(path);
574
	hd.h_data = NULL;
575
	hash_table_enter(&created_directories, &hd);
576
577
	if (cvsroot_is_remote() || cvs_server_active == 1)
578
		cvs_validate_directory(path);
579
580
	dir = xstrdup(path);
581
582
	STRIP_SLASH(dir);
583
584
	if (cvs_server_active == 0)
585
		cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
586
587
	repo[0] = '\0';
588
	rpath[0] = '\0';
589
590
	if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
591
		if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
592
			if ((fgets(repo, sizeof(repo), fp)) == NULL)
593
				fatal("cvs_mkpath: bad repository file");
594
			repo[strcspn(repo, "\n")] = '\0';
595
			(void)fclose(fp);
596
		}
597
	}
598
599
	for (sp = dir; sp != NULL; sp = dp) {
600
		dp = strchr(sp, '/');
601
		if (dp != NULL)
602
			*(dp++) = '\0';
603
604
		if (sp == dir && module_repo_root != NULL) {
605
			len = strlcpy(repo, module_repo_root, sizeof(repo));
606
			if (len >= (int)sizeof(repo))
607
				fatal("cvs_mkpath: overflow");
608
		} else if (strcmp(sp, ".")) {
609
			if (repo[0] != '\0') {
610
				len = strlcat(repo, "/", sizeof(repo));
611
				if (len >= (int)sizeof(repo))
612
					fatal("cvs_mkpath: overflow");
613
			}
614
615
			len = strlcat(repo, sp, sizeof(repo));
616
			if (len >= (int)sizeof(repo))
617
				fatal("cvs_mkpath: overflow");
618
		}
619
620
		if (rpath[0] != '\0') {
621
			len = strlcat(rpath, "/", sizeof(rpath));
622
			if (len >= (int)sizeof(rpath))
623
				fatal("cvs_mkpath: overflow");
624
		}
625
626
		len = strlcat(rpath, sp, sizeof(rpath));
627
		if (len >= (int)sizeof(rpath))
628
			fatal("cvs_mkpath: overflow");
629
630
		if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
631
			fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
632
633
		if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
634
			continue;
635
636
		cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
637
		    tag, NULL);
638
639
		if (dp != NULL) {
640
			if ((p = strchr(dp, '/')) != NULL)
641
				*p = '\0';
642
643
			entry = xmalloc(CVS_ENT_MAXLINELEN);
644
			cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
645
			    entry, CVS_ENT_MAXLINELEN);
646
647
			ent = cvs_ent_open(rpath);
648
			cvs_ent_add(ent, entry);
649
			free(entry);
650
651
			if (p != NULL)
652
				*p = '/';
653
		}
654
	}
655
656
	free(dir);
657
}
658
659
void
660
cvs_mkdir(const char *path, mode_t mode)
661
{
662
	size_t len;
663
	char *sp, *dp, *dir, rpath[PATH_MAX];
664
665
	if (cvsroot_is_remote() || cvs_server_active == 1)
666
		cvs_validate_directory(path);
667
668
	dir = xstrdup(path);
669
670
	STRIP_SLASH(dir);
671
672
	if (cvs_server_active == 0)
673
		cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir);
674
675
	rpath[0] = '\0';
676
677
	for (sp = dir; sp != NULL; sp = dp) {
678
		dp = strchr(sp, '/');
679
		if (dp != NULL)
680
			*(dp++) = '\0';
681
682
		len = strlcat(rpath, "/", sizeof(rpath));
683
		if (len >= (int)sizeof(rpath))
684
			fatal("cvs_mkdir: overflow");
685
686
		len = strlcat(rpath, sp, sizeof(rpath));
687
		if (len >= (int)sizeof(rpath))
688
			fatal("cvs_mkdir: overflow");
689
		if (1 == len)
690
			continue;
691
692
		if (mkdir(rpath, mode) == -1 && errno != EEXIST)
693
			fatal("cvs_mkdir: %s: %s", rpath, strerror(errno));
694
	}
695
696
	free(dir);
697
}
698
699
/*
700
 * Split the contents of a file into a list of lines.
701
 */
702
struct rcs_lines *
703
cvs_splitlines(u_char *data, size_t len)
704
{
705
	u_char *p, *c;
706
	size_t i, tlen;
707
	struct rcs_lines *lines;
708
	struct rcs_line *lp;
709
710
	lines = xcalloc(1, sizeof(*lines));
711
	TAILQ_INIT(&(lines->l_lines));
712
713
	lp = xcalloc(1, sizeof(*lp));
714
	TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
715
716
	p = c = data;
717
	for (i = 0; i < len; i++) {
718
		if (*p == '\n' || (i == len - 1)) {
719
			tlen = p - c + 1;
720
			lp = xcalloc(1, sizeof(*lp));
721
			lp->l_line = c;
722
			lp->l_len = tlen;
723
			lp->l_lineno = ++(lines->l_nblines);
724
			TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
725
			c = p + 1;
726
		}
727
		p++;
728
	}
729
730
	return (lines);
731
}
732
733
void
734
cvs_freelines(struct rcs_lines *lines)
735
{
736
	struct rcs_line *lp;
737
738
	while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
739
		TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
740
		if (lp->l_needsfree == 1)
741
			free(lp->l_line);
742
		free(lp);
743
	}
744
745
	free(lines);
746
}
747
748
/*
749
 * cvs_strsplit()
750
 *
751
 * Split a string <str> of <sep>-separated values and allocate
752
 * an argument vector for the values found.
753
 */
754
struct cvs_argvector *
755
cvs_strsplit(char *str, const char *sep)
756
{
757
	struct cvs_argvector *av;
758
	size_t i = 0;
759
	char *cp, *p;
760
761
	cp = xstrdup(str);
762
	av = xmalloc(sizeof(*av));
763
	av->str = cp;
764
	av->argv = xmalloc(sizeof(*(av->argv)));
765
766
	while ((p = strsep(&cp, sep)) != NULL) {
767
		av->argv[i++] = p;
768
		av->argv = xreallocarray(av->argv,
769
		    i + 1, sizeof(*(av->argv)));
770
	}
771
	av->argv[i] = NULL;
772
773
	return (av);
774
}
775
776
/*
777
 * cvs_argv_destroy()
778
 *
779
 * Free an argument vector previously allocated by cvs_strsplit().
780
 */
781
void
782
cvs_argv_destroy(struct cvs_argvector *av)
783
{
784
	free(av->str);
785
	free(av->argv);
786
	free(av);
787
}
788
789
u_int
790
cvs_revision_select(RCSFILE *file, char *range)
791
{
792
	int i;
793
	u_int nrev;
794
	char *lstr, *rstr;
795
	struct rcs_delta *rdp;
796
	struct cvs_argvector *revargv, *revrange;
797
	RCSNUM *lnum, *rnum;
798
799
	nrev = 0;
800
	lnum = rnum = NULL;
801
802
	revargv = cvs_strsplit(range, ",");
803
	for (i = 0; revargv->argv[i] != NULL; i++) {
804
		revrange = cvs_strsplit(revargv->argv[i], ":");
805
		if (revrange->argv[0] == NULL)
806
			fatal("invalid revision range: %s", revargv->argv[i]);
807
		else if (revrange->argv[1] == NULL)
808
			lstr = rstr = revrange->argv[0];
809
		else {
810
			if (revrange->argv[2] != NULL)
811
				fatal("invalid revision range: %s",
812
				    revargv->argv[i]);
813
814
			lstr = revrange->argv[0];
815
			rstr = revrange->argv[1];
816
817
			if (strcmp(lstr, "") == 0)
818
				lstr = NULL;
819
			if (strcmp(rstr, "") == 0)
820
				rstr = NULL;
821
		}
822
823
		if (lstr == NULL)
824
			lstr = RCS_HEAD_INIT;
825
826
		if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
827
			fatal("cvs_revision_select: could not translate tag `%s'", lstr);
828
829
		if (rstr != NULL) {
830
			if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
831
				fatal("cvs_revision_select: could not translate tag `%s'", rstr);
832
		} else {
833
			rnum = rcsnum_alloc();
834
			rcsnum_cpy(file->rf_head, rnum, 0);
835
		}
836
837
		cvs_argv_destroy(revrange);
838
839
		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
840
			if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
841
			    rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
842
			    !(rdp->rd_flags & RCS_RD_SELECT)) {
843
				rdp->rd_flags |= RCS_RD_SELECT;
844
				nrev++;
845
			}
846
		}
847
848
		free(lnum);
849
		free(rnum);
850
	}
851
852
	cvs_argv_destroy(revargv);
853
854
	return (nrev);
855
}
856
857
int
858
cvs_yesno(void)
859
{
860
	int c, ret;
861
862
	ret = 0;
863
864
	fflush(stderr);
865
	fflush(stdout);
866
867
	if ((c = getchar()) != 'y' && c != 'Y')
868
		ret = -1;
869
	else
870
		while (c != EOF && c != '\n')
871
			c = getchar();
872
873
	return (ret);
874
}
875
876
/*
877
 * cvs_exec()
878
 *
879
 * Execute <prog> and send <in> to the STDIN if not NULL.
880
 * If <needwait> == 1, return the result of <prog>,
881
 * else, 0 or -1 if an error occur.
882
 */
883
int
884
cvs_exec(char *prog, char *in, int needwait)
885
{
886
	pid_t pid;
887
	size_t size;
888
	int fds[2], st;
889
	char *argp[4] = { "sh", "-c", prog, NULL };
890
891
	if (in != NULL && pipe(fds) < 0) {
892
		cvs_log(LP_ERR, "cvs_exec: pipe failed");
893
		return (-1);
894
	}
895
896
	if ((pid = fork()) == -1) {
897
		cvs_log(LP_ERR, "cvs_exec: fork failed");
898
		return (-1);
899
	} else if (pid == 0) {
900
		if (in != NULL) {
901
			close(fds[1]);
902
			dup2(fds[0], STDIN_FILENO);
903
		}
904
905
		setenv("CVSROOT", current_cvsroot->cr_dir, 1);
906
		execv(_PATH_BSHELL, argp);
907
		cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
908
		_exit(127);
909
	}
910
911
	if (in != NULL) {
912
		close(fds[0]);
913
		size = strlen(in);
914
		if (atomicio(vwrite, fds[1], in, size) != size)
915
			cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
916
		close(fds[1]);
917
	}
918
919
	if (needwait == 1) {
920
		while (waitpid(pid, &st, 0) == -1)
921
			;
922
		if (!WIFEXITED(st)) {
923
			errno = EINTR;
924
			return (-1);
925
		}
926
		return (WEXITSTATUS(st));
927
	}
928
929
	return (0);
930
}