Annotation of src/usr.bin/cvs/util.c, Revision 1.54
1.54 ! joris 1: /* $OpenBSD: util.c,v 1.53 2005/10/07 21:47:32 reyk 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:
1.54 ! joris 42: #if !defined(RCSPROG)
! 43:
1.1 jfb 44: /* letter -> mode type map */
45: static const int cvs_modetypes[26] = {
46: -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
47: -1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
48: };
49:
50: /* letter -> mode map */
51: static const mode_t cvs_modes[3][26] = {
52: {
53: 0, 0, 0, 0, 0, 0, 0, /* a - g */
54: 0, 0, 0, 0, 0, 0, 0, /* h - m */
55: 0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */
56: 0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */
57: },
58: {
59: 0, 0, 0, 0, 0, 0, 0, /* a - g */
60: 0, 0, 0, 0, 0, 0, 0, /* h - m */
61: 0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */
62: 0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */
63: },
64: {
65: 0, 0, 0, 0, 0, 0, 0, /* a - g */
66: 0, 0, 0, 0, 0, 0, 0, /* h - m */
67: 0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */
68: 0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */
69: }
70: };
71:
72:
73: /* octal -> string */
74: static const char *cvs_modestr[8] = {
75: "", "x", "w", "wx", "r", "rx", "rw", "rwx"
76: };
77:
78:
1.11 krapht 79: pid_t cvs_exec_pid;
80:
1.1 jfb 81:
82: /*
83: * cvs_readrepo()
84: *
85: * Read the path stored in the `Repository' CVS file for a given directory
86: * <dir>, and store that path into the buffer pointed to by <dst>, whose size
87: * is <len>.
88: */
89: int
90: cvs_readrepo(const char *dir, char *dst, size_t len)
91: {
1.37 xsa 92: size_t dlen, l;
1.1 jfb 93: FILE *fp;
94: char repo_path[MAXPATHLEN];
95:
1.37 xsa 96: l = cvs_path_cat(dir, "CVS/Repository", repo_path, sizeof(repo_path));
97: if (l >= sizeof(repo_path))
1.21 xsa 98: return (NULL);
99:
1.1 jfb 100: fp = fopen(repo_path, "r");
1.21 xsa 101: if (fp == NULL)
1.1 jfb 102: return (-1);
103:
104: if (fgets(dst, (int)len, fp) == NULL) {
105: if (ferror(fp)) {
106: cvs_log(LP_ERRNO, "failed to read from `%s'",
107: repo_path);
108: }
109: (void)fclose(fp);
110: return (-1);
111: }
112: dlen = strlen(dst);
113: if ((dlen > 0) && (dst[dlen - 1] == '\n'))
114: dst[--dlen] = '\0';
115:
116: (void)fclose(fp);
117: return (0);
1.9 jfb 118: }
119:
120:
121: /*
1.1 jfb 122: * cvs_strtomode()
123: *
124: * Read the contents of the string <str> and generate a permission mode from
125: * the contents of <str>, which is assumed to have the mode format of CVS.
126: * The CVS protocol specification states that any modes or mode types that are
127: * not recognized should be silently ignored. This function does not return
128: * an error in such cases, but will issue warnings.
129: * Returns 0 on success, or -1 on failure.
130: */
131: int
132: cvs_strtomode(const char *str, mode_t *mode)
133: {
1.2 jfb 134: char type;
1.1 jfb 135: mode_t m;
136: char buf[32], ms[4], *sp, *ep;
137:
138: m = 0;
1.30 jfb 139: if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf)) {
140: return (-1);
141: }
1.1 jfb 142: sp = buf;
143: ep = sp;
144:
145: for (sp = buf; ep != NULL; sp = ep + 1) {
146: ep = strchr(sp, ',');
147: if (ep != NULL)
1.2 jfb 148: *ep = '\0';
1.1 jfb 149:
1.14 weingart 150: memset(ms, 0, sizeof ms);
151: if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
152: sscanf(sp, "%c=", &type) != 1) {
1.1 jfb 153: cvs_log(LP_WARN, "failed to scan mode string `%s'", sp);
154: continue;
155: }
156:
157: if ((type <= 'a') || (type >= 'z') ||
158: (cvs_modetypes[type - 'a'] == -1)) {
159: cvs_log(LP_WARN,
160: "invalid mode type `%c'"
161: " (`u', `g' or `o' expected), ignoring", type);
162: continue;
163: }
164:
165: /* make type contain the actual mode index */
166: type = cvs_modetypes[type - 'a'];
167:
168: for (sp = ms; *sp != '\0'; sp++) {
169: if ((*sp <= 'a') || (*sp >= 'z') ||
1.5 jfb 170: (cvs_modes[(int)type][*sp - 'a'] == 0)) {
1.1 jfb 171: cvs_log(LP_WARN,
172: "invalid permission bit `%c'", *sp);
1.15 deraadt 173: } else
1.5 jfb 174: m |= cvs_modes[(int)type][*sp - 'a'];
1.1 jfb 175: }
176: }
177:
178: *mode = m;
179:
180: return (0);
181: }
182:
183:
184: /*
185: * cvs_modetostr()
186: *
1.30 jfb 187: * Generate a CVS-format string to represent the permissions mask on a file
188: * from the mode <mode> and store the result in <buf>, which can accept up to
189: * <len> bytes (including the terminating NUL byte). The result is guaranteed
190: * to be NUL-terminated.
1.1 jfb 191: * Returns 0 on success, or -1 on failure.
192: */
193: int
194: cvs_modetostr(mode_t mode, char *buf, size_t len)
195: {
196: size_t l;
197: char tmp[16], *bp;
198: mode_t um, gm, om;
199:
200: um = (mode & S_IRWXU) >> 6;
201: gm = (mode & S_IRWXG) >> 3;
202: om = mode & S_IRWXO;
203:
204: bp = buf;
205: *bp = '\0';
206: l = 0;
207:
208: if (um) {
209: snprintf(tmp, sizeof(tmp), "u=%s", cvs_modestr[um]);
1.9 jfb 210: l = strlcat(buf, tmp, len);
1.1 jfb 211: }
212: if (gm) {
213: if (um)
214: strlcat(buf, ",", len);
215: snprintf(tmp, sizeof(tmp), "g=%s", cvs_modestr[gm]);
1.9 jfb 216: strlcat(buf, tmp, len);
1.1 jfb 217: }
218: if (om) {
219: if (um || gm)
220: strlcat(buf, ",", len);
221: snprintf(tmp, sizeof(tmp), "o=%s", cvs_modestr[gm]);
1.9 jfb 222: strlcat(buf, tmp, len);
1.1 jfb 223: }
224:
225: return (0);
226: }
227:
228: /*
229: * cvs_cksum()
230: *
231: * Calculate the MD5 checksum of the file whose path is <file> and generate
232: * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is
233: * given in <len> and must be at least 33.
234: * Returns 0 on success, or -1 on failure.
235: */
236: int
237: cvs_cksum(const char *file, char *dst, size_t len)
238: {
239: if (len < CVS_CKSUM_LEN) {
240: cvs_log(LP_WARN, "buffer too small for checksum");
241: return (-1);
242: }
243: if (MD5File(file, dst) == NULL) {
1.19 jfb 244: cvs_log(LP_ERRNO, "failed to generate checksum for %s", file);
1.1 jfb 245: return (-1);
246: }
247:
248: return (0);
249: }
250:
251: /*
252: * cvs_splitpath()
253: *
1.7 jfb 254: * Split a path <path> into the base portion and the filename portion.
255: * The path is copied in <base> and the last delimiter is replaced by a NUL
256: * byte. The <file> pointer is set to point to the first character after
257: * that delimiter.
1.1 jfb 258: * Returns 0 on success, or -1 on failure.
259: */
260: int
1.7 jfb 261: cvs_splitpath(const char *path, char *base, size_t blen, char **file)
1.1 jfb 262: {
263: size_t rlen;
1.7 jfb 264: char *sp;
1.1 jfb 265:
1.7 jfb 266: if ((rlen = strlcpy(base, path, blen)) >= blen)
267: return (-1);
1.6 jfb 268:
1.7 jfb 269: while ((rlen > 0) && (base[rlen - 1] == '/'))
270: base[--rlen] = '\0';
271:
272: sp = strrchr(base, '/');
1.1 jfb 273: if (sp == NULL) {
1.7 jfb 274: strlcpy(base, "./", blen);
275: strlcat(base, path, blen);
276: sp = base + 1;
1.1 jfb 277: }
278:
1.7 jfb 279: *sp = '\0';
280: if (file != NULL)
281: *file = sp + 1;
1.1 jfb 282:
283: return (0);
284: }
285:
286: /*
287: * cvs_getargv()
288: *
289: * Parse a line contained in <line> and generate an argument vector by
290: * splitting the line on spaces and tabs. The resulting vector is stored in
291: * <argv>, which can accept up to <argvlen> entries.
1.20 david 292: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 293: */
294: int
295: cvs_getargv(const char *line, char **argv, int argvlen)
296: {
297: u_int i;
298: int argc, err;
299: char linebuf[256], qbuf[128], *lp, *cp, *arg;
300:
301: strlcpy(linebuf, line, sizeof(linebuf));
1.27 pat 302: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 303: argc = 0;
304:
305: /* build the argument vector */
306: err = 0;
307: for (lp = linebuf; lp != NULL;) {
308: if (*lp == '"') {
309: /* double-quoted string */
310: lp++;
311: i = 0;
312: memset(qbuf, 0, sizeof(qbuf));
313: while (*lp != '"') {
1.16 jfb 314: if (*lp == '\\')
315: lp++;
1.1 jfb 316: if (*lp == '\0') {
317: cvs_log(LP_ERR, "no terminating quote");
318: err++;
319: break;
1.16 jfb 320: }
1.1 jfb 321:
1.9 jfb 322: qbuf[i++] = *lp++;
1.1 jfb 323: if (i == sizeof(qbuf)) {
324: err++;
325: break;
326: }
327: }
328:
329: arg = qbuf;
1.15 deraadt 330: } else {
1.1 jfb 331: cp = strsep(&lp, " \t");
332: if (cp == NULL)
333: break;
334: else if (*cp == '\0')
335: continue;
336:
337: arg = cp;
338: }
339:
1.16 jfb 340: if (argc == argvlen) {
341: err++;
342: break;
343: }
344:
1.1 jfb 345: argv[argc] = strdup(arg);
346: if (argv[argc] == NULL) {
347: cvs_log(LP_ERRNO, "failed to copy argument");
348: err++;
349: break;
350: }
351: argc++;
352: }
353:
1.43 xsa 354: if (err != 0) {
1.1 jfb 355: /* ditch the argument vector */
356: for (i = 0; i < (u_int)argc; i++)
357: free(argv[i]);
358: argc = -1;
359: }
360:
361: return (argc);
1.17 jfb 362: }
363:
364:
365: /*
366: * cvs_makeargv()
367: *
1.20 david 368: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 369: * arguments found in <line> and return it.
370: */
1.42 xsa 371: char **
1.17 jfb 372: cvs_makeargv(const char *line, int *argc)
373: {
374: int i, ret;
375: char *argv[1024], **copy;
1.27 pat 376: size_t size;
1.17 jfb 377:
378: ret = cvs_getargv(line, argv, 1024);
379: if (ret == -1)
380: return (NULL);
381:
1.27 pat 382: size = (ret + 1) * sizeof(char *);
383: copy = (char **)malloc(size);
1.17 jfb 384: if (copy == NULL) {
385: cvs_log(LP_ERRNO, "failed to allocate argument vector");
1.27 pat 386: cvs_freeargv(argv, ret);
1.17 jfb 387: return (NULL);
388: }
1.27 pat 389: memset(copy, 0, size);
1.17 jfb 390:
391: for (i = 0; i < ret; i++)
392: copy[i] = argv[i];
393: copy[ret] = NULL;
394:
395: *argc = ret;
396: return (copy);
1.1 jfb 397: }
398:
399:
400: /*
401: * cvs_freeargv()
402: *
403: * Free an argument vector previously generated by cvs_getargv().
404: */
405: void
406: cvs_freeargv(char **argv, int argc)
407: {
408: int i;
409:
410: for (i = 0; i < argc; i++)
1.16 jfb 411: if (argv[i] != NULL)
412: free(argv[i]);
1.2 jfb 413: }
414:
415:
416: /*
417: * cvs_mkadmin()
418: *
1.5 jfb 419: * Create the CVS administrative files within the directory <cdir>. If the
420: * files already exist, they are kept as is.
1.2 jfb 421: * Returns 0 on success, or -1 on failure.
422: */
423: int
1.52 xsa 424: cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath,
425: char *tag, char *date, int nb)
1.2 jfb 426: {
1.37 xsa 427: size_t l;
1.28 joris 428: char path[MAXPATHLEN];
1.2 jfb 429: FILE *fp;
430: CVSENTRIES *ef;
1.5 jfb 431: struct stat st;
1.13 jfb 432:
1.52 xsa 433: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s, %d)",
434: dpath, rootpath, repopath, tag ? tag : "", date ? date : "", nb);
435:
1.37 xsa 436: l = cvs_path_cat(dpath, CVS_PATH_CVSDIR, path, sizeof(path));
437: if (l >= sizeof(path))
1.21 xsa 438: return (-1);
439:
1.28 joris 440: if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) {
1.2 jfb 441: cvs_log(LP_ERRNO, "failed to create directory %s", path);
442: return (-1);
443: }
444:
1.5 jfb 445: /* just create an empty Entries file */
1.13 jfb 446: ef = cvs_ent_open(dpath, O_WRONLY);
1.48 moritz 447: if (ef != NULL)
448: cvs_ent_close(ef);
1.2 jfb 449:
1.37 xsa 450: l = cvs_path_cat(dpath, CVS_PATH_ROOTSPEC, path, sizeof(path));
451: if (l >= sizeof(path))
1.21 xsa 452: return (-1);
453:
1.47 xsa 454: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.5 jfb 455: fp = fopen(path, "w");
456: if (fp == NULL) {
457: cvs_log(LP_ERRNO, "failed to open %s", path);
458: return (-1);
459: }
1.28 joris 460: if (rootpath != NULL)
461: fprintf(fp, "%s\n", rootpath);
1.5 jfb 462: (void)fclose(fp);
1.2 jfb 463: }
464:
1.37 xsa 465: l = cvs_path_cat(dpath, CVS_PATH_REPOSITORY, path, sizeof(path));
466: if (l >= sizeof(path))
1.21 xsa 467: return (-1);
468:
1.47 xsa 469: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.2 jfb 470: fp = fopen(path, "w");
471: if (fp == NULL) {
472: cvs_log(LP_ERRNO, "failed to open %s", path);
473: return (-1);
474: }
1.28 joris 475: if (repopath != NULL)
476: fprintf(fp, "%s\n", repopath);
1.2 jfb 477: (void)fclose(fp);
478: }
479:
1.52 xsa 480: /* create CVS/Tag file (if needed) */
481: (void)cvs_write_tagfile(tag, date, nb);
482:
1.2 jfb 483: return (0);
1.11 krapht 484: }
485:
486:
487: /*
488: * cvs_exec()
489: */
490: int
491: cvs_exec(int argc, char **argv, int fds[3])
492: {
493: int ret;
494: pid_t pid;
495:
496: if ((pid = fork()) == -1) {
497: cvs_log(LP_ERRNO, "failed to fork");
498: return (-1);
499: } else if (pid == 0) {
500: execvp(argv[0], argv);
1.13 jfb 501: cvs_log(LP_ERRNO, "failed to exec %s", argv[0]);
502: exit(1);
1.11 krapht 503: }
504:
505: if (waitpid(pid, &ret, 0) == -1)
1.13 jfb 506: cvs_log(LP_ERRNO, "failed to waitpid");
1.11 krapht 507:
508: return (ret);
1.38 xsa 509: }
510:
511: /*
512: * cvs_chdir()
513: *
514: * Change to directory.
515: * chdir() wrapper with an error message.
516: * Returns 0 on success, or -1 on failure.
1.50 xsa 517: */
1.38 xsa 518: int
519: cvs_chdir(const char *path)
520: {
521: if (chdir(path) == -1) {
522: cvs_log(LP_ERRNO, "cannot change to dir `%s'", path);
1.49 xsa 523: return (-1);
524: }
525:
526: return (0);
527: }
528:
529: /*
530: * cvs_rename()
531: * Change the name of a file.
532: * rename() wrapper with an error message.
533: * Returns 0 on success, or -1 on failure.
534: */
535: int
536: cvs_rename(const char *from, const char *to)
537: {
538: cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
539:
540: if (cvs_noexec == 1)
541: return (0);
542:
543: if (rename(from, to) == -1) {
544: cvs_log(LP_ERRNO, "cannot rename file `%s' to `%s'", from, to);
1.40 xsa 545: return (-1);
546: }
547:
548: return (0);
549: }
550:
551: /*
552: * cvs_unlink()
553: *
554: * Removes the link named by <path>.
555: * unlink() wrapper with an error message.
556: * Returns 0 on success, or -1 on failure.
557: */
558: int
559: cvs_unlink(const char *path)
560: {
561: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
562:
563: if (cvs_noexec == 1)
564: return (0);
565:
1.44 joris 566: if ((unlink(path) == -1) && (errno != ENOENT)) {
1.40 xsa 567: cvs_log(LP_ERRNO, "cannot remove `%s'", path);
1.38 xsa 568: return (-1);
569: }
570:
571: return (0);
1.1 jfb 572: }
1.24 joris 573:
574: /*
1.45 xsa 575: * cvs_rmdir()
1.30 jfb 576: *
577: * Remove a directory tree from disk.
578: * Returns 0 on success, or -1 on failure.
1.24 joris 579: */
580: int
1.45 xsa 581: cvs_rmdir(const char *path)
1.24 joris 582: {
1.33 pat 583: int ret = -1;
1.30 jfb 584: size_t len;
1.24 joris 585: DIR *dirp;
586: struct dirent *ent;
587: char fpath[MAXPATHLEN];
1.46 xsa 588:
589: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
590:
591: if (cvs_noexec == 1)
592: return (0);
1.24 joris 593:
594: if ((dirp = opendir(path)) == NULL) {
595: cvs_log(LP_ERRNO, "failed to open '%s'", path);
1.30 jfb 596: return (-1);
1.24 joris 597: }
598:
599: while ((ent = readdir(dirp)) != NULL) {
600: if (!strcmp(ent->d_name, ".") ||
601: !strcmp(ent->d_name, ".."))
602: continue;
603:
1.30 jfb 604: len = cvs_path_cat(path, ent->d_name, fpath, sizeof(fpath));
1.33 pat 605: if (len >= sizeof(fpath))
606: goto done;
1.24 joris 607:
608: if (ent->d_type == DT_DIR) {
1.45 xsa 609: if (cvs_rmdir(fpath) == -1)
1.33 pat 610: goto done;
1.41 xsa 611: } else if ((cvs_unlink(fpath) == -1) && (errno != ENOENT))
1.33 pat 612: goto done;
1.24 joris 613: }
614:
615:
1.30 jfb 616: if ((rmdir(path) == -1) && (errno != ENOENT)) {
1.24 joris 617: cvs_log(LP_ERRNO, "failed to remove '%s'", path);
1.33 pat 618: goto done;
1.30 jfb 619: }
1.24 joris 620:
1.33 pat 621: ret = 0;
622: done:
623: closedir(dirp);
1.34 joris 624: return (ret);
625: }
626:
627: /*
628: * Create a directory, and the parent directories if needed.
629: * based upon mkpath() from mkdir.c
630: */
631: int
632: cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
633: {
634: size_t l;
635: int len, ret;
636: char *d, *s;
637: struct stat sb;
638: char rpath[MAXPATHLEN], entry[MAXPATHLEN];
639: CVSENTRIES *entf;
640: struct cvs_ent *ent;
641:
642: if (create_adm == 1 && (root == NULL || repo == NULL)) {
643: cvs_log(LP_ERR, "missing stuff in cvs_create_dir");
644: return (-1);
645: }
646:
647: if ((s = strdup(path)) == NULL)
648: return (-1);
649:
650: if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath) ||
651: strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
652: errno = ENAMETOOLONG;
653: cvs_log(LP_ERRNO, "%s", rpath);
654: free(s);
655: return (-1);
656: }
657:
658: ret = -1;
659: entf = NULL;
660: d = strtok(s, "/");
661: while (d != NULL) {
662: if (stat(d, &sb)) {
663: /* try to create the directory */
664: if ((errno != ENOENT) || (mkdir(d, 0755) &&
665: errno != EEXIST)) {
666: cvs_log(LP_ERRNO, "failed to create `%s'", d);
667: goto done;
668: }
669: } else if (!S_ISDIR(sb.st_mode)) {
670: cvs_log(LP_ERR, "`%s' not a directory", d);
671: goto done;
672: }
673:
674: /*
675: * Create administrative files if requested.
676: */
1.43 xsa 677: if (create_adm == 1) {
1.34 joris 678: l = strlcat(rpath, d, sizeof(rpath));
679: if (l >= sizeof(rpath))
680: goto done;
681:
682: l = strlcat(rpath, "/", sizeof(rpath));
683: if (l >= sizeof(rpath))
684: goto done;
685:
1.52 xsa 686: if (cvs_mkadmin(d, root, rpath, NULL, NULL, 0) < 0) {
1.34 joris 687: cvs_log(LP_ERR, "failed to create adm files");
688: goto done;
689: }
690: }
691:
692: /*
693: * Add it to the parent directory entry file.
694: * (if any).
695: */
696: entf = cvs_ent_open(".", O_RDWR);
697: if (entf != NULL && strcmp(d, ".")) {
698: len = snprintf(entry, sizeof(entry), "D/%s////", d);
699: if (len == -1 || len >= (int)sizeof(entry)) {
700: errno = ENAMETOOLONG;
701: cvs_log(LP_ERRNO, "%s", entry);
702: goto done;
703: }
704:
705: if ((ent = cvs_ent_parse(entry)) == NULL) {
706: cvs_log(LP_ERR, "failed to parse entry");
707: goto done;
708: }
709:
710: cvs_ent_remove(entf, d);
711:
712: if (cvs_ent_add(entf, ent) < 0) {
713: cvs_log(LP_ERR, "failed to add entry");
714: goto done;
715: }
716: }
717:
718: if (entf != NULL) {
719: cvs_ent_close(entf);
720: entf = NULL;
721: }
722:
723: /*
724: * All went ok, switch to the newly created directory.
725: */
1.39 xsa 726: if (cvs_chdir(d) == -1)
1.34 joris 727: goto done;
728:
729: d = strtok(NULL, "/");
730: }
731:
732: ret = 0;
733: done:
734: if (entf != NULL)
735: cvs_ent_close(entf);
736: free(s);
1.33 pat 737: return (ret);
1.24 joris 738: }
739:
1.30 jfb 740: /*
741: * cvs_path_cat()
742: *
743: * Concatenate the two paths <base> and <end> and store the generated path
744: * into the buffer <dst>, which can accept up to <dlen> bytes, including the
745: * NUL byte. The result is guaranteed to be NUL-terminated.
746: * Returns the number of bytes necessary to store the full resulting path,
747: * not including the NUL byte (a value equal to or larger than <dlen>
748: * indicates truncation).
749: */
1.29 jfb 750: size_t
751: cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen)
752: {
753: size_t len;
754:
755: len = strlcpy(dst, base, dlen);
756: if (len >= dlen - 1) {
757: errno = ENAMETOOLONG;
758: cvs_log(LP_ERRNO, "%s", dst);
759: } else {
760: dst[len] = '/';
761: dst[len + 1] = '\0';
762: len = strlcat(dst, end, dlen);
763: if (len >= dlen) {
764: errno = ENAMETOOLONG;
765: cvs_log(LP_ERRNO, "%s", dst);
766: }
767: }
768:
769: return (len);
1.35 xsa 770: }
771:
772: /*
773: * cvs_rcs_getpath()
774: *
775: * Get the RCS path of the file <file> and store it in <buf>, which is
776: * of size <len>. For portability, it is recommended that <buf> always be
777: * at least MAXPATHLEN bytes long.
778: * Returns a pointer to the start of the path on success, or NULL on failure.
779: */
1.42 xsa 780: char *
1.35 xsa 781: cvs_rcs_getpath(CVSFILE *file, char *buf, size_t len)
782: {
783: int l;
784: char *repo;
785: struct cvsroot *root;
786:
787: root = CVS_DIR_ROOT(file);
788: repo = CVS_DIR_REPO(file);
789:
1.36 deraadt 790: l = snprintf(buf, len, "%s/%s/%s%s",
1.35 xsa 791: root->cr_dir, repo, file->cf_name, RCS_FILE_EXT);
792: if (l == -1 || l >= (int)len) {
793: errno = ENAMETOOLONG;
794: cvs_log(LP_ERRNO, "%s", buf);
795: return (NULL);
796: }
797:
798: return (buf);
1.51 xsa 799: }
800:
801: /*
802: * cvs_write_tagfile()
803: *
804: * Write the CVS/Tag file for current directory.
805: */
806: void
807: cvs_write_tagfile(char *tag, char *date, int nb)
808: {
809: FILE *fp;
810: char tagpath[MAXPATHLEN];
811:
812: if (cvs_noexec == 1)
813: return;
814:
815: if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath))
816: return;
817:
818: if ((tag != NULL) || (date != NULL)) {
819: fp = fopen(tagpath, "w+");
820: if (fp == NULL) {
821: if (errno != ENOENT)
822: cvs_log(LP_NOTICE,
823: "failed to open `%s' : %s", tagpath,
824: strerror(errno));
825: return;
826: }
827: if (tag != NULL) {
828: if (nb != 0)
829: fprintf(fp, "N%s\n", tag);
830: else
831: fprintf(fp, "T%s\n", tag);
832: } else {
833: fprintf(fp, "D%s\n", date);
834: }
835: (void)fclose(fp);
836: } else {
837: cvs_unlink(tagpath);
838: return;
839: }
840: }
841:
842: /*
843: * cvs_parse_tagfile()
844: *
845: * Parse the CVS/Tag file for current directory.
846: *
847: * If it contains a branch tag, sets <tagp>.
848: * If it contains a date, sets <datep>.
849: * If it contains a non-branch tag, sets <nbp>.
850: *
851: * Returns nothing but an error message, and sets <tagp>, <datep> to NULL
852: * and <nbp> to 0.
853: */
854: void
855: cvs_parse_tagfile(char **tagp, char **datep, int *nbp)
856: {
857: FILE *fp;
858: int linenum;
859: size_t len;
860: char linebuf[128], tagpath[MAXPATHLEN];
861:
862: if (tagp != NULL)
863: *tagp = (char *)NULL;
864:
865: if (datep != NULL)
866: *datep = (char *)NULL;
867:
868: if (nbp != NULL)
869: *nbp = 0;
870:
871: if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath))
872: return;
873:
874: fp = fopen(tagpath, "r");
875: if (fp == NULL) {
876: if (errno != ENOENT)
877: cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
878: strerror(errno));
879: return;
880: }
881:
882: linenum = 0;
883:
884: while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
885: linenum++;
886: if ((len = strlen(linebuf)) == 0)
887: continue;
888: if (linebuf[len -1] != '\n') {
889: cvs_log(LP_WARN, "line too long in `%s:%d'", tagpath,
890: linenum);
891: break;
892: }
893: linebuf[--len] = '\0';
894:
1.53 reyk 895: switch (*linebuf) {
1.51 xsa 896: case 'T':
897: if (tagp != NULL)
898: *tagp = strdup(linebuf);
899: break;
900: case 'D':
901: if (datep != NULL)
902: *datep = strdup(linebuf);
903: break;
904: case 'N':
905: if (tagp != NULL)
906: *tagp = strdup(linebuf);
907: if (nbp != NULL)
908: *nbp = 1;
909: break;
910: default:
911: break;
912: }
913: }
914: if (ferror(fp))
915: cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
916:
917: (void)fclose(fp);
1.54 ! joris 918: }
! 919:
! 920: #endif /* !RCSPROG */
! 921:
! 922: /*
! 923: * Split the contents of a file into a list of lines.
! 924: */
! 925: struct cvs_lines *
! 926: cvs_splitlines(const char *fcont)
! 927: {
! 928: char *dcp;
! 929: struct cvs_lines *lines;
! 930: struct cvs_line *lp;
! 931:
! 932: lines = (struct cvs_lines *)malloc(sizeof(*lines));
! 933: if (lines == NULL)
! 934: return (NULL);
! 935:
! 936: TAILQ_INIT(&(lines->l_lines));
! 937: lines->l_nblines = 0;
! 938: lines->l_data = strdup(fcont);
! 939: if (lines->l_data == NULL) {
! 940: free(lines);
! 941: return (NULL);
! 942: }
! 943:
! 944: lp = (struct cvs_line *)malloc(sizeof(*lp));
! 945: if (lp == NULL) {
! 946: cvs_freelines(lines);
! 947: return (NULL);
! 948: }
! 949:
! 950: lp->l_line = NULL;
! 951: lp->l_lineno = 0;
! 952: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
! 953:
! 954: for (dcp = lines->l_data; *dcp != '\0';) {
! 955: lp = (struct cvs_line *)malloc(sizeof(*lp));
! 956: if (lp == NULL) {
! 957: cvs_freelines(lines);
! 958: return (NULL);
! 959: }
! 960:
! 961: lp->l_line = dcp;
! 962: lp->l_lineno = ++(lines->l_nblines);
! 963: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
! 964:
! 965: dcp = strchr(dcp, '\n');
! 966: if (dcp == NULL)
! 967: break;
! 968: *(dcp++) = '\0';
! 969: }
! 970:
! 971: return (lines);
! 972: }
! 973:
! 974: void
! 975: cvs_freelines(struct cvs_lines *lines)
! 976: {
! 977: struct cvs_line *lp;
! 978:
! 979: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
! 980: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
! 981: free(lp);
! 982: }
! 983:
! 984: free(lines->l_data);
! 985: free(lines);
! 986: }
! 987:
! 988: BUF *
! 989: cvs_patchfile(const char *data, const char *patch,
! 990: int (*p)(struct cvs_lines *, struct cvs_lines *))
! 991: {
! 992: struct cvs_lines *dlines, *plines;
! 993: struct cvs_line *lp;
! 994: size_t len;
! 995: int lineno;
! 996: BUF *res;
! 997:
! 998: len = strlen(data);
! 999:
! 1000: if ((dlines = cvs_splitlines(data)) == NULL)
! 1001: return (NULL);
! 1002:
! 1003: if ((plines = cvs_splitlines(patch)) == NULL)
! 1004: return (NULL);
! 1005:
! 1006: if (p(dlines, plines) < 0) {
! 1007: cvs_freelines(dlines);
! 1008: cvs_freelines(plines);
! 1009: return (NULL);
! 1010: }
! 1011:
! 1012: if ((res = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL) {
! 1013: cvs_freelines(dlines);
! 1014: cvs_freelines(plines);
! 1015: return (NULL);
! 1016: }
! 1017:
! 1018: lineno = 0;
! 1019: TAILQ_FOREACH(lp, &dlines->l_lines, l_list) {
! 1020: if (lineno != 0)
! 1021: cvs_buf_fappend(res, "%s\n", lp->l_line);
! 1022: lineno++;
! 1023: }
! 1024:
! 1025: cvs_freelines(dlines);
! 1026: cvs_freelines(plines);
! 1027: return (res);
1.29 jfb 1028: }