1*b92e16f1Sxsa /* $OpenBSD: annotate.c,v 1.48 2008/02/02 16:44:11 xsa 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 22e9658789Stobias #include <errno.h> 235e4c4390Stobias #include <stdlib.h> 245e4c4390Stobias #include <string.h> 255e4c4390Stobias #include <time.h> 261f8531bdSotto #include <unistd.h> 27e0d97c9bSxsa 28e0d97c9bSxsa #include "cvs.h" 29e0d97c9bSxsa #include "remote.h" 30e0d97c9bSxsa 31e0d97c9bSxsa void cvs_annotate_local(struct cvs_file *); 32e0d97c9bSxsa 3337cbc181Stobias extern char *cvs_specified_tag; 3437cbc181Stobias 35e0d97c9bSxsa static int force_head = 0; 36e0d97c9bSxsa 37e0d97c9bSxsa struct cvs_cmd cvs_cmd_annotate = { 38f331ff59Stobias CVS_OP_ANNOTATE, CVS_USE_WDIR, "annotate", 39e0d97c9bSxsa { "ann", "blame" }, 40e0d97c9bSxsa "Show last revision where each line was modified", 41e0d97c9bSxsa "[-flR] [-D date | -r rev] [file ...]", 42e0d97c9bSxsa "D:flRr:", 43e0d97c9bSxsa NULL, 44e0d97c9bSxsa cvs_annotate 45e0d97c9bSxsa }; 46e0d97c9bSxsa 47e9658789Stobias struct cvs_cmd cvs_cmd_rannotate = { 48e9658789Stobias CVS_OP_RANNOTATE, 0, "rannotate", 49e9658789Stobias { "rann", "ra" }, 50e9658789Stobias "Show last revision where each line was modified", 51*b92e16f1Sxsa "[-flR] [-D date | -r rev] module ...", 52e9658789Stobias "D:flRr:", 53e9658789Stobias NULL, 54e9658789Stobias cvs_annotate 55e9658789Stobias }; 56e9658789Stobias 57e0d97c9bSxsa int 58e0d97c9bSxsa cvs_annotate(int argc, char **argv) 59e0d97c9bSxsa { 60e0d97c9bSxsa int ch, flags; 61e0d97c9bSxsa char *arg = "."; 62e0d97c9bSxsa struct cvs_recursion cr; 63e0d97c9bSxsa 64e0d97c9bSxsa flags = CR_RECURSE_DIRS; 65e0d97c9bSxsa 66e0d97c9bSxsa while ((ch = getopt(argc, argv, cvs_cmd_annotate.cmd_opts)) != -1) { 67e0d97c9bSxsa switch (ch) { 68e0d97c9bSxsa case 'D': 69e0d97c9bSxsa break; 70e0d97c9bSxsa case 'f': 71e0d97c9bSxsa force_head = 1; 72e0d97c9bSxsa break; 73e0d97c9bSxsa case 'l': 74e0d97c9bSxsa flags &= ~CR_RECURSE_DIRS; 75e0d97c9bSxsa break; 76e0d97c9bSxsa case 'R': 77bcf22459Stobias flags |= CR_RECURSE_DIRS; 78e0d97c9bSxsa break; 79e0d97c9bSxsa case 'r': 80efaa2accStobias cvs_specified_tag = optarg; 81e0d97c9bSxsa break; 82e0d97c9bSxsa default: 83e0d97c9bSxsa fatal("%s", cvs_cmd_annotate.cmd_synopsis); 84e0d97c9bSxsa } 85e0d97c9bSxsa } 86e0d97c9bSxsa 87e0d97c9bSxsa argc -= optind; 88e0d97c9bSxsa argv += optind; 89e0d97c9bSxsa 90e9658789Stobias if (cvs_cmdop == CVS_OP_RANNOTATE) 91e9658789Stobias flags |= CR_REPO; 92e9658789Stobias 93e0d97c9bSxsa cr.enterdir = NULL; 94e0d97c9bSxsa cr.leavedir = NULL; 95e0d97c9bSxsa 96e0d97c9bSxsa if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 9780f6ca9bSjoris cvs_client_connect_to_server(); 98e0d97c9bSxsa cr.fileproc = cvs_client_sendfile; 99e0d97c9bSxsa 100e0d97c9bSxsa if (force_head == 1) 101e0d97c9bSxsa cvs_client_send_request("Argument -f"); 102e0d97c9bSxsa 103e0d97c9bSxsa if (!(flags & CR_RECURSE_DIRS)) 104e0d97c9bSxsa cvs_client_send_request("Argument -l"); 105e0d97c9bSxsa 106efaa2accStobias if (cvs_specified_tag != NULL) 107efaa2accStobias cvs_client_send_request("Argument -r%s", 108efaa2accStobias cvs_specified_tag); 109e0d97c9bSxsa } else { 110e9658789Stobias if (cvs_cmdop == CVS_OP_RANNOTATE && 111e9658789Stobias chdir(current_cvsroot->cr_dir) == -1) 112e9658789Stobias fatal("cvs_annotate: %s", strerror(errno)); 113e9658789Stobias 114e0d97c9bSxsa cr.fileproc = cvs_annotate_local; 115e0d97c9bSxsa } 116e0d97c9bSxsa 117e0d97c9bSxsa cr.flags = flags; 118e0d97c9bSxsa 119e9658789Stobias if (cvs_cmdop == CVS_OP_ANNOTATE || 120e9658789Stobias current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 121e0d97c9bSxsa if (argc > 0) 122e0d97c9bSxsa cvs_file_run(argc, argv, &cr); 123e0d97c9bSxsa else 124e0d97c9bSxsa cvs_file_run(1, &arg, &cr); 125e9658789Stobias } 126e0d97c9bSxsa 127e0d97c9bSxsa if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 128e0d97c9bSxsa cvs_client_send_files(argv, argc); 129e0d97c9bSxsa cvs_client_senddir("."); 130e9658789Stobias 131e9658789Stobias cvs_client_send_request((cvs_cmdop == CVS_OP_RANNOTATE) ? 132e9658789Stobias "rannotate" : "annotate"); 133e9658789Stobias 134e0d97c9bSxsa cvs_client_get_responses(); 135e0d97c9bSxsa } 136e0d97c9bSxsa 137e0d97c9bSxsa return (0); 138e0d97c9bSxsa } 139e0d97c9bSxsa 140e0d97c9bSxsa void 141e0d97c9bSxsa cvs_annotate_local(struct cvs_file *cf) 142e0d97c9bSxsa { 1435e4c4390Stobias int i; 1445e4c4390Stobias char date[10], rnum[13], *p; 14537cbc181Stobias RCSNUM *bnum, *rev; 146efaa2accStobias struct cvs_line *line; 1475e4c4390Stobias struct cvs_line **alines; 1485e4c4390Stobias 149e0d97c9bSxsa cvs_log(LP_TRACE, "cvs_annotate_local(%s)", cf->file_path); 150e0d97c9bSxsa 15151ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag); 152e0d97c9bSxsa 1535e4c4390Stobias if (cf->file_status == FILE_UNKNOWN || cf->file_status == FILE_UNLINK || 1545e4c4390Stobias cf->file_type != CVS_FILE) 155e0d97c9bSxsa return; 156e0d97c9bSxsa 15737cbc181Stobias if (cvs_specified_tag != NULL) { 158f1f4c0cfStobias if ((rev = rcs_translate_tag(cvs_specified_tag, 159f1f4c0cfStobias cf->file_rcs)) == NULL) { 160f1f4c0cfStobias if (!force_head) 161f1f4c0cfStobias /* Stick at weird GNU cvs, ignore error. */ 162f1f4c0cfStobias return; 16337cbc181Stobias 16411aca5deStobias /* -f is not allowed for unknown symbols */ 16511aca5deStobias rev = rcsnum_parse(cvs_specified_tag); 16611aca5deStobias if (rev == NULL) 16711aca5deStobias fatal("no such tag %s", cvs_specified_tag); 16811aca5deStobias 16911aca5deStobias rcsnum_free(rev); 170f1f4c0cfStobias rev = rcsnum_alloc(); 171f1f4c0cfStobias rcsnum_cpy(cf->file_rcs->rf_head, rev, 0); 172f1f4c0cfStobias } 1735e4c4390Stobias 17437cbc181Stobias /* 17537cbc181Stobias * If this is a revision in a branch, we have to go first 17637cbc181Stobias * from HEAD to branch, then down to 1.1. After that, take 17737cbc181Stobias * annotated branch and go up to branch revision. This must 17837cbc181Stobias * be done this way due to different handling of "a" and 17937cbc181Stobias * "d" in rcs file for annotation. 18037cbc181Stobias */ 18137cbc181Stobias if (!RCSNUM_ISBRANCHREV(rev)) { 18237cbc181Stobias bnum = rev; 18337cbc181Stobias } else { 18437cbc181Stobias bnum = rcsnum_alloc(); 18537cbc181Stobias rcsnum_cpy(rev, bnum, 2); 1865e4c4390Stobias } 18737cbc181Stobias 18837cbc181Stobias rcs_rev_getlines(cf->file_rcs, bnum, &alines); 18937cbc181Stobias 19037cbc181Stobias /* 19137cbc181Stobias * Go into branch and receive annotations for branch revision, 19237cbc181Stobias * with inverted "a" and "d" meaning. 19337cbc181Stobias */ 19437cbc181Stobias if (bnum != rev) { 19537cbc181Stobias rcs_annotate_getlines(cf->file_rcs, rev, &alines); 19637cbc181Stobias rcsnum_free(bnum); 1975e4c4390Stobias } 198efaa2accStobias rcsnum_free(rev); 19937cbc181Stobias } else { 20037cbc181Stobias rcs_rev_getlines(cf->file_rcs, cf->file_rcs->rf_head, &alines); 2015e4c4390Stobias } 2025e4c4390Stobias 2035e4c4390Stobias /* Stick at weird GNU cvs, ignore error. */ 2045e4c4390Stobias if (alines == NULL) 2055e4c4390Stobias return; 2065e4c4390Stobias 2075e4c4390Stobias cvs_log(LP_RCS, "Annotations for %s", cf->file_path); 2085e4c4390Stobias cvs_log(LP_RCS, "***************"); 2095e4c4390Stobias 2105e4c4390Stobias for (i = 0; alines[i] != NULL; i++) { 211efaa2accStobias line = alines[i]; 2125e4c4390Stobias 213efaa2accStobias rcsnum_tostr(line->l_delta->rd_num, rnum, sizeof(rnum)); 214efaa2accStobias strftime(date, sizeof(date), "%d-%b-%y", 215efaa2accStobias &(line->l_delta->rd_date)); 216efaa2accStobias if (line->l_len && line->l_line[line->l_len - 1] == '\n') 217efaa2accStobias line->l_line[line->l_len - 1] = '\0'; 218efaa2accStobias else { 219efaa2accStobias p = xmalloc(line->l_len + 1); 220efaa2accStobias memcpy(p, line->l_line, line->l_len); 221efaa2accStobias p[line->l_len] = '\0'; 222efaa2accStobias 223efaa2accStobias if (line->l_needsfree) 224efaa2accStobias xfree(line->l_line); 225efaa2accStobias line->l_line = p; 226efaa2accStobias line->l_len++; 227efaa2accStobias line->l_needsfree = 1; 2285e4c4390Stobias } 2295e4c4390Stobias cvs_printf("%-12.12s (%-8.8s %s): %s\n", rnum, 230efaa2accStobias line->l_delta->rd_author, date, line->l_line); 2315e4c4390Stobias 232efaa2accStobias if (line->l_needsfree) 233efaa2accStobias xfree(line->l_line); 234efaa2accStobias xfree(line); 2355e4c4390Stobias } 2365e4c4390Stobias 2375e4c4390Stobias xfree(alines); 238e0d97c9bSxsa } 239