1*f331ff59Stobias /* $OpenBSD: annotate.c,v 1.45 2008/01/31 10:15:05 tobias Exp $ */ 2e0d97c9bSxsa /* 35e4c4390Stobias * Copyright (c) 2007 Tobias Stoeckmann <tobias@openbsd.org> 4efaa2accStobias * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org> 5e0d97c9bSxsa * 6e0d97c9bSxsa * Permission to use, copy, modify, and distribute this software for any 7e0d97c9bSxsa * purpose with or without fee is hereby granted, provided that the above 8e0d97c9bSxsa * copyright notice and this permission notice appear in all copies. 9e0d97c9bSxsa * 10e0d97c9bSxsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11e0d97c9bSxsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12e0d97c9bSxsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13e0d97c9bSxsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14e0d97c9bSxsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15e0d97c9bSxsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16e0d97c9bSxsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17e0d97c9bSxsa */ 18e0d97c9bSxsa 191f8531bdSotto #include <sys/param.h> 201f8531bdSotto #include <sys/dirent.h> 215e4c4390Stobias 225e4c4390Stobias #include <stdlib.h> 235e4c4390Stobias #include <string.h> 245e4c4390Stobias #include <time.h> 251f8531bdSotto #include <unistd.h> 26e0d97c9bSxsa 27e0d97c9bSxsa #include "cvs.h" 28e0d97c9bSxsa #include "remote.h" 29e0d97c9bSxsa 30e0d97c9bSxsa void cvs_annotate_local(struct cvs_file *); 31e0d97c9bSxsa 3237cbc181Stobias extern char *cvs_specified_tag; 3337cbc181Stobias 34e0d97c9bSxsa static int force_head = 0; 35e0d97c9bSxsa 36e0d97c9bSxsa struct cvs_cmd cvs_cmd_annotate = { 37*f331ff59Stobias CVS_OP_ANNOTATE, CVS_USE_WDIR, "annotate", 38e0d97c9bSxsa { "ann", "blame" }, 39e0d97c9bSxsa "Show last revision where each line was modified", 40e0d97c9bSxsa "[-flR] [-D date | -r rev] [file ...]", 41e0d97c9bSxsa "D:flRr:", 42e0d97c9bSxsa NULL, 43e0d97c9bSxsa cvs_annotate 44e0d97c9bSxsa }; 45e0d97c9bSxsa 46e0d97c9bSxsa int 47e0d97c9bSxsa cvs_annotate(int argc, char **argv) 48e0d97c9bSxsa { 49e0d97c9bSxsa int ch, flags; 50e0d97c9bSxsa char *arg = "."; 51e0d97c9bSxsa struct cvs_recursion cr; 52e0d97c9bSxsa 53e0d97c9bSxsa flags = CR_RECURSE_DIRS; 54e0d97c9bSxsa 55e0d97c9bSxsa while ((ch = getopt(argc, argv, cvs_cmd_annotate.cmd_opts)) != -1) { 56e0d97c9bSxsa switch (ch) { 57e0d97c9bSxsa case 'D': 58e0d97c9bSxsa break; 59e0d97c9bSxsa case 'f': 60e0d97c9bSxsa force_head = 1; 61e0d97c9bSxsa break; 62e0d97c9bSxsa case 'l': 63e0d97c9bSxsa flags &= ~CR_RECURSE_DIRS; 64e0d97c9bSxsa break; 65e0d97c9bSxsa case 'R': 66bcf22459Stobias flags |= CR_RECURSE_DIRS; 67e0d97c9bSxsa break; 68e0d97c9bSxsa case 'r': 69efaa2accStobias cvs_specified_tag = optarg; 70e0d97c9bSxsa break; 71e0d97c9bSxsa default: 72e0d97c9bSxsa fatal("%s", cvs_cmd_annotate.cmd_synopsis); 73e0d97c9bSxsa } 74e0d97c9bSxsa } 75e0d97c9bSxsa 76e0d97c9bSxsa argc -= optind; 77e0d97c9bSxsa argv += optind; 78e0d97c9bSxsa 79e0d97c9bSxsa cr.enterdir = NULL; 80e0d97c9bSxsa cr.leavedir = NULL; 81e0d97c9bSxsa 82e0d97c9bSxsa if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 8380f6ca9bSjoris cvs_client_connect_to_server(); 84e0d97c9bSxsa cr.fileproc = cvs_client_sendfile; 85e0d97c9bSxsa 86e0d97c9bSxsa if (force_head == 1) 87e0d97c9bSxsa cvs_client_send_request("Argument -f"); 88e0d97c9bSxsa 89e0d97c9bSxsa if (!(flags & CR_RECURSE_DIRS)) 90e0d97c9bSxsa cvs_client_send_request("Argument -l"); 91e0d97c9bSxsa 92efaa2accStobias if (cvs_specified_tag != NULL) 93efaa2accStobias cvs_client_send_request("Argument -r%s", 94efaa2accStobias cvs_specified_tag); 95e0d97c9bSxsa } else { 96e0d97c9bSxsa cr.fileproc = cvs_annotate_local; 97e0d97c9bSxsa } 98e0d97c9bSxsa 99e0d97c9bSxsa cr.flags = flags; 100e0d97c9bSxsa 101e0d97c9bSxsa if (argc > 0) 102e0d97c9bSxsa cvs_file_run(argc, argv, &cr); 103e0d97c9bSxsa else 104e0d97c9bSxsa cvs_file_run(1, &arg, &cr); 105e0d97c9bSxsa 106e0d97c9bSxsa if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 107e0d97c9bSxsa cvs_client_send_files(argv, argc); 108e0d97c9bSxsa cvs_client_senddir("."); 109e0d97c9bSxsa cvs_client_send_request("annotate"); 110e0d97c9bSxsa cvs_client_get_responses(); 111e0d97c9bSxsa } 112e0d97c9bSxsa 113e0d97c9bSxsa return (0); 114e0d97c9bSxsa } 115e0d97c9bSxsa 116e0d97c9bSxsa void 117e0d97c9bSxsa cvs_annotate_local(struct cvs_file *cf) 118e0d97c9bSxsa { 1195e4c4390Stobias int i; 1205e4c4390Stobias char date[10], rnum[13], *p; 12137cbc181Stobias RCSNUM *bnum, *rev; 122efaa2accStobias struct cvs_line *line; 1235e4c4390Stobias struct cvs_line **alines; 1245e4c4390Stobias 125e0d97c9bSxsa cvs_log(LP_TRACE, "cvs_annotate_local(%s)", cf->file_path); 126e0d97c9bSxsa 12751ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag); 128e0d97c9bSxsa 1295e4c4390Stobias if (cf->file_status == FILE_UNKNOWN || cf->file_status == FILE_UNLINK || 1305e4c4390Stobias cf->file_type != CVS_FILE) 131e0d97c9bSxsa return; 132e0d97c9bSxsa 13337cbc181Stobias if (cvs_specified_tag != NULL) { 134f1f4c0cfStobias if ((rev = rcs_translate_tag(cvs_specified_tag, 135f1f4c0cfStobias cf->file_rcs)) == NULL) { 136f1f4c0cfStobias if (!force_head) 137f1f4c0cfStobias /* Stick at weird GNU cvs, ignore error. */ 138f1f4c0cfStobias return; 13937cbc181Stobias 140f1f4c0cfStobias rev = rcsnum_alloc(); 141f1f4c0cfStobias rcsnum_cpy(cf->file_rcs->rf_head, rev, 0); 142f1f4c0cfStobias } 1435e4c4390Stobias 14437cbc181Stobias /* 14537cbc181Stobias * If this is a revision in a branch, we have to go first 14637cbc181Stobias * from HEAD to branch, then down to 1.1. After that, take 14737cbc181Stobias * annotated branch and go up to branch revision. This must 14837cbc181Stobias * be done this way due to different handling of "a" and 14937cbc181Stobias * "d" in rcs file for annotation. 15037cbc181Stobias */ 15137cbc181Stobias if (!RCSNUM_ISBRANCHREV(rev)) { 15237cbc181Stobias bnum = rev; 15337cbc181Stobias } else { 15437cbc181Stobias bnum = rcsnum_alloc(); 15537cbc181Stobias rcsnum_cpy(rev, bnum, 2); 1565e4c4390Stobias } 15737cbc181Stobias 15837cbc181Stobias rcs_rev_getlines(cf->file_rcs, bnum, &alines); 15937cbc181Stobias 16037cbc181Stobias /* 16137cbc181Stobias * Go into branch and receive annotations for branch revision, 16237cbc181Stobias * with inverted "a" and "d" meaning. 16337cbc181Stobias */ 16437cbc181Stobias if (bnum != rev) { 16537cbc181Stobias rcs_annotate_getlines(cf->file_rcs, rev, &alines); 16637cbc181Stobias rcsnum_free(bnum); 1675e4c4390Stobias } 168efaa2accStobias rcsnum_free(rev); 16937cbc181Stobias } else { 17037cbc181Stobias rcs_rev_getlines(cf->file_rcs, cf->file_rcs->rf_head, &alines); 1715e4c4390Stobias } 1725e4c4390Stobias 1735e4c4390Stobias /* Stick at weird GNU cvs, ignore error. */ 1745e4c4390Stobias if (alines == NULL) 1755e4c4390Stobias return; 1765e4c4390Stobias 1775e4c4390Stobias cvs_log(LP_RCS, "Annotations for %s", cf->file_path); 1785e4c4390Stobias cvs_log(LP_RCS, "***************"); 1795e4c4390Stobias 1805e4c4390Stobias for (i = 0; alines[i] != NULL; i++) { 181efaa2accStobias line = alines[i]; 1825e4c4390Stobias 183efaa2accStobias rcsnum_tostr(line->l_delta->rd_num, rnum, sizeof(rnum)); 184efaa2accStobias strftime(date, sizeof(date), "%d-%b-%y", 185efaa2accStobias &(line->l_delta->rd_date)); 186efaa2accStobias if (line->l_len && line->l_line[line->l_len - 1] == '\n') 187efaa2accStobias line->l_line[line->l_len - 1] = '\0'; 188efaa2accStobias else { 189efaa2accStobias p = xmalloc(line->l_len + 1); 190efaa2accStobias memcpy(p, line->l_line, line->l_len); 191efaa2accStobias p[line->l_len] = '\0'; 192efaa2accStobias 193efaa2accStobias if (line->l_needsfree) 194efaa2accStobias xfree(line->l_line); 195efaa2accStobias line->l_line = p; 196efaa2accStobias line->l_len++; 197efaa2accStobias line->l_needsfree = 1; 1985e4c4390Stobias } 1995e4c4390Stobias cvs_printf("%-12.12s (%-8.8s %s): %s\n", rnum, 200efaa2accStobias line->l_delta->rd_author, date, line->l_line); 2015e4c4390Stobias 202efaa2accStobias if (line->l_needsfree) 203efaa2accStobias xfree(line->l_line); 204efaa2accStobias xfree(line); 2055e4c4390Stobias } 2065e4c4390Stobias 2075e4c4390Stobias xfree(alines); 208e0d97c9bSxsa } 209