Annotation of src/usr.bin/cvs/util.c, Revision 1.158
1.158 ! nicm 1: /* $OpenBSD: util.c,v 1.157 2015/01/16 06:40:07 deraadt 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.158 ! nicm 242: free(argv[i]);
1.1 jfb 243: argc = -1;
244: }
245:
1.158 ! nicm 246: free(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.158 ! nicm 287: free(argv[i]);
1.38 xsa 288: }
289:
290: /*
291: * cvs_chdir()
292: *
1.63 xsa 293: * Change to directory <path>.
294: * If <rm> is equal to `1', <path> is removed if chdir() fails so we
295: * do not have temporary directories leftovers.
1.60 xsa 296: * Returns 0 on success.
1.50 xsa 297: */
1.38 xsa 298: int
1.63 xsa 299: cvs_chdir(const char *path, int rm)
1.38 xsa 300: {
1.63 xsa 301: if (chdir(path) == -1) {
302: if (rm == 1)
303: cvs_unlink(path);
1.60 xsa 304: fatal("cvs_chdir: `%s': %s", path, strerror(errno));
1.63 xsa 305: }
1.49 xsa 306:
307: return (0);
308: }
309:
310: /*
311: * cvs_rename()
312: * Change the name of a file.
313: * rename() wrapper with an error message.
1.60 xsa 314: * Returns 0 on success.
1.49 xsa 315: */
316: int
317: cvs_rename(const char *from, const char *to)
318: {
1.90 joris 319: if (cvs_server_active == 0)
320: cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
1.49 xsa 321:
322: if (cvs_noexec == 1)
323: return (0);
324:
1.60 xsa 325: if (rename(from, to) == -1)
326: fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
1.40 xsa 327:
328: return (0);
329: }
330:
331: /*
332: * cvs_unlink()
333: *
334: * Removes the link named by <path>.
335: * unlink() wrapper with an error message.
336: * Returns 0 on success, or -1 on failure.
337: */
338: int
339: cvs_unlink(const char *path)
340: {
1.90 joris 341: if (cvs_server_active == 0)
342: cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
1.40 xsa 343:
1.145 joris 344: if (cvs_noexec == 1 && disable_fast_checkout != 0)
1.40 xsa 345: return (0);
346:
1.78 deraadt 347: if (unlink(path) == -1 && errno != ENOENT) {
1.94 xsa 348: cvs_log(LP_ERRNO, "%s", path);
1.38 xsa 349: return (-1);
350: }
351:
352: return (0);
1.1 jfb 353: }
1.24 joris 354:
355: /*
1.45 xsa 356: * cvs_rmdir()
1.30 jfb 357: *
358: * Remove a directory tree from disk.
359: * Returns 0 on success, or -1 on failure.
1.24 joris 360: */
361: int
1.45 xsa 362: cvs_rmdir(const char *path)
1.24 joris 363: {
1.99 todd 364: int type, ret = -1;
1.24 joris 365: DIR *dirp;
366: struct dirent *ent;
1.99 todd 367: struct stat st;
1.157 deraadt 368: char fpath[PATH_MAX];
1.46 xsa 369:
1.90 joris 370: if (cvs_server_active == 0)
371: cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
1.46 xsa 372:
1.145 joris 373: if (cvs_noexec == 1 && disable_fast_checkout != 0)
1.46 xsa 374: return (0);
1.24 joris 375:
376: if ((dirp = opendir(path)) == NULL) {
1.79 joris 377: cvs_log(LP_ERR, "failed to open '%s'", path);
1.30 jfb 378: return (-1);
1.24 joris 379: }
380:
381: while ((ent = readdir(dirp)) != NULL) {
382: if (!strcmp(ent->d_name, ".") ||
383: !strcmp(ent->d_name, ".."))
384: continue;
385:
1.105 xsa 386: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
387: path, ent->d_name);
1.24 joris 388:
1.99 todd 389: if (ent->d_type == DT_UNKNOWN) {
1.104 todd 390: if (lstat(fpath, &st) == -1)
1.99 todd 391: fatal("'%s': %s", fpath, strerror(errno));
392:
393: switch (st.st_mode & S_IFMT) {
394: case S_IFDIR:
395: type = CVS_DIR;
396: break;
397: case S_IFREG:
398: type = CVS_FILE;
399: break;
400: default:
401: fatal("'%s': Unknown file type in copy",
402: fpath);
403: }
404: } else {
405: switch (ent->d_type) {
406: case DT_DIR:
407: type = CVS_DIR;
408: break;
409: case DT_REG:
410: type = CVS_FILE;
411: break;
412: default:
413: fatal("'%s': Unknown file type in copy",
414: fpath);
415: }
416: }
417: switch (type) {
1.128 xsa 418: case CVS_DIR:
1.45 xsa 419: if (cvs_rmdir(fpath) == -1)
1.33 pat 420: goto done;
1.99 todd 421: break;
422: case CVS_FILE:
423: if (cvs_unlink(fpath) == -1 && errno != ENOENT)
424: goto done;
425: break;
426: default:
427: fatal("type %d unknown, shouldn't happen", type);
428: }
1.24 joris 429: }
430:
431:
1.78 deraadt 432: if (rmdir(path) == -1 && errno != ENOENT) {
1.93 xsa 433: cvs_log(LP_ERRNO, "%s", path);
1.33 pat 434: goto done;
1.30 jfb 435: }
1.24 joris 436:
1.33 pat 437: ret = 0;
438: done:
439: closedir(dirp);
1.34 joris 440: return (ret);
441: }
442:
1.51 xsa 443: void
1.81 joris 444: cvs_get_repository_path(const char *dir, char *dst, size_t len)
445: {
1.157 deraadt 446: char buf[PATH_MAX];
1.81 joris 447:
448: cvs_get_repository_name(dir, buf, sizeof(buf));
1.105 xsa 449: (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
1.139 joris 450: cvs_validate_directory(dst);
1.81 joris 451: }
452:
453: void
454: cvs_get_repository_name(const char *dir, char *dst, size_t len)
1.51 xsa 455: {
456: FILE *fp;
1.157 deraadt 457: char fpath[PATH_MAX];
1.116 tobias 458:
1.127 tobias 459: dst[0] = '\0';
460:
461: if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
462: if (strlcpy(dst, dir, len) >= len)
1.116 tobias 463: fatal("cvs_get_repository_name: truncation");
464: return;
465: }
1.79 joris 466:
1.127 tobias 467: switch (cvs_cmdop) {
468: case CVS_OP_EXPORT:
469: if (strcmp(dir, "."))
470: if (strlcpy(dst, dir, len) >= len)
471: fatal("cvs_get_repository_name: truncation");
472: break;
473: case CVS_OP_IMPORT:
474: if (strlcpy(dst, import_repository, len) >= len)
475: fatal("cvs_get_repository_name: truncation");
476: if (strlcat(dst, "/", len) >= len)
477: fatal("cvs_get_repository_name: truncation");
1.84 joris 478:
1.127 tobias 479: if (strcmp(dir, "."))
480: if (strlcat(dst, dir, len) >= len)
1.85 joris 481: fatal("cvs_get_repository_name: truncation");
1.127 tobias 482: break;
483: default:
484: (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
485: dir, CVS_PATH_REPOSITORY);
486: if ((fp = fopen(fpath, "r")) != NULL) {
487: if ((fgets(dst, len, fp)) == NULL)
488: fatal("%s: bad repository file", fpath);
489: dst[strcspn(dst, "\n")] = '\0';
490: (void)fclose(fp);
491: } else if (cvs_cmdop != CVS_OP_CHECKOUT)
492: fatal("%s is missing", fpath);
493: break;
1.51 xsa 494: }
495: }
496:
497: void
1.87 xsa 498: cvs_mkadmin(const char *path, const char *root, const char *repo,
1.131 tobias 499: char *tag, char *date)
1.51 xsa 500: {
501: FILE *fp;
1.141 tobias 502: int fd;
1.157 deraadt 503: char buf[PATH_MAX];
1.147 joris 504: struct hash_data *hdata, hd;
505:
506: hdata = hash_table_find(&created_cvs_directories, path, strlen(path));
507: if (hdata != NULL)
508: return;
509:
510: hd.h_key = xstrdup(path);
511: hd.h_data = NULL;
512: hash_table_enter(&created_cvs_directories, &hd);
1.51 xsa 513:
1.90 joris 514: if (cvs_server_active == 0)
1.131 tobias 515: cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
1.90 joris 516: path, root, repo, (tag != NULL) ? tag : "",
1.131 tobias 517: (date != NULL) ? date : "");
1.51 xsa 518:
1.105 xsa 519: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
1.51 xsa 520:
1.79 joris 521: if (mkdir(buf, 0755) == -1 && errno != EEXIST)
522: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
523:
1.143 joris 524: if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
525: (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
1.140 joris 526: (void)xsnprintf(buf, sizeof(buf), "%s/%s",
527: path, CVS_PATH_ROOTSPEC);
1.79 joris 528:
1.140 joris 529: if ((fp = fopen(buf, "w")) == NULL)
530: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.79 joris 531:
1.140 joris 532: fprintf(fp, "%s\n", root);
533: (void)fclose(fp);
534: }
1.51 xsa 535:
1.105 xsa 536: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
1.51 xsa 537:
1.79 joris 538: if ((fp = fopen(buf, "w")) == NULL)
539: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
1.51 xsa 540:
1.79 joris 541: fprintf(fp, "%s\n", repo);
1.51 xsa 542: (void)fclose(fp);
1.87 xsa 543:
1.131 tobias 544: cvs_write_tagfile(path, tag, date);
1.141 tobias 545:
546: (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES);
547:
548: if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask))
549: == -1) {
550: if (errno == EEXIST)
551: return;
552: fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
553: }
554:
555: if (atomicio(vwrite, fd, "D\n", 2) != 2)
556: fatal("cvs_mkadmin: %s", strerror(errno));
557: close(fd);
1.54 joris 558: }
559:
1.79 joris 560: void
1.112 joris 561: cvs_mkpath(const char *path, char *tag)
1.72 niallo 562: {
1.122 tobias 563: CVSENTRIES *ent;
1.79 joris 564: FILE *fp;
565: size_t len;
1.147 joris 566: struct hash_data *hdata, hd;
1.157 deraadt 567: char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX];
1.147 joris 568:
569: hdata = hash_table_find(&created_directories, path, strlen(path));
570: if (hdata != NULL)
571: return;
572:
573: hd.h_key = xstrdup(path);
574: hd.h_data = NULL;
575: hash_table_enter(&created_directories, &hd);
1.138 joris 576:
577: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL ||
578: cvs_server_active == 1)
579: cvs_validate_directory(path);
1.79 joris 580:
581: dir = xstrdup(path);
582:
583: STRIP_SLASH(dir);
1.90 joris 584:
585: if (cvs_server_active == 0)
586: cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
1.72 niallo 587:
1.79 joris 588: repo[0] = '\0';
589: rpath[0] = '\0';
1.72 niallo 590:
1.148 joris 591: if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
1.79 joris 592: if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
1.91 thib 593: if ((fgets(repo, sizeof(repo), fp)) == NULL)
594: fatal("cvs_mkpath: bad repository file");
1.120 gilles 595: repo[strcspn(repo, "\n")] = '\0';
1.79 joris 596: (void)fclose(fp);
597: }
1.72 niallo 598: }
599:
1.79 joris 600: for (sp = dir; sp != NULL; sp = dp) {
601: dp = strchr(sp, '/');
602: if (dp != NULL)
603: *(dp++) = '\0';
604:
1.130 joris 605: if (sp == dir && module_repo_root != NULL) {
606: len = strlcpy(repo, module_repo_root, sizeof(repo));
607: if (len >= (int)sizeof(repo))
608: fatal("cvs_mkpath: overflow");
1.136 joris 609: } else if (strcmp(sp, ".")) {
1.130 joris 610: if (repo[0] != '\0') {
611: len = strlcat(repo, "/", sizeof(repo));
612: if (len >= (int)sizeof(repo))
613: fatal("cvs_mkpath: overflow");
614: }
615:
616: len = strlcat(repo, sp, sizeof(repo));
1.79 joris 617: if (len >= (int)sizeof(repo))
618: fatal("cvs_mkpath: overflow");
619: }
620:
621: if (rpath[0] != '\0') {
622: len = strlcat(rpath, "/", sizeof(rpath));
623: if (len >= (int)sizeof(rpath))
624: fatal("cvs_mkpath: overflow");
625: }
626:
627: len = strlcat(rpath, sp, sizeof(rpath));
628: if (len >= (int)sizeof(rpath))
629: fatal("cvs_mkpath: overflow");
630:
631: if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
632: fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
1.125 tobias 633:
634: if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
635: continue;
1.72 niallo 636:
1.98 joris 637: cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
1.131 tobias 638: tag, NULL);
1.122 tobias 639:
640: if (dp != NULL) {
641: if ((p = strchr(dp, '/')) != NULL)
642: *p = '\0';
1.135 xsa 643:
644: entry = xmalloc(CVS_ENT_MAXLINELEN);
645: cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
646: entry, CVS_ENT_MAXLINELEN);
647:
1.122 tobias 648: ent = cvs_ent_open(rpath);
649: cvs_ent_add(ent, entry);
1.158 ! nicm 650: free(entry);
1.135 xsa 651:
1.122 tobias 652: if (p != NULL)
653: *p = '/';
1.112 joris 654: }
1.153 nicm 655: }
656:
1.158 ! nicm 657: free(dir);
1.153 nicm 658: }
659:
660: void
661: cvs_mkdir(const char *path, mode_t mode)
662: {
663: size_t len;
1.157 deraadt 664: char *sp, *dp, *dir, rpath[PATH_MAX];
1.153 nicm 665:
666: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL ||
667: cvs_server_active == 1)
668: cvs_validate_directory(path);
669:
670: dir = xstrdup(path);
671:
672: STRIP_SLASH(dir);
673:
674: if (cvs_server_active == 0)
675: cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir);
676:
677: rpath[0] = '\0';
678:
679: for (sp = dir; sp != NULL; sp = dp) {
680: dp = strchr(sp, '/');
681: if (dp != NULL)
682: *(dp++) = '\0';
683:
684: len = strlcat(rpath, "/", sizeof(rpath));
685: if (len >= (int)sizeof(rpath))
686: fatal("cvs_mkdir: overflow");
687:
688: len = strlcat(rpath, sp, sizeof(rpath));
689: if (len >= (int)sizeof(rpath))
690: fatal("cvs_mkdir: overflow");
691: if (1 == len)
692: continue;
693:
694: if (mkdir(rpath, mode) == -1 && errno != EEXIST)
695: fatal("cvs_mkdir: %s: %s", rpath, strerror(errno));
1.79 joris 696: }
1.72 niallo 697:
1.158 ! nicm 698: free(dir);
1.72 niallo 699: }
1.54 joris 700:
701: /*
702: * Split the contents of a file into a list of lines.
703: */
1.152 ray 704: struct rcs_lines *
1.106 otto 705: cvs_splitlines(u_char *data, size_t len)
1.54 joris 706: {
1.97 niallo 707: u_char *p, *c;
708: size_t i, tlen;
1.152 ray 709: struct rcs_lines *lines;
710: struct rcs_line *lp;
1.54 joris 711:
1.124 tobias 712: lines = xcalloc(1, sizeof(*lines));
1.54 joris 713: TAILQ_INIT(&(lines->l_lines));
714:
1.124 tobias 715: lp = xcalloc(1, sizeof(*lp));
1.54 joris 716: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
717:
1.97 niallo 718: p = c = data;
719: for (i = 0; i < len; i++) {
720: if (*p == '\n' || (i == len - 1)) {
721: tlen = p - c + 1;
1.124 tobias 722: lp = xcalloc(1, sizeof(*lp));
1.97 niallo 723: lp->l_line = c;
724: lp->l_len = tlen;
725: lp->l_lineno = ++(lines->l_nblines);
726: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
727: c = p + 1;
728: }
729: p++;
1.54 joris 730: }
731:
732: return (lines);
733: }
734:
735: void
1.152 ray 736: cvs_freelines(struct rcs_lines *lines)
1.54 joris 737: {
1.152 ray 738: struct rcs_line *lp;
1.54 joris 739:
740: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
741: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
1.102 niallo 742: if (lp->l_needsfree == 1)
1.158 ! nicm 743: free(lp->l_line);
! 744: free(lp);
1.54 joris 745: }
746:
1.158 ! nicm 747: free(lines);
1.70 niallo 748: }
749:
1.71 xsa 750: /*
751: * cvs_strsplit()
752: *
753: * Split a string <str> of <sep>-separated values and allocate
754: * an argument vector for the values found.
755: */
1.73 pat 756: struct cvs_argvector *
1.71 xsa 757: cvs_strsplit(char *str, const char *sep)
758: {
1.73 pat 759: struct cvs_argvector *av;
1.76 ray 760: size_t i = 0;
1.71 xsa 761: char *cp, *p;
762:
763: cp = xstrdup(str);
1.77 ray 764: av = xmalloc(sizeof(*av));
1.73 pat 765: av->str = cp;
1.113 ray 766: av->argv = xmalloc(sizeof(*(av->argv)));
1.71 xsa 767:
768: while ((p = strsep(&cp, sep)) != NULL) {
1.73 pat 769: av->argv[i++] = p;
1.156 deraadt 770: av->argv = xreallocarray(av->argv,
1.77 ray 771: i + 1, sizeof(*(av->argv)));
1.71 xsa 772: }
1.73 pat 773: av->argv[i] = NULL;
774:
775: return (av);
776: }
1.71 xsa 777:
1.73 pat 778: /*
779: * cvs_argv_destroy()
780: *
781: * Free an argument vector previously allocated by cvs_strsplit().
782: */
783: void
784: cvs_argv_destroy(struct cvs_argvector *av)
785: {
1.158 ! nicm 786: free(av->str);
! 787: free(av->argv);
! 788: free(av);
1.82 joris 789: }
790:
791: u_int
792: cvs_revision_select(RCSFILE *file, char *range)
793: {
794: int i;
795: u_int nrev;
796: char *lstr, *rstr;
797: struct rcs_delta *rdp;
798: struct cvs_argvector *revargv, *revrange;
799: RCSNUM *lnum, *rnum;
800:
801: nrev = 0;
802: lnum = rnum = NULL;
803:
804: revargv = cvs_strsplit(range, ",");
805: for (i = 0; revargv->argv[i] != NULL; i++) {
806: revrange = cvs_strsplit(revargv->argv[i], ":");
807: if (revrange->argv[0] == NULL)
808: fatal("invalid revision range: %s", revargv->argv[i]);
809: else if (revrange->argv[1] == NULL)
810: lstr = rstr = revrange->argv[0];
811: else {
812: if (revrange->argv[2] != NULL)
813: fatal("invalid revision range: %s",
814: revargv->argv[i]);
815:
816: lstr = revrange->argv[0];
817: rstr = revrange->argv[1];
818:
819: if (strcmp(lstr, "") == 0)
820: lstr = NULL;
821: if (strcmp(rstr, "") == 0)
822: rstr = NULL;
823: }
824:
825: if (lstr == NULL)
826: lstr = RCS_HEAD_INIT;
827:
1.100 niallo 828: if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
829: fatal("cvs_revision_select: could not translate tag `%s'", lstr);
1.82 joris 830:
831: if (rstr != NULL) {
1.100 niallo 832: if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
833: fatal("cvs_revision_select: could not translate tag `%s'", rstr);
1.82 joris 834: } else {
835: rnum = rcsnum_alloc();
836: rcsnum_cpy(file->rf_head, rnum, 0);
837: }
838:
839: cvs_argv_destroy(revrange);
840:
841: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
842: if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
843: rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
844: !(rdp->rd_flags & RCS_RD_SELECT)) {
845: rdp->rd_flags |= RCS_RD_SELECT;
846: nrev++;
847: }
848: }
1.129 joris 849:
850: rcsnum_free(lnum);
851: rcsnum_free(rnum);
1.82 joris 852: }
853:
854: cvs_argv_destroy(revargv);
855:
856: return (nrev);
1.95 xsa 857: }
858:
859: int
860: cvs_yesno(void)
861: {
862: int c, ret;
863:
864: ret = 0;
865:
866: fflush(stderr);
867: fflush(stdout);
868:
869: if ((c = getchar()) != 'y' && c != 'Y')
870: ret = -1;
871: else
872: while (c != EOF && c != '\n')
873: c = getchar();
874:
875: return (ret);
1.132 joris 876: }
877:
1.144 joris 878: /*
879: * cvs_exec()
880: *
881: * Execute <prog> and send <in> to the STDIN if not NULL.
882: * If <needwait> == 1, return the result of <prog>,
883: * else, 0 or -1 if an error occur.
884: */
885: int
1.145 joris 886: cvs_exec(char *prog, const char *in, int needwait)
1.132 joris 887: {
888: pid_t pid;
1.151 joris 889: size_t size;
890: int fds[2], st;
1.144 joris 891: char *argp[4] = { "sh", "-c", prog, NULL };
1.132 joris 892:
1.144 joris 893: if (in != NULL && pipe(fds) < 0) {
894: cvs_log(LP_ERR, "cvs_exec: pipe failed");
895: return (-1);
896: }
1.132 joris 897:
898: if ((pid = fork()) == -1) {
899: cvs_log(LP_ERR, "cvs_exec: fork failed");
1.144 joris 900: return (-1);
1.132 joris 901: } else if (pid == 0) {
1.144 joris 902: if (in != NULL) {
903: close(fds[1]);
904: dup2(fds[0], STDIN_FILENO);
905: }
906:
907: setenv("CVSROOT", current_cvsroot->cr_dir, 1);
1.132 joris 908: execv(_PATH_BSHELL, argp);
1.144 joris 909: cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
1.132 joris 910: _exit(127);
911: }
1.144 joris 912:
913: if (in != NULL) {
914: close(fds[0]);
915: size = strlen(in);
916: if (atomicio(vwrite, fds[1], in, size) != size)
917: cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
918: close(fds[1]);
919: }
920:
921: if (needwait == 1) {
922: while (waitpid(pid, &st, 0) == -1)
923: ;
924: if (!WIFEXITED(st)) {
925: errno = EINTR;
926: return (-1);
927: }
928: return (WEXITSTATUS(st));
929: }
930:
931: return (0);
1.71 xsa 932: }