1 /* $OpenBSD: annotate.c,v 1.65 2015/11/05 09:48:21 nicm Exp $ */ 2 /* 3 * Copyright (c) 2007 Tobias Stoeckmann <tobias@openbsd.org> 4 * Copyright (c) 2006 Xavier Santolaria <xsa@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 <sys/types.h> 20 #include <sys/dirent.h> 21 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <time.h> 26 #include <unistd.h> 27 28 #include "cvs.h" 29 #include "remote.h" 30 31 void cvs_annotate_local(struct cvs_file *); 32 33 extern char *cvs_specified_tag; 34 35 static int force_head = 0; 36 37 struct cvs_cmd cvs_cmd_annotate = { 38 CVS_OP_ANNOTATE, CVS_USE_WDIR, "annotate", 39 { "ann", "blame" }, 40 "Show last revision where each line was modified", 41 "[-flR] [-D date | -r rev] [file ...]", 42 "D:flRr:", 43 NULL, 44 cvs_annotate 45 }; 46 47 struct cvs_cmd cvs_cmd_rannotate = { 48 CVS_OP_RANNOTATE, 0, "rannotate", 49 { "rann", "ra" }, 50 "Show last revision where each line was modified", 51 "[-flR] [-D date | -r rev] module ...", 52 "D:flRr:", 53 NULL, 54 cvs_annotate 55 }; 56 57 int 58 cvs_annotate(int argc, char **argv) 59 { 60 int ch, flags; 61 char *arg = "."; 62 char *dateflag = NULL; 63 struct cvs_recursion cr; 64 65 flags = CR_RECURSE_DIRS; 66 67 while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_ANNOTATE ? 68 cvs_cmd_annotate.cmd_opts : cvs_cmd_rannotate.cmd_opts)) != -1) { 69 switch (ch) { 70 case 'D': 71 dateflag = optarg; 72 if ((cvs_specified_date = date_parse(dateflag)) == -1) 73 fatal("invalid date: %s", dateflag); 74 break; 75 case 'f': 76 force_head = 1; 77 break; 78 case 'l': 79 flags &= ~CR_RECURSE_DIRS; 80 break; 81 case 'R': 82 flags |= CR_RECURSE_DIRS; 83 break; 84 case 'r': 85 cvs_specified_tag = optarg; 86 break; 87 default: 88 fatal("%s", cvs_cmdop == CVS_OP_ANNOTATE ? 89 cvs_cmd_annotate.cmd_synopsis : 90 cvs_cmd_rannotate.cmd_synopsis); 91 } 92 } 93 94 argc -= optind; 95 argv += optind; 96 97 if (cvs_cmdop == CVS_OP_RANNOTATE) { 98 if (argc == 0) 99 fatal("%s", cvs_cmd_rannotate.cmd_synopsis); 100 101 flags |= CR_REPO; 102 } 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 (dateflag != NULL) 112 cvs_client_send_request("Argument -D%s", dateflag); 113 114 if (force_head == 1) 115 cvs_client_send_request("Argument -f"); 116 117 if (!(flags & CR_RECURSE_DIRS)) 118 cvs_client_send_request("Argument -l"); 119 120 if (cvs_specified_tag != NULL) 121 cvs_client_send_request("Argument -r%s", 122 cvs_specified_tag); 123 } else { 124 if (cvs_cmdop == CVS_OP_RANNOTATE && 125 chdir(current_cvsroot->cr_dir) == -1) 126 fatal("cvs_annotate: %s", strerror(errno)); 127 128 cr.fileproc = cvs_annotate_local; 129 } 130 131 cr.flags = flags; 132 133 if (cvs_cmdop == CVS_OP_ANNOTATE || 134 current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 135 if (argc > 0) 136 cvs_file_run(argc, argv, &cr); 137 else 138 cvs_file_run(1, &arg, &cr); 139 } 140 141 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 142 cvs_client_send_files(argv, argc); 143 cvs_client_senddir("."); 144 145 cvs_client_send_request((cvs_cmdop == CVS_OP_RANNOTATE) ? 146 "rannotate" : "annotate"); 147 148 cvs_client_get_responses(); 149 } 150 151 return (0); 152 } 153 154 void 155 cvs_annotate_local(struct cvs_file *cf) 156 { 157 int i; 158 char date[10], rnum[13], *p; 159 RCSNUM *bnum, *rev; 160 struct rcs_line *line; 161 struct rcs_line **alines; 162 163 cvs_log(LP_TRACE, "cvs_annotate_local(%s)", cf->file_path); 164 165 cvs_file_classify(cf, cvs_directory_tag); 166 167 if (cf->file_rcs == NULL || cf->file_rcs->rf_head == NULL) 168 return; 169 170 if (cvs_specified_tag != NULL) { 171 if ((rev = rcs_translate_tag(cvs_specified_tag, 172 cf->file_rcs)) == NULL) { 173 if (!force_head) 174 /* Stick at weird GNU cvs, ignore error. */ 175 return; 176 177 /* -f is not allowed for unknown symbols */ 178 rev = rcsnum_parse(cvs_specified_tag); 179 if (rev == NULL) 180 fatal("no such tag %s", cvs_specified_tag); 181 rcsnum_free(rev); 182 rev = rcsnum_alloc(); 183 rcsnum_cpy(cf->file_rcs->rf_head, rev, 0); 184 } 185 186 /* 187 * If this is a revision in a branch, we have to go first 188 * from HEAD to branch, then down to 1.1. After that, take 189 * annotated branch and go up to branch revision. This must 190 * be done this way due to different handling of "a" and 191 * "d" in rcs file for annotation. 192 */ 193 if (!RCSNUM_ISBRANCHREV(rev)) { 194 bnum = rev; 195 } else { 196 bnum = rcsnum_alloc(); 197 rcsnum_cpy(rev, bnum, 2); 198 } 199 200 rcs_rev_getlines(cf->file_rcs, bnum, &alines); 201 202 /* 203 * Go into branch and receive annotations for branch revision, 204 * with inverted "a" and "d" meaning. 205 */ 206 if (bnum != rev) { 207 rcs_annotate_getlines(cf->file_rcs, rev, &alines); 208 rcsnum_free(bnum); 209 } 210 rcsnum_free(rev); 211 } else { 212 rcs_rev_getlines(cf->file_rcs, (cvs_specified_date != -1 || 213 cvs_directory_date != -1) ? cf->file_rcsrev : 214 cf->file_rcs->rf_head, &alines); 215 } 216 217 /* Stick at weird GNU cvs, ignore error. */ 218 if (alines == NULL) 219 return; 220 221 cvs_log(LP_RCS, "Annotations for %s", cf->file_path); 222 cvs_log(LP_RCS, "***************"); 223 224 for (i = 0; alines[i] != NULL; i++) { 225 line = alines[i]; 226 227 rcsnum_tostr(line->l_delta->rd_num, rnum, sizeof(rnum)); 228 strftime(date, sizeof(date), "%d-%b-%y", 229 &(line->l_delta->rd_date)); 230 if (line->l_len && line->l_line[line->l_len - 1] == '\n') 231 line->l_line[line->l_len - 1] = '\0'; 232 else { 233 p = xmalloc(line->l_len + 1); 234 memcpy(p, line->l_line, line->l_len); 235 p[line->l_len] = '\0'; 236 237 if (line->l_needsfree) 238 free(line->l_line); 239 line->l_line = p; 240 line->l_len++; 241 line->l_needsfree = 1; 242 } 243 cvs_printf("%-12.12s (%-8.8s %s): %s\n", rnum, 244 line->l_delta->rd_author, date, line->l_line); 245 246 if (line->l_needsfree) 247 free(line->l_line); 248 free(line); 249 } 250 251 free(alines); 252 } 253