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