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