Annotation of src/usr.bin/cvs/util.c, Revision 1.162
1.162 ! deraadt 1: /* $OpenBSD: util.c,v 1.161 2017/08/28 19:33:20 otto 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:
1.160 joris 577: if (cvsroot_is_remote() || cvs_server_active == 1)
1.138 joris 578: cvs_validate_directory(path);
1.79 joris 579:
580: dir = xstrdup(path);
581:
582: STRIP_SLASH(dir);
1.90 joris 583:
584: if (cvs_server_active == 0)
585: cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
1.72 niallo 586:
1.79 joris 587: repo[0] = '\0';
588: rpath[0] = '\0';
1.72 niallo 589:
1.148 joris 590: if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
1.79 joris 591: if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
1.91 thib 592: if ((fgets(repo, sizeof(repo), fp)) == NULL)
593: fatal("cvs_mkpath: bad repository file");
1.120 gilles 594: repo[strcspn(repo, "\n")] = '\0';
1.79 joris 595: (void)fclose(fp);
596: }
1.72 niallo 597: }
598:
1.79 joris 599: for (sp = dir; sp != NULL; sp = dp) {
600: dp = strchr(sp, '/');
601: if (dp != NULL)
602: *(dp++) = '\0';
603:
1.130 joris 604: if (sp == dir && module_repo_root != NULL) {
605: len = strlcpy(repo, module_repo_root, sizeof(repo));
606: if (len >= (int)sizeof(repo))
607: fatal("cvs_mkpath: overflow");
1.136 joris 608: } else if (strcmp(sp, ".")) {
1.130 joris 609: if (repo[0] != '\0') {
610: len = strlcat(repo, "/", sizeof(repo));
611: if (len >= (int)sizeof(repo))
612: fatal("cvs_mkpath: overflow");
613: }
614:
615: len = strlcat(repo, sp, sizeof(repo));
1.79 joris 616: if (len >= (int)sizeof(repo))
617: fatal("cvs_mkpath: overflow");
618: }
619:
620: if (rpath[0] != '\0') {
621: len = strlcat(rpath, "/", sizeof(rpath));
622: if (len >= (int)sizeof(rpath))
623: fatal("cvs_mkpath: overflow");
624: }
625:
626: len = strlcat(rpath, sp, sizeof(rpath));
627: if (len >= (int)sizeof(rpath))
628: fatal("cvs_mkpath: overflow");
629:
630: if (mkdir(rpath, 0755) == -1 && errno != EEXIST)
631: fatal("cvs_mkpath: %s: %s", rpath, strerror(errno));
1.125 tobias 632:
633: if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
634: continue;
1.72 niallo 635:
1.98 joris 636: cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
1.131 tobias 637: tag, NULL);
1.122 tobias 638:
639: if (dp != NULL) {
640: if ((p = strchr(dp, '/')) != NULL)
641: *p = '\0';
1.135 xsa 642:
643: entry = xmalloc(CVS_ENT_MAXLINELEN);
644: cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0,
645: entry, CVS_ENT_MAXLINELEN);
646:
1.122 tobias 647: ent = cvs_ent_open(rpath);
648: cvs_ent_add(ent, entry);
1.158 nicm 649: free(entry);
1.135 xsa 650:
1.122 tobias 651: if (p != NULL)
652: *p = '/';
1.112 joris 653: }
1.153 nicm 654: }
655:
1.158 nicm 656: free(dir);
1.153 nicm 657: }
658:
659: void
660: cvs_mkdir(const char *path, mode_t mode)
661: {
662: size_t len;
1.157 deraadt 663: char *sp, *dp, *dir, rpath[PATH_MAX];
1.153 nicm 664:
1.160 joris 665: if (cvsroot_is_remote() || cvs_server_active == 1)
1.153 nicm 666: cvs_validate_directory(path);
667:
668: dir = xstrdup(path);
669:
670: STRIP_SLASH(dir);
671:
672: if (cvs_server_active == 0)
673: cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir);
674:
675: rpath[0] = '\0';
676:
677: for (sp = dir; sp != NULL; sp = dp) {
678: dp = strchr(sp, '/');
679: if (dp != NULL)
680: *(dp++) = '\0';
681:
682: len = strlcat(rpath, "/", sizeof(rpath));
683: if (len >= (int)sizeof(rpath))
684: fatal("cvs_mkdir: overflow");
685:
686: len = strlcat(rpath, sp, sizeof(rpath));
687: if (len >= (int)sizeof(rpath))
688: fatal("cvs_mkdir: overflow");
689: if (1 == len)
690: continue;
691:
692: if (mkdir(rpath, mode) == -1 && errno != EEXIST)
693: fatal("cvs_mkdir: %s: %s", rpath, strerror(errno));
1.79 joris 694: }
1.72 niallo 695:
1.158 nicm 696: free(dir);
1.72 niallo 697: }
1.54 joris 698:
699: /*
700: * Split the contents of a file into a list of lines.
701: */
1.152 ray 702: struct rcs_lines *
1.106 otto 703: cvs_splitlines(u_char *data, size_t len)
1.54 joris 704: {
1.97 niallo 705: u_char *p, *c;
706: size_t i, tlen;
1.152 ray 707: struct rcs_lines *lines;
708: struct rcs_line *lp;
1.54 joris 709:
1.124 tobias 710: lines = xcalloc(1, sizeof(*lines));
1.54 joris 711: TAILQ_INIT(&(lines->l_lines));
712:
1.124 tobias 713: lp = xcalloc(1, sizeof(*lp));
1.54 joris 714: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
715:
1.97 niallo 716: p = c = data;
717: for (i = 0; i < len; i++) {
718: if (*p == '\n' || (i == len - 1)) {
719: tlen = p - c + 1;
1.124 tobias 720: lp = xcalloc(1, sizeof(*lp));
1.97 niallo 721: lp->l_line = c;
722: lp->l_len = tlen;
723: lp->l_lineno = ++(lines->l_nblines);
724: TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
725: c = p + 1;
726: }
727: p++;
1.54 joris 728: }
729:
730: return (lines);
731: }
732:
733: void
1.152 ray 734: cvs_freelines(struct rcs_lines *lines)
1.54 joris 735: {
1.152 ray 736: struct rcs_line *lp;
1.54 joris 737:
738: while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
739: TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
1.102 niallo 740: if (lp->l_needsfree == 1)
1.158 nicm 741: free(lp->l_line);
742: free(lp);
1.54 joris 743: }
744:
1.158 nicm 745: free(lines);
1.70 niallo 746: }
747:
1.71 xsa 748: /*
749: * cvs_strsplit()
750: *
751: * Split a string <str> of <sep>-separated values and allocate
752: * an argument vector for the values found.
753: */
1.73 pat 754: struct cvs_argvector *
1.71 xsa 755: cvs_strsplit(char *str, const char *sep)
756: {
1.73 pat 757: struct cvs_argvector *av;
1.76 ray 758: size_t i = 0;
1.71 xsa 759: char *cp, *p;
760:
761: cp = xstrdup(str);
1.77 ray 762: av = xmalloc(sizeof(*av));
1.73 pat 763: av->str = cp;
1.113 ray 764: av->argv = xmalloc(sizeof(*(av->argv)));
1.71 xsa 765:
766: while ((p = strsep(&cp, sep)) != NULL) {
1.73 pat 767: av->argv[i++] = p;
1.156 deraadt 768: av->argv = xreallocarray(av->argv,
1.77 ray 769: i + 1, sizeof(*(av->argv)));
1.71 xsa 770: }
1.73 pat 771: av->argv[i] = NULL;
772:
773: return (av);
774: }
1.71 xsa 775:
1.73 pat 776: /*
777: * cvs_argv_destroy()
778: *
779: * Free an argument vector previously allocated by cvs_strsplit().
780: */
781: void
782: cvs_argv_destroy(struct cvs_argvector *av)
783: {
1.158 nicm 784: free(av->str);
785: free(av->argv);
786: free(av);
1.82 joris 787: }
788:
789: u_int
790: cvs_revision_select(RCSFILE *file, char *range)
791: {
792: int i;
793: u_int nrev;
794: char *lstr, *rstr;
795: struct rcs_delta *rdp;
796: struct cvs_argvector *revargv, *revrange;
797: RCSNUM *lnum, *rnum;
798:
799: nrev = 0;
800: lnum = rnum = NULL;
801:
802: revargv = cvs_strsplit(range, ",");
803: for (i = 0; revargv->argv[i] != NULL; i++) {
804: revrange = cvs_strsplit(revargv->argv[i], ":");
805: if (revrange->argv[0] == NULL)
806: fatal("invalid revision range: %s", revargv->argv[i]);
807: else if (revrange->argv[1] == NULL)
808: lstr = rstr = revrange->argv[0];
809: else {
810: if (revrange->argv[2] != NULL)
811: fatal("invalid revision range: %s",
812: revargv->argv[i]);
813:
814: lstr = revrange->argv[0];
815: rstr = revrange->argv[1];
816:
817: if (strcmp(lstr, "") == 0)
818: lstr = NULL;
819: if (strcmp(rstr, "") == 0)
820: rstr = NULL;
821: }
822:
823: if (lstr == NULL)
824: lstr = RCS_HEAD_INIT;
825:
1.100 niallo 826: if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
827: fatal("cvs_revision_select: could not translate tag `%s'", lstr);
1.82 joris 828:
829: if (rstr != NULL) {
1.100 niallo 830: if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
831: fatal("cvs_revision_select: could not translate tag `%s'", rstr);
1.82 joris 832: } else {
833: rnum = rcsnum_alloc();
834: rcsnum_cpy(file->rf_head, rnum, 0);
835: }
836:
837: cvs_argv_destroy(revrange);
838:
839: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
840: if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 &&
841: rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 &&
842: !(rdp->rd_flags & RCS_RD_SELECT)) {
843: rdp->rd_flags |= RCS_RD_SELECT;
844: nrev++;
845: }
846: }
1.129 joris 847:
1.159 fcambus 848: free(lnum);
849: free(rnum);
1.82 joris 850: }
851:
852: cvs_argv_destroy(revargv);
853:
854: return (nrev);
1.95 xsa 855: }
856:
857: int
858: cvs_yesno(void)
859: {
860: int c, ret;
861:
862: ret = 0;
863:
864: fflush(stderr);
865: fflush(stdout);
866:
867: if ((c = getchar()) != 'y' && c != 'Y')
868: ret = -1;
869: else
870: while (c != EOF && c != '\n')
871: c = getchar();
872:
873: return (ret);
1.132 joris 874: }
875:
1.144 joris 876: /*
877: * cvs_exec()
878: *
879: * Execute <prog> and send <in> to the STDIN if not NULL.
880: * If <needwait> == 1, return the result of <prog>,
881: * else, 0 or -1 if an error occur.
882: */
883: int
1.161 otto 884: cvs_exec(char *prog, char *in, int needwait)
1.132 joris 885: {
886: pid_t pid;
1.151 joris 887: size_t size;
888: int fds[2], st;
1.144 joris 889: char *argp[4] = { "sh", "-c", prog, NULL };
1.132 joris 890:
1.162 ! deraadt 891: if (in != NULL && pipe(fds) == -1) {
1.144 joris 892: cvs_log(LP_ERR, "cvs_exec: pipe failed");
893: return (-1);
894: }
1.132 joris 895:
896: if ((pid = fork()) == -1) {
897: cvs_log(LP_ERR, "cvs_exec: fork failed");
1.144 joris 898: return (-1);
1.132 joris 899: } else if (pid == 0) {
1.144 joris 900: if (in != NULL) {
901: close(fds[1]);
902: dup2(fds[0], STDIN_FILENO);
903: }
904:
905: setenv("CVSROOT", current_cvsroot->cr_dir, 1);
1.132 joris 906: execv(_PATH_BSHELL, argp);
1.144 joris 907: cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
1.132 joris 908: _exit(127);
909: }
1.144 joris 910:
911: if (in != NULL) {
912: close(fds[0]);
913: size = strlen(in);
914: if (atomicio(vwrite, fds[1], in, size) != size)
915: cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN");
916: close(fds[1]);
917: }
918:
919: if (needwait == 1) {
920: while (waitpid(pid, &st, 0) == -1)
921: ;
922: if (!WIFEXITED(st)) {
923: errno = EINTR;
924: return (-1);
925: }
926: return (WEXITSTATUS(st));
927: }
928:
929: return (0);
1.71 xsa 930: }