Annotation of src/usr.bin/cvs/util.c, Revision 1.34
1.34 ! joris 1: /* $OpenBSD: util.c,v 1.33 2005/06/14 03:56:14 pat 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.21 xsa 90: int l;
1.1 jfb 91: size_t dlen;
92: FILE *fp;
93: char repo_path[MAXPATHLEN];
94:
1.21 xsa 95: l = snprintf(repo_path, sizeof(repo_path), "%s/CVS/Repository", dir);
96: if (l == -1 || l >= (int)sizeof(repo_path)) {
97: errno = ENAMETOOLONG;
98: cvs_log(LP_ERRNO, "%s", repo_path);
99: return (NULL);
100: }
101:
1.1 jfb 102: fp = fopen(repo_path, "r");
1.21 xsa 103: if (fp == NULL)
1.1 jfb 104: return (-1);
105:
106: if (fgets(dst, (int)len, fp) == NULL) {
107: if (ferror(fp)) {
108: cvs_log(LP_ERRNO, "failed to read from `%s'",
109: repo_path);
110: }
111: (void)fclose(fp);
112: return (-1);
113: }
114: dlen = strlen(dst);
115: if ((dlen > 0) && (dst[dlen - 1] == '\n'))
116: dst[--dlen] = '\0';
117:
118: (void)fclose(fp);
119: return (0);
1.9 jfb 120: }
121:
122:
123: /*
1.1 jfb 124: * cvs_strtomode()
125: *
126: * Read the contents of the string <str> and generate a permission mode from
127: * the contents of <str>, which is assumed to have the mode format of CVS.
128: * The CVS protocol specification states that any modes or mode types that are
129: * not recognized should be silently ignored. This function does not return
130: * an error in such cases, but will issue warnings.
131: * Returns 0 on success, or -1 on failure.
132: */
133: int
134: cvs_strtomode(const char *str, mode_t *mode)
135: {
1.2 jfb 136: char type;
1.1 jfb 137: mode_t m;
138: char buf[32], ms[4], *sp, *ep;
139:
140: m = 0;
1.30 jfb 141: if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf)) {
142: return (-1);
143: }
1.1 jfb 144: sp = buf;
145: ep = sp;
146:
147: for (sp = buf; ep != NULL; sp = ep + 1) {
148: ep = strchr(sp, ',');
149: if (ep != NULL)
1.2 jfb 150: *ep = '\0';
1.1 jfb 151:
1.14 weingart 152: memset(ms, 0, sizeof ms);
153: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
154: sscanf(sp, "%c=", &type) != 1) {
1.1 jfb 155: cvs_log(LP_WARN, "failed to scan mode string `%s'", sp);
156: continue;
157: }
158:
159: if ((type <= 'a') || (type >= 'z') ||
160: (cvs_modetypes[type - 'a'] == -1)) {
161: cvs_log(LP_WARN,
162: "invalid mode type `%c'"
163: " (`u', `g' or `o' expected), ignoring", type);
164: continue;
165: }
166:
167: /* make type contain the actual mode index */
168: type = cvs_modetypes[type - 'a'];
169:
170: for (sp = ms; *sp != '\0'; sp++) {
171: if ((*sp <= 'a') || (*sp >= 'z') ||
1.5 jfb 172: (cvs_modes[(int)type][*sp - 'a'] == 0)) {
1.1 jfb 173: cvs_log(LP_WARN,
174: "invalid permission bit `%c'", *sp);
1.15 deraadt 175: } else
1.5 jfb 176: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 177: }
178: }
179:
180: *mode = m;
181:
182: return (0);
183: }
184:
185:
186: /*
187: * cvs_modetostr()
188: *
1.30 jfb 189: * Generate a CVS-format string to represent the permissions mask on a file
190: * from the mode <mode> and store the result in <buf>, which can accept up to
191: * <len> bytes (including the terminating NUL byte). The result is guaranteed
192: * to be NUL-terminated.
1.1 jfb 193: * Returns 0 on success, or -1 on failure.
194: */
195: int
196: cvs_modetostr(mode_t mode, char *buf, size_t len)
197: {
198: size_t l;
199: char tmp[16], *bp;
200: mode_t um, gm, om;
201:
202: um = (mode & S_IRWXU) >> 6;
203: gm = (mode & S_IRWXG) >> 3;
204: om = mode & S_IRWXO;
205:
206: bp = buf;
207: *bp = '\0';
208: l = 0;
209:
210: if (um) {
211: snprintf(tmp, sizeof(tmp), "u=%s", cvs_modestr[um]);
1.9 jfb 212: l = strlcat(buf, tmp, len);
1.1 jfb 213: }
214: if (gm) {
215: if (um)
216: strlcat(buf, ",", len);
217: snprintf(tmp, sizeof(tmp), "g=%s", cvs_modestr[gm]);
1.9 jfb 218: strlcat(buf, tmp, len);
1.1 jfb 219: }
220: if (om) {
221: if (um || gm)
222: strlcat(buf, ",", len);
223: snprintf(tmp, sizeof(tmp), "o=%s", cvs_modestr[gm]);
1.9 jfb 224: strlcat(buf, tmp, len);
1.1 jfb 225: }
226:
227: return (0);
228: }
229:
230: /*
231: * cvs_cksum()
232: *
233: * Calculate the MD5 checksum of the file whose path is <file> and generate
234: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
235: * given in <len> and must be at least 33.
236: * Returns 0 on success, or -1 on failure.
237: */
238: int
239: cvs_cksum(const char *file, char *dst, size_t len)
240: {
241: if (len < CVS_CKSUM_LEN) {
242: cvs_log(LP_WARN, "buffer too small for checksum");
243: return (-1);
244: }
245: if (MD5File(file, dst) == NULL) {
1.19 jfb 246: cvs_log(LP_ERRNO, "failed to generate checksum for %s", file);
1.1 jfb 247: return (-1);
248: }
249:
250: return (0);
251: }
252:
253: /*
254: * cvs_splitpath()
255: *
1.7 jfb 256: * Split a path <path> into the base portion and the filename portion.
257: * The path is copied in <base> and the last delimiter is replaced by a NUL
258: * byte. The <file> pointer is set to point to the first character after
259: * that delimiter.
1.1 jfb 260: * Returns 0 on success, or -1 on failure.
261: */
262: int
1.7 jfb 263: cvs_splitpath(const char *path, char *base, size_t blen, char **file)
1.1 jfb 264: {
265: size_t rlen;
1.7 jfb 266: char *sp;
1.1 jfb 267:
1.7 jfb 268: if ((rlen = strlcpy(base, path, blen)) >= blen)
269: return (-1);
1.6 jfb 270:
1.7 jfb 271: while ((rlen > 0) && (base[rlen - 1] == '/'))
272: base[--rlen] = '\0';
273:
274: sp = strrchr(base, '/');
1.1 jfb 275: if (sp == NULL) {
1.7 jfb 276: strlcpy(base, "./", blen);
277: strlcat(base, path, blen);
278: sp = base + 1;
1.1 jfb 279: }
280:
1.7 jfb 281: *sp = '\0';
282: if (file != NULL)
283: *file = sp + 1;
1.1 jfb 284:
285: return (0);
286: }
287:
288:
289: /*
290: * cvs_getargv()
291: *
292: * Parse a line contained in <line> and generate an argument vector by
293: * splitting the line on spaces and tabs. The resulting vector is stored in
294: * <argv>, which can accept up to <argvlen> entries.
1.20 david 295: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 296: */
297: int
298: cvs_getargv(const char *line, char **argv, int argvlen)
299: {
300: u_int i;
301: int argc, err;
302: char linebuf[256], qbuf[128], *lp, *cp, *arg;
303:
304: strlcpy(linebuf, line, sizeof(linebuf));
1.27 pat 305: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 306: argc = 0;
307:
308: /* build the argument vector */
309: err = 0;
310: for (lp = linebuf; lp != NULL;) {
311: if (*lp == '"') {
312: /* double-quoted string */
313: lp++;
314: i = 0;
315: memset(qbuf, 0, sizeof(qbuf));
316: while (*lp != '"') {
1.16 jfb 317: if (*lp == '\\')
318: lp++;
1.1 jfb 319: if (*lp == '\0') {
320: cvs_log(LP_ERR, "no terminating quote");
321: err++;
322: break;
1.16 jfb 323: }
1.1 jfb 324:
1.9 jfb 325: qbuf[i++] = *lp++;
1.1 jfb 326: if (i == sizeof(qbuf)) {
327: err++;
328: break;
329: }
330: }
331:
332: arg = qbuf;
1.15 deraadt 333: } else {
1.1 jfb 334: cp = strsep(&lp, " \t");
335: if (cp == NULL)
336: break;
337: else if (*cp == '\0')
338: continue;
339:
340: arg = cp;
341: }
342:
1.16 jfb 343: if (argc == argvlen) {
344: err++;
345: break;
346: }
347:
1.1 jfb 348: argv[argc] = strdup(arg);
349: if (argv[argc] == NULL) {
350: cvs_log(LP_ERRNO, "failed to copy argument");
351: err++;
352: break;
353: }
354: argc++;
355: }
356:
357: if (err) {
358: /* ditch the argument vector */
359: for (i = 0; i < (u_int)argc; i++)
360: free(argv[i]);
361: argc = -1;
362: }
363:
364: return (argc);
1.17 jfb 365: }
366:
367:
368: /*
369: * cvs_makeargv()
370: *
1.20 david 371: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 372: * arguments found in <line> and return it.
373: */
374: char**
375: cvs_makeargv(const char *line, int *argc)
376: {
377: int i, ret;
378: char *argv[1024], **copy;
1.27 pat 379: size_t size;
1.17 jfb 380:
381: ret = cvs_getargv(line, argv, 1024);
382: if (ret == -1)
383: return (NULL);
384:
1.27 pat 385: size = (ret + 1) * sizeof(char *);
386: copy = (char **)malloc(size);
1.17 jfb 387: if (copy == NULL) {
388: cvs_log(LP_ERRNO, "failed to allocate argument vector");
1.27 pat 389: cvs_freeargv(argv, ret);
1.17 jfb 390: return (NULL);
391: }
1.27 pat 392: memset(copy, 0, size);
1.17 jfb 393:
394: for (i = 0; i < ret; i++)
395: copy[i] = argv[i];
396: copy[ret] = NULL;
397:
398: *argc = ret;
399: return (copy);
1.1 jfb 400: }
401:
402:
403: /*
404: * cvs_freeargv()
405: *
406: * Free an argument vector previously generated by cvs_getargv().
407: */
408: void
409: cvs_freeargv(char **argv, int argc)
410: {
411: int i;
412:
413: for (i = 0; i < argc; i++)
1.16 jfb 414: if (argv[i] != NULL)
415: free(argv[i]);
1.2 jfb 416: }
417:
418:
419: /*
420: * cvs_mkadmin()
421: *
1.5 jfb 422: * Create the CVS administrative files within the directory <cdir>. If the
423: * files already exist, they are kept as is.
1.2 jfb 424: * Returns 0 on success, or -1 on failure.
425: */
426: int
1.28 joris 427: cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath)
1.2 jfb 428: {
1.21 xsa 429: int l;
1.28 joris 430: char path[MAXPATHLEN];
1.2 jfb 431: FILE *fp;
432: CVSENTRIES *ef;
1.5 jfb 433: struct stat st;
1.13 jfb 434:
1.21 xsa 435: l = snprintf(path, sizeof(path), "%s/" CVS_PATH_CVSDIR, dpath);
436: if (l == -1 || l >= (int)sizeof(path)) {
437: errno = ENAMETOOLONG;
438: cvs_log(LP_ERRNO, "%s", path);
439: return (-1);
440: }
441:
1.28 joris 442: if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) {
1.2 jfb 443: cvs_log(LP_ERRNO, "failed to create directory %s", path);
444: return (-1);
445: }
446:
1.5 jfb 447: /* just create an empty Entries file */
1.13 jfb 448: ef = cvs_ent_open(dpath, O_WRONLY);
1.2 jfb 449: (void)cvs_ent_close(ef);
450:
1.21 xsa 451: l = snprintf(path, sizeof(path), "%s/" CVS_PATH_ROOTSPEC, dpath);
452: if (l == -1 || l >= (int)sizeof(path)) {
453: errno = ENAMETOOLONG;
454: cvs_log(LP_ERRNO, "%s", path);
455: return (-1);
456: }
457:
1.28 joris 458: if ((stat(path, &st) != 0) && (errno == ENOENT)) {
1.5 jfb 459: fp = fopen(path, "w");
460: if (fp == NULL) {
461: cvs_log(LP_ERRNO, "failed to open %s", path);
462: return (-1);
463: }
1.28 joris 464: if (rootpath != NULL)
465: fprintf(fp, "%s\n", rootpath);
1.5 jfb 466: (void)fclose(fp);
1.2 jfb 467: }
468:
1.22 xsa 469: l = snprintf(path, sizeof(path), "%s/" CVS_PATH_REPOSITORY, dpath);
1.21 xsa 470: if (l == -1 || l >= (int)sizeof(path)) {
471: errno = ENAMETOOLONG;
472: cvs_log(LP_ERRNO, "%s", path);
473: return (-1);
474: }
475:
1.28 joris 476: if ((stat(path, &st) != 0) && (errno == ENOENT)) {
1.2 jfb 477: fp = fopen(path, "w");
478: if (fp == NULL) {
479: cvs_log(LP_ERRNO, "failed to open %s", path);
480: return (-1);
481: }
1.28 joris 482: if (repopath != NULL)
483: fprintf(fp, "%s\n", repopath);
1.2 jfb 484: (void)fclose(fp);
485: }
486:
487: return (0);
1.11 krapht 488: }
489:
490:
491: /*
492: * cvs_exec()
493: */
494: int
495: cvs_exec(int argc, char **argv, int fds[3])
496: {
497: int ret;
498: pid_t pid;
499:
500: if ((pid = fork()) == -1) {
501: cvs_log(LP_ERRNO, "failed to fork");
502: return (-1);
503: } else if (pid == 0) {
504: execvp(argv[0], argv);
1.13 jfb 505: cvs_log(LP_ERRNO, "failed to exec %s", argv[0]);
506: exit(1);
1.11 krapht 507: }
508:
509: if (waitpid(pid, &ret, 0) == -1)
1.13 jfb 510: cvs_log(LP_ERRNO, "failed to waitpid");
1.11 krapht 511:
512: return (ret);
1.1 jfb 513: }
1.24 joris 514:
515: /*
1.30 jfb 516: * cvs_remove_dir()
517: *
518: * Remove a directory tree from disk.
519: * Returns 0 on success, or -1 on failure.
1.24 joris 520: */
521: int
522: cvs_remove_dir(const char *path)
523: {
1.33 pat 524: int ret = -1;
1.30 jfb 525: size_t len;
1.24 joris 526: DIR *dirp;
527: struct dirent *ent;
528: char fpath[MAXPATHLEN];
529:
530: if ((dirp = opendir(path)) == NULL) {
531: cvs_log(LP_ERRNO, "failed to open '%s'", path);
1.30 jfb 532: return (-1);
1.24 joris 533: }
534:
535: while ((ent = readdir(dirp)) != NULL) {
536: if (!strcmp(ent->d_name, ".") ||
537: !strcmp(ent->d_name, ".."))
538: continue;
539:
1.30 jfb 540: len = cvs_path_cat(path, ent->d_name, fpath, sizeof(fpath));
1.33 pat 541: if (len >= sizeof(fpath))
542: goto done;
1.24 joris 543:
544: if (ent->d_type == DT_DIR) {
1.33 pat 545: if (cvs_remove_dir(fpath) == -1)
546: goto done;
1.30 jfb 547: } else if ((unlink(fpath) == -1) && (errno != ENOENT)) {
548: cvs_log(LP_ERRNO, "failed to remove '%s'", fpath);
1.33 pat 549: goto done;
1.24 joris 550: }
551: }
552:
553:
1.30 jfb 554: if ((rmdir(path) == -1) && (errno != ENOENT)) {
1.24 joris 555: cvs_log(LP_ERRNO, "failed to remove '%s'", path);
1.33 pat 556: goto done;
1.30 jfb 557: }
1.24 joris 558:
1.33 pat 559: ret = 0;
560: done:
561: closedir(dirp);
1.34 ! joris 562: return (ret);
! 563: }
! 564:
! 565: /*
! 566: * Create a directory, and the parent directories if needed.
! 567: * based upon mkpath() from mkdir.c
! 568: */
! 569: int
! 570: cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
! 571: {
! 572: size_t l;
! 573: int len, ret;
! 574: char *d, *s;
! 575: struct stat sb;
! 576: char rpath[MAXPATHLEN], entry[MAXPATHLEN];
! 577: CVSENTRIES *entf;
! 578: struct cvs_ent *ent;
! 579:
! 580: if (create_adm == 1 && (root == NULL || repo == NULL)) {
! 581: cvs_log(LP_ERR, "missing stuff in cvs_create_dir");
! 582: return (-1);
! 583: }
! 584:
! 585: if ((s = strdup(path)) == NULL)
! 586: return (-1);
! 587:
! 588: if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath) ||
! 589: strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
! 590: errno = ENAMETOOLONG;
! 591: cvs_log(LP_ERRNO, "%s", rpath);
! 592: free(s);
! 593: return (-1);
! 594: }
! 595:
! 596: ret = -1;
! 597: entf = NULL;
! 598: d = strtok(s, "/");
! 599: while (d != NULL) {
! 600: if (stat(d, &sb)) {
! 601: /* try to create the directory */
! 602: if ((errno != ENOENT) || (mkdir(d, 0755) &&
! 603: errno != EEXIST)) {
! 604: cvs_log(LP_ERRNO, "failed to create `%s'", d);
! 605: goto done;
! 606: }
! 607: } else if (!S_ISDIR(sb.st_mode)) {
! 608: cvs_log(LP_ERR, "`%s' not a directory", d);
! 609: goto done;
! 610: }
! 611:
! 612: /*
! 613: * Create administrative files if requested.
! 614: */
! 615: if (create_adm) {
! 616: l = strlcat(rpath, d, sizeof(rpath));
! 617: if (l >= sizeof(rpath))
! 618: goto done;
! 619:
! 620: l = strlcat(rpath, "/", sizeof(rpath));
! 621: if (l >= sizeof(rpath))
! 622: goto done;
! 623:
! 624: if (cvs_mkadmin(d, root, rpath) < 0) {
! 625: cvs_log(LP_ERR, "failed to create adm files");
! 626: goto done;
! 627: }
! 628: }
! 629:
! 630: /*
! 631: * Add it to the parent directory entry file.
! 632: * (if any).
! 633: */
! 634: entf = cvs_ent_open(".", O_RDWR);
! 635: if (entf != NULL && strcmp(d, ".")) {
! 636: len = snprintf(entry, sizeof(entry), "D/%s////", d);
! 637: if (len == -1 || len >= (int)sizeof(entry)) {
! 638: errno = ENAMETOOLONG;
! 639: cvs_log(LP_ERRNO, "%s", entry);
! 640: goto done;
! 641: }
! 642:
! 643: if ((ent = cvs_ent_parse(entry)) == NULL) {
! 644: cvs_log(LP_ERR, "failed to parse entry");
! 645: goto done;
! 646: }
! 647:
! 648: cvs_ent_remove(entf, d);
! 649:
! 650: if (cvs_ent_add(entf, ent) < 0) {
! 651: cvs_log(LP_ERR, "failed to add entry");
! 652: goto done;
! 653: }
! 654: }
! 655:
! 656: if (entf != NULL) {
! 657: cvs_ent_close(entf);
! 658: entf = NULL;
! 659: }
! 660:
! 661: /*
! 662: * All went ok, switch to the newly created directory.
! 663: */
! 664: if (chdir(d) == -1) {
! 665: cvs_log(LP_ERRNO, "failed to change dir to `%s'", d);
! 666: goto done;
! 667: }
! 668:
! 669: d = strtok(NULL, "/");
! 670: }
! 671:
! 672: ret = 0;
! 673: done:
! 674: if (entf != NULL)
! 675: cvs_ent_close(entf);
! 676: free(s);
1.33 pat 677: return (ret);
1.24 joris 678: }
679:
1.30 jfb 680: /*
681: * cvs_path_cat()
682: *
683: * Concatenate the two paths <base> and <end> and store the generated path
684: * into the buffer <dst>, which can accept up to <dlen> bytes, including the
685: * NUL byte. The result is guaranteed to be NUL-terminated.
686: * Returns the number of bytes necessary to store the full resulting path,
687: * not including the NUL byte (a value equal to or larger than <dlen>
688: * indicates truncation).
689: */
1.29 jfb 690: size_t
691: cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen)
692: {
693: size_t len;
694:
695: len = strlcpy(dst, base, dlen);
696: if (len >= dlen - 1) {
697: errno = ENAMETOOLONG;
698: cvs_log(LP_ERRNO, "%s", dst);
699: } else {
700: dst[len] = '/';
701: dst[len + 1] = '\0';
702: len = strlcat(dst, end, dlen);
703: if (len >= dlen) {
704: errno = ENAMETOOLONG;
705: cvs_log(LP_ERRNO, "%s", dst);
706: }
707: }
708:
709: return (len);
710: }