Annotation of src/usr.bin/cvs/util.c, Revision 1.115
1.115 ! tobias 1: /* $OpenBSD: util.c,v 1.114 2007/09/02 11:40:03 tobias Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.71 xsa 4: * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
5: * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
1.9 jfb 6: * All rights reserved.
1.1 jfb 7: *
1.9 jfb 8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
1.1 jfb 11: *
1.9 jfb 12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
1.1 jfb 14: * 2. The name of the author may not be used to endorse or promote products
1.9 jfb 15: * derived from this software without specific prior written permission.
1.1 jfb 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
1.9 jfb 26: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 27: */
28:
1.107 otto 29: #include <sys/stat.h>
30: #include <sys/wait.h>
31:
32: #include <errno.h>
33: #include <md5.h>
34: #include <stdlib.h>
35: #include <string.h>
36: #include <unistd.h>
1.1 jfb 37:
38: #include "cvs.h"
1.112 joris 39: #include "remote.h"
1.1 jfb 40:
41: /* letter -> mode type map */
42: static const int cvs_modetypes[26] = {
43: -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
44: -1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
45: };
46:
47: /* letter -> mode map */
48: static const mode_t cvs_modes[3][26] = {
49: {
50: 0, 0, 0, 0, 0, 0, 0, /* a - g */
51: 0, 0, 0, 0, 0, 0, 0, /* h - m */
52: 0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */
53: 0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */
54: },
55: {
56: 0, 0, 0, 0, 0, 0, 0, /* a - g */
57: 0, 0, 0, 0, 0, 0, 0, /* h - m */
58: 0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */
59: 0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */
60: },
61: {
62: 0, 0, 0, 0, 0, 0, 0, /* a - g */
63: 0, 0, 0, 0, 0, 0, 0, /* h - m */
64: 0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */
65: 0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */
66: }
67: };
68:
69:
70: /* octal -> string */
71: static const char *cvs_modestr[8] = {
72: "", "x", "w", "wx", "r", "rx", "rw", "rwx"
73: };
74:
1.9 jfb 75: /*
1.1 jfb 76: * cvs_strtomode()
77: *
78: * Read the contents of the string <str> and generate a permission mode from
79: * the contents of <str>, which is assumed to have the mode format of CVS.
80: * The CVS protocol specification states that any modes or mode types that are
81: * not recognized should be silently ignored. This function does not return
82: * an error in such cases, but will issue warnings.
83: */
1.64 joris 84: void
1.1 jfb 85: cvs_strtomode(const char *str, mode_t *mode)
86: {
1.2 jfb 87: char type;
1.64 joris 88: size_t l;
1.1 jfb 89: mode_t m;
90: char buf[32], ms[4], *sp, *ep;
91:
92: m = 0;
1.64 joris 93: l = strlcpy(buf, str, sizeof(buf));
94: if (l >= sizeof(buf))
95: fatal("cvs_strtomode: string truncation");
96:
1.1 jfb 97: sp = buf;
98: ep = sp;
99:
100: for (sp = buf; ep != NULL; sp = ep + 1) {
101: ep = strchr(sp, ',');
102: if (ep != NULL)
1.2 jfb 103: *ep = '\0';
1.1 jfb 104:
1.14 weingart 105: memset(ms, 0, sizeof ms);
106: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
107: sscanf(sp, "%c=", &type) != 1) {
1.89 joris 108: fatal("failed to scan mode string `%s'", sp);
1.1 jfb 109: }
110:
1.78 deraadt 111: if (type <= 'a' || type >= 'z' ||
112: cvs_modetypes[type - 'a'] == -1) {
1.79 joris 113: cvs_log(LP_ERR,
1.1 jfb 114: "invalid mode type `%c'"
115: " (`u', `g' or `o' expected), ignoring", type);
116: continue;
117: }
118:
119: /* make type contain the actual mode index */
120: type = cvs_modetypes[type - 'a'];
121:
122: for (sp = ms; *sp != '\0'; sp++) {
1.78 deraadt 123: if (*sp <= 'a' || *sp >= 'z' ||
124: cvs_modes[(int)type][*sp - 'a'] == 0) {
1.89 joris 125: fatal("invalid permission bit `%c'", *sp);
1.15 deraadt 126: } else
1.5 jfb 127: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 128: }
129: }
130:
131: *mode = m;
132: }
133:
134: /*
135: * cvs_modetostr()
136: *
1.30 jfb 137: * Generate a CVS-format string to represent the permissions mask on a file
138: * from the mode <mode> and store the result in <buf>, which can accept up to
139: * <len> bytes (including the terminating NUL byte). The result is guaranteed
140: * to be NUL-terminated.
1.1 jfb 141: */
1.65 joris 142: void
1.1 jfb 143: cvs_modetostr(mode_t mode, char *buf, size_t len)
144: {
145: char tmp[16], *bp;
146: mode_t um, gm, om;
147:
148: um = (mode & S_IRWXU) >> 6;
149: gm = (mode & S_IRWXG) >> 3;
150: om = mode & S_IRWXO;
151:
152: bp = buf;
153: *bp = '\0';
154:
155: if (um) {
1.68 xsa 156: if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
157: strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 158: fatal("cvs_modetostr: overflow for user mode");
159:
1.68 xsa 160: if (strlcat(buf, tmp, len) >= len)
1.65 joris 161: fatal("cvs_modetostr: string truncation");
1.1 jfb 162: }
1.65 joris 163:
1.1 jfb 164: if (gm) {
1.65 joris 165: if (um) {
1.68 xsa 166: if (strlcat(buf, ",", len) >= len)
1.65 joris 167: fatal("cvs_modetostr: string truncation");
168: }
169:
1.68 xsa 170: if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
171: strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 172: fatal("cvs_modetostr: overflow for group mode");
173:
1.68 xsa 174: if (strlcat(buf, tmp, len) >= len)
1.65 joris 175: fatal("cvs_modetostr: string truncation");
1.1 jfb 176: }
1.65 joris 177:
1.1 jfb 178: if (om) {
1.65 joris 179: if (um || gm) {
1.68 xsa 180: if (strlcat(buf, ",", len) >= len)
1.65 joris 181: fatal("cvs_modetostr: string truncation");
182: }
183:
1.68 xsa 184: if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
185: strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 186: fatal("cvs_modetostr: overflow for others mode");
187:
1.68 xsa 188: if (strlcat(buf, tmp, len) >= len)
1.65 joris 189: fatal("cvs_modetostr: string truncation");
1.1 jfb 190: }
191: }
192:
193: /*
194: * cvs_cksum()
195: *
196: * Calculate the MD5 checksum of the file whose path is <file> and generate
197: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
198: * given in <len> and must be at least 33.
199: * Returns 0 on success, or -1 on failure.
200: */
201: int
202: cvs_cksum(const char *file, char *dst, size_t len)
203: {
204: if (len < CVS_CKSUM_LEN) {
1.79 joris 205: cvs_log(LP_ERR, "buffer too small for checksum");
1.1 jfb 206: return (-1);
207: }
208: if (MD5File(file, dst) == NULL) {
1.79 joris 209: cvs_log(LP_ERR, "failed to generate checksum for %s", file);
1.1 jfb 210: return (-1);
211: }
212:
213: return (0);
214: }
215:
216: /*
217: * cvs_getargv()
218: *
219: * Parse a line contained in <line> and generate an argument vector by
220: * splitting the line on spaces and tabs. The resulting vector is stored in
221: * <argv>, which can accept up to <argvlen> entries.
1.20 david 222: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 223: */
224: int
225: cvs_getargv(const char *line, char **argv, int argvlen)
226: {
1.65 joris 227: size_t l;
1.1 jfb 228: u_int i;
1.67 xsa 229: int argc, error;
1.1 jfb 230: char linebuf[256], qbuf[128], *lp, *cp, *arg;
231:
1.65 joris 232: l = strlcpy(linebuf, line, sizeof(linebuf));
233: if (l >= sizeof(linebuf))
234: fatal("cvs_getargv: string truncation");
235:
1.27 pat 236: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 237: argc = 0;
238:
239: /* build the argument vector */
1.67 xsa 240: error = 0;
1.1 jfb 241: for (lp = linebuf; lp != NULL;) {
242: if (*lp == '"') {
243: /* double-quoted string */
244: lp++;
245: i = 0;
246: memset(qbuf, 0, sizeof(qbuf));
247: while (*lp != '"') {
1.16 jfb 248: if (*lp == '\\')
249: lp++;
1.1 jfb 250: if (*lp == '\0') {
251: cvs_log(LP_ERR, "no terminating quote");
1.67 xsa 252: error++;
1.1 jfb 253: break;
1.16 jfb 254: }
1.1 jfb 255:
1.9 jfb 256: qbuf[i++] = *lp++;
1.110 ray 257: if (i == sizeof(qbuf) - 1) {
1.67 xsa 258: error++;
1.1 jfb 259: break;
260: }
261: }
1.114 tobias 262: if (error)
263: break;
264: lp++;
1.1 jfb 265:
266: arg = qbuf;
1.15 deraadt 267: } else {
1.1 jfb 268: cp = strsep(&lp, " \t");
269: if (cp == NULL)
270: break;
271: else if (*cp == '\0')
272: continue;
273:
274: arg = cp;
275: }
276:
1.16 jfb 277: if (argc == argvlen) {
1.67 xsa 278: error++;
1.16 jfb 279: break;
280: }
281:
1.59 joris 282: argv[argc] = xstrdup(arg);
1.1 jfb 283: argc++;
284: }
285:
1.67 xsa 286: if (error != 0) {
1.1 jfb 287: /* ditch the argument vector */
288: for (i = 0; i < (u_int)argc; i++)
1.59 joris 289: xfree(argv[i]);
1.1 jfb 290: argc = -1;
291: }
292:
293: return (argc);
1.17 jfb 294: }
295:
296: /*
297: * cvs_makeargv()
298: *
1.20 david 299: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 300: * arguments found in <line> and return it.
301: */
1.42 xsa 302: char **
1.17 jfb 303: cvs_makeargv(const char *line, int *argc)
304: {
305: int i, ret;
306: char *argv[1024], **copy;
307:
308: ret = cvs_getargv(line, argv, 1024);
309: if (ret == -1)
310: return (NULL);
311:
1.77 ray 312: copy = xcalloc(ret + 1, sizeof(char *));
1.17 jfb 313:
314: for (i = 0; i < ret; i++)
315: copy[i] = argv[i];
316: copy[ret] = NULL;
317:
318: *argc = ret;
319: return (copy);
1.1 jfb 320: }
321:
322: /*
323: * cvs_freeargv()
324: *
325: * Free an argument vector previously generated by cvs_getargv().
326: */
327: void
328: cvs_freeargv(char **argv, int argc)
329: {
330: int i;
331:
332: for (i = 0; i < argc; i++)
1.16 jfb 333: if (argv[i] != NULL)
1.59 joris 334: xfree(argv[i]);
1.38 xsa 335: }
336:
337: /*
338: * cvs_chdir()
339: *
1.63 xsa 340: * Change to directory <path>.
341: * If <rm> is equal to `1', <path> is removed if chdir() fails so we
342: * do not have temporary directories leftovers.
1.60 xsa 343: * Returns 0 on success.
1.50 xsa 344: */
1.38 xsa 345: int
1.63 xsa 346: cvs_chdir(const char *path, int rm)
1.38 xsa 347: {
1.63 xsa 348: if (chdir(path) == -1) {
349: if (rm == 1)
350: cvs_unlink(path);
1.60 xsa 351: fatal("cvs_chdir: `%s': %s", path, strerror(errno));
1.63 xsa 352: }
1.49 xsa 353:
354: return (0);
355: }
356:
357: /*
358: * cvs_rename()
359: * Change the name of a file.
360: * rename() wrapper with an error message.
1.60 xsa 361: * Returns 0 on success.
1.49 xsa 362: */
363: int
364: cvs_rename(const char *from, const char *to)
365: {
1.90 joris 366: if (cvs_server_active == 0)
367: cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
1.49 xsa 368:
369: if (cvs_noexec == 1)
370: return (0);
371:
1.60 xsa 372: if (rename(from, to) == -1)
373: fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
1.40 xsa 374:
375: return (0);
376: }
377:
378: /*
379: * cvs_unlink()
380: *
381: * Removes the link named by <path>.
382: * unlink() wrapper with an error message.
383: * Returns 0 on success, or -1 on failure.
384: */
385: int
386: cvs_unlink(const char *path)
387: {
1.90 joris 388: if (cvs_server_active == 0)
389: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
1.40 xsa 390:
391: if (cvs_noexec == 1)
392: return (0);
393:
1.78 deraadt 394: if (unlink(path) == -1 && errno != ENOENT) {
1.94 xsa 395: cvs_log(LP_ERRNO, "%s", path);
1.38 xsa 396: return (-1);
397: }
398:
399: return (0);
1.1 jfb 400: }
1.24 joris 401:
402: /*
1.45 xsa 403: * cvs_rmdir()
1.30 jfb 404: *
405: * Remove a directory tree from disk.
406: * Returns 0 on success, or -1 on failure.
1.24 joris 407: */
408: int
1.45 xsa 409: cvs_rmdir(const char *path)
1.24 joris 410: {
1.99 todd 411: int type, ret = -1;
1.24 joris 412: DIR *dirp;
413: struct dirent *ent;
1.99 todd 414: struct stat st;
1.24 joris 415: char fpath[MAXPATHLEN];
1.46 xsa 416:
1.90 joris 417: if (cvs_server_active == 0)
418: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
1.46 xsa 419:
420: if (cvs_noexec == 1)
421: return (0);
1.24 joris 422:
423: if ((dirp = opendir(path)) == NULL) {
1.79 joris 424: cvs_log(LP_ERR, "failed to open '%s'", path);
1.30 jfb 425: return (-1);
1.24 joris 426: }
427:
428: while ((ent = readdir(dirp)) != NULL) {
429: if (!strcmp(ent->d_name, ".") ||
430: !strcmp(ent->d_name, ".."))
431: continue;
432:
1.105 xsa 433: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
434: path, ent->d_name);
1.24 joris 435:
1.99 todd 436: if (ent->d_type == DT_UNKNOWN) {
1.104 todd 437: if (lstat(fpath, &st) == -1)
1.99 todd 438: fatal("'%s': %s", fpath, strerror(errno));
439:
440: switch (st.st_mode & S_IFMT) {
441: case S_IFDIR:
442: type = CVS_DIR;
443: break;
444: case S_IFREG:
445: type = CVS_FILE;
446: break;
447: default:
448: fatal("'%s': Unknown file type in copy",
449: fpath);
450: }
451: } else {
452: switch (ent->d_type) {
453: case DT_DIR:
454: type = CVS_DIR;
455: break;
456: case DT_REG:
457: type = CVS_FILE;
458: break;
459: default:
460: fatal("'%s': Unknown file type in copy",
461: fpath);
462: }
463: }
464: switch (type) {
465: case CVS_DIR:
1.45 xsa 466: if (cvs_rmdir(fpath) == -1)
1.33 pat 467: goto done;
1.99 todd 468: break;
469: case CVS_FILE:
470: if (cvs_unlink(fpath) == -1 && errno != ENOENT)
471: goto done;
472: break;
473: default:
474: fatal("type %d unknown, shouldn't happen", type);
475: }
1.24 joris 476: }
477:
478:
1.78 deraadt 479: if (rmdir(path) == -1 && errno != ENOENT) {
1.93 xsa 480: cvs_log(LP_ERRNO, "%s", path);
1.33 pat 481: goto done;
1.30 jfb 482: }
1.24 joris 483:
1.33 pat 484: ret = 0;
485: done:
486: closedir(dirp);
1.34 joris 487: return (ret);
488: }
489:
1.51 xsa 490: void
1.81 joris 491: cvs_get_repository_path(const char *dir, char *dst, size_t len)
492: {
493: char buf[MAXPATHLEN];
494:
495: cvs_get_repository_name(dir, buf, sizeof(buf));
1.105 xsa 496: (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
1.81 joris 497: }
498:
499: void
500: cvs_get_repository_name(const char *dir, char *dst, size_t len)
1.51 xsa 501: {
502: FILE *fp;
1.81 joris 503: char *s, fpath[MAXPATHLEN];
1.79 joris 504:
1.105 xsa 505: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
506: dir, CVS_PATH_REPOSITORY);
1.79 joris 507:
508: if ((fp = fopen(fpath, "r")) != NULL) {
1.101 thib 509: if ((fgets(dst, len, fp)) == NULL)
510: fatal("cvs_get_repository_name: bad repository file");
1.51 xsa 511:
1.81 joris 512: if ((s = strchr(dst, '\n')) != NULL)
1.79 joris 513: *s = '\0';
1.51 xsa 514:
515: (void)fclose(fp);
1.81 joris 516: } else {
1.84 joris 517: dst[0] = '\0';
518:
519: if (cvs_cmdop == CVS_OP_IMPORT) {
520: if (strlcpy(dst, import_repository, len) >= len)
1.85 joris 521: fatal("cvs_get_repository_name: truncation");
1.84 joris 522: if (strlcat(dst, "/", len) >= len)
1.85 joris 523: fatal("cvs_get_repository_name: truncation");
524:
525: if (strcmp(dir, ".")) {
526: if (strlcat(dst, dir, len) >= len) {
527: fatal("cvs_get_repository_name: "
528: "truncation");
529: }
530: }
531: } else {
1.98 joris 532: if (cvs_cmdop != CVS_OP_CHECKOUT) {
533: if (strlcat(dst, dir, len) >= len)
534: fatal("cvs_get_repository_name: "
535: "truncation");
536: }
1.84 joris 537: }
1.51 xsa 538: }
539: }
540:
541: void
1.87 xsa 542: cvs_mkadmin(const char *path, const char *root, const char *repo,
543: char *tag, char *date, int nb)
1.51 xsa 544: {
545: FILE *fp;
1.79 joris 546: struct stat st;
547: char buf[MAXPATHLEN];
1.51 xsa 548:
1.90 joris 549: if (cvs_server_active == 0)
550: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s, %d)",
551: path, root, repo, (tag != NULL) ? tag : "",
552: (date != NULL) ? date : "", nb);
1.51 xsa 553:
1.105 xsa 554: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
1.51 xsa 555:
1.79 joris 556: if (stat(buf, &st) != -1)
557: return;
1.51 xsa 558:
1.79 joris 559: if (mkdir(buf, 0755) == -1 && errno != EEXIST)
560: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
561:
1.105 xsa 562: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ROOTSPEC);
1.79 joris 563:
564: if ((fp = fopen(buf, "w")) == NULL)
565: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
566:
567: fprintf(fp, "%s\n", root);
568: (void)fclose(fp);
1.51 xsa 569:
1.105 xsa 570: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
1.51 xsa 571:
1.79 joris 572: if ((fp = fopen(buf, "w")) == NULL)
573: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 574:
1.79 joris 575: fprintf(fp, "%s\n", repo);
576: (void)fclose(fp);
1.51 xsa 577:
1.105 xsa 578: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
1.51 xsa 579:
1.79 joris 580: if ((fp = fopen(buf, "w")) == NULL)
581: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 582: (void)fclose(fp);
1.87 xsa 583:
584: cvs_write_tagfile(path, tag, date, nb);
1.54 joris 585: }
586:
1.79 joris 587: void
1.112 joris 588: cvs_mkpath(const char *path, char *tag)
1.72 niallo 589: {
1.79 joris 590: FILE *fp;
591: size_t len;
592: char *sp, *dp, *dir, rpath[MAXPATHLEN], repo[MAXPATHLEN];
593:
594: dir = xstrdup(path);
595:
596: STRIP_SLASH(dir);
1.90 joris 597:
598: if (cvs_server_active == 0)
599: cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
1.72 niallo 600:
1.79 joris 601: repo[0] = '\0';
602: rpath[0] = '\0';
1.72 niallo 603:
1.79 joris 604: if (cvs_cmdop == CVS_OP_UPDATE) {
605: if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
1.91 thib 606: if ((fgets(repo, sizeof(repo), fp)) == NULL)
607: fatal("cvs_mkpath: bad repository file");
608: if ((len = strlen(repo)) && repo[len - 1] == '\n')
609: repo[len - 1] = '\0';
1.79 joris 610: (void)fclose(fp);
611: }
1.72 niallo 612: }
613:
1.79 joris 614: for (sp = dir; sp != NULL; sp = dp) {
615: dp = strchr(sp, '/');
616: if (dp != NULL)
617: *(dp++) = '\0';
618:
619: if (repo[0] != '\0') {
620: len = strlcat(repo, "/", sizeof(repo));
621: if (len >= (int)sizeof(repo))
622: fatal("cvs_mkpath: overflow");
623: }
624:
625: len = strlcat(repo, sp, sizeof(repo));
626: if (len >= (int)sizeof(repo))
627: fatal("cvs_mkpath: overflow");
628:
629: if (rpath[0] != '\0') {
630: len = strlcat(rpath, "/", sizeof(rpath));
631: if (len >= (int)sizeof(rpath))
632: fatal("cvs_mkpath: overflow");
633: }
634:
635: len = strlcat(rpath, sp, sizeof(rpath));
636: if (len >= (int)sizeof(rpath))
637: fatal("cvs_mkpath: overflow");
638:
639: if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
640: fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
1.72 niallo 641:
1.98 joris 642: cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
1.112 joris 643: tag, NULL, 0);
644:
645: if (cvs_server_active == 1 && strcmp(rpath, ".")) {
646: if (tag != NULL)
647: cvs_server_set_sticky(rpath, tag);
648: }
1.79 joris 649: }
1.72 niallo 650:
1.79 joris 651: xfree(dir);
1.72 niallo 652: }
1.54 joris 653:
654: /*
655: * Split the contents of a file into a list of lines.
656: */
657: struct cvs_lines *
1.106 otto 658: cvs_splitlines(u_char *data, size_t len)
1.54 joris 659: {
1.97 niallo 660: u_char *p, *c;
661: size_t i, tlen;
1.54 joris 662: struct cvs_lines *lines;
663: struct cvs_line *lp;
664:
1.77 ray 665: lines = xmalloc(sizeof(*lines));
1.97 niallo 666: memset(lines, 0, sizeof(*lines));
1.54 joris 667: TAILQ_INIT(&(lines->l_lines));
668:
1.77 ray 669: lp = xmalloc(sizeof(*lp));
1.97 niallo 670: memset(lp, 0, sizeof(*lp));
1.54 joris 671: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
672:
1.97 niallo 673: p = c = data;
674: for (i = 0; i < len; i++) {
675: if (*p == '\n' || (i == len - 1)) {
676: tlen = p - c + 1;
677: lp = xmalloc(sizeof(*lp));
1.102 niallo 678: memset(lp, 0, sizeof(*lp));
1.97 niallo 679: lp->l_line = c;
680: lp->l_len = tlen;
681: lp->l_lineno = ++(lines->l_nblines);
682: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
683: c = p + 1;
684: }
685: p++;
1.54 joris 686: }
687:
688: return (lines);
689: }
690:
691: void
692: cvs_freelines(struct cvs_lines *lines)
693: {
694: struct cvs_line *lp;
695:
696: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
697: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
1.102 niallo 698: if (lp->l_needsfree == 1)
699: xfree(lp->l_line);
1.59 joris 700: xfree(lp);
1.54 joris 701: }
702:
1.59 joris 703: xfree(lines);
1.70 niallo 704: }
705:
1.71 xsa 706: /*
707: * cvs_strsplit()
708: *
709: * Split a string <str> of <sep>-separated values and allocate
710: * an argument vector for the values found.
711: */
1.73 pat 712: struct cvs_argvector *
1.71 xsa 713: cvs_strsplit(char *str, const char *sep)
714: {
1.73 pat 715: struct cvs_argvector *av;
1.76 ray 716: size_t i = 0;
1.71 xsa 717: char *cp, *p;
718:
719: cp = xstrdup(str);
1.77 ray 720: av = xmalloc(sizeof(*av));
1.73 pat 721: av->str = cp;
1.113 ray 722: av->argv = xmalloc(sizeof(*(av->argv)));
1.71 xsa 723:
724: while ((p = strsep(&cp, sep)) != NULL) {
1.73 pat 725: av->argv[i++] = p;
1.111 ray 726: av->argv = xrealloc(av->argv,
1.77 ray 727: i + 1, sizeof(*(av->argv)));
1.71 xsa 728: }
1.73 pat 729: av->argv[i] = NULL;
730:
731: return (av);
732: }
1.71 xsa 733:
1.73 pat 734: /*
735: * cvs_argv_destroy()
736: *
737: * Free an argument vector previously allocated by cvs_strsplit().
738: */
739: void
740: cvs_argv_destroy(struct cvs_argvector *av)
741: {
742: xfree(av->str);
743: xfree(av->argv);
744: xfree(av);
1.82 joris 745: }
746:
747: u_int
748: cvs_revision_select(RCSFILE *file, char *range)
749: {
750: int i;
751: u_int nrev;
752: char *lstr, *rstr;
753: struct rcs_delta *rdp;
754: struct cvs_argvector *revargv, *revrange;
755: RCSNUM *lnum, *rnum;
756:
757: nrev = 0;
758: lnum = rnum = NULL;
759:
760: revargv = cvs_strsplit(range, ",");
761: for (i = 0; revargv->argv[i] != NULL; i++) {
762: revrange = cvs_strsplit(revargv->argv[i], ":");
763: if (revrange->argv[0] == NULL)
764: fatal("invalid revision range: %s", revargv->argv[i]);
765: else if (revrange->argv[1] == NULL)
766: lstr = rstr = revrange->argv[0];
767: else {
768: if (revrange->argv[2] != NULL)
769: fatal("invalid revision range: %s",
770: revargv->argv[i]);
771:
772: lstr = revrange->argv[0];
773: rstr = revrange->argv[1];
774:
775: if (strcmp(lstr, "") == 0)
776: lstr = NULL;
777: if (strcmp(rstr, "") == 0)
778: rstr = NULL;
779: }
780:
781: if (lstr == NULL)
782: lstr = RCS_HEAD_INIT;
783:
1.100 niallo 784: if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
785: fatal("cvs_revision_select: could not translate tag `%s'", lstr);
1.82 joris 786:
787: if (rstr != NULL) {
1.100 niallo 788: if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
789: fatal("cvs_revision_select: could not translate tag `%s'", rstr);
1.82 joris 790: } else {
791: rnum = rcsnum_alloc();
792: rcsnum_cpy(file->rf_head, rnum, 0);
793: }
794:
795: cvs_argv_destroy(revrange);
796:
797: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
798: if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
799: rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
800: !(rdp->rd_flags & RCS_RD_SELECT)) {
801: rdp->rd_flags |= RCS_RD_SELECT;
802: nrev++;
803: }
804: }
805: }
806:
807: cvs_argv_destroy(revargv);
808:
809: if (lnum != NULL)
810: rcsnum_free(lnum);
811: if (rnum != NULL)
812: rcsnum_free(rnum);
813:
814: return (nrev);
1.95 xsa 815: }
816:
817: int
818: cvs_yesno(void)
819: {
820: int c, ret;
821:
822: ret = 0;
823:
824: fflush(stderr);
825: fflush(stdout);
826:
827: if ((c = getchar()) != 'y' && c != 'Y')
828: ret = -1;
829: else
830: while (c != EOF && c != '\n')
831: c = getchar();
832:
833: return (ret);
1.71 xsa 834: }