Annotation of src/usr.bin/cvs/util.c, Revision 1.58
1.58 ! joris 1: /* $OpenBSD: util.c,v 1.57 2005/12/03 03:59:47 joris 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:
1.11 krapht 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: * cvs_getargv()
286: *
287: * Parse a line contained in <line> and generate an argument vector by
288: * splitting the line on spaces and tabs. The resulting vector is stored in
289: * <argv>, which can accept up to <argvlen> entries.
1.20 david 290: * Returns the number of arguments in the vector, or -1 if an error occurred.
1.1 jfb 291: */
292: int
293: cvs_getargv(const char *line, char **argv, int argvlen)
294: {
295: u_int i;
296: int argc, err;
297: char linebuf[256], qbuf[128], *lp, *cp, *arg;
298:
299: strlcpy(linebuf, line, sizeof(linebuf));
1.27 pat 300: memset(argv, 0, argvlen * sizeof(char *));
1.1 jfb 301: argc = 0;
302:
303: /* build the argument vector */
304: err = 0;
305: for (lp = linebuf; lp != NULL;) {
306: if (*lp == '"') {
307: /* double-quoted string */
308: lp++;
309: i = 0;
310: memset(qbuf, 0, sizeof(qbuf));
311: while (*lp != '"') {
1.16 jfb 312: if (*lp == '\\')
313: lp++;
1.1 jfb 314: if (*lp == '\0') {
315: cvs_log(LP_ERR, "no terminating quote");
316: err++;
317: break;
1.16 jfb 318: }
1.1 jfb 319:
1.9 jfb 320: qbuf[i++] = *lp++;
1.1 jfb 321: if (i == sizeof(qbuf)) {
322: err++;
323: break;
324: }
325: }
326:
327: arg = qbuf;
1.15 deraadt 328: } else {
1.1 jfb 329: cp = strsep(&lp, " \t");
330: if (cp == NULL)
331: break;
332: else if (*cp == '\0')
333: continue;
334:
335: arg = cp;
336: }
337:
1.16 jfb 338: if (argc == argvlen) {
339: err++;
340: break;
341: }
342:
1.1 jfb 343: argv[argc] = strdup(arg);
344: if (argv[argc] == NULL) {
345: cvs_log(LP_ERRNO, "failed to copy argument");
346: err++;
347: break;
348: }
349: argc++;
350: }
351:
1.43 xsa 352: if (err != 0) {
1.1 jfb 353: /* ditch the argument vector */
354: for (i = 0; i < (u_int)argc; i++)
355: free(argv[i]);
356: argc = -1;
357: }
358:
359: return (argc);
1.17 jfb 360: }
361:
362:
363: /*
364: * cvs_makeargv()
365: *
1.20 david 366: * Allocate an argument vector large enough to accommodate for all the
1.17 jfb 367: * arguments found in <line> and return it.
368: */
1.42 xsa 369: char **
1.17 jfb 370: cvs_makeargv(const char *line, int *argc)
371: {
372: int i, ret;
373: char *argv[1024], **copy;
1.27 pat 374: size_t size;
1.17 jfb 375:
376: ret = cvs_getargv(line, argv, 1024);
377: if (ret == -1)
378: return (NULL);
379:
1.27 pat 380: size = (ret + 1) * sizeof(char *);
381: copy = (char **)malloc(size);
1.17 jfb 382: if (copy == NULL) {
383: cvs_log(LP_ERRNO, "failed to allocate argument vector");
1.27 pat 384: cvs_freeargv(argv, ret);
1.17 jfb 385: return (NULL);
386: }
1.27 pat 387: memset(copy, 0, size);
1.17 jfb 388:
389: for (i = 0; i < ret; i++)
390: copy[i] = argv[i];
391: copy[ret] = NULL;
392:
393: *argc = ret;
394: return (copy);
1.1 jfb 395: }
396:
397:
398: /*
399: * cvs_freeargv()
400: *
401: * Free an argument vector previously generated by cvs_getargv().
402: */
403: void
404: cvs_freeargv(char **argv, int argc)
405: {
406: int i;
407:
408: for (i = 0; i < argc; i++)
1.16 jfb 409: if (argv[i] != NULL)
410: free(argv[i]);
1.2 jfb 411: }
412:
413:
414: /*
415: * cvs_mkadmin()
416: *
1.5 jfb 417: * Create the CVS administrative files within the directory <cdir>. If the
418: * files already exist, they are kept as is.
1.2 jfb 419: * Returns 0 on success, or -1 on failure.
420: */
421: int
1.52 xsa 422: cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath,
423: char *tag, char *date, int nb)
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.52 xsa 431: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s, %d)",
432: dpath, rootpath, repopath, tag ? tag : "", date ? date : "", nb);
433:
1.37 xsa 434: l = cvs_path_cat(dpath, CVS_PATH_CVSDIR, path, sizeof(path));
435: if (l >= sizeof(path))
1.21 xsa 436: return (-1);
437:
1.28 joris 438: if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) {
1.2 jfb 439: cvs_log(LP_ERRNO, "failed to create directory %s", path);
440: return (-1);
441: }
442:
1.5 jfb 443: /* just create an empty Entries file */
1.13 jfb 444: ef = cvs_ent_open(dpath, O_WRONLY);
1.48 moritz 445: if (ef != NULL)
446: cvs_ent_close(ef);
1.2 jfb 447:
1.37 xsa 448: l = cvs_path_cat(dpath, CVS_PATH_ROOTSPEC, path, sizeof(path));
449: if (l >= sizeof(path))
1.21 xsa 450: return (-1);
451:
1.47 xsa 452: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.5 jfb 453: fp = fopen(path, "w");
454: if (fp == NULL) {
455: cvs_log(LP_ERRNO, "failed to open %s", path);
456: return (-1);
457: }
1.28 joris 458: if (rootpath != NULL)
459: fprintf(fp, "%s\n", rootpath);
1.5 jfb 460: (void)fclose(fp);
1.2 jfb 461: }
462:
1.37 xsa 463: l = cvs_path_cat(dpath, CVS_PATH_REPOSITORY, path, sizeof(path));
464: if (l >= sizeof(path))
1.21 xsa 465: return (-1);
466:
1.47 xsa 467: if ((stat(path, &st) == -1) && (errno == ENOENT)) {
1.2 jfb 468: fp = fopen(path, "w");
469: if (fp == NULL) {
470: cvs_log(LP_ERRNO, "failed to open %s", path);
471: return (-1);
472: }
1.28 joris 473: if (repopath != NULL)
474: fprintf(fp, "%s\n", repopath);
1.2 jfb 475: (void)fclose(fp);
476: }
477:
1.52 xsa 478: /* create CVS/Tag file (if needed) */
1.56 joris 479: /* XXX correct? */
480: if (tag != NULL || date != NULL)
481: (void)cvs_write_tagfile(tag, date, nb);
1.52 xsa 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:
1.56 joris 642: if (create_adm == 1 && (root == NULL)) {
1.34 joris 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:
1.56 joris 650: rpath[0] = '\0';
651: if (repo != NULL) {
652: if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath)) {
653: errno = ENAMETOOLONG;
654: cvs_log(LP_ERRNO, "%s", rpath);
655: free(s);
656: return (-1);
657: }
658:
659: if (strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
660: errno = ENAMETOOLONG;
661: cvs_log(LP_ERRNO, "%s", rpath);
662: free(s);
663: return (-1);
664: }
1.34 joris 665: }
666:
667: ret = -1;
668: entf = NULL;
669: d = strtok(s, "/");
670: while (d != NULL) {
671: if (stat(d, &sb)) {
672: /* try to create the directory */
673: if ((errno != ENOENT) || (mkdir(d, 0755) &&
674: errno != EEXIST)) {
675: cvs_log(LP_ERRNO, "failed to create `%s'", d);
676: goto done;
677: }
678: } else if (!S_ISDIR(sb.st_mode)) {
679: cvs_log(LP_ERR, "`%s' not a directory", d);
680: goto done;
681: }
682:
683: /*
684: * Create administrative files if requested.
685: */
1.43 xsa 686: if (create_adm == 1) {
1.34 joris 687: l = strlcat(rpath, d, sizeof(rpath));
688: if (l >= sizeof(rpath))
689: goto done;
690:
691: l = strlcat(rpath, "/", sizeof(rpath));
692: if (l >= sizeof(rpath))
693: goto done;
694:
1.52 xsa 695: if (cvs_mkadmin(d, root, rpath, NULL, NULL, 0) < 0) {
1.34 joris 696: cvs_log(LP_ERR, "failed to create adm files");
697: goto done;
698: }
699: }
700:
701: /*
702: * Add it to the parent directory entry file.
703: * (if any).
704: */
705: entf = cvs_ent_open(".", O_RDWR);
706: if (entf != NULL && strcmp(d, ".")) {
707: len = snprintf(entry, sizeof(entry), "D/%s////", d);
708: if (len == -1 || len >= (int)sizeof(entry)) {
709: errno = ENAMETOOLONG;
710: cvs_log(LP_ERRNO, "%s", entry);
711: goto done;
712: }
713:
714: if ((ent = cvs_ent_parse(entry)) == NULL) {
715: cvs_log(LP_ERR, "failed to parse entry");
716: goto done;
717: }
718:
1.56 joris 719: cvs_ent_remove(entf, d, 0);
1.34 joris 720:
721: if (cvs_ent_add(entf, ent) < 0) {
722: cvs_log(LP_ERR, "failed to add entry");
723: goto done;
724: }
725: }
726:
727: if (entf != NULL) {
728: cvs_ent_close(entf);
729: entf = NULL;
730: }
731:
732: /*
733: * All went ok, switch to the newly created directory.
734: */
1.39 xsa 735: if (cvs_chdir(d) == -1)
1.34 joris 736: goto done;
737:
738: d = strtok(NULL, "/");
739: }
740:
741: ret = 0;
742: done:
743: if (entf != NULL)
744: cvs_ent_close(entf);
745: free(s);
1.33 pat 746: return (ret);
1.24 joris 747: }
748:
1.30 jfb 749: /*
750: * cvs_path_cat()
751: *
752: * Concatenate the two paths <base> and <end> and store the generated path
753: * into the buffer <dst>, which can accept up to <dlen> bytes, including the
754: * NUL byte. The result is guaranteed to be NUL-terminated.
755: * Returns the number of bytes necessary to store the full resulting path,
756: * not including the NUL byte (a value equal to or larger than <dlen>
757: * indicates truncation).
758: */
1.29 jfb 759: size_t
760: cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen)
761: {
762: size_t len;
763:
764: len = strlcpy(dst, base, dlen);
765: if (len >= dlen - 1) {
766: errno = ENAMETOOLONG;
767: cvs_log(LP_ERRNO, "%s", dst);
768: } else {
769: dst[len] = '/';
770: dst[len + 1] = '\0';
771: len = strlcat(dst, end, dlen);
772: if (len >= dlen) {
773: errno = ENAMETOOLONG;
774: cvs_log(LP_ERRNO, "%s", dst);
775: }
776: }
777:
778: return (len);
1.35 xsa 779: }
780:
781: /*
782: * cvs_rcs_getpath()
783: *
784: * Get the RCS path of the file <file> and store it in <buf>, which is
785: * of size <len>. For portability, it is recommended that <buf> always be
786: * at least MAXPATHLEN bytes long.
787: * Returns a pointer to the start of the path on success, or NULL on failure.
788: */
1.42 xsa 789: char *
1.35 xsa 790: cvs_rcs_getpath(CVSFILE *file, char *buf, size_t len)
791: {
792: int l;
793: char *repo;
794: struct cvsroot *root;
795:
796: root = CVS_DIR_ROOT(file);
797: repo = CVS_DIR_REPO(file);
798:
1.36 deraadt 799: l = snprintf(buf, len, "%s/%s/%s%s",
1.35 xsa 800: root->cr_dir, repo, file->cf_name, RCS_FILE_EXT);
801: if (l == -1 || l >= (int)len) {
802: errno = ENAMETOOLONG;
803: cvs_log(LP_ERRNO, "%s", buf);
804: return (NULL);
805: }
806:
807: return (buf);
1.51 xsa 808: }
809:
810: /*
811: * cvs_write_tagfile()
812: *
813: * Write the CVS/Tag file for current directory.
1.55 xsa 814: */
1.51 xsa 815: void
816: cvs_write_tagfile(char *tag, char *date, int nb)
817: {
818: FILE *fp;
819: char tagpath[MAXPATHLEN];
820:
821: if (cvs_noexec == 1)
822: return;
823:
824: if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath))
825: return;
826:
827: if ((tag != NULL) || (date != NULL)) {
828: fp = fopen(tagpath, "w+");
829: if (fp == NULL) {
830: if (errno != ENOENT)
831: cvs_log(LP_NOTICE,
832: "failed to open `%s' : %s", tagpath,
833: strerror(errno));
834: return;
835: }
836: if (tag != NULL) {
837: if (nb != 0)
838: fprintf(fp, "N%s\n", tag);
839: else
840: fprintf(fp, "T%s\n", tag);
841: } else {
842: fprintf(fp, "D%s\n", date);
843: }
844: (void)fclose(fp);
845: } else {
846: cvs_unlink(tagpath);
847: return;
848: }
849: }
850:
851: /*
852: * cvs_parse_tagfile()
853: *
854: * Parse the CVS/Tag file for current directory.
855: *
856: * If it contains a branch tag, sets <tagp>.
857: * If it contains a date, sets <datep>.
858: * If it contains a non-branch tag, sets <nbp>.
1.55 xsa 859: *
1.51 xsa 860: * Returns nothing but an error message, and sets <tagp>, <datep> to NULL
861: * and <nbp> to 0.
862: */
863: void
864: cvs_parse_tagfile(char **tagp, char **datep, int *nbp)
865: {
866: FILE *fp;
867: int linenum;
868: size_t len;
869: char linebuf[128], tagpath[MAXPATHLEN];
870:
871: if (tagp != NULL)
872: *tagp = (char *)NULL;
873:
874: if (datep != NULL)
875: *datep = (char *)NULL;
876:
877: if (nbp != NULL)
878: *nbp = 0;
879:
880: if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath))
881: return;
882:
883: fp = fopen(tagpath, "r");
884: if (fp == NULL) {
885: if (errno != ENOENT)
886: cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath,
887: strerror(errno));
888: return;
889: }
890:
891: linenum = 0;
892:
893: while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
894: linenum++;
895: if ((len = strlen(linebuf)) == 0)
896: continue;
897: if (linebuf[len -1] != '\n') {
898: cvs_log(LP_WARN, "line too long in `%s:%d'", tagpath,
899: linenum);
900: break;
901: }
902: linebuf[--len] = '\0';
903:
1.53 reyk 904: switch (*linebuf) {
1.51 xsa 905: case 'T':
906: if (tagp != NULL)
907: *tagp = strdup(linebuf);
908: break;
909: case 'D':
910: if (datep != NULL)
911: *datep = strdup(linebuf);
912: break;
913: case 'N':
914: if (tagp != NULL)
915: *tagp = strdup(linebuf);
916: if (nbp != NULL)
917: *nbp = 1;
918: break;
919: default:
920: break;
921: }
922: }
923: if (ferror(fp))
924: cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath);
925:
926: (void)fclose(fp);
1.54 joris 927: }
928:
929: #endif /* !RCSPROG */
930:
931: /*
932: * Split the contents of a file into a list of lines.
933: */
934: struct cvs_lines *
935: cvs_splitlines(const char *fcont)
936: {
937: char *dcp;
938: struct cvs_lines *lines;
939: struct cvs_line *lp;
940:
941: lines = (struct cvs_lines *)malloc(sizeof(*lines));
942: if (lines == NULL)
943: return (NULL);
944:
945: TAILQ_INIT(&(lines->l_lines));
946: lines->l_nblines = 0;
947: lines->l_data = strdup(fcont);
948: if (lines->l_data == NULL) {
949: free(lines);
950: return (NULL);
951: }
952:
953: lp = (struct cvs_line *)malloc(sizeof(*lp));
954: if (lp == NULL) {
955: cvs_freelines(lines);
956: return (NULL);
957: }
958:
959: lp->l_line = NULL;
960: lp->l_lineno = 0;
961: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
962:
963: for (dcp = lines->l_data; *dcp != '\0';) {
964: lp = (struct cvs_line *)malloc(sizeof(*lp));
965: if (lp == NULL) {
966: cvs_freelines(lines);
967: return (NULL);
968: }
969:
970: lp->l_line = dcp;
971: lp->l_lineno = ++(lines->l_nblines);
972: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
973:
974: dcp = strchr(dcp, '\n');
975: if (dcp == NULL)
976: break;
977: *(dcp++) = '\0';
978: }
979:
980: return (lines);
981: }
982:
983: void
984: cvs_freelines(struct cvs_lines *lines)
985: {
986: struct cvs_line *lp;
987:
988: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
989: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
990: free(lp);
991: }
992:
993: free(lines->l_data);
994: free(lines);
995: }
996:
997: BUF *
998: cvs_patchfile(const char *data, const char *patch,
999: int (*p)(struct cvs_lines *, struct cvs_lines *))
1000: {
1001: struct cvs_lines *dlines, *plines;
1002: struct cvs_line *lp;
1003: size_t len;
1004: int lineno;
1005: BUF *res;
1006:
1007: len = strlen(data);
1008:
1009: if ((dlines = cvs_splitlines(data)) == NULL)
1010: return (NULL);
1011:
1012: if ((plines = cvs_splitlines(patch)) == NULL)
1013: return (NULL);
1014:
1015: if (p(dlines, plines) < 0) {
1016: cvs_freelines(dlines);
1017: cvs_freelines(plines);
1018: return (NULL);
1019: }
1020:
1021: if ((res = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL) {
1022: cvs_freelines(dlines);
1023: cvs_freelines(plines);
1024: return (NULL);
1025: }
1026:
1027: lineno = 0;
1028: TAILQ_FOREACH(lp, &dlines->l_lines, l_list) {
1029: if (lineno != 0)
1030: cvs_buf_fappend(res, "%s\n", lp->l_line);
1031: lineno++;
1032: }
1033:
1034: cvs_freelines(dlines);
1035: cvs_freelines(plines);
1036: return (res);
1.58 ! joris 1037: }
! 1038:
! 1039: /*
! 1040: * a hack to mimic and thus match gnu cvs behaviour.
! 1041: */
! 1042: time_t
! 1043: cvs_hack_time(time_t oldtime, int togmt)
! 1044: {
! 1045: int l;
! 1046: struct tm *t;
! 1047: char tbuf[32];
! 1048:
! 1049: if (togmt == 1) {
! 1050: t = gmtime(&oldtime);
! 1051: if (t == NULL)
! 1052: return (0);
! 1053:
! 1054: return (mktime(t));
! 1055: }
! 1056:
! 1057: t = localtime(&oldtime);
! 1058:
! 1059: l = snprintf(tbuf, sizeof(tbuf), "%d/%d/%d GMT %d:%d:%d",
! 1060: t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour,
! 1061: t->tm_min, t->tm_sec);
! 1062: if (l == -1 || l >= (int)sizeof(tbuf))
! 1063: return (0);
! 1064:
! 1065: return (cvs_date_parse(tbuf));
1.29 jfb 1066: }