Annotation of src/usr.bin/rcs/rlog.c, Revision 1.61
1.61 ! xsa 1: /* $OpenBSD: rlog.c,v 1.60 2008/02/02 16:21:38 xsa Exp $ */
1.1 joris 2: /*
3: * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
1.20 xsa 4: * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
1.1 joris 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:
1.57 xsa 28: #include <err.h>
29: #include <libgen.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
33: #include <unistd.h>
1.1 joris 34:
1.19 xsa 35: #include "rcsprog.h"
1.3 niallo 36: #include "diff.h"
1.1 joris 37:
1.44 ray 38: static void rlog_file(const char *, RCSFILE *);
1.32 xsa 39: static void rlog_rev_print(struct rcs_delta *);
1.1 joris 40:
1.38 xsa 41: #define RLOG_OPTSTRING "hLl::NqRr::s:TtVw::x::z::"
1.1 joris 42: #define REVSEP "----------------------------"
43: #define REVEND \
1.6 xsa 44: "============================================================================="
1.1 joris 45:
1.36 ray 46: static int hflag, Lflag, lflag, rflag, tflag, Nflag, wflag;
1.23 xsa 47: static char *llist = NULL;
1.20 xsa 48: static char *slist = NULL;
49: static char *wlist = NULL;
1.36 ray 50: static char *revisions = NULL;
1.1 joris 51:
1.21 xsa 52: void
53: rlog_usage(void)
54: {
55: fprintf(stderr,
1.59 ray 56: "usage: rlog [-bhLNRtV] [-ddates] [-l[lockers]] [-r[revs]]\n"
1.28 xsa 57: " [-sstates] [-w[logins]] [-xsuffixes]\n"
58: " [-ztz] file ...\n");
1.21 xsa 59: }
60:
1.1 joris 61: int
62: rlog_main(int argc, char **argv)
63: {
1.44 ray 64: RCSFILE *file;
1.1 joris 65: int Rflag;
1.59 ray 66: int i, ch, fd, status;
1.1 joris 67: char fpath[MAXPATHLEN];
68:
1.45 joris 69: rcsnum_flags |= RCSNUM_NO_MAGIC;
1.59 ray 70: hflag = Rflag = rflag = status = 0;
1.36 ray 71: while ((ch = rcs_getopt(argc, argv, RLOG_OPTSTRING)) != -1) {
1.1 joris 72: switch (ch) {
73: case 'h':
74: hflag = 1;
75: break;
1.11 xsa 76: case 'L':
77: Lflag = 1;
78: break;
1.23 xsa 79: case 'l':
80: lflag = 1;
81: llist = rcs_optarg;
82: break;
1.1 joris 83: case 'N':
84: Nflag = 1;
85: break;
86: case 'q':
1.47 xsa 87: /*
88: * kept for compatibility
89: */
1.1 joris 90: break;
1.37 xsa 91: case 'R':
92: Rflag = 1;
93: break;
1.36 ray 94: case 'r':
95: rflag = 1;
96: revisions = rcs_optarg;
97: break;
1.20 xsa 98: case 's':
99: slist = rcs_optarg;
100: break;
1.8 xsa 101: case 'T':
102: /*
103: * kept for compatibility
104: */
105: break;
1.1 joris 106: case 't':
107: tflag = 1;
108: break;
109: case 'V':
110: printf("%s\n", rcs_version);
111: exit(0);
1.20 xsa 112: case 'w':
113: wflag = 1;
114: wlist = rcs_optarg;
115: break;
1.14 xsa 116: case 'x':
1.33 ray 117: /* Use blank extension if none given. */
118: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.14 xsa 119: break;
1.26 joris 120: case 'z':
121: timezone_flag = rcs_optarg;
1.27 xsa 122: break;
1.1 joris 123: default:
1.21 xsa 124: (usage());
125: exit(1);
1.1 joris 126: }
127: }
128:
1.5 joris 129: argc -= rcs_optind;
130: argv += rcs_optind;
1.1 joris 131:
132: if (argc == 0) {
1.48 xsa 133: warnx("no input file");
1.1 joris 134: (usage)();
135: exit(1);
136: }
137:
1.41 deraadt 138: if (hflag == 1 && tflag == 1) {
1.48 xsa 139: warnx("warning: -t overrides -h.");
1.7 xsa 140: hflag = 0;
141: }
1.6 xsa 142:
1.1 joris 143: for (i = 0; i < argc; i++) {
1.55 ray 144: fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
145: if (fd < 0) {
1.56 niallo 146: warn("%s", fpath);
1.59 ray 147: status = 1;
1.1 joris 148: continue;
1.55 ray 149: }
1.1 joris 150:
1.52 joris 151: if ((file = rcs_open(fpath, fd,
1.59 ray 152: RCS_READ|RCS_PARSE_FULLY)) == NULL) {
153: status = 1;
1.11 xsa 154: continue;
1.59 ray 155: }
1.11 xsa 156:
1.41 deraadt 157: if (Lflag == 1 && TAILQ_EMPTY(&(file->rf_locks))) {
1.11 xsa 158: rcs_close(file);
159: continue;
160: }
161:
1.9 xsa 162: if (Rflag == 1) {
163: printf("%s\n", fpath);
1.11 xsa 164: rcs_close(file);
1.9 xsa 165: continue;
166: }
167:
1.44 ray 168: rlog_file(argv[i], file);
1.9 xsa 169:
1.1 joris 170: rcs_close(file);
171: }
172:
1.59 ray 173: return (status);
1.1 joris 174: }
175:
1.32 xsa 176: static void
1.44 ray 177: rlog_file(const char *fname, RCSFILE *file)
1.1 joris 178: {
1.58 xsa 179: char numb[RCS_REV_BUFSZ];
1.36 ray 180: u_int nrev;
1.1 joris 181: struct rcs_sym *sym;
182: struct rcs_access *acp;
1.20 xsa 183: struct rcs_delta *rdp;
1.10 xsa 184: struct rcs_lock *lkp;
1.45 joris 185: char *workfile, *p;
1.1 joris 186:
1.36 ray 187: if (rflag == 1)
1.46 xsa 188: nrev = rcs_rev_select(file, revisions);
1.36 ray 189: else
190: nrev = file->rf_ndelta;
191:
1.45 joris 192: if ((workfile = basename(fname)) == NULL)
1.50 xsa 193: err(1, "basename");
1.45 joris 194:
195: /*
196: * In case they specified 'foo,v' as argument.
197: */
198: if ((p = strrchr(workfile, ',')) != NULL)
199: *p = '\0';
200:
1.43 ray 201: printf("\nRCS file: %s", file->rf_path);
1.45 joris 202: printf("\nWorking file: %s", workfile);
1.1 joris 203: printf("\nhead:");
204: if (file->rf_head != NULL)
205: printf(" %s", rcsnum_tostr(file->rf_head, numb, sizeof(numb)));
206:
207: printf("\nbranch:");
208: if (rcs_branch_get(file) != NULL) {
209: printf(" %s", rcsnum_tostr(rcs_branch_get(file),
210: numb, sizeof(numb)));
211: }
212:
213: printf("\nlocks: %s", (file->rf_flags & RCS_SLOCK) ? "strict" : "");
1.10 xsa 214: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)
215: printf("\n\t%s: %s", lkp->rl_name,
216: rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
1.1 joris 217: printf("\naccess list:\n");
218: TAILQ_FOREACH(acp, &(file->rf_access), ra_list)
219: printf("\t%s\n", acp->ra_name);
220:
221: if (Nflag == 0) {
222: printf("symbolic names:\n");
223: TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) {
224: printf("\t%s: %s\n", sym->rs_name,
225: rcsnum_tostr(sym->rs_num, numb, sizeof(numb)));
226: }
227: }
228:
229: printf("keyword substitution: %s\n",
230: file->rf_expand == NULL ? "kv" : file->rf_expand);
231:
1.20 xsa 232: printf("total revisions: %u", file->rf_ndelta);
233:
1.41 deraadt 234: if (file->rf_head != NULL && hflag == 0 && tflag == 0)
1.36 ray 235: printf(";\tselected revisions: %u", nrev);
1.20 xsa 236:
237: printf("\n");
238:
1.1 joris 239:
1.41 deraadt 240: if (hflag == 0 || tflag == 1)
1.13 xsa 241: printf("description:\n%s", file->rf_desc);
1.1 joris 242:
1.42 ray 243: if (hflag == 0 && tflag == 0 &&
244: !(lflag == 1 && TAILQ_EMPTY(&file->rf_locks))) {
1.36 ray 245: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
246: /*
247: * if selections are enabled verify that entry is
1.39 niallo 248: * selected.
1.36 ray 249: */
1.41 deraadt 250: if (rflag == 0 || (rdp->rd_flags & RCS_RD_SELECT))
1.36 ray 251: rlog_rev_print(rdp);
252: }
1.20 xsa 253: }
1.1 joris 254:
255: printf("%s\n", REVEND);
1.16 xsa 256: }
257:
258: static void
1.20 xsa 259: rlog_rev_print(struct rcs_delta *rdp)
1.16 xsa 260: {
1.20 xsa 261: int i, found;
1.40 joris 262: struct tm t;
1.58 xsa 263: char *author, numb[RCS_REV_BUFSZ], *fmt, timeb[RCS_TIME_BUFSZ];
1.51 joris 264: struct rcs_argvector *largv, *sargv, *wargv;
1.60 xsa 265: struct rcs_branch *rb;
266: struct rcs_delta *nrdp;
1.20 xsa 267:
268: i = found = 0;
269: author = NULL;
270:
1.23 xsa 271: /* -l[lockers] */
272: if (lflag == 1) {
1.42 ray 273: if (rdp->rd_locker != NULL)
274: found++;
1.23 xsa 275:
276: if (llist != NULL) {
277: /* if locker is empty, no need to go further. */
278: if (rdp->rd_locker == NULL)
279: return;
1.51 joris 280: largv = rcs_strsplit(llist, ",");
1.34 pat 281: for (i = 0; largv->argv[i] != NULL; i++) {
282: if (strcmp(rdp->rd_locker, largv->argv[i])
283: == 0) {
1.23 xsa 284: found++;
285: break;
286: }
287: found = 0;
288: }
1.51 joris 289: rcs_argv_destroy(largv);
1.23 xsa 290: }
291: }
1.40 joris 292:
1.20 xsa 293: /* -sstates */
294: if (slist != NULL) {
1.51 joris 295: sargv = rcs_strsplit(slist, ",");
1.34 pat 296: for (i = 0; sargv->argv[i] != NULL; i++) {
297: if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
1.20 xsa 298: found++;
299: break;
300: }
301: found = 0;
302: }
1.51 joris 303: rcs_argv_destroy(sargv);
1.20 xsa 304: }
1.40 joris 305:
1.20 xsa 306: /* -w[logins] */
307: if (wflag == 1) {
308: if (wlist != NULL) {
1.51 joris 309: wargv = rcs_strsplit(wlist, ",");
1.34 pat 310: for (i = 0; wargv->argv[i] != NULL; i++) {
311: if (strcmp(rdp->rd_author, wargv->argv[i])
312: == 0) {
1.20 xsa 313: found++;
314: break;
315: }
316: found = 0;
317: }
1.51 joris 318: rcs_argv_destroy(wargv);
1.20 xsa 319: } else {
320: if ((author = getlogin()) == NULL)
1.50 xsa 321: err(1, "getlogin");
1.16 xsa 322:
1.20 xsa 323: if (strcmp(rdp->rd_author, author) == 0)
324: found++;
325: }
326: }
1.16 xsa 327:
1.20 xsa 328: /* XXX dirty... */
1.41 deraadt 329: if ((((slist != NULL && wflag == 1) ||
330: (slist != NULL && lflag == 1) ||
331: (lflag == 1 && wflag == 1)) && found < 2) ||
332: (((slist != NULL && lflag == 1 && wflag == 1) ||
333: (slist != NULL || lflag == 1 || wflag == 1)) && found == 0))
1.20 xsa 334: return;
335:
336: printf("%s\n", REVSEP);
337:
338: rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
339:
1.22 xsa 340: printf("revision %s", numb);
341: if (rdp->rd_locker != NULL)
342: printf("\tlocked by: %s;", rdp->rd_locker);
1.40 joris 343:
344: if (timezone_flag != NULL) {
345: rcs_set_tz(timezone_flag, rdp, &t);
1.53 xsa 346: fmt = "%Y-%m-%d %H:%M:%S%z";
1.40 joris 347: } else {
348: t = rdp->rd_date;
349: fmt = "%Y/%m/%d %H:%M:%S";
350: }
351:
1.60 xsa 352: (void)strftime(timeb, sizeof(timeb), fmt, &t);
1.40 joris 353:
1.60 xsa 354: printf("\ndate: %s; author: %s; state: %s;", timeb, rdp->rd_author,
1.41 deraadt 355: rdp->rd_state);
1.60 xsa 356:
357: /*
358: * If we are a branch revision, the diff of this revision is stored
359: * in place.
360: * Otherwise, it is stored in the previous revision as a reversed diff.
361: */
362: if (RCSNUM_ISBRANCHREV(rdp->rd_num))
363: nrdp = rdp;
364: else
365: nrdp = TAILQ_NEXT(rdp, rd_list);
366:
367: /*
368: * We do not write diff stats for the first revision of the default
369: * branch, since it was not a diff but a full text.
370: */
371: if (nrdp != NULL && rdp->rd_num->rn_len == nrdp->rd_num->rn_len) {
372: int added, removed;
1.61 ! xsa 373:
1.60 xsa 374: rcs_delta_stats(nrdp, &added, &removed);
375: if (RCSNUM_ISBRANCHREV(rdp->rd_num))
376: printf(" lines: +%d -%d", added, removed);
377: else
378: printf(" lines: +%d -%d", removed, added);
1.61 ! xsa 379: }
! 380: printf("\n");
! 381:
! 382: if (!TAILQ_EMPTY(&(rdp->rd_branches))) {
! 383: printf("branches:");
! 384: TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) {
! 385: RCSNUM *branch;
! 386: branch = rcsnum_revtobr(rb->rb_num);
! 387: (void)rcsnum_tostr(branch, numb, sizeof(numb));
! 388: printf(" %s;", numb);
! 389: rcsnum_free(branch);
1.60 xsa 390: }
391: printf("\n");
1.61 ! xsa 392: }
1.40 joris 393:
1.20 xsa 394: printf("%s", rdp->rd_log);
1.1 joris 395: }