Annotation of src/usr.bin/cvs/util.c, Revision 1.113
1.113 ! ray 1: /* $OpenBSD: util.c,v 1.112 2007/07/03 13:22:43 joris 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: }
262:
263: arg = qbuf;
1.15 deraadt 264: } else {
1.1 jfb 265: cp = strsep(&lp, " \t");
266: if (cp == NULL)
267: break;
268: else if (*cp == '\0')
269: continue;
270:
271: arg = cp;
272: }
273:
1.16 jfb 274: if (argc == argvlen) {
1.67 xsa 275: error++;
1.16 jfb 276: break;
277: }
278:
1.59 joris 279: argv[argc] = xstrdup(arg);
1.1 jfb 280: argc++;
281: }
282:
1.67 xsa 283: if (error != 0) {
1.1 jfb 284: /* ditch the argument vector */
285: for (i = 0; i < (u_int)argc; i++)
1.59 joris 286: xfree(argv[i]);
1.1 jfb 287: argc = -1;
288: }
289:
290: return (argc);
1.17 jfb 291: }
292:
293: /*
294: * cvs_makeargv()
295: *
1.20 david 296: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 297: * arguments found in <line> and return it.
298: */
1.42 xsa 299: char **
1.17 jfb 300: cvs_makeargv(const char *line, int *argc)
301: {
302: int i, ret;
303: char *argv[1024], **copy;
304:
305: ret = cvs_getargv(line, argv, 1024);
306: if (ret == -1)
307: return (NULL);
308:
1.77 ray 309: copy = xcalloc(ret + 1, sizeof(char *));
1.17 jfb 310:
311: for (i = 0; i < ret; i++)
312: copy[i] = argv[i];
313: copy[ret] = NULL;
314:
315: *argc = ret;
316: return (copy);
1.1 jfb 317: }
318:
319: /*
320: * cvs_freeargv()
321: *
322: * Free an argument vector previously generated by cvs_getargv().
323: */
324: void
325: cvs_freeargv(char **argv, int argc)
326: {
327: int i;
328:
329: for (i = 0; i < argc; i++)
1.16 jfb 330: if (argv[i] != NULL)
1.59 joris 331: xfree(argv[i]);
1.38 xsa 332: }
333:
334: /*
335: * cvs_chdir()
336: *
1.63 xsa 337: * Change to directory <path>.
338: * If <rm> is equal to `1', <path> is removed if chdir() fails so we
339: * do not have temporary directories leftovers.
1.60 xsa 340: * Returns 0 on success.
1.50 xsa 341: */
1.38 xsa 342: int
1.63 xsa 343: cvs_chdir(const char *path, int rm)
1.38 xsa 344: {
1.63 xsa 345: if (chdir(path) == -1) {
346: if (rm == 1)
347: cvs_unlink(path);
1.60 xsa 348: fatal("cvs_chdir: `%s': %s", path, strerror(errno));
1.63 xsa 349: }
1.49 xsa 350:
351: return (0);
352: }
353:
354: /*
355: * cvs_rename()
356: * Change the name of a file.
357: * rename() wrapper with an error message.
1.60 xsa 358: * Returns 0 on success.
1.49 xsa 359: */
360: int
361: cvs_rename(const char *from, const char *to)
362: {
1.90 joris 363: if (cvs_server_active == 0)
364: cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
1.49 xsa 365:
366: if (cvs_noexec == 1)
367: return (0);
368:
1.60 xsa 369: if (rename(from, to) == -1)
370: fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
1.40 xsa 371:
372: return (0);
373: }
374:
375: /*
376: * cvs_unlink()
377: *
378: * Removes the link named by <path>.
379: * unlink() wrapper with an error message.
380: * Returns 0 on success, or -1 on failure.
381: */
382: int
383: cvs_unlink(const char *path)
384: {
1.90 joris 385: if (cvs_server_active == 0)
386: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
1.40 xsa 387:
388: if (cvs_noexec == 1)
389: return (0);
390:
1.78 deraadt 391: if (unlink(path) == -1 && errno != ENOENT) {
1.94 xsa 392: cvs_log(LP_ERRNO, "%s", path);
1.38 xsa 393: return (-1);
394: }
395:
396: return (0);
1.1 jfb 397: }
1.24 joris 398:
399: /*
1.45 xsa 400: * cvs_rmdir()
1.30 jfb 401: *
402: * Remove a directory tree from disk.
403: * Returns 0 on success, or -1 on failure.
1.24 joris 404: */
405: int
1.45 xsa 406: cvs_rmdir(const char *path)
1.24 joris 407: {
1.99 todd 408: int type, ret = -1;
1.24 joris 409: DIR *dirp;
410: struct dirent *ent;
1.99 todd 411: struct stat st;
1.24 joris 412: char fpath[MAXPATHLEN];
1.46 xsa 413:
1.90 joris 414: if (cvs_server_active == 0)
415: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
1.46 xsa 416:
417: if (cvs_noexec == 1)
418: return (0);
1.24 joris 419:
420: if ((dirp = opendir(path)) == NULL) {
1.79 joris 421: cvs_log(LP_ERR, "failed to open '%s'", path);
1.30 jfb 422: return (-1);
1.24 joris 423: }
424:
425: while ((ent = readdir(dirp)) != NULL) {
426: if (!strcmp(ent->d_name, ".") ||
427: !strcmp(ent->d_name, ".."))
428: continue;
429:
1.105 xsa 430: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
431: path, ent->d_name);
1.24 joris 432:
1.99 todd 433: if (ent->d_type == DT_UNKNOWN) {
1.104 todd 434: if (lstat(fpath, &st) == -1)
1.99 todd 435: fatal("'%s': %s", fpath, strerror(errno));
436:
437: switch (st.st_mode & S_IFMT) {
438: case S_IFDIR:
439: type = CVS_DIR;
440: break;
441: case S_IFREG:
442: type = CVS_FILE;
443: break;
444: default:
445: fatal("'%s': Unknown file type in copy",
446: fpath);
447: }
448: } else {
449: switch (ent->d_type) {
450: case DT_DIR:
451: type = CVS_DIR;
452: break;
453: case DT_REG:
454: type = CVS_FILE;
455: break;
456: default:
457: fatal("'%s': Unknown file type in copy",
458: fpath);
459: }
460: }
461: switch (type) {
462: case CVS_DIR:
1.45 xsa 463: if (cvs_rmdir(fpath) == -1)
1.33 pat 464: goto done;
1.99 todd 465: break;
466: case CVS_FILE:
467: if (cvs_unlink(fpath) == -1 && errno != ENOENT)
468: goto done;
469: break;
470: default:
471: fatal("type %d unknown, shouldn't happen", type);
472: }
1.24 joris 473: }
474:
475:
1.78 deraadt 476: if (rmdir(path) == -1 && errno != ENOENT) {
1.93 xsa 477: cvs_log(LP_ERRNO, "%s", path);
1.33 pat 478: goto done;
1.30 jfb 479: }
1.24 joris 480:
1.33 pat 481: ret = 0;
482: done:
483: closedir(dirp);
1.34 joris 484: return (ret);
485: }
486:
1.51 xsa 487: void
1.81 joris 488: cvs_get_repository_path(const char *dir, char *dst, size_t len)
489: {
490: char buf[MAXPATHLEN];
491:
492: cvs_get_repository_name(dir, buf, sizeof(buf));
1.105 xsa 493: (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
1.81 joris 494: }
495:
496: void
497: cvs_get_repository_name(const char *dir, char *dst, size_t len)
1.51 xsa 498: {
499: FILE *fp;
1.81 joris 500: char *s, fpath[MAXPATHLEN];
1.79 joris 501:
1.105 xsa 502: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
503: dir, CVS_PATH_REPOSITORY);
1.79 joris 504:
505: if ((fp = fopen(fpath, "r")) != NULL) {
1.101 thib 506: if ((fgets(dst, len, fp)) == NULL)
507: fatal("cvs_get_repository_name: bad repository file");
1.51 xsa 508:
1.81 joris 509: if ((s = strchr(dst, '\n')) != NULL)
1.79 joris 510: *s = '\0';
1.51 xsa 511:
512: (void)fclose(fp);
1.81 joris 513: } else {
1.84 joris 514: dst[0] = '\0';
515:
516: if (cvs_cmdop == CVS_OP_IMPORT) {
517: if (strlcpy(dst, import_repository, len) >= len)
1.85 joris 518: fatal("cvs_get_repository_name: truncation");
1.84 joris 519: if (strlcat(dst, "/", len) >= len)
1.85 joris 520: fatal("cvs_get_repository_name: truncation");
521:
522: if (strcmp(dir, ".")) {
523: if (strlcat(dst, dir, len) >= len) {
524: fatal("cvs_get_repository_name: "
525: "truncation");
526: }
527: }
528: } else {
1.98 joris 529: if (cvs_cmdop != CVS_OP_CHECKOUT) {
530: if (strlcat(dst, dir, len) >= len)
531: fatal("cvs_get_repository_name: "
532: "truncation");
533: }
1.84 joris 534: }
1.51 xsa 535: }
536: }
537:
538: void
1.87 xsa 539: cvs_mkadmin(const char *path, const char *root, const char *repo,
540: char *tag, char *date, int nb)
1.51 xsa 541: {
542: FILE *fp;
1.79 joris 543: struct stat st;
544: char buf[MAXPATHLEN];
1.51 xsa 545:
1.90 joris 546: if (cvs_server_active == 0)
547: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s, %d)",
548: path, root, repo, (tag != NULL) ? tag : "",
549: (date != NULL) ? date : "", nb);
1.51 xsa 550:
1.105 xsa 551: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
1.51 xsa 552:
1.79 joris 553: if (stat(buf, &st) != -1)
554: return;
1.51 xsa 555:
1.79 joris 556: if (mkdir(buf, 0755) == -1 && errno != EEXIST)
557: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
558:
1.105 xsa 559: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ROOTSPEC);
1.79 joris 560:
561: if ((fp = fopen(buf, "w")) == NULL)
562: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
563:
564: fprintf(fp, "%s\n", root);
565: (void)fclose(fp);
1.51 xsa 566:
1.105 xsa 567: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
1.51 xsa 568:
1.79 joris 569: if ((fp = fopen(buf, "w")) == NULL)
570: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 571:
1.79 joris 572: fprintf(fp, "%s\n", repo);
573: (void)fclose(fp);
1.51 xsa 574:
1.105 xsa 575: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
1.51 xsa 576:
1.79 joris 577: if ((fp = fopen(buf, "w")) == NULL)
578: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 579: (void)fclose(fp);
1.87 xsa 580:
581: cvs_write_tagfile(path, tag, date, nb);
1.54 joris 582: }
583:
1.79 joris 584: void
1.112 joris 585: cvs_mkpath(const char *path, char *tag)
1.72 niallo 586: {
1.79 joris 587: FILE *fp;
588: size_t len;
589: char *sp, *dp, *dir, rpath[MAXPATHLEN], repo[MAXPATHLEN];
590:
591: dir = xstrdup(path);
592:
593: STRIP_SLASH(dir);
1.90 joris 594:
595: if (cvs_server_active == 0)
596: cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
1.72 niallo 597:
1.79 joris 598: repo[0] = '\0';
599: rpath[0] = '\0';
1.72 niallo 600:
1.79 joris 601: if (cvs_cmdop == CVS_OP_UPDATE) {
602: if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
1.91 thib 603: if ((fgets(repo, sizeof(repo), fp)) == NULL)
604: fatal("cvs_mkpath: bad repository file");
605: if ((len = strlen(repo)) && repo[len - 1] == '\n')
606: repo[len - 1] = '\0';
1.79 joris 607: (void)fclose(fp);
608: }
1.72 niallo 609: }
610:
1.79 joris 611: for (sp = dir; sp != NULL; sp = dp) {
612: dp = strchr(sp, '/');
613: if (dp != NULL)
614: *(dp++) = '\0';
615:
616: if (repo[0] != '\0') {
617: len = strlcat(repo, "/", sizeof(repo));
618: if (len >= (int)sizeof(repo))
619: fatal("cvs_mkpath: overflow");
620: }
621:
622: len = strlcat(repo, sp, sizeof(repo));
623: if (len >= (int)sizeof(repo))
624: fatal("cvs_mkpath: overflow");
625:
626: if (rpath[0] != '\0') {
627: len = strlcat(rpath, "/", sizeof(rpath));
628: if (len >= (int)sizeof(rpath))
629: fatal("cvs_mkpath: overflow");
630: }
631:
632: len = strlcat(rpath, sp, sizeof(rpath));
633: if (len >= (int)sizeof(rpath))
634: fatal("cvs_mkpath: overflow");
635:
636: if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
637: fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
1.72 niallo 638:
1.98 joris 639: cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
1.112 joris 640: tag, NULL, 0);
641:
642: if (cvs_server_active == 1 && strcmp(rpath, ".")) {
643: if (tag != NULL)
644: cvs_server_set_sticky(rpath, tag);
645: }
1.79 joris 646: }
1.72 niallo 647:
1.79 joris 648: xfree(dir);
1.72 niallo 649: }
1.54 joris 650:
651: /*
652: * Split the contents of a file into a list of lines.
653: */
654: struct cvs_lines *
1.106 otto 655: cvs_splitlines(u_char *data, size_t len)
1.54 joris 656: {
1.97 niallo 657: u_char *p, *c;
658: size_t i, tlen;
1.54 joris 659: struct cvs_lines *lines;
660: struct cvs_line *lp;
661:
1.77 ray 662: lines = xmalloc(sizeof(*lines));
1.97 niallo 663: memset(lines, 0, sizeof(*lines));
1.54 joris 664: TAILQ_INIT(&(lines->l_lines));
665:
1.77 ray 666: lp = xmalloc(sizeof(*lp));
1.97 niallo 667: memset(lp, 0, sizeof(*lp));
1.54 joris 668: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
669:
1.97 niallo 670: p = c = data;
671: for (i = 0; i < len; i++) {
672: if (*p == '\n' || (i == len - 1)) {
673: tlen = p - c + 1;
674: lp = xmalloc(sizeof(*lp));
1.102 niallo 675: memset(lp, 0, sizeof(*lp));
1.97 niallo 676: lp->l_line = c;
677: lp->l_len = tlen;
678: lp->l_lineno = ++(lines->l_nblines);
679: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
680: c = p + 1;
681: }
682: p++;
1.54 joris 683: }
684:
685: return (lines);
686: }
687:
688: void
689: cvs_freelines(struct cvs_lines *lines)
690: {
691: struct cvs_line *lp;
692:
693: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
694: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
1.102 niallo 695: if (lp->l_needsfree == 1)
696: xfree(lp->l_line);
1.59 joris 697: xfree(lp);
1.54 joris 698: }
699:
1.59 joris 700: xfree(lines);
1.54 joris 701: }
702:
703: BUF *
1.106 otto 704: cvs_patchfile(u_char *data, size_t dlen, u_char *patch, size_t plen,
1.54 joris 705: int (*p)(struct cvs_lines *, struct cvs_lines *))
706: {
707: struct cvs_lines *dlines, *plines;
708: struct cvs_line *lp;
709: BUF *res;
710:
1.97 niallo 711: if ((dlines = cvs_splitlines(data, dlen)) == NULL)
1.54 joris 712: return (NULL);
713:
1.97 niallo 714: if ((plines = cvs_splitlines(patch, plen)) == NULL)
1.54 joris 715: return (NULL);
716:
717: if (p(dlines, plines) < 0) {
718: cvs_freelines(dlines);
719: cvs_freelines(plines);
720: return (NULL);
721: }
722:
1.97 niallo 723: res = cvs_buf_alloc(1024, BUF_AUTOEXT);
1.54 joris 724: TAILQ_FOREACH(lp, &dlines->l_lines, l_list) {
1.97 niallo 725: if (lp->l_line == NULL)
726: continue;
727: cvs_buf_append(res, lp->l_line, lp->l_len);
1.54 joris 728: }
729:
730: cvs_freelines(dlines);
731: cvs_freelines(plines);
732: return (res);
1.70 niallo 733: }
734:
1.71 xsa 735: /*
736: * cvs_strsplit()
737: *
738: * Split a string <str> of <sep>-separated values and allocate
739: * an argument vector for the values found.
740: */
1.73 pat 741: struct cvs_argvector *
1.71 xsa 742: cvs_strsplit(char *str, const char *sep)
743: {
1.73 pat 744: struct cvs_argvector *av;
1.76 ray 745: size_t i = 0;
1.71 xsa 746: char *cp, *p;
747:
748: cp = xstrdup(str);
1.77 ray 749: av = xmalloc(sizeof(*av));
1.73 pat 750: av->str = cp;
1.113 ! ray 751: av->argv = xmalloc(sizeof(*(av->argv)));
1.71 xsa 752:
753: while ((p = strsep(&cp, sep)) != NULL) {
1.73 pat 754: av->argv[i++] = p;
1.111 ray 755: av->argv = xrealloc(av->argv,
1.77 ray 756: i + 1, sizeof(*(av->argv)));
1.71 xsa 757: }
1.73 pat 758: av->argv[i] = NULL;
759:
760: return (av);
761: }
1.71 xsa 762:
1.73 pat 763: /*
764: * cvs_argv_destroy()
765: *
766: * Free an argument vector previously allocated by cvs_strsplit().
767: */
768: void
769: cvs_argv_destroy(struct cvs_argvector *av)
770: {
771: xfree(av->str);
772: xfree(av->argv);
773: xfree(av);
1.82 joris 774: }
775:
776: u_int
777: cvs_revision_select(RCSFILE *file, char *range)
778: {
779: int i;
780: u_int nrev;
781: char *lstr, *rstr;
782: struct rcs_delta *rdp;
783: struct cvs_argvector *revargv, *revrange;
784: RCSNUM *lnum, *rnum;
785:
786: nrev = 0;
787: lnum = rnum = NULL;
788:
789: revargv = cvs_strsplit(range, ",");
790: for (i = 0; revargv->argv[i] != NULL; i++) {
791: revrange = cvs_strsplit(revargv->argv[i], ":");
792: if (revrange->argv[0] == NULL)
793: fatal("invalid revision range: %s", revargv->argv[i]);
794: else if (revrange->argv[1] == NULL)
795: lstr = rstr = revrange->argv[0];
796: else {
797: if (revrange->argv[2] != NULL)
798: fatal("invalid revision range: %s",
799: revargv->argv[i]);
800:
801: lstr = revrange->argv[0];
802: rstr = revrange->argv[1];
803:
804: if (strcmp(lstr, "") == 0)
805: lstr = NULL;
806: if (strcmp(rstr, "") == 0)
807: rstr = NULL;
808: }
809:
810: if (lstr == NULL)
811: lstr = RCS_HEAD_INIT;
812:
1.100 niallo 813: if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
814: fatal("cvs_revision_select: could not translate tag `%s'", lstr);
1.82 joris 815:
816: if (rstr != NULL) {
1.100 niallo 817: if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
818: fatal("cvs_revision_select: could not translate tag `%s'", rstr);
1.82 joris 819: } else {
820: rnum = rcsnum_alloc();
821: rcsnum_cpy(file->rf_head, rnum, 0);
822: }
823:
824: cvs_argv_destroy(revrange);
825:
826: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
827: if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
828: rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
829: !(rdp->rd_flags & RCS_RD_SELECT)) {
830: rdp->rd_flags |= RCS_RD_SELECT;
831: nrev++;
832: }
833: }
834: }
835:
836: cvs_argv_destroy(revargv);
837:
838: if (lnum != NULL)
839: rcsnum_free(lnum);
840: if (rnum != NULL)
841: rcsnum_free(rnum);
842:
843: return (nrev);
1.95 xsa 844: }
845:
846: int
847: cvs_yesno(void)
848: {
849: int c, ret;
850:
851: ret = 0;
852:
853: fflush(stderr);
854: fflush(stdout);
855:
856: if ((c = getchar()) != 'y' && c != 'Y')
857: ret = -1;
858: else
859: while (c != EOF && c != '\n')
860: c = getchar();
861:
862: return (ret);
1.71 xsa 863: }