1 /* $OpenBSD: rlog.c,v 1.55 2006/05/27 05:49:14 ray Exp $ */ 2 /* 3 * Copyright (c) 2005 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "includes.h" 29 30 #include "rcsprog.h" 31 #include "diff.h" 32 33 static void rlog_file(const char *, RCSFILE *); 34 static void rlog_rev_print(struct rcs_delta *); 35 36 #define RLOG_OPTSTRING "hLl::NqRr::s:TtVw::x::z::" 37 #define REVSEP "----------------------------" 38 #define REVEND \ 39 "=============================================================================" 40 41 static int hflag, Lflag, lflag, rflag, tflag, Nflag, wflag; 42 static char *llist = NULL; 43 static char *slist = NULL; 44 static char *wlist = NULL; 45 static char *revisions = NULL; 46 47 void 48 rlog_usage(void) 49 { 50 fprintf(stderr, 51 "usage: rlog [-bhLNqRtV] [-ddates] [-l[lockers]] [-r[revs]]\n" 52 " [-sstates] [-w[logins]] [-xsuffixes]\n" 53 " [-ztz] file ...\n"); 54 } 55 56 int 57 rlog_main(int argc, char **argv) 58 { 59 RCSFILE *file; 60 int Rflag; 61 int i, ch, fd; 62 char fpath[MAXPATHLEN]; 63 64 rcsnum_flags |= RCSNUM_NO_MAGIC; 65 hflag = Rflag = rflag = 0; 66 while ((ch = rcs_getopt(argc, argv, RLOG_OPTSTRING)) != -1) { 67 switch (ch) { 68 case 'h': 69 hflag = 1; 70 break; 71 case 'L': 72 Lflag = 1; 73 break; 74 case 'l': 75 lflag = 1; 76 llist = rcs_optarg; 77 break; 78 case 'N': 79 Nflag = 1; 80 break; 81 case 'q': 82 /* 83 * kept for compatibility 84 */ 85 break; 86 case 'R': 87 Rflag = 1; 88 break; 89 case 'r': 90 rflag = 1; 91 revisions = rcs_optarg; 92 break; 93 case 's': 94 slist = rcs_optarg; 95 break; 96 case 'T': 97 /* 98 * kept for compatibility 99 */ 100 break; 101 case 't': 102 tflag = 1; 103 break; 104 case 'V': 105 printf("%s\n", rcs_version); 106 exit(0); 107 case 'w': 108 wflag = 1; 109 wlist = rcs_optarg; 110 break; 111 case 'x': 112 /* Use blank extension if none given. */ 113 rcs_suffixes = rcs_optarg ? rcs_optarg : ""; 114 break; 115 case 'z': 116 timezone_flag = rcs_optarg; 117 break; 118 default: 119 (usage()); 120 exit(1); 121 } 122 } 123 124 argc -= rcs_optind; 125 argv += rcs_optind; 126 127 if (argc == 0) { 128 warnx("no input file"); 129 (usage)(); 130 exit(1); 131 } 132 133 if (hflag == 1 && tflag == 1) { 134 warnx("warning: -t overrides -h."); 135 hflag = 0; 136 } 137 138 for (i = 0; i < argc; i++) { 139 fd = rcs_choosefile(argv[i], fpath, sizeof(fpath)); 140 if (fd < 0) { 141 warnx("%s", fpath); 142 continue; 143 } 144 145 if ((file = rcs_open(fpath, fd, 146 RCS_READ|RCS_PARSE_FULLY)) == NULL) 147 continue; 148 149 if (Lflag == 1 && TAILQ_EMPTY(&(file->rf_locks))) { 150 rcs_close(file); 151 continue; 152 } 153 154 if (Rflag == 1) { 155 printf("%s\n", fpath); 156 rcs_close(file); 157 continue; 158 } 159 160 rlog_file(argv[i], file); 161 162 rcs_close(file); 163 } 164 165 return (0); 166 } 167 168 static void 169 rlog_file(const char *fname, RCSFILE *file) 170 { 171 char numb[64]; 172 u_int nrev; 173 struct rcs_sym *sym; 174 struct rcs_access *acp; 175 struct rcs_delta *rdp; 176 struct rcs_lock *lkp; 177 char *workfile, *p; 178 179 if (rflag == 1) 180 nrev = rcs_rev_select(file, revisions); 181 else 182 nrev = file->rf_ndelta; 183 184 if ((workfile = basename(fname)) == NULL) 185 err(1, "basename"); 186 187 /* 188 * In case they specified 'foo,v' as argument. 189 */ 190 if ((p = strrchr(workfile, ',')) != NULL) 191 *p = '\0'; 192 193 printf("\nRCS file: %s", file->rf_path); 194 printf("\nWorking file: %s", workfile); 195 printf("\nhead:"); 196 if (file->rf_head != NULL) 197 printf(" %s", rcsnum_tostr(file->rf_head, numb, sizeof(numb))); 198 199 printf("\nbranch:"); 200 if (rcs_branch_get(file) != NULL) { 201 printf(" %s", rcsnum_tostr(rcs_branch_get(file), 202 numb, sizeof(numb))); 203 } 204 205 printf("\nlocks: %s", (file->rf_flags & RCS_SLOCK) ? "strict" : ""); 206 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) 207 printf("\n\t%s: %s", lkp->rl_name, 208 rcsnum_tostr(lkp->rl_num, numb, sizeof(numb))); 209 printf("\naccess list:\n"); 210 TAILQ_FOREACH(acp, &(file->rf_access), ra_list) 211 printf("\t%s\n", acp->ra_name); 212 213 if (Nflag == 0) { 214 printf("symbolic names:\n"); 215 TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) { 216 printf("\t%s: %s\n", sym->rs_name, 217 rcsnum_tostr(sym->rs_num, numb, sizeof(numb))); 218 } 219 } 220 221 printf("keyword substitution: %s\n", 222 file->rf_expand == NULL ? "kv" : file->rf_expand); 223 224 printf("total revisions: %u", file->rf_ndelta); 225 226 if (file->rf_head != NULL && hflag == 0 && tflag == 0) 227 printf(";\tselected revisions: %u", nrev); 228 229 printf("\n"); 230 231 232 if (hflag == 0 || tflag == 1) 233 printf("description:\n%s", file->rf_desc); 234 235 if (hflag == 0 && tflag == 0 && 236 !(lflag == 1 && TAILQ_EMPTY(&file->rf_locks))) { 237 TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) { 238 /* 239 * if selections are enabled verify that entry is 240 * selected. 241 */ 242 if (rflag == 0 || (rdp->rd_flags & RCS_RD_SELECT)) 243 rlog_rev_print(rdp); 244 } 245 } 246 247 printf("%s\n", REVEND); 248 } 249 250 static void 251 rlog_rev_print(struct rcs_delta *rdp) 252 { 253 int i, found; 254 struct tm t; 255 char *author, numb[64], *fmt, timeb[64]; 256 struct rcs_argvector *largv, *sargv, *wargv; 257 258 i = found = 0; 259 author = NULL; 260 261 /* -l[lockers] */ 262 if (lflag == 1) { 263 if (rdp->rd_locker != NULL) 264 found++; 265 266 if (llist != NULL) { 267 /* if locker is empty, no need to go further. */ 268 if (rdp->rd_locker == NULL) 269 return; 270 largv = rcs_strsplit(llist, ","); 271 for (i = 0; largv->argv[i] != NULL; i++) { 272 if (strcmp(rdp->rd_locker, largv->argv[i]) 273 == 0) { 274 found++; 275 break; 276 } 277 found = 0; 278 } 279 rcs_argv_destroy(largv); 280 } 281 } 282 283 /* -sstates */ 284 if (slist != NULL) { 285 sargv = rcs_strsplit(slist, ","); 286 for (i = 0; sargv->argv[i] != NULL; i++) { 287 if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) { 288 found++; 289 break; 290 } 291 found = 0; 292 } 293 rcs_argv_destroy(sargv); 294 } 295 296 /* -w[logins] */ 297 if (wflag == 1) { 298 if (wlist != NULL) { 299 wargv = rcs_strsplit(wlist, ","); 300 for (i = 0; wargv->argv[i] != NULL; i++) { 301 if (strcmp(rdp->rd_author, wargv->argv[i]) 302 == 0) { 303 found++; 304 break; 305 } 306 found = 0; 307 } 308 rcs_argv_destroy(wargv); 309 } else { 310 if ((author = getlogin()) == NULL) 311 err(1, "getlogin"); 312 313 if (strcmp(rdp->rd_author, author) == 0) 314 found++; 315 } 316 } 317 318 /* XXX dirty... */ 319 if ((((slist != NULL && wflag == 1) || 320 (slist != NULL && lflag == 1) || 321 (lflag == 1 && wflag == 1)) && found < 2) || 322 (((slist != NULL && lflag == 1 && wflag == 1) || 323 (slist != NULL || lflag == 1 || wflag == 1)) && found == 0)) 324 return; 325 326 printf("%s\n", REVSEP); 327 328 rcsnum_tostr(rdp->rd_num, numb, sizeof(numb)); 329 330 printf("revision %s", numb); 331 if (rdp->rd_locker != NULL) 332 printf("\tlocked by: %s;", rdp->rd_locker); 333 334 if (timezone_flag != NULL) { 335 rcs_set_tz(timezone_flag, rdp, &t); 336 fmt = "%Y-%m-%d %H:%M:%S%z"; 337 } else { 338 t = rdp->rd_date; 339 fmt = "%Y/%m/%d %H:%M:%S"; 340 } 341 342 strftime(timeb, sizeof(timeb), fmt, &t); 343 344 printf("\ndate: %s; author: %s; state: %s;\n", timeb, rdp->rd_author, 345 rdp->rd_state); 346 347 printf("%s", rdp->rd_log); 348 } 349