1 /* $OpenBSD: getlog.c,v 1.69 2007/01/11 02:35:55 joris Exp $ */ 2 /* 3 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> 4 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "includes.h" 20 21 #include "cvs.h" 22 #include "diff.h" 23 #include "log.h" 24 #include "remote.h" 25 26 #define LOG_REVSEP \ 27 "----------------------------" 28 29 #define LOG_REVEND \ 30 "=============================================================================" 31 32 #define L_HEAD 0x01 33 #define L_HEAD_DESCR 0x02 34 #define L_NAME 0x04 35 #define L_NOTAGS 0x08 36 #define L_LOGINS 0x10 37 #define L_STATES 0x20 38 39 void cvs_log_local(struct cvs_file *); 40 41 static void log_rev_print(struct rcs_delta *); 42 43 int runflags = 0; 44 char *logrev = NULL; 45 char *slist = NULL; 46 char *wlist = NULL; 47 48 struct cvs_cmd cvs_cmd_log = { 49 CVS_OP_LOG, 0, "log", 50 { "lo" }, 51 "Print out history information for files", 52 "[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]", 53 "bd:hlNRr:s:tw:", 54 NULL, 55 cvs_getlog 56 }; 57 58 int 59 cvs_getlog(int argc, char **argv) 60 { 61 int ch; 62 int flags; 63 char *arg = "."; 64 struct cvs_recursion cr; 65 66 rcsnum_flags |= RCSNUM_NO_MAGIC; 67 flags = CR_RECURSE_DIRS; 68 69 while ((ch = getopt(argc, argv, cvs_cmd_log.cmd_opts)) != -1) { 70 switch (ch) { 71 case 'h': 72 runflags |= L_HEAD; 73 break; 74 case 'l': 75 flags &= ~CR_RECURSE_DIRS; 76 break; 77 case 'N': 78 runflags |= L_NOTAGS; 79 break; 80 case 'R': 81 runflags |= L_NAME; 82 case 'r': 83 logrev = optarg; 84 break; 85 case 's': 86 runflags |= L_STATES; 87 slist = optarg; 88 break; 89 case 't': 90 runflags |= L_HEAD_DESCR; 91 break; 92 case 'w': 93 runflags |= L_LOGINS; 94 wlist = optarg; 95 break; 96 default: 97 fatal("%s", cvs_cmd_log.cmd_synopsis); 98 } 99 } 100 101 argc -= optind; 102 argv += optind; 103 104 cr.enterdir = NULL; 105 cr.leavedir = NULL; 106 107 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 108 cvs_client_connect_to_server(); 109 cr.fileproc = cvs_client_sendfile; 110 111 if (runflags & L_HEAD) 112 cvs_client_send_request("Argument -h"); 113 114 if (!(flags & CR_RECURSE_DIRS)) 115 cvs_client_send_request("Argument -l"); 116 117 if (runflags & L_NOTAGS) 118 cvs_client_send_request("Argument -N"); 119 120 if (runflags & L_NAME) 121 cvs_client_send_request("Argument -R"); 122 123 if (logrev != NULL) 124 cvs_client_send_request("Argument -r%s", logrev); 125 126 if (runflags & L_STATES) 127 cvs_client_send_request("Argument -s%s", slist); 128 129 if (runflags & L_HEAD_DESCR) 130 cvs_client_send_request("Argument -t"); 131 132 if (runflags & L_LOGINS) 133 cvs_client_send_request("Argument -w%s", wlist); 134 } else { 135 cr.fileproc = cvs_log_local; 136 } 137 138 cr.flags = flags; 139 140 if (argc > 0) 141 cvs_file_run(argc, argv, &cr); 142 else 143 cvs_file_run(1, &arg, &cr); 144 145 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 146 cvs_client_send_files(argv, argc); 147 cvs_client_senddir("."); 148 cvs_client_send_request("log"); 149 cvs_client_get_responses(); 150 } 151 152 return (0); 153 } 154 155 void 156 cvs_log_local(struct cvs_file *cf) 157 { 158 u_int nrev; 159 struct rcs_sym *sym; 160 struct rcs_lock *lkp; 161 struct rcs_delta *rdp; 162 struct rcs_access *acp; 163 char numb[32]; 164 165 cvs_log(LP_TRACE, "cvs_log_local(%s)", cf->file_path); 166 167 cvs_file_classify(cf, NULL, 0); 168 169 if (cf->file_status == FILE_UNKNOWN) { 170 if (verbosity > 0) 171 cvs_log(LP_ERR, "nothing known about %s", 172 cf->file_path); 173 return; 174 } else if (cf->file_status == FILE_ADDED) { 175 if (verbosity > 0) 176 cvs_log(LP_ERR, "%s has been added, but not committed", 177 cf->file_path); 178 return; 179 } 180 181 if (cf->file_type == CVS_DIR) { 182 if (verbosity > 1) 183 cvs_log(LP_NOTICE, "Logging %s", cf->file_path); 184 return; 185 } 186 187 if (runflags & L_NAME) { 188 cvs_printf("%s\n", cf->file_rpath); 189 return; 190 } 191 192 cvs_printf("\nRCS file: %s", cf->file_rpath); 193 cvs_printf("\nWorking file: %s", cf->file_path); 194 cvs_printf("\nhead:"); 195 if (cf->file_rcs->rf_head != NULL) 196 cvs_printf(" %s", rcsnum_tostr(cf->file_rcs->rf_head, 197 numb, sizeof(numb))); 198 199 cvs_printf("\nbranch:"); 200 if (rcs_branch_get(cf->file_rcs) != NULL) { 201 cvs_printf(" %s", rcsnum_tostr(rcs_branch_get(cf->file_rcs), 202 numb, sizeof(numb))); 203 } 204 205 cvs_printf("\nlocks: %s", (cf->file_rcs->rf_flags & RCS_SLOCK) 206 ? "strict" : ""); 207 TAILQ_FOREACH(lkp, &(cf->file_rcs->rf_locks), rl_list) 208 cvs_printf("\n\t%s: %s", lkp->rl_name, 209 rcsnum_tostr(lkp->rl_num, numb, sizeof(numb))); 210 211 cvs_printf("\naccess list:\n"); 212 TAILQ_FOREACH(acp, &(cf->file_rcs->rf_access), ra_list) 213 cvs_printf("\t%s\n", acp->ra_name); 214 215 if (!(runflags & L_NOTAGS)) { 216 cvs_printf("symbolic names:\n"); 217 TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list) { 218 cvs_printf("\t%s: %s\n", sym->rs_name, 219 rcsnum_tostr(sym->rs_num, numb, sizeof(numb))); 220 } 221 } 222 223 cvs_printf("keyword substitution: %s\n", 224 cf->file_rcs->rf_expand == NULL ? "kv" : cf->file_rcs->rf_expand); 225 226 cvs_printf("total revisions: %u", cf->file_rcs->rf_ndelta); 227 228 if (logrev != NULL) 229 nrev = cvs_revision_select(cf->file_rcs, logrev); 230 else 231 nrev = cf->file_rcs->rf_ndelta; 232 233 if (cf->file_rcs->rf_head != NULL && 234 !(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR)) 235 cvs_printf(";\tselected revisions: %u", nrev); 236 237 cvs_printf("\n"); 238 239 if (!(runflags & L_HEAD) || (runflags & L_HEAD_DESCR)) 240 cvs_printf("description:\n%s", cf->file_rcs->rf_desc); 241 242 if (!(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR)) { 243 TAILQ_FOREACH(rdp, &(cf->file_rcs->rf_delta), rd_list) { 244 /* 245 * if selections are enabled verify that entry is 246 * selected. 247 */ 248 if (logrev == NULL || (rdp->rd_flags & RCS_RD_SELECT)) 249 log_rev_print(rdp); 250 } 251 } 252 253 cvs_printf("%s\n", LOG_REVEND); 254 } 255 256 static void 257 log_rev_print(struct rcs_delta *rdp) 258 { 259 int i, found; 260 char numb[32], timeb[32]; 261 struct cvs_argvector *sargv, *wargv; 262 263 i = found = 0; 264 265 /* -s states */ 266 if (runflags & L_STATES) { 267 sargv = cvs_strsplit(slist, ","); 268 for (i = 0; sargv->argv[i] != NULL; i++) { 269 if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) { 270 found++; 271 break; 272 } 273 found = 0; 274 } 275 cvs_argv_destroy(sargv); 276 } 277 278 /* -w[logins] */ 279 if (runflags & L_LOGINS) { 280 wargv = cvs_strsplit(wlist, ","); 281 for (i = 0; wargv->argv[i] != NULL; i++) { 282 if (strcmp(rdp->rd_author, wargv->argv[i]) == 0) { 283 found++; 284 break; 285 } 286 found = 0; 287 } 288 cvs_argv_destroy(wargv); 289 } 290 291 if ((runflags & (L_STATES|L_LOGINS)) && found == 0) 292 return; 293 294 cvs_printf("%s\n", LOG_REVSEP); 295 296 rcsnum_tostr(rdp->rd_num, numb, sizeof(numb)); 297 cvs_printf("revision %s", numb); 298 299 strftime(timeb, sizeof(timeb), "%Y/%m/%d %H:%M:%S", &rdp->rd_date); 300 cvs_printf("\ndate: %s; author: %s; state: %s;\n", 301 timeb, rdp->rd_author, rdp->rd_state); 302 cvs_printf("%s", rdp->rd_log); 303 } 304