Annotation of src/usr.bin/cvs/util.c, Revision 1.48
1.48 ! moritz 1: /* $OpenBSD: util.c,v 1.47 2005/07/27 19:24:14 xsa Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.9 jfb 4: * All rights reserved.
1.1 jfb 5: *
1.9 jfb 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.9 jfb 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.9 jfb 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.9 jfb 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
1.12 krapht 29: #include <sys/wait.h>
1.1 jfb 30:
31: #include <errno.h>
1.2 jfb 32: #include <fcntl.h>
1.31 xsa 33: #include <md5.h>
1.1 jfb 34: #include <stdio.h>
35: #include <stdlib.h>
1.31 xsa 36: #include <string.h>
1.1 jfb 37: #include <unistd.h>
38:
39: #include "cvs.h"
40: #include "log.h"
41:
42: /* letter -> mode type map */
43: static const int cvs_modetypes[26] = {
44: -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
45: -1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
46: };
47:
48: /* letter -> mode map */
49: static const mode_t cvs_modes[3][26] = {
50: {
51: 0, 0, 0, 0, 0, 0, 0, /* a - g */
52: 0, 0, 0, 0, 0, 0, 0, /* h - m */
53: 0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */
54: 0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */
55: },
56: {
57: 0, 0, 0, 0, 0, 0, 0, /* a - g */
58: 0, 0, 0, 0, 0, 0, 0, /* h - m */
59: 0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */
60: 0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */
61: },
62: {
63: 0, 0, 0, 0, 0, 0, 0, /* a - g */
64: 0, 0, 0, 0, 0, 0, 0, /* h - m */
65: 0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */
66: 0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */
67: }
68: };
69:
70:
71: /* octal -> string */
72: static const char *cvs_modestr[8] = {
73: "", "x", "w", "wx", "r", "rx", "rw", "rwx"
74: };
75:
76:
1.11 krapht 77: pid_t cvs_exec_pid;
78:
1.1 jfb 79:
80: /*
81: * cvs_readrepo()
82: *
83: * Read the path stored in the `Repository' CVS file for a given directory
84: * <dir>, and store that path into the buffer pointed to by <dst>, whose size
85: * is <len>.
86: */
87: int
88: cvs_readrepo(const char *dir, char *dst, size_t len)
89: {
1.37 xsa 90: size_t dlen, l;
1.1 jfb 91: FILE *fp;
92: char repo_path[MAXPATHLEN];
93:
1.37 xsa 94: l = cvs_path_cat(dir, "CVS/Repository", repo_path, sizeof(repo_path));
95: if (l >= sizeof(repo_path))
1.21 xsa 96: return (NULL);
97:
1.1 jfb 98: fp = fopen(repo_path, "r");
1.21 xsa 99: if (fp == NULL)
1.1 jfb 100: return (-1);
101:
102: if (fgets(dst, (int)len, fp) == NULL) {
103: if (ferror(fp)) {
104: cvs_log(LP_ERRNO, "failed to read from `%s'",
105: repo_path);
106: }
107: (void)fclose(fp);
108: return (-1);
109: }
110: dlen = strlen(dst);
111: if ((dlen > 0) && (dst[dlen - 1] == '\n'))
112: dst[--dlen] = '\0';
113:
114: (void)fclose(fp);
115: return (0);
1.9 jfb 116: }
117:
118:
119: /*
1.1 jfb 120: * cvs_strtomode()
121: *
122: * Read the contents of the string <str> and generate a permission mode from
123: * the contents of <str>, which is assumed to have the mode format of CVS.
124: * The CVS protocol specification states that any modes or mode types that are
125: * not recognized should be silently ignored. This function does not return
126: * an error in such cases, but will issue warnings.
127: * Returns 0 on success, or -1 on failure.
128: */
129: int
130: cvs_strtomode(const char *str, mode_t *mode)
131: {
1.2 jfb 132: char type;
1.1 jfb 133: mode_t m;
134: char buf[32], ms[4], *sp, *ep;
135:
136: m = 0;
1.30 jfb 137: if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf)) {
138: return (-1);
139: }
1.1 jfb 140: sp = buf;
141: ep = sp;
142:
143: for (sp = buf; ep != NULL; sp = ep + 1) {
144: ep = strchr(sp, ',');
145: if (ep != NULL)
1.2 jfb 146: *ep = '\0';
1.1 jfb 147:
1.14 weingart 148: memset(ms, 0, sizeof ms);
149: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
150: sscanf(sp, "%c=", &type) != 1) {
1.1 jfb 151: cvs_log(LP_WARN, "failed to scan mode string `%s'", sp);
152: continue;
153: }
154:
155: if ((type <= 'a') || (type >= 'z') ||
156: (cvs_modetypes[type - 'a'] == -1)) {
157: cvs_log(LP_WARN,
158: "invalid mode type `%c'"
159: " (`u', `g' or `o' expected), ignoring", type);
160: continue;
161: }
162:
163: /* make type contain the actual mode index */
164: type = cvs_modetypes[type - 'a'];
165:
166: for (sp = ms; *sp != '\0'; sp++) {
167: if ((*sp <= 'a') || (*sp >= 'z') ||
1.5 jfb 168: (cvs_modes[(int)type][*sp - 'a'] == 0)) {
1.1 jfb 169: cvs_log(LP_WARN,
170: "invalid permission bit `%c'", *sp);
1.15 deraadt 171: } else
1.5 jfb 172: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 173: }
174: }
175:
176: *mode = m;
177:
178: return (0);
179: }
180:
181:
182: /*
183: * cvs_modetostr()
184: *
1.30 jfb 185: * Generate a CVS-format string to represent the permissions mask on a file
186: * from the mode <mode> and store the result in <buf>, which can accept up to
187: * <len> bytes (including the terminating NUL byte). The result is guaranteed
188: * to be NUL-terminated.
1.1 jfb 189: * Returns 0 on success, or -1 on failure.
190: */
191: int
192: cvs_modetostr(mode_t mode, char *buf, size_t len)
193: {
194: size_t l;
195: char tmp[16], *bp;
196: mode_t um, gm, om;
197:
198: um = (mode & S_IRWXU) >> 6;
199: gm = (mode & S_IRWXG) >> 3;
200: om = mode & S_IRWXO;
201:
202: bp = buf;
203: *bp = '\0';
204: l = 0;
205:
206: if (um) {
207: snprintf(tmp, sizeof(tmp), "u=%s", cvs_modestr[um]);
1.9 jfb 208: l = strlcat(buf, tmp, len);
1.1 jfb 209: }
210: if (gm) {
211: if (um)
212: strlcat(buf, ",", len);
213: snprintf(tmp, sizeof(tmp), "g=%s", cvs_modestr[gm]);
1.9 jfb 214: strlcat(buf, tmp, len);
1.1 jfb 215: }
216: if (om) {
217: if (um || gm)
218: strlcat(buf, ",", len);
219: snprintf(tmp, sizeof(tmp), "o=%s", cvs_modestr[gm]);
1.9 jfb 220: strlcat(buf, tmp, len);
1.1 jfb 221: }
222:
223: return (0);
224: }
225:
226: /*
227: * cvs_cksum()
228: *
229: * Calculate the MD5 checksum of the file whose path is <file> and generate
230: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
231: * given in <len> and must be at least 33.
232: * Returns 0 on success, or -1 on failure.
233: */
234: int
235: cvs_cksum(const char *file, char *dst, size_t len)
236: {
237: if (len < CVS_CKSUM_LEN) {
238: cvs_log(LP_WARN, "buffer too small for checksum");
239: return (-1);
240: }
241: if (MD5File(file, dst) == NULL) {
1.19 jfb 242: cvs_log(LP_ERRNO, "failed to generate checksum for %s", file);
1.1 jfb 243: return (-1);
244: }
245:
246: return (0);
247: }
248:
249: /*
250: * cvs_splitpath()
251: *
1.7 jfb 252: * Split a path <path> into the base portion and the filename portion.
253: * The path is copied in <base> and the last delimiter is replaced by a NUL
254: * byte. The <file> pointer is set to point to the first character after
255: * that delimiter.
1.1 jfb 256: * Returns 0 on success, or -1 on failure.
257: */
258: int
1.7 jfb 259: cvs_splitpath(const char *path, char *base, size_t blen, char **file)
1.1 jfb 260: {
261: size_t rlen;
1.7 jfb 262: char *sp;
1.1 jfb 263:
1.7 jfb 264: if ((rlen = strlcpy(base, path, blen)) >= blen)
265: return (-1);
1.6 jfb 266:
1.7 jfb 267: while ((rlen > 0) && (base[rlen - 1] == '/'))
268: base[--rlen] = '\0';
269:
270: sp = strrchr(base, '/');
1.1 jfb 271: if (sp == NULL) {
1.7 jfb 272: strlcpy(base, "./", blen);
273: strlcat(base, path, blen);
274: sp = base + 1;
1.1 jfb 275: }
276:
1.7 jfb 277: *sp = '\0';
278: if (file != NULL)
279: *file = sp + 1;
1.1 jfb 280:
281: return (0);
282: }
283:
284:
285: /*
286: * cvs_getargv()
287: *
288: * Parse a line contained in <line> and generate an argument vector by
289: * splitting the line on spaces and tabs. The resulting vector is stored in
290: * <argv>, which can accept up to <argvlen> entries.
1.20 david 291: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 292: */
293: int
294: cvs_getargv(const char *line, char **argv, int argvlen)
295: {
296: u_int i;
297: int argc, err;
298: char linebuf[256], qbuf[128], *lp, *cp, *arg;
299:
300: strlcpy(linebuf, line, sizeof(linebuf));
1.27 pat 301: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 302: argc = 0;
303:
304: /* build the argument vector */
305: err = 0;
306: for (lp = linebuf; lp != NULL;) {
307: if (*lp == '"') {
308: /* double-quoted string */
309: lp++;
310: i = 0;
311: memset(qbuf, 0, sizeof(qbuf));
312: while (*lp != '"') {
1.16 jfb 313: if (*lp == '\\')
314: lp++;
1.1 jfb 315: if (*lp == '\0') {
316: cvs_log(LP_ERR, "no terminating quote");
317: err++;
318: break;
1.16 jfb 319: }
1.1 jfb 320:
1.9 jfb 321: qbuf[i++] = *lp++;
1.1 jfb 322: if (i == sizeof(qbuf)) {
323: err++;
324: break;
325: }
326: }
327:
328: arg = qbuf;
1.15 deraadt 329: } else {
1.1 jfb 330: cp = strsep(&lp, " \t");
331: if (cp == NULL)
332: break;
333: else if (*cp == '\0')
334: continue;
335:
336: arg = cp;
337: }
338:
1.16 jfb 339: if (argc == argvlen) {
340: err++;
341: break;
342: }
343:
1.1 jfb 344: argv[argc] = strdup(arg);
345: if (argv[argc] == NULL) {
346: cvs_log(LP_ERRNO, "failed to copy argument");
347: err++;
348: break;
349: }
350: argc++;
351: }
352:
1.43 xsa 353: if (err != 0) {
1.1 jfb 354: /* ditch the argument vector */
355: for (i = 0; i < (u_int)argc; i++)
356: free(argv[i]);
357: argc = -1;
358: }
359:
360: return (argc);
1.17 jfb 361: }
362:
363:
364: /*
365: * cvs_makeargv()
366: *
1.20 david 367: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 368: * arguments found in <line> and return it.
369: */
1.42 xsa 370: char **
1.17 jfb 371: cvs_makeargv(const char *line, int *argc)
372: {
373: int i, ret;
374: char *argv[1024], **copy;
1.27 pat 375: size_t size;
1.17 jfb 376:
377: ret = cvs_getargv(line, argv, 1024);
378: if (ret == -1)
379: return (NULL);
380:
1.27 pat 381: size = (ret + 1) * sizeof(char *);
382: copy = (char **)malloc(size);
1.17 jfb 383: if (copy == NULL) {
384: cvs_log(LP_ERRNO, "failed to allocate argument vector");
1.27 pat 385: cvs_freeargv(argv, ret);
1.17 jfb 386: return (NULL);
387: }
1.27 pat 388: memset(copy, 0, size);
1.17 jfb 389:
390: for (i = 0; i < ret; i++)
391: copy[i] = argv[i];
392: copy[ret] = NULL;
393:
394: *argc = ret;
395: return (copy);
1.1 jfb 396: }
397:
398:
399: /*
400: * cvs_freeargv()
401: *
402: * Free an argument vector previously generated by cvs_getargv().
403: */
404: void
405: cvs_freeargv(char **argv, int argc)
406: {
407: int i;
408:
409: for (i = 0; i < argc; i++)
1.16 jfb 410: if (argv[i] != NULL)
411: free(argv[i]);
1.2 jfb 412: }
413:
414:
415: /*
416: * cvs_mkadmin()
417: *
1.5 jfb 418: * Create the CVS administrative files within the directory <cdir>. If the
419: * files already exist, they are kept as is.
1.2 jfb 420: * Returns 0 on success, or -1 on failure.
421: */
422: int
1.28 joris 423: cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath)
1.2 jfb 424: {
1.37 xsa 425: size_t l;
1.28 joris 426: char path[MAXPATHLEN];
1.2 jfb 427: FILE *fp;
428: CVSENTRIES *ef;
1.5 jfb 429: struct stat st;
1.13 jfb 430:
1.37 xsa 431: l = cvs_path_cat(dpath, CVS_PATH_CVSDIR, path, sizeof(path));
432: if (l >= sizeof(path))
1.21 xsa 433: return (-1);
434:
1.28 joris 435: if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) {
1.2 jfb 436: cvs_log(LP_ERRNO, "failed to create directory %s", path);
437: return (-1);
438: }
439:
1.5 jfb 440: /* just create an empty Entries file */
1.13 jfb 441: ef = cvs_ent_open(dpath, O_WRONLY);
1.48 ! moritz 442: if (ef != NULL)
! 443: cvs_ent_close(ef);
1.2 jfb 444:
1.37 xsa 445: l = cvs_path_cat(dpath, CVS_PATH_ROOTSPEC, path, sizeof(path));
446: if (l >= sizeof(path))
1.21 xsa 447: return (-1);
448:
1.47 xsa 449: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.5 jfb 450: fp = fopen(path, "w");
451: if (fp == NULL) {
452: cvs_log(LP_ERRNO, "failed to open %s", path);
453: return (-1);
454: }
1.28 joris 455: if (rootpath != NULL)
456: fprintf(fp, "%s\n", rootpath);
1.5 jfb 457: (void)fclose(fp);
1.2 jfb 458: }
459:
1.37 xsa 460: l = cvs_path_cat(dpath, CVS_PATH_REPOSITORY, path, sizeof(path));
461: if (l >= sizeof(path))
1.21 xsa 462: return (-1);
463:
1.47 xsa 464: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.2 jfb 465: fp = fopen(path, "w");
466: if (fp == NULL) {
467: cvs_log(LP_ERRNO, "failed to open %s", path);
468: return (-1);
469: }
1.28 joris 470: if (repopath != NULL)
471: fprintf(fp, "%s\n", repopath);
1.2 jfb 472: (void)fclose(fp);
473: }
474:
475: return (0);
1.11 krapht 476: }
477:
478:
479: /*
480: * cvs_exec()
481: */
482: int
483: cvs_exec(int argc, char **argv, int fds[3])
484: {
485: int ret;
486: pid_t pid;
487:
488: if ((pid = fork()) == -1) {
489: cvs_log(LP_ERRNO, "failed to fork");
490: return (-1);
491: } else if (pid == 0) {
492: execvp(argv[0], argv);
1.13 jfb 493: cvs_log(LP_ERRNO, "failed to exec %s", argv[0]);
494: exit(1);
1.11 krapht 495: }
496:
497: if (waitpid(pid, &ret, 0) == -1)
1.13 jfb 498: cvs_log(LP_ERRNO, "failed to waitpid");
1.11 krapht 499:
500: return (ret);
1.38 xsa 501: }
502:
503: /*
504: * cvs_chdir()
505: *
506: * Change to directory.
507: * chdir() wrapper with an error message.
508: * Returns 0 on success, or -1 on failure.
509: */
510: int
511: cvs_chdir(const char *path)
512: {
513: if (chdir(path) == -1) {
514: cvs_log(LP_ERRNO, "cannot change to dir `%s'", path);
1.40 xsa 515: return (-1);
516: }
517:
518: return (0);
519: }
520:
521: /*
522: * cvs_unlink()
523: *
524: * Removes the link named by <path>.
525: * unlink() wrapper with an error message.
526: * Returns 0 on success, or -1 on failure.
527: */
528: int
529: cvs_unlink(const char *path)
530: {
531: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
532:
533: if (cvs_noexec == 1)
534: return (0);
535:
1.44 joris 536: if ((unlink(path) == -1) && (errno != ENOENT)) {
1.40 xsa 537: cvs_log(LP_ERRNO, "cannot remove `%s'", path);
1.38 xsa 538: return (-1);
539: }
540:
541: return (0);
1.1 jfb 542: }
1.24 joris 543:
544: /*
1.45 xsa 545: * cvs_rmdir()
1.30 jfb 546: *
547: * Remove a directory tree from disk.
548: * Returns 0 on success, or -1 on failure.
1.24 joris 549: */
550: int
1.45 xsa 551: cvs_rmdir(const char *path)
1.24 joris 552: {
1.33 pat 553: int ret = -1;
1.30 jfb 554: size_t len;
1.24 joris 555: DIR *dirp;
556: struct dirent *ent;
557: char fpath[MAXPATHLEN];
1.46 xsa 558:
559: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
560:
561: if (cvs_noexec == 1)
562: return (0);
1.24 joris 563:
564: if ((dirp = opendir(path)) == NULL) {
565: cvs_log(LP_ERRNO, "failed to open '%s'", path);
1.30 jfb 566: return (-1);
1.24 joris 567: }
568:
569: while ((ent = readdir(dirp)) != NULL) {
570: if (!strcmp(ent->d_name, ".") ||
571: !strcmp(ent->d_name, ".."))
572: continue;
573:
1.30 jfb 574: len = cvs_path_cat(path, ent->d_name, fpath, sizeof(fpath));
1.33 pat 575: if (len >= sizeof(fpath))
576: goto done;
1.24 joris 577:
578: if (ent->d_type == DT_DIR) {
1.45 xsa 579: if (cvs_rmdir(fpath) == -1)
1.33 pat 580: goto done;
1.41 xsa 581: } else if ((cvs_unlink(fpath) == -1) && (errno != ENOENT))
1.33 pat 582: goto done;
1.24 joris 583: }
584:
585:
1.30 jfb 586: if ((rmdir(path) == -1) && (errno != ENOENT)) {
1.24 joris 587: cvs_log(LP_ERRNO, "failed to remove '%s'", path);
1.33 pat 588: goto done;
1.30 jfb 589: }
1.24 joris 590:
1.33 pat 591: ret = 0;
592: done:
593: closedir(dirp);
1.34 joris 594: return (ret);
595: }
596:
597: /*
598: * Create a directory, and the parent directories if needed.
599: * based upon mkpath() from mkdir.c
600: */
601: int
602: cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
603: {
604: size_t l;
605: int len, ret;
606: char *d, *s;
607: struct stat sb;
608: char rpath[MAXPATHLEN], entry[MAXPATHLEN];
609: CVSENTRIES *entf;
610: struct cvs_ent *ent;
611:
612: if (create_adm == 1 && (root == NULL || repo == NULL)) {
613: cvs_log(LP_ERR, "missing stuff in cvs_create_dir");
614: return (-1);
615: }
616:
617: if ((s = strdup(path)) == NULL)
618: return (-1);
619:
620: if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath) ||
621: strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
622: errno = ENAMETOOLONG;
623: cvs_log(LP_ERRNO, "%s", rpath);
624: free(s);
625: return (-1);
626: }
627:
628: ret = -1;
629: entf = NULL;
630: d = strtok(s, "/");
631: while (d != NULL) {
632: if (stat(d, &sb)) {
633: /* try to create the directory */
634: if ((errno != ENOENT) || (mkdir(d, 0755) &&
635: errno != EEXIST)) {
636: cvs_log(LP_ERRNO, "failed to create `%s'", d);
637: goto done;
638: }
639: } else if (!S_ISDIR(sb.st_mode)) {
640: cvs_log(LP_ERR, "`%s' not a directory", d);
641: goto done;
642: }
643:
644: /*
645: * Create administrative files if requested.
646: */
1.43 xsa 647: if (create_adm == 1) {
1.34 joris 648: l = strlcat(rpath, d, sizeof(rpath));
649: if (l >= sizeof(rpath))
650: goto done;
651:
652: l = strlcat(rpath, "/", sizeof(rpath));
653: if (l >= sizeof(rpath))
654: goto done;
655:
656: if (cvs_mkadmin(d, root, rpath) < 0) {
657: cvs_log(LP_ERR, "failed to create adm files");
658: goto done;
659: }
660: }
661:
662: /*
663: * Add it to the parent directory entry file.
664: * (if any).
665: */
666: entf = cvs_ent_open(".", O_RDWR);
667: if (entf != NULL && strcmp(d, ".")) {
668: len = snprintf(entry, sizeof(entry), "D/%s////", d);
669: if (len == -1 || len >= (int)sizeof(entry)) {
670: errno = ENAMETOOLONG;
671: cvs_log(LP_ERRNO, "%s", entry);
672: goto done;
673: }
674:
675: if ((ent = cvs_ent_parse(entry)) == NULL) {
676: cvs_log(LP_ERR, "failed to parse entry");
677: goto done;
678: }
679:
680: cvs_ent_remove(entf, d);
681:
682: if (cvs_ent_add(entf, ent) < 0) {
683: cvs_log(LP_ERR, "failed to add entry");
684: goto done;
685: }
686: }
687:
688: if (entf != NULL) {
689: cvs_ent_close(entf);
690: entf = NULL;
691: }
692:
693: /*
694: * All went ok, switch to the newly created directory.
695: */
1.39 xsa 696: if (cvs_chdir(d) == -1)
1.34 joris 697: goto done;
698:
699: d = strtok(NULL, "/");
700: }
701:
702: ret = 0;
703: done:
704: if (entf != NULL)
705: cvs_ent_close(entf);
706: free(s);
1.33 pat 707: return (ret);
1.24 joris 708: }
709:
1.30 jfb 710: /*
711: * cvs_path_cat()
712: *
713: * Concatenate the two paths <base> and <end> and store the generated path
714: * into the buffer <dst>, which can accept up to <dlen> bytes, including the
715: * NUL byte. The result is guaranteed to be NUL-terminated.
716: * Returns the number of bytes necessary to store the full resulting path,
717: * not including the NUL byte (a value equal to or larger than <dlen>
718: * indicates truncation).
719: */
1.29 jfb 720: size_t
721: cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen)
722: {
723: size_t len;
724:
725: len = strlcpy(dst, base, dlen);
726: if (len >= dlen - 1) {
727: errno = ENAMETOOLONG;
728: cvs_log(LP_ERRNO, "%s", dst);
729: } else {
730: dst[len] = '/';
731: dst[len + 1] = '\0';
732: len = strlcat(dst, end, dlen);
733: if (len >= dlen) {
734: errno = ENAMETOOLONG;
735: cvs_log(LP_ERRNO, "%s", dst);
736: }
737: }
738:
739: return (len);
1.35 xsa 740: }
741:
742: /*
743: * cvs_rcs_getpath()
744: *
745: * Get the RCS path of the file <file> and store it in <buf>, which is
746: * of size <len>. For portability, it is recommended that <buf> always be
747: * at least MAXPATHLEN bytes long.
748: * Returns a pointer to the start of the path on success, or NULL on failure.
749: */
1.42 xsa 750: char *
1.35 xsa 751: cvs_rcs_getpath(CVSFILE *file, char *buf, size_t len)
752: {
753: int l;
754: char *repo;
755: struct cvsroot *root;
756:
757: root = CVS_DIR_ROOT(file);
758: repo = CVS_DIR_REPO(file);
759:
1.36 deraadt 760: l = snprintf(buf, len, "%s/%s/%s%s",
1.35 xsa 761: root->cr_dir, repo, file->cf_name, RCS_FILE_EXT);
762: if (l == -1 || l >= (int)len) {
763: errno = ENAMETOOLONG;
764: cvs_log(LP_ERRNO, "%s", buf);
765: return (NULL);
766: }
767:
768: return (buf);
1.29 jfb 769: }