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