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