1 /* $OpenBSD: util.c,v 1.162 2019/06/28 13:35:00 deraadt Exp $ */
2 /*
3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4 * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
5 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
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
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32
33 #include <atomicio.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <paths.h>
39 #include <unistd.h>
40
41 #include "cvs.h"
42 #include "remote.h"
43 #include "hash.h"
44
45 extern int print_stdout;
46 extern int build_dirs;
47 extern int disable_fast_checkout;
48
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
83 /*
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 */
92 void
cvs_strtomode(const char * str,mode_t * mode)93 cvs_strtomode(const char *str, mode_t *mode)
94 {
95 char type;
96 size_t l;
97 mode_t m;
98 char buf[32], ms[4], *sp, *ep;
99
100 m = 0;
101 l = strlcpy(buf, str, sizeof(buf));
102 if (l >= sizeof(buf))
103 fatal("cvs_strtomode: string truncation");
104
105 sp = buf;
106 ep = sp;
107
108 for (sp = buf; ep != NULL; sp = ep + 1) {
109 ep = strchr(sp, ',');
110 if (ep != NULL)
111 *ep = '\0';
112
113 memset(ms, 0, sizeof ms);
114 if (sscanf(sp, "%c=%3s", &type, ms) != 2 &&
115 sscanf(sp, "%c=", &type) != 1) {
116 fatal("failed to scan mode string `%s'", sp);
117 }
118
119 if (type <= 'a' || type >= 'z' ||
120 cvs_modetypes[type - 'a'] == -1) {
121 cvs_log(LP_ERR,
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++) {
131 if (*sp <= 'a' || *sp >= 'z' ||
132 cvs_modes[(int)type][*sp - 'a'] == 0) {
133 fatal("invalid permission bit `%c'", *sp);
134 } else
135 m |= cvs_modes[(int)type][*sp - 'a'];
136 }
137 }
138
139 *mode = m;
140 }
141
142 /*
143 * cvs_modetostr()
144 *
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.
149 */
150 void
cvs_modetostr(mode_t mode,char * buf,size_t len)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) {
164 if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) ||
165 strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp))
166 fatal("cvs_modetostr: overflow for user mode");
167
168 if (strlcat(buf, tmp, len) >= len)
169 fatal("cvs_modetostr: string truncation");
170 }
171
172 if (gm) {
173 if (um) {
174 if (strlcat(buf, ",", len) >= len)
175 fatal("cvs_modetostr: string truncation");
176 }
177
178 if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) ||
179 strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
180 fatal("cvs_modetostr: overflow for group mode");
181
182 if (strlcat(buf, tmp, len) >= len)
183 fatal("cvs_modetostr: string truncation");
184 }
185
186 if (om) {
187 if (um || gm) {
188 if (strlcat(buf, ",", len) >= len)
189 fatal("cvs_modetostr: string truncation");
190 }
191
192 if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) ||
193 strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp))
194 fatal("cvs_modetostr: overflow for others mode");
195
196 if (strlcat(buf, tmp, len) >= len)
197 fatal("cvs_modetostr: string truncation");
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.
207 * Returns the number of arguments in the vector, or -1 if an error occurred.
208 */
209 int
cvs_getargv(const char * line,char ** argv,int argvlen)210 cvs_getargv(const char *line, char **argv, int argvlen)
211 {
212 u_int i;
213 int argc, error;
214 char *linebuf, *lp, *cp;
215
216 linebuf = xstrdup(line);
217
218 memset(argv, 0, argvlen * sizeof(char *));
219 argc = 0;
220
221 /* build the argument vector */
222 error = 0;
223 for (lp = linebuf; lp != NULL;) {
224 cp = strsep(&lp, " \t");
225 if (cp == NULL)
226 break;
227 else if (*cp == '\0')
228 continue;
229
230 if (argc == argvlen) {
231 error++;
232 break;
233 }
234
235 argv[argc] = xstrdup(cp);
236 argc++;
237 }
238
239 if (error != 0) {
240 /* ditch the argument vector */
241 for (i = 0; i < (u_int)argc; i++)
242 free(argv[i]);
243 argc = -1;
244 }
245
246 free(linebuf);
247 return (argc);
248 }
249
250 /*
251 * cvs_makeargv()
252 *
253 * Allocate an argument vector large enough to accommodate for all the
254 * arguments found in <line> and return it.
255 */
256 char **
cvs_makeargv(const char * line,int * argc)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
266 copy = xcalloc(ret + 1, sizeof(char *));
267
268 for (i = 0; i < ret; i++)
269 copy[i] = argv[i];
270 copy[ret] = NULL;
271
272 *argc = ret;
273 return (copy);
274 }
275
276 /*
277 * cvs_freeargv()
278 *
279 * Free an argument vector previously generated by cvs_getargv().
280 */
281 void
cvs_freeargv(char ** argv,int argc)282 cvs_freeargv(char **argv, int argc)
283 {
284 int i;
285
286 for (i = 0; i < argc; i++)
287 free(argv[i]);
288 }
289
290 /*
291 * cvs_chdir()
292 *
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.
296 * Returns 0 on success.
297 */
298 int
cvs_chdir(const char * path,int rm)299 cvs_chdir(const char *path, int rm)
300 {
301 if (chdir(path) == -1) {
302 if (rm == 1)
303 cvs_unlink(path);
304 fatal("cvs_chdir: `%s': %s", path, strerror(errno));
305 }
306
307 return (0);
308 }
309
310 /*
311 * cvs_rename()
312 * Change the name of a file.
313 * rename() wrapper with an error message.
314 * Returns 0 on success.
315 */
316 int
cvs_rename(const char * from,const char * to)317 cvs_rename(const char *from, const char *to)
318 {
319 if (cvs_server_active == 0)
320 cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to);
321
322 if (cvs_noexec == 1)
323 return (0);
324
325 if (rename(from, to) == -1)
326 fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno));
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
cvs_unlink(const char * path)339 cvs_unlink(const char *path)
340 {
341 if (cvs_server_active == 0)
342 cvs_log(LP_TRACE, "cvs_unlink(%s)", path);
343
344 if (cvs_noexec == 1 && disable_fast_checkout != 0)
345 return (0);
346
347 if (unlink(path) == -1 && errno != ENOENT) {
348 cvs_log(LP_ERRNO, "%s", path);
349 return (-1);
350 }
351
352 return (0);
353 }
354
355 /*
356 * cvs_rmdir()
357 *
358 * Remove a directory tree from disk.
359 * Returns 0 on success, or -1 on failure.
360 */
361 int
cvs_rmdir(const char * path)362 cvs_rmdir(const char *path)
363 {
364 int type, ret = -1;
365 DIR *dirp;
366 struct dirent *ent;
367 struct stat st;
368 char fpath[PATH_MAX];
369
370 if (cvs_server_active == 0)
371 cvs_log(LP_TRACE, "cvs_rmdir(%s)", path);
372
373 if (cvs_noexec == 1 && disable_fast_checkout != 0)
374 return (0);
375
376 if ((dirp = opendir(path)) == NULL) {
377 cvs_log(LP_ERR, "failed to open '%s'", path);
378 return (-1);
379 }
380
381 while ((ent = readdir(dirp)) != NULL) {
382 if (!strcmp(ent->d_name, ".") ||
383 !strcmp(ent->d_name, ".."))
384 continue;
385
386 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
387 path, ent->d_name);
388
389 if (ent->d_type == DT_UNKNOWN) {
390 if (lstat(fpath, &st) == -1)
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) {
418 case CVS_DIR:
419 if (cvs_rmdir(fpath) == -1)
420 goto done;
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 }
429 }
430
431
432 if (rmdir(path) == -1 && errno != ENOENT) {
433 cvs_log(LP_ERRNO, "%s", path);
434 goto done;
435 }
436
437 ret = 0;
438 done:
439 closedir(dirp);
440 return (ret);
441 }
442
443 void
cvs_get_repository_path(const char * dir,char * dst,size_t len)444 cvs_get_repository_path(const char *dir, char *dst, size_t len)
445 {
446 char buf[PATH_MAX];
447
448 cvs_get_repository_name(dir, buf, sizeof(buf));
449 (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf);
450 cvs_validate_directory(dst);
451 }
452
453 void
cvs_get_repository_name(const char * dir,char * dst,size_t len)454 cvs_get_repository_name(const char *dir, char *dst, size_t len)
455 {
456 FILE *fp;
457 char fpath[PATH_MAX];
458
459 dst[0] = '\0';
460
461 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) {
462 if (strlcpy(dst, dir, len) >= len)
463 fatal("cvs_get_repository_name: truncation");
464 return;
465 }
466
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");
478
479 if (strcmp(dir, "."))
480 if (strlcat(dst, dir, len) >= len)
481 fatal("cvs_get_repository_name: truncation");
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;
494 }
495 }
496
497 void
cvs_mkadmin(const char * path,const char * root,const char * repo,char * tag,char * date)498 cvs_mkadmin(const char *path, const char *root, const char *repo,
499 char *tag, char *date)
500 {
501 FILE *fp;
502 int fd;
503 char buf[PATH_MAX];
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);
513
514 if (cvs_server_active == 0)
515 cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)",
516 path, root, repo, (tag != NULL) ? tag : "",
517 (date != NULL) ? date : "");
518
519 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR);
520
521 if (mkdir(buf, 0755) == -1 && errno != EEXIST)
522 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
523
524 if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD ||
525 (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) {
526 (void)xsnprintf(buf, sizeof(buf), "%s/%s",
527 path, CVS_PATH_ROOTSPEC);
528
529 if ((fp = fopen(buf, "w")) == NULL)
530 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
531
532 fprintf(fp, "%s\n", root);
533 (void)fclose(fp);
534 }
535
536 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY);
537
538 if ((fp = fopen(buf, "w")) == NULL)
539 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno));
540
541 fprintf(fp, "%s\n", repo);
542 (void)fclose(fp);
543
544 cvs_write_tagfile(path, tag, date);
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);
558 }
559
560 void
cvs_mkpath(const char * path,char * tag)561 cvs_mkpath(const char *path, char *tag)
562 {
563 CVSENTRIES *ent;
564 FILE *fp;
565 size_t len;
566 struct hash_data *hdata, hd;
567 char *entry, *sp, *dp, *dir, *p, rpath[PATH_MAX], repo[PATH_MAX];
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);
576
577 if (cvsroot_is_remote() || cvs_server_active == 1)
578 cvs_validate_directory(path);
579
580 dir = xstrdup(path);
581
582 STRIP_SLASH(dir);
583
584 if (cvs_server_active == 0)
585 cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir);
586
587 repo[0] = '\0';
588 rpath[0] = '\0';
589
590 if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) {
591 if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) {
592 if ((fgets(repo, sizeof(repo), fp)) == NULL)
593 fatal("cvs_mkpath: bad repository file");
594 repo[strcspn(repo, "\n")] = '\0';
595 (void)fclose(fp);
596 }
597 }
598
599 for (sp = dir; sp != NULL; sp = dp) {
600 dp = strchr(sp, '/');
601 if (dp != NULL)
602 *(dp++) = '\0';
603
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");
608 } else if (strcmp(sp, ".")) {
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));
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));
632
633 if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active)
634 continue;
635
636 cvs_mkadmin(rpath, current_cvsroot->cr_str, repo,
637 tag, NULL);
638
639 if (dp != NULL) {
640 if ((p = strchr(dp, '/')) != NULL)
641 *p = '\0';
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
647 ent = cvs_ent_open(rpath);
648 cvs_ent_add(ent, entry);
649 free(entry);
650
651 if (p != NULL)
652 *p = '/';
653 }
654 }
655
656 free(dir);
657 }
658
659 void
cvs_mkdir(const char * path,mode_t mode)660 cvs_mkdir(const char *path, mode_t mode)
661 {
662 size_t len;
663 char *sp, *dp, *dir, rpath[PATH_MAX];
664
665 if (cvsroot_is_remote() || cvs_server_active == 1)
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));
694 }
695
696 free(dir);
697 }
698
699 /*
700 * Split the contents of a file into a list of lines.
701 */
702 struct rcs_lines *
cvs_splitlines(u_char * data,size_t len)703 cvs_splitlines(u_char *data, size_t len)
704 {
705 u_char *p, *c;
706 size_t i, tlen;
707 struct rcs_lines *lines;
708 struct rcs_line *lp;
709
710 lines = xcalloc(1, sizeof(*lines));
711 TAILQ_INIT(&(lines->l_lines));
712
713 lp = xcalloc(1, sizeof(*lp));
714 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
715
716 p = c = data;
717 for (i = 0; i < len; i++) {
718 if (*p == '\n' || (i == len - 1)) {
719 tlen = p - c + 1;
720 lp = xcalloc(1, sizeof(*lp));
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++;
728 }
729
730 return (lines);
731 }
732
733 void
cvs_freelines(struct rcs_lines * lines)734 cvs_freelines(struct rcs_lines *lines)
735 {
736 struct rcs_line *lp;
737
738 while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
739 TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
740 if (lp->l_needsfree == 1)
741 free(lp->l_line);
742 free(lp);
743 }
744
745 free(lines);
746 }
747
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 */
754 struct cvs_argvector *
cvs_strsplit(char * str,const char * sep)755 cvs_strsplit(char *str, const char *sep)
756 {
757 struct cvs_argvector *av;
758 size_t i = 0;
759 char *cp, *p;
760
761 cp = xstrdup(str);
762 av = xmalloc(sizeof(*av));
763 av->str = cp;
764 av->argv = xmalloc(sizeof(*(av->argv)));
765
766 while ((p = strsep(&cp, sep)) != NULL) {
767 av->argv[i++] = p;
768 av->argv = xreallocarray(av->argv,
769 i + 1, sizeof(*(av->argv)));
770 }
771 av->argv[i] = NULL;
772
773 return (av);
774 }
775
776 /*
777 * cvs_argv_destroy()
778 *
779 * Free an argument vector previously allocated by cvs_strsplit().
780 */
781 void
cvs_argv_destroy(struct cvs_argvector * av)782 cvs_argv_destroy(struct cvs_argvector *av)
783 {
784 free(av->str);
785 free(av->argv);
786 free(av);
787 }
788
789 u_int
cvs_revision_select(RCSFILE * file,char * range)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
826 if ((lnum = rcs_translate_tag(lstr, file)) == NULL)
827 fatal("cvs_revision_select: could not translate tag `%s'", lstr);
828
829 if (rstr != NULL) {
830 if ((rnum = rcs_translate_tag(rstr, file)) == NULL)
831 fatal("cvs_revision_select: could not translate tag `%s'", rstr);
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 }
847
848 free(lnum);
849 free(rnum);
850 }
851
852 cvs_argv_destroy(revargv);
853
854 return (nrev);
855 }
856
857 int
cvs_yesno(void)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);
874 }
875
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
cvs_exec(char * prog,char * in,int needwait)884 cvs_exec(char *prog, char *in, int needwait)
885 {
886 pid_t pid;
887 size_t size;
888 int fds[2], st;
889 char *argp[4] = { "sh", "-c", prog, NULL };
890
891 if (in != NULL && pipe(fds) == -1) {
892 cvs_log(LP_ERR, "cvs_exec: pipe failed");
893 return (-1);
894 }
895
896 if ((pid = fork()) == -1) {
897 cvs_log(LP_ERR, "cvs_exec: fork failed");
898 return (-1);
899 } else if (pid == 0) {
900 if (in != NULL) {
901 close(fds[1]);
902 dup2(fds[0], STDIN_FILENO);
903 }
904
905 setenv("CVSROOT", current_cvsroot->cr_dir, 1);
906 execv(_PATH_BSHELL, argp);
907 cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog);
908 _exit(127);
909 }
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);
930 }
931