1 /* $OpenBSD: rcsclean.c,v 1.55 2015/11/02 16:45:21 nicm Exp $ */ 2 /* 3 * Copyright (c) 2005 Joris Vink <joris@openbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/types.h> 28 29 #include <dirent.h> 30 #include <err.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 35 #include "rcsprog.h" 36 #include "diff.h" 37 38 static void rcsclean_file(char *, const char *); 39 40 static int nflag = 0; 41 static int kflag = RCS_KWEXP_ERR; 42 static int uflag = 0; 43 static int flags = 0; 44 static char *locker = NULL; 45 46 int 47 rcsclean_main(int argc, char **argv) 48 { 49 int i, ch; 50 char *rev_str; 51 DIR *dirp; 52 struct dirent *dp; 53 54 rev_str = NULL; 55 56 while ((ch = rcs_getopt(argc, argv, "k:n::q::r::Tu::Vx::")) != -1) { 57 switch (ch) { 58 case 'k': 59 kflag = rcs_kflag_get(rcs_optarg); 60 if (RCS_KWEXP_INVAL(kflag)) { 61 warnx("invalid RCS keyword substitution mode"); 62 (usage)(); 63 } 64 break; 65 case 'n': 66 rcs_setrevstr(&rev_str, rcs_optarg); 67 nflag = 1; 68 break; 69 case 'q': 70 rcs_setrevstr(&rev_str, rcs_optarg); 71 flags |= QUIET; 72 break; 73 case 'r': 74 rcs_setrevstr(&rev_str, rcs_optarg); 75 break; 76 case 'T': 77 flags |= PRESERVETIME; 78 break; 79 case 'u': 80 rcs_setrevstr(&rev_str, rcs_optarg); 81 uflag = 1; 82 break; 83 case 'V': 84 printf("%s\n", rcs_version); 85 exit(0); 86 case 'x': 87 /* Use blank extension if none given. */ 88 rcs_suffixes = rcs_optarg ? rcs_optarg : ""; 89 break; 90 default: 91 (usage)(); 92 } 93 } 94 95 argc -= rcs_optind; 96 argv += rcs_optind; 97 98 if ((locker = getlogin()) == NULL) 99 err(1, "getlogin"); 100 101 if (argc == 0) { 102 if ((dirp = opendir(".")) == NULL) { 103 warn("opendir"); 104 (usage)(); 105 } 106 107 while ((dp = readdir(dirp)) != NULL) { 108 if (dp->d_type == DT_DIR) 109 continue; 110 rcsclean_file(dp->d_name, rev_str); 111 } 112 113 (void)closedir(dirp); 114 } else 115 for (i = 0; i < argc; i++) 116 rcsclean_file(argv[i], rev_str); 117 118 return (0); 119 } 120 121 __dead void 122 rcsclean_usage(void) 123 { 124 fprintf(stderr, 125 "usage: rcsclean [-TV] [-kmode] [-n[rev]] [-q[rev]] [-r[rev]]\n" 126 " [-u[rev]] [-xsuffixes] [-ztz] [file ...]\n"); 127 128 exit(1); 129 } 130 131 static void 132 rcsclean_file(char *fname, const char *rev_str) 133 { 134 int fd, match; 135 RCSFILE *file; 136 char fpath[PATH_MAX], numb[RCS_REV_BUFSZ]; 137 RCSNUM *rev; 138 BUF *b1, *b2; 139 time_t rcs_mtime = -1; 140 141 b1 = b2 = NULL; 142 file = NULL; 143 rev = NULL; 144 145 if ((fd = rcs_choosefile(fname, fpath, sizeof(fpath))) < 0) 146 goto out; 147 148 if ((file = rcs_open(fpath, fd, RCS_RDWR)) == NULL) 149 goto out; 150 151 if (flags & PRESERVETIME) 152 rcs_mtime = rcs_get_mtime(file); 153 154 rcs_kwexp_set(file, kflag); 155 156 if (rev_str == NULL) 157 rev = file->rf_head; 158 else if ((rev = rcs_getrevnum(rev_str, file)) == NULL) { 159 warnx("%s: Symbolic name `%s' is undefined.", fpath, rev_str); 160 goto out; 161 } 162 163 if ((b1 = rcs_getrev(file, rev)) == NULL) { 164 warnx("failed to get needed revision"); 165 goto out; 166 } 167 if ((b2 = buf_load(fname)) == NULL) { 168 warnx("failed to load `%s'", fname); 169 goto out; 170 } 171 172 /* If buffer lengths are the same, compare contents as well. */ 173 if (buf_len(b1) != buf_len(b2)) 174 match = 0; 175 else { 176 size_t len, n; 177 178 len = buf_len(b1); 179 180 match = 1; 181 for (n = 0; n < len; ++n) 182 if (buf_getc(b1, n) != buf_getc(b2, n)) { 183 match = 0; 184 break; 185 } 186 } 187 188 if (match == 1) { 189 if (uflag == 1 && !TAILQ_EMPTY(&(file->rf_locks))) { 190 if (!(flags & QUIET) && nflag == 0) { 191 printf("rcs -u%s %s\n", 192 rcsnum_tostr(rev, numb, sizeof(numb)), 193 fpath); 194 } 195 (void)rcs_lock_remove(file, locker, rev); 196 } 197 198 if (TAILQ_EMPTY(&(file->rf_locks))) { 199 if (!(flags & QUIET)) 200 printf("rm -f %s\n", fname); 201 202 if (nflag == 0) 203 (void)unlink(fname); 204 } 205 } 206 207 rcs_write(file); 208 if (flags & PRESERVETIME) 209 rcs_set_mtime(file, rcs_mtime); 210 211 out: 212 buf_free(b1); 213 buf_free(b2); 214 if (file != NULL) 215 rcs_close(file); 216 } 217