Annotation of src/usr.bin/cvs/util.c, Revision 1.146
1.146 ! joris 1: /* $OpenBSD: util.c,v 1.145 2008/06/12 07:16:14 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>
1.141 tobias 30: #include <sys/types.h>
1.107 otto 31: #include <sys/wait.h>
32:
1.141 tobias 33: #include <atomicio.h>
1.107 otto 34: #include <errno.h>
1.141 tobias 35: #include <fcntl.h>
1.107 otto 36: #include <md5.h>
37: #include <stdlib.h>
38: #include <string.h>
1.132 joris 39: #include <paths.h>
1.107 otto 40: #include <unistd.h>
1.1 jfb 41:
42: #include "cvs.h"
1.112 joris 43: #include "remote.h"
1.1 jfb 44:
1.116 tobias 45: extern int print_stdout;
1.143 joris 46: extern int build_dirs;
1.145 joris 47: extern int disable_fast_checkout;
1.116 tobias 48:
1.1 jfb 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:
1.9 jfb 83: /*
1.1 jfb 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: */
1.64 joris 92: void
1.1 jfb 93: cvs_strtomode(const char *str, mode_t *mode)
94: {
1.2 jfb 95: char type;
1.64 joris 96: size_t l;
1.1 jfb 97: mode_t m;
98: char buf[32], ms[4], *sp, *ep;
99:
100: m = 0;
1.64 joris 101: l = strlcpy(buf, str, sizeof(buf));
102: if (l >= sizeof(buf))
103: fatal("cvs_strtomode: string truncation");
104:
1.1 jfb 105: sp = buf;
106: ep = sp;
107:
108: for (sp = buf; ep != NULL; sp = ep + 1) {
109: ep = strchr(sp, ',');
110: if (ep != NULL)
1.2 jfb 111: *ep = '\0';
1.1 jfb 112:
1.14 weingart 113: memset(ms, 0, sizeof ms);
114: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
115: sscanf(sp, "%c=", &type) != 1) {
1.89 joris 116: fatal("failed to scan mode string `%s'", sp);
1.1 jfb 117: }
118:
1.78 deraadt 119: if (type <= 'a' || type >= 'z' ||
120: cvs_modetypes[type - 'a'] == -1) {
1.79 joris 121: cvs_log(LP_ERR,
1.1 jfb 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++) {
1.78 deraadt 131: if (*sp <= 'a' || *sp >= 'z' ||
132: cvs_modes[(int)type][*sp - 'a'] == 0) {
1.89 joris 133: fatal("invalid permission bit `%c'", *sp);
1.15 deraadt 134: } else
1.5 jfb 135: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 136: }
137: }
138:
139: *mode = m;
140: }
141:
142: /*
143: * cvs_modetostr()
144: *
1.30 jfb 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.
1.1 jfb 149: */
1.65 joris 150: void
1.1 jfb 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) {
1.68 xsa 164: if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
165: strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 166: fatal("cvs_modetostr: overflow for user mode");
167:
1.68 xsa 168: if (strlcat(buf, tmp, len) >= len)
1.65 joris 169: fatal("cvs_modetostr: string truncation");
1.1 jfb 170: }
1.65 joris 171:
1.1 jfb 172: if (gm) {
1.65 joris 173: if (um) {
1.68 xsa 174: if (strlcat(buf, ",", len) >= len)
1.65 joris 175: fatal("cvs_modetostr: string truncation");
176: }
177:
1.68 xsa 178: if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
179: strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 180: fatal("cvs_modetostr: overflow for group mode");
181:
1.68 xsa 182: if (strlcat(buf, tmp, len) >= len)
1.65 joris 183: fatal("cvs_modetostr: string truncation");
1.1 jfb 184: }
1.65 joris 185:
1.1 jfb 186: if (om) {
1.65 joris 187: if (um || gm) {
1.68 xsa 188: if (strlcat(buf, ",", len) >= len)
1.65 joris 189: fatal("cvs_modetostr: string truncation");
190: }
191:
1.68 xsa 192: if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
193: strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
1.65 joris 194: fatal("cvs_modetostr: overflow for others mode");
195:
1.68 xsa 196: if (strlcat(buf, tmp, len) >= len)
1.65 joris 197: fatal("cvs_modetostr: string truncation");
1.1 jfb 198: }
199: }
200:
201: /*
202: * cvs_cksum()
203: *
204: * Calculate the MD5 checksum of the file whose path is <file> and generate
205: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
206: * given in <len> and must be at least 33.
207: * Returns 0 on success, or -1 on failure.
208: */
209: int
210: cvs_cksum(const char *file, char *dst, size_t len)
211: {
212: if (len < CVS_CKSUM_LEN) {
1.79 joris 213: cvs_log(LP_ERR, "buffer too small for checksum");
1.1 jfb 214: return (-1);
215: }
216: if (MD5File(file, dst) == NULL) {
1.79 joris 217: cvs_log(LP_ERR, "failed to generate checksum for %s", file);
1.1 jfb 218: return (-1);
219: }
220:
221: return (0);
222: }
223:
224: /*
225: * cvs_getargv()
226: *
227: * Parse a line contained in <line> and generate an argument vector by
228: * splitting the line on spaces and tabs. The resulting vector is stored in
229: * <argv>, which can accept up to <argvlen> entries.
1.20 david 230: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 231: */
232: int
233: cvs_getargv(const char *line, char **argv, int argvlen)
234: {
235: u_int i;
1.67 xsa 236: int argc, error;
1.118 tobias 237: char *linebuf, *lp, *cp;
1.1 jfb 238:
1.117 tobias 239: linebuf = xstrdup(line);
1.65 joris 240:
1.27 pat 241: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 242: argc = 0;
243:
244: /* build the argument vector */
1.67 xsa 245: error = 0;
1.1 jfb 246: for (lp = linebuf; lp != NULL;) {
1.118 tobias 247: cp = strsep(&lp, " \t");
248: if (cp == NULL)
249: break;
250: else if (*cp == '\0')
251: continue;
1.1 jfb 252:
1.16 jfb 253: if (argc == argvlen) {
1.67 xsa 254: error++;
1.16 jfb 255: break;
256: }
257:
1.118 tobias 258: argv[argc] = xstrdup(cp);
1.1 jfb 259: argc++;
260: }
261:
1.67 xsa 262: if (error != 0) {
1.1 jfb 263: /* ditch the argument vector */
264: for (i = 0; i < (u_int)argc; i++)
1.59 joris 265: xfree(argv[i]);
1.1 jfb 266: argc = -1;
267: }
268:
1.117 tobias 269: xfree(linebuf);
1.1 jfb 270: return (argc);
1.17 jfb 271: }
272:
273: /*
274: * cvs_makeargv()
275: *
1.20 david 276: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 277: * arguments found in <line> and return it.
278: */
1.42 xsa 279: char **
1.17 jfb 280: cvs_makeargv(const char *line, int *argc)
281: {
282: int i, ret;
283: char *argv[1024], **copy;
284:
285: ret = cvs_getargv(line, argv, 1024);
286: if (ret == -1)
287: return (NULL);
288:
1.77 ray 289: copy = xcalloc(ret + 1, sizeof(char *));
1.17 jfb 290:
291: for (i = 0; i < ret; i++)
292: copy[i] = argv[i];
293: copy[ret] = NULL;
294:
295: *argc = ret;
296: return (copy);
1.1 jfb 297: }
298:
299: /*
300: * cvs_freeargv()
301: *
302: * Free an argument vector previously generated by cvs_getargv().
303: */
304: void
305: cvs_freeargv(char **argv, int argc)
306: {
307: int i;
308:
309: for (i = 0; i < argc; i++)
1.16 jfb 310: if (argv[i] != NULL)
1.59 joris 311: xfree(argv[i]);
1.38 xsa 312: }
313:
314: /*
315: * cvs_chdir()
316: *
1.63 xsa 317: * Change to directory <path>.
318: * If <rm> is equal to `1', <path> is removed if chdir() fails so we
319: * do not have temporary directories leftovers.
1.60 xsa 320: * Returns 0 on success.
1.50 xsa 321: */
1.38 xsa 322: int
1.63 xsa 323: cvs_chdir(const char *path, int rm)
1.38 xsa 324: {
1.63 xsa 325: if (chdir(path) == -1) {
326: if (rm == 1)
327: cvs_unlink(path);
1.60 xsa 328: fatal("cvs_chdir: `%s': %s", path, strerror(errno));
1.63 xsa 329: }
1.49 xsa 330:
331: return (0);
332: }
333:
334: /*
335: * cvs_rename()
336: * Change the name of a file.
337: * rename() wrapper with an error message.
1.60 xsa 338: * Returns 0 on success.
1.49 xsa 339: */
340: int
341: cvs_rename(const char *from, const char *to)
342: {
1.90 joris 343: if (cvs_server_active == 0)
344: cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
1.49 xsa 345:
346: if (cvs_noexec == 1)
347: return (0);
348:
1.60 xsa 349: if (rename(from, to) == -1)
350: fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
1.40 xsa 351:
352: return (0);
353: }
354:
355: /*
356: * cvs_unlink()
357: *
358: * Removes the link named by <path>.
359: * unlink() wrapper with an error message.
360: * Returns 0 on success, or -1 on failure.
361: */
362: int
363: cvs_unlink(const char *path)
364: {
1.90 joris 365: if (cvs_server_active == 0)
366: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
1.40 xsa 367:
1.145 joris 368: if (cvs_noexec == 1 && disable_fast_checkout != 0)
1.40 xsa 369: return (0);
370:
1.78 deraadt 371: if (unlink(path) == -1 && errno != ENOENT) {
1.94 xsa 372: cvs_log(LP_ERRNO, "%s", path);
1.38 xsa 373: return (-1);
374: }
375:
376: return (0);
1.1 jfb 377: }
1.24 joris 378:
379: /*
1.45 xsa 380: * cvs_rmdir()
1.30 jfb 381: *
382: * Remove a directory tree from disk.
383: * Returns 0 on success, or -1 on failure.
1.24 joris 384: */
385: int
1.45 xsa 386: cvs_rmdir(const char *path)
1.24 joris 387: {
1.99 todd 388: int type, ret = -1;
1.24 joris 389: DIR *dirp;
390: struct dirent *ent;
1.99 todd 391: struct stat st;
1.24 joris 392: char fpath[MAXPATHLEN];
1.46 xsa 393:
1.90 joris 394: if (cvs_server_active == 0)
395: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
1.46 xsa 396:
1.145 joris 397: if (cvs_noexec == 1 && disable_fast_checkout != 0)
1.46 xsa 398: return (0);
1.24 joris 399:
400: if ((dirp = opendir(path)) == NULL) {
1.79 joris 401: cvs_log(LP_ERR, "failed to open '%s'", path);
1.30 jfb 402: return (-1);
1.24 joris 403: }
404:
405: while ((ent = readdir(dirp)) != NULL) {
406: if (!strcmp(ent->d_name, ".") ||
407: !strcmp(ent->d_name, ".."))
408: continue;
409:
1.105 xsa 410: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
411: path, ent->d_name);
1.24 joris 412:
1.99 todd 413: if (ent->d_type == DT_UNKNOWN) {
1.104 todd 414: if (lstat(fpath, &st) == -1)
1.99 todd 415: fatal("'%s': %s", fpath, strerror(errno));
416:
417: switch (st.st_mode & S_IFMT) {
418: case S_IFDIR:
419: type = CVS_DIR;
420: break;
421: case S_IFREG:
422: type = CVS_FILE;
423: break;
424: default:
425: fatal("'%s': Unknown file type in copy",
426: fpath);
427: }
428: } else {
429: switch (ent->d_type) {
430: case DT_DIR:
431: type = CVS_DIR;
432: break;
433: case DT_REG:
434: type = CVS_FILE;
435: break;
436: default:
437: fatal("'%s': Unknown file type in copy",
438: fpath);
439: }
440: }
441: switch (type) {
1.128 xsa 442: case CVS_DIR:
1.45 xsa 443: if (cvs_rmdir(fpath) == -1)
1.33 pat 444: goto done;
1.99 todd 445: break;
446: case CVS_FILE:
447: if (cvs_unlink(fpath) == -1 && errno != ENOENT)
448: goto done;
449: break;
450: default:
451: fatal("type %d unknown, shouldn't happen", type);
452: }
1.24 joris 453: }
454:
455:
1.78 deraadt 456: if (rmdir(path) == -1 && errno != ENOENT) {
1.93 xsa 457: cvs_log(LP_ERRNO, "%s", path);
1.33 pat 458: goto done;
1.30 jfb 459: }
1.24 joris 460:
1.33 pat 461: ret = 0;
462: done:
463: closedir(dirp);
1.34 joris 464: return (ret);
465: }
466:
1.51 xsa 467: void
1.81 joris 468: cvs_get_repository_path(const char *dir, char *dst, size_t len)
469: {
470: char buf[MAXPATHLEN];
471:
472: cvs_get_repository_name(dir, buf, sizeof(buf));
1.105 xsa 473: (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
1.139 joris 474: cvs_validate_directory(dst);
1.81 joris 475: }
476:
477: void
478: cvs_get_repository_name(const char *dir, char *dst, size_t len)
1.51 xsa 479: {
480: FILE *fp;
1.123 tobias 481: char fpath[MAXPATHLEN];
1.116 tobias 482:
1.127 tobias 483: dst[0] = '\0';
484:
485: if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
486: if (strlcpy(dst, dir, len) >= len)
1.116 tobias 487: fatal("cvs_get_repository_name: truncation");
488: return;
489: }
1.79 joris 490:
1.127 tobias 491: switch (cvs_cmdop) {
492: case CVS_OP_EXPORT:
493: if (strcmp(dir, "."))
494: if (strlcpy(dst, dir, len) >= len)
495: fatal("cvs_get_repository_name: truncation");
496: break;
497: case CVS_OP_IMPORT:
498: if (strlcpy(dst, import_repository, len) >= len)
499: fatal("cvs_get_repository_name: truncation");
500: if (strlcat(dst, "/", len) >= len)
501: fatal("cvs_get_repository_name: truncation");
1.84 joris 502:
1.127 tobias 503: if (strcmp(dir, "."))
504: if (strlcat(dst, dir, len) >= len)
1.85 joris 505: fatal("cvs_get_repository_name: truncation");
1.127 tobias 506: break;
507: default:
508: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
509: dir, CVS_PATH_REPOSITORY);
510: if ((fp = fopen(fpath, "r")) != NULL) {
511: if ((fgets(dst, len, fp)) == NULL)
512: fatal("%s: bad repository file", fpath);
513: dst[strcspn(dst, "\n")] = '\0';
514: (void)fclose(fp);
515: } else if (cvs_cmdop != CVS_OP_CHECKOUT)
516: fatal("%s is missing", fpath);
517: break;
1.51 xsa 518: }
519: }
520:
521: void
1.87 xsa 522: cvs_mkadmin(const char *path, const char *root, const char *repo,
1.131 tobias 523: char *tag, char *date)
1.51 xsa 524: {
525: FILE *fp;
1.141 tobias 526: int fd;
1.79 joris 527: char buf[MAXPATHLEN];
1.51 xsa 528:
1.90 joris 529: if (cvs_server_active == 0)
1.131 tobias 530: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
1.90 joris 531: path, root, repo, (tag != NULL) ? tag : "",
1.131 tobias 532: (date != NULL) ? date : "");
1.51 xsa 533:
1.105 xsa 534: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
1.51 xsa 535:
1.79 joris 536: if (mkdir(buf, 0755) == -1 && errno != EEXIST)
537: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
538:
1.143 joris 539: if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
540: (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
1.140 joris 541: (void)xsnprintf(buf, sizeof(buf), "%s/%s",
542: path, CVS_PATH_ROOTSPEC);
1.79 joris 543:
1.140 joris 544: if ((fp = fopen(buf, "w")) == NULL)
545: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.79 joris 546:
1.140 joris 547: fprintf(fp, "%s\n", root);
548: (void)fclose(fp);
549: }
1.51 xsa 550:
1.105 xsa 551: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
1.51 xsa 552:
1.79 joris 553: if ((fp = fopen(buf, "w")) == NULL)
554: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 555:
1.79 joris 556: fprintf(fp, "%s\n", repo);
1.51 xsa 557: (void)fclose(fp);
1.87 xsa 558:
1.131 tobias 559: cvs_write_tagfile(path, tag, date);
1.141 tobias 560:
561: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
562:
563: if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask))
564: == -1) {
565: if (errno == EEXIST)
566: return;
567: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
568: }
569:
570: if (atomicio(vwrite, fd, "D\n", 2) != 2)
571: fatal("cvs_mkadmin: %s", strerror(errno));
572: close(fd);
1.54 joris 573: }
574:
1.79 joris 575: void
1.112 joris 576: cvs_mkpath(const char *path, char *tag)
1.72 niallo 577: {
1.122 tobias 578: CVSENTRIES *ent;
1.79 joris 579: FILE *fp;
580: size_t len;
1.135 xsa 581: char *entry, sticky[CVS_REV_BUFSZ];
1.122 tobias 582: char *sp, *dp, *dir, *p, rpath[MAXPATHLEN], repo[MAXPATHLEN];
1.138 joris 583:
584: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL ||
585: cvs_server_active == 1)
586: cvs_validate_directory(path);
1.79 joris 587:
588: dir = xstrdup(path);
589:
590: STRIP_SLASH(dir);
1.90 joris 591:
592: if (cvs_server_active == 0)
593: cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
1.72 niallo 594:
1.79 joris 595: repo[0] = '\0';
596: rpath[0] = '\0';
1.72 niallo 597:
1.137 tobias 598: if (cvs_cmdop == CVS_OP_UPDATE || cvs_cmdop == CVS_OP_COMMIT) {
1.79 joris 599: if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
1.91 thib 600: if ((fgets(repo, sizeof(repo), fp)) == NULL)
601: fatal("cvs_mkpath: bad repository file");
1.120 gilles 602: repo[strcspn(repo, "\n")] = '\0';
1.79 joris 603: (void)fclose(fp);
604: }
1.72 niallo 605: }
606:
1.79 joris 607: for (sp = dir; sp != NULL; sp = dp) {
608: dp = strchr(sp, '/');
609: if (dp != NULL)
610: *(dp++) = '\0';
611:
1.130 joris 612: if (sp == dir && module_repo_root != NULL) {
613: len = strlcpy(repo, module_repo_root, sizeof(repo));
614: if (len >= (int)sizeof(repo))
615: fatal("cvs_mkpath: overflow");
1.136 joris 616: } else if (strcmp(sp, ".")) {
1.130 joris 617: if (repo[0] != '\0') {
618: len = strlcat(repo, "/", sizeof(repo));
619: if (len >= (int)sizeof(repo))
620: fatal("cvs_mkpath: overflow");
621: }
622:
623: len = strlcat(repo, sp, sizeof(repo));
1.79 joris 624: if (len >= (int)sizeof(repo))
625: fatal("cvs_mkpath: overflow");
626: }
627:
628: if (rpath[0] != '\0') {
629: len = strlcat(rpath, "/", sizeof(rpath));
630: if (len >= (int)sizeof(rpath))
631: fatal("cvs_mkpath: overflow");
632: }
633:
634: len = strlcat(rpath, sp, sizeof(rpath));
635: if (len >= (int)sizeof(rpath))
636: fatal("cvs_mkpath: overflow");
637:
638: if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
639: fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
1.125 tobias 640:
641: if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
642: continue;
1.72 niallo 643:
1.98 joris 644: cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
1.131 tobias 645: tag, NULL);
1.122 tobias 646:
647: if (dp != NULL) {
648: if ((p = strchr(dp, '/')) != NULL)
649: *p = '\0';
1.135 xsa 650:
651: entry = xmalloc(CVS_ENT_MAXLINELEN);
652: cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
653: entry, CVS_ENT_MAXLINELEN);
654:
1.122 tobias 655: ent = cvs_ent_open(rpath);
656: cvs_ent_add(ent, entry);
1.135 xsa 657: xfree(entry);
658:
1.122 tobias 659: if (p != NULL)
660: *p = '/';
661: }
1.112 joris 662:
663: if (cvs_server_active == 1 && strcmp(rpath, ".")) {
1.119 joris 664: if (tag != NULL) {
665: (void)xsnprintf(sticky, sizeof(sticky),
666: "T%s", tag);
667: cvs_server_set_sticky(rpath, sticky);
668: }
1.112 joris 669: }
1.79 joris 670: }
1.72 niallo 671:
1.79 joris 672: xfree(dir);
1.72 niallo 673: }
1.54 joris 674:
675: /*
676: * Split the contents of a file into a list of lines.
677: */
678: struct cvs_lines *
1.106 otto 679: cvs_splitlines(u_char *data, size_t len)
1.54 joris 680: {
1.97 niallo 681: u_char *p, *c;
682: size_t i, tlen;
1.54 joris 683: struct cvs_lines *lines;
684: struct cvs_line *lp;
685:
1.124 tobias 686: lines = xcalloc(1, sizeof(*lines));
1.54 joris 687: TAILQ_INIT(&(lines->l_lines));
688:
1.124 tobias 689: lp = xcalloc(1, sizeof(*lp));
1.54 joris 690: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
691:
1.97 niallo 692: p = c = data;
693: for (i = 0; i < len; i++) {
694: if (*p == '\n' || (i == len - 1)) {
695: tlen = p - c + 1;
1.124 tobias 696: lp = xcalloc(1, sizeof(*lp));
1.97 niallo 697: lp->l_line = c;
698: lp->l_len = tlen;
699: lp->l_lineno = ++(lines->l_nblines);
700: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
701: c = p + 1;
702: }
703: p++;
1.54 joris 704: }
705:
706: return (lines);
707: }
708:
709: void
710: cvs_freelines(struct cvs_lines *lines)
711: {
712: struct cvs_line *lp;
713:
714: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
715: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
1.102 niallo 716: if (lp->l_needsfree == 1)
717: xfree(lp->l_line);
1.59 joris 718: xfree(lp);
1.54 joris 719: }
720:
1.59 joris 721: xfree(lines);
1.70 niallo 722: }
723:
1.71 xsa 724: /*
725: * cvs_strsplit()
726: *
727: * Split a string <str> of <sep>-separated values and allocate
728: * an argument vector for the values found.
729: */
1.73 pat 730: struct cvs_argvector *
1.71 xsa 731: cvs_strsplit(char *str, const char *sep)
732: {
1.73 pat 733: struct cvs_argvector *av;
1.76 ray 734: size_t i = 0;
1.71 xsa 735: char *cp, *p;
736:
737: cp = xstrdup(str);
1.77 ray 738: av = xmalloc(sizeof(*av));
1.73 pat 739: av->str = cp;
1.113 ray 740: av->argv = xmalloc(sizeof(*(av->argv)));
1.71 xsa 741:
742: while ((p = strsep(&cp, sep)) != NULL) {
1.73 pat 743: av->argv[i++] = p;
1.111 ray 744: av->argv = xrealloc(av->argv,
1.77 ray 745: i + 1, sizeof(*(av->argv)));
1.71 xsa 746: }
1.73 pat 747: av->argv[i] = NULL;
748:
749: return (av);
750: }
1.71 xsa 751:
1.73 pat 752: /*
753: * cvs_argv_destroy()
754: *
755: * Free an argument vector previously allocated by cvs_strsplit().
756: */
757: void
758: cvs_argv_destroy(struct cvs_argvector *av)
759: {
760: xfree(av->str);
761: xfree(av->argv);
762: xfree(av);
1.82 joris 763: }
764:
765: u_int
766: cvs_revision_select(RCSFILE *file, char *range)
767: {
768: int i;
769: u_int nrev;
770: char *lstr, *rstr;
771: struct rcs_delta *rdp;
772: struct cvs_argvector *revargv, *revrange;
773: RCSNUM *lnum, *rnum;
774:
775: nrev = 0;
776: lnum = rnum = NULL;
777:
778: revargv = cvs_strsplit(range, ",");
779: for (i = 0; revargv->argv[i] != NULL; i++) {
780: revrange = cvs_strsplit(revargv->argv[i], ":");
781: if (revrange->argv[0] == NULL)
782: fatal("invalid revision range: %s", revargv->argv[i]);
783: else if (revrange->argv[1] == NULL)
784: lstr = rstr = revrange->argv[0];
785: else {
786: if (revrange->argv[2] != NULL)
787: fatal("invalid revision range: %s",
788: revargv->argv[i]);
789:
790: lstr = revrange->argv[0];
791: rstr = revrange->argv[1];
792:
793: if (strcmp(lstr, "") == 0)
794: lstr = NULL;
795: if (strcmp(rstr, "") == 0)
796: rstr = NULL;
797: }
798:
799: if (lstr == NULL)
800: lstr = RCS_HEAD_INIT;
801:
1.100 niallo 802: if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
803: fatal("cvs_revision_select: could not translate tag `%s'", lstr);
1.82 joris 804:
805: if (rstr != NULL) {
1.100 niallo 806: if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
807: fatal("cvs_revision_select: could not translate tag `%s'", rstr);
1.82 joris 808: } else {
809: rnum = rcsnum_alloc();
810: rcsnum_cpy(file->rf_head, rnum, 0);
811: }
812:
813: cvs_argv_destroy(revrange);
814:
815: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
816: if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
817: rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
818: !(rdp->rd_flags & RCS_RD_SELECT)) {
819: rdp->rd_flags |= RCS_RD_SELECT;
820: nrev++;
821: }
822: }
1.129 joris 823:
824: rcsnum_free(lnum);
825: rcsnum_free(rnum);
1.82 joris 826: }
827:
828: cvs_argv_destroy(revargv);
829:
830: return (nrev);
1.95 xsa 831: }
832:
833: int
834: cvs_yesno(void)
835: {
836: int c, ret;
837:
838: ret = 0;
839:
840: fflush(stderr);
841: fflush(stdout);
842:
843: if ((c = getchar()) != 'y' && c != 'Y')
844: ret = -1;
845: else
846: while (c != EOF && c != '\n')
847: c = getchar();
848:
849: return (ret);
1.132 joris 850: }
851:
1.144 joris 852: /*
853: * cvs_exec()
854: *
855: * Execute <prog> and send <in> to the STDIN if not NULL.
856: * If <needwait> == 1, return the result of <prog>,
857: * else, 0 or -1 if an error occur.
858: */
859: int
1.145 joris 860: cvs_exec(char *prog, const char *in, int needwait)
1.132 joris 861: {
862: pid_t pid;
1.144 joris 863: int fds[2], size, st;
864: char *argp[4] = { "sh", "-c", prog, NULL };
1.132 joris 865:
1.144 joris 866: if (in != NULL && pipe(fds) < 0) {
867: cvs_log(LP_ERR, "cvs_exec: pipe failed");
868: return (-1);
869: }
1.132 joris 870:
871: if ((pid = fork()) == -1) {
872: cvs_log(LP_ERR, "cvs_exec: fork failed");
1.144 joris 873: return (-1);
1.132 joris 874: } else if (pid == 0) {
1.144 joris 875: if (in != NULL) {
876: close(fds[1]);
877: dup2(fds[0], STDIN_FILENO);
878: }
879:
880: setenv("CVSROOT", current_cvsroot->cr_dir, 1);
1.132 joris 881: execv(_PATH_BSHELL, argp);
1.144 joris 882: cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
1.132 joris 883: _exit(127);
884: }
1.144 joris 885:
886: if (in != NULL) {
887: close(fds[0]);
888: size = strlen(in);
889: if (atomicio(vwrite, fds[1], in, size) != size)
890: cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
891: close(fds[1]);
892: }
893:
894: if (needwait == 1) {
895: while (waitpid(pid, &st, 0) == -1)
896: ;
897: if (!WIFEXITED(st)) {
898: errno = EINTR;
899: return (-1);
900: }
901: return (WEXITSTATUS(st));
902: }
903:
904: return (0);
1.71 xsa 905: }