log.c revision 128266
117721Speter/* 217721Speter * Copyright (c) 1992, Brian Berliner and Jeff Polk 317721Speter * Copyright (c) 1989-1992, Brian Berliner 417721Speter * 517721Speter * You may distribute under the terms of the GNU General Public License as 632785Speter * specified in the README file that comes with the CVS source distribution. 717721Speter * 817721Speter * Print Log Information 917721Speter * 1017721Speter * Prints the RCS "log" (rlog) information for the specified files. With no 1117721Speter * argument, prints the log information for all the files in the directory 1217721Speter * (recursive by default). 1317721Speter */ 1417721Speter 1517721Speter#include "cvs.h" 1617721Speter 1725839Speter/* This structure holds information parsed from the -r option. */ 1817721Speter 1925839Speterstruct option_revlist 2025839Speter{ 2125839Speter /* The next -r option. */ 2225839Speter struct option_revlist *next; 2325839Speter /* The first revision to print. This is NULL if the range is 2425839Speter :rev, or if no revision is given. */ 2525839Speter char *first; 2625839Speter /* The last revision to print. This is NULL if the range is rev:, 2725839Speter or if no revision is given. If there is no colon, first and 2825839Speter last are the same. */ 2925839Speter char *last; 3025839Speter /* Nonzero if there was a trailing `.', which means to print only 3125839Speter the head revision of a branch. */ 3225839Speter int branchhead; 3381404Speter /* Nonzero if first and last are inclusive. */ 3481404Speter int inclusive; 3525839Speter}; 3625839Speter 3725839Speter/* This structure holds information derived from option_revlist given 3825839Speter a particular RCS file. */ 3925839Speter 4025839Speterstruct revlist 4125839Speter{ 4225839Speter /* The next pair. */ 4325839Speter struct revlist *next; 4425839Speter /* The first numeric revision to print. */ 4525839Speter char *first; 4625839Speter /* The last numeric revision to print. */ 4725839Speter char *last; 4825839Speter /* The number of fields in these revisions (one more than 4925839Speter numdots). */ 5025839Speter int fields; 5181404Speter /* Whether first & last are to be included or excluded. */ 5281404Speter int inclusive; 5325839Speter}; 5425839Speter 5525839Speter/* This structure holds information parsed from the -d option. */ 5625839Speter 5725839Speterstruct datelist 5825839Speter{ 5925839Speter /* The next date. */ 6025839Speter struct datelist *next; 6125839Speter /* The starting date. */ 6225839Speter char *start; 6325839Speter /* The ending date. */ 6425839Speter char *end; 6525839Speter /* Nonzero if the range is inclusive rather than exclusive. */ 6625839Speter int inclusive; 6725839Speter}; 6825839Speter 6925839Speter/* This structure is used to pass information through start_recursion. */ 7025839Speterstruct log_data 7125839Speter{ 7225839Speter /* Nonzero if the -R option was given, meaning that only the name 7325839Speter of the RCS file should be printed. */ 7425839Speter int nameonly; 7525839Speter /* Nonzero if the -h option was given, meaning that only header 7625839Speter information should be printed. */ 7725839Speter int header; 7825839Speter /* Nonzero if the -t option was given, meaning that only the 7925839Speter header and the descriptive text should be printed. */ 8025839Speter int long_header; 8125839Speter /* Nonzero if the -N option was seen, meaning that tag information 8225839Speter should not be printed. */ 8325839Speter int notags; 8425839Speter /* Nonzero if the -b option was seen, meaning that only revisions 8525839Speter on the default branch should be printed. */ 8625839Speter int default_branch; 87102840Speter /* Nonzero if the -S option was seen, meaning that the header/name 88102840Speter should be suppressed if no revisions are selected. */ 89102840Speter int sup_header; 9025839Speter /* If not NULL, the value given for the -r option, which lists 9125839Speter sets of revisions to be printed. */ 9225839Speter struct option_revlist *revlist; 9325839Speter /* If not NULL, the date pairs given for the -d option, which 9425839Speter select date ranges to print. */ 9525839Speter struct datelist *datelist; 9625839Speter /* If not NULL, the single dates given for the -d option, which 9725839Speter select specific revisions to print based on a date. */ 9825839Speter struct datelist *singledatelist; 9925839Speter /* If not NULL, the list of states given for the -s option, which 10025839Speter only prints revisions of given states. */ 10125839Speter List *statelist; 10225839Speter /* If not NULL, the list of login names given for the -w option, 10325839Speter which only prints revisions checked in by given users. */ 10425839Speter List *authorlist; 10525839Speter}; 10625839Speter 10725839Speter/* This structure is used to pass information through walklist. */ 10825839Speterstruct log_data_and_rcs 10925839Speter{ 11025839Speter struct log_data *log_data; 11125839Speter struct revlist *revlist; 11225839Speter RCSNode *rcs; 11325839Speter}; 11425839Speter 11581404Speterstatic int rlog_proc PROTO((int argc, char **argv, char *xwhere, 11681404Speter char *mwhere, char *mfile, int shorten, 11781404Speter int local_specified, char *mname, char *msg)); 118128266Speterstatic Dtype log_dirproc PROTO ((void *callerdat, const char *dir, 119128266Speter const char *repository, 120128266Speter const char *update_dir, 121128266Speter List *entries)); 12225839Speterstatic int log_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 12325839Speterstatic struct option_revlist *log_parse_revlist PROTO ((const char *)); 12425839Speterstatic void log_parse_date PROTO ((struct log_data *, const char *)); 12525839Speterstatic void log_parse_list PROTO ((List **, const char *)); 12625839Speterstatic struct revlist *log_expand_revlist PROTO ((RCSNode *, 12725839Speter struct option_revlist *, 12825839Speter int)); 12925839Speterstatic void log_free_revlist PROTO ((struct revlist *)); 13025839Speterstatic int log_version_requested PROTO ((struct log_data *, struct revlist *, 13125839Speter RCSNode *, RCSVers *)); 13225839Speterstatic int log_symbol PROTO ((Node *, void *)); 13325839Speterstatic int log_count PROTO ((Node *, void *)); 13425839Speterstatic int log_fix_singledate PROTO ((Node *, void *)); 13525839Speterstatic int log_count_print PROTO ((Node *, void *)); 13625839Speterstatic void log_tree PROTO ((struct log_data *, struct revlist *, 13725839Speter RCSNode *, const char *)); 13825839Speterstatic void log_abranch PROTO ((struct log_data *, struct revlist *, 13925839Speter RCSNode *, const char *)); 14025839Speterstatic void log_version PROTO ((struct log_data *, struct revlist *, 14125839Speter RCSNode *, RCSVers *, int)); 14225839Speterstatic int log_branch PROTO ((Node *, void *)); 14325839Speterstatic int version_compare PROTO ((const char *, const char *, int)); 14425839Speter 14581404Speterstatic struct log_data log_data; 14681404Speterstatic int is_rlog; 14781404Speter 14817721Speterstatic const char *const log_usage[] = 14917721Speter{ 15025839Speter "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n", 15125839Speter " [-w[logins]] [files...]\n", 15217721Speter "\t-l\tLocal directory only, no recursion.\n", 15325839Speter "\t-R\tOnly print name of RCS file.\n", 15425839Speter "\t-h\tOnly print header.\n", 15525839Speter "\t-t\tOnly print header and descriptive text.\n", 15625839Speter "\t-N\tDo not list tags.\n", 157102840Speter "\t-S\tDo not print name/header if no revisions selected.\n", 15825839Speter "\t-b\tOnly list revisions on the default branch.\n", 159102840Speter "\t-r[revisions]\tA comma-separated list of revisions to print:\n", 16081404Speter "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n", 161102840Speter "\t rev1::rev2 Between rev1 and rev2, excluding rev1.\n", 16281404Speter "\t rev: rev and following revisions on the same branch.\n", 16381404Speter "\t rev:: After rev on the same branch.\n", 16481404Speter "\t :rev rev and previous revisions on the same branch.\n", 165102840Speter "\t ::rev rev and previous revisions on the same branch.\n", 16681404Speter "\t rev Just rev.\n", 16781404Speter "\t branch All revisions on the branch.\n", 16881404Speter "\t branch. The last revision on the branch.\n", 169102840Speter "\t-d dates\tA semicolon-separated list of dates\n", 170102840Speter "\t \t(D1<D2 for range, D for latest before).\n", 17125839Speter "\t-s states\tOnly list revisions with specified states.\n", 17225839Speter "\t-w[logins]\tOnly list revisions checked in by specified logins.\n", 17332785Speter "(Specify the --help global option for a list of other help options)\n", 17417721Speter NULL 17517721Speter}; 17617721Speter 17766525Speter#ifdef CLIENT_SUPPORT 17866525Speter 17966525Speter/* Helper function for send_arg_list. */ 18066525Speterstatic int send_one PROTO ((Node *, void *)); 18166525Speter 18266525Speterstatic int 18366525Spetersend_one (node, closure) 18466525Speter Node *node; 18566525Speter void *closure; 18666525Speter{ 18766525Speter char *option = (char *) closure; 18866525Speter 18966525Speter send_to_server ("Argument ", 0); 19066525Speter send_to_server (option, 0); 19166525Speter if (strcmp (node->key, "@@MYSELF") == 0) 19266525Speter /* It is a bare -w option. Note that we must send it as 19366525Speter -w rather than messing with getcaller() or something (which on 19466525Speter the client will return garbage). */ 19566525Speter ; 19666525Speter else 19766525Speter send_to_server (node->key, 0); 19866525Speter send_to_server ("\012", 0); 19966525Speter return 0; 20066525Speter} 20166525Speter 20266525Speter/* For each element in ARG, send an argument consisting of OPTION 20366525Speter concatenated with that element. */ 20466525Speterstatic void send_arg_list PROTO ((char *, List *)); 20566525Speter 20666525Speterstatic void 20766525Spetersend_arg_list (option, arg) 20866525Speter char *option; 20966525Speter List *arg; 21066525Speter{ 21166525Speter if (arg == NULL) 21266525Speter return; 21366525Speter walklist (arg, send_one, (void *)option); 21466525Speter} 21566525Speter 21666525Speter#endif 21766525Speter 21817721Speterint 21917721Spetercvslog (argc, argv) 22017721Speter int argc; 22117721Speter char **argv; 22217721Speter{ 22325839Speter int c; 22417721Speter int err = 0; 22517721Speter int local = 0; 22666525Speter struct option_revlist **prl; 22717721Speter 228128266Speter is_rlog = (strcmp (cvs_cmd_name, "rlog") == 0); 22981404Speter 23017721Speter if (argc == -1) 23117721Speter usage (log_usage); 23217721Speter 23325839Speter memset (&log_data, 0, sizeof log_data); 23466525Speter prl = &log_data.revlist; 23517721Speter 23626065Speter optind = 0; 237102840Speter while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::")) != -1) 23825839Speter { 23925839Speter switch (c) 24025839Speter { 24125839Speter case 'b': 24225839Speter log_data.default_branch = 1; 24325839Speter break; 24425839Speter case 'd': 24525839Speter log_parse_date (&log_data, optarg); 24625839Speter break; 24725839Speter case 'h': 24825839Speter log_data.header = 1; 24925839Speter break; 25025839Speter case 'l': 25125839Speter local = 1; 25225839Speter break; 25325839Speter case 'N': 25425839Speter log_data.notags = 1; 25525839Speter break; 256102840Speter case 'S': 257102840Speter log_data.sup_header = 1; 258102840Speter break; 25925839Speter case 'R': 26025839Speter log_data.nameonly = 1; 26125839Speter break; 26225839Speter case 'r': 26366525Speter *prl = log_parse_revlist (optarg); 26466525Speter prl = &(*prl)->next; 26525839Speter break; 26625839Speter case 's': 26725839Speter log_parse_list (&log_data.statelist, optarg); 26825839Speter break; 26925839Speter case 't': 27025839Speter log_data.long_header = 1; 27125839Speter break; 27225839Speter case 'w': 27325839Speter if (optarg != NULL) 27425839Speter log_parse_list (&log_data.authorlist, optarg); 27525839Speter else 27666525Speter log_parse_list (&log_data.authorlist, "@@MYSELF"); 27725839Speter break; 27825839Speter case '?': 27925839Speter default: 28025839Speter usage (log_usage); 28125839Speter break; 28225839Speter } 28325839Speter } 28481404Speter argc -= optind; 28581404Speter argv += optind; 28625839Speter 28717721Speter wrap_setup (); 28817721Speter 28917721Speter#ifdef CLIENT_SUPPORT 29081404Speter if (current_parsed_root->isremote) 29125839Speter { 29266525Speter struct datelist *p; 29366525Speter struct option_revlist *rp; 29466525Speter char datetmp[MAXDATELEN]; 29525839Speter 29617721Speter /* We're the local client. Fire up the remote server. */ 29717721Speter start_server (); 29881404Speter 29981404Speter if (is_rlog && !supported_request ("rlog")) 30081404Speter error (1, 0, "server does not support rlog"); 30181404Speter 30217721Speter ign_setup (); 30317721Speter 30466525Speter if (log_data.default_branch) 30566525Speter send_arg ("-b"); 30617721Speter 30766525Speter while (log_data.datelist != NULL) 30866525Speter { 30966525Speter p = log_data.datelist; 31066525Speter log_data.datelist = p->next; 31166525Speter send_to_server ("Argument -d\012", 0); 31266525Speter send_to_server ("Argument ", 0); 31366525Speter date_to_internet (datetmp, p->start); 31466525Speter send_to_server (datetmp, 0); 31566525Speter if (p->inclusive) 31666525Speter send_to_server ("<=", 0); 31766525Speter else 31866525Speter send_to_server ("<", 0); 31966525Speter date_to_internet (datetmp, p->end); 32066525Speter send_to_server (datetmp, 0); 32166525Speter send_to_server ("\012", 0); 32266525Speter if (p->start) 32366525Speter free (p->start); 32466525Speter if (p->end) 32566525Speter free (p->end); 32666525Speter free (p); 32766525Speter } 32866525Speter while (log_data.singledatelist != NULL) 32966525Speter { 33066525Speter p = log_data.singledatelist; 33166525Speter log_data.singledatelist = p->next; 33266525Speter send_to_server ("Argument -d\012", 0); 33366525Speter send_to_server ("Argument ", 0); 33466525Speter date_to_internet (datetmp, p->end); 33566525Speter send_to_server (datetmp, 0); 33666525Speter send_to_server ("\012", 0); 33766525Speter if (p->end) 33866525Speter free (p->end); 33966525Speter free (p); 34066525Speter } 34166525Speter 34266525Speter if (log_data.header) 34366525Speter send_arg ("-h"); 34466525Speter if (local) 34566525Speter send_arg("-l"); 34666525Speter if (log_data.notags) 34766525Speter send_arg("-N"); 348102840Speter if (log_data.sup_header) 349102840Speter send_arg("-S"); 35066525Speter if (log_data.nameonly) 35166525Speter send_arg("-R"); 35266525Speter if (log_data.long_header) 35366525Speter send_arg("-t"); 35417721Speter 35566525Speter while (log_data.revlist != NULL) 35666525Speter { 35766525Speter rp = log_data.revlist; 35866525Speter log_data.revlist = rp->next; 35966525Speter send_to_server ("Argument -r", 0); 36066525Speter if (rp->branchhead) 36166525Speter { 36266525Speter if (rp->first != NULL) 36366525Speter send_to_server (rp->first, 0); 36466525Speter send_to_server (".", 1); 36566525Speter } 36666525Speter else 36766525Speter { 36866525Speter if (rp->first != NULL) 36966525Speter send_to_server (rp->first, 0); 37066525Speter send_to_server (":", 1); 37181404Speter if (!rp->inclusive) 37281404Speter send_to_server (":", 1); 37366525Speter if (rp->last != NULL) 37466525Speter send_to_server (rp->last, 0); 37566525Speter } 37666525Speter send_to_server ("\012", 0); 37766525Speter if (rp->first) 37866525Speter free (rp->first); 37966525Speter if (rp->last) 38066525Speter free (rp->last); 38166525Speter free (rp); 38266525Speter } 38366525Speter send_arg_list ("-s", log_data.statelist); 38466525Speter dellist (&log_data.statelist); 38566525Speter send_arg_list ("-w", log_data.authorlist); 38666525Speter dellist (&log_data.authorlist); 387107484Speter send_arg ("--"); 38866525Speter 38981404Speter if (is_rlog) 39081404Speter { 39181404Speter int i; 39281404Speter for (i = 0; i < argc; i++) 39381404Speter send_arg (argv[i]); 39481404Speter send_to_server ("rlog\012", 0); 39581404Speter } 39681404Speter else 39781404Speter { 39881404Speter send_files (argc, argv, local, 0, SEND_NO_CONTENTS); 39981404Speter send_file_names (argc, argv, SEND_EXPAND_WILD); 40081404Speter send_to_server ("log\012", 0); 40181404Speter } 40217721Speter err = get_responses_and_close (); 40317721Speter return err; 40417721Speter } 40517721Speter#endif 40617721Speter 40766525Speter /* OK, now that we know we are local/server, we can resolve @@MYSELF 40866525Speter into our user name. */ 40966525Speter if (findnode (log_data.authorlist, "@@MYSELF") != NULL) 41066525Speter log_parse_list (&log_data.authorlist, getcaller ()); 41166525Speter 41281404Speter if (is_rlog) 41381404Speter { 41481404Speter DBM *db; 41581404Speter int i; 41681404Speter db = open_module (); 41781404Speter for (i = 0; i < argc; i++) 41881404Speter { 41981404Speter err += do_module (db, argv[i], MISC, "Logging", rlog_proc, 420102840Speter (char *) NULL, 0, local, 0, 0, (char *) NULL); 42181404Speter } 42281404Speter close_module (db); 42381404Speter } 42481404Speter else 42581404Speter { 42681404Speter err = rlog_proc (argc + 1, argv - 1, (char *) NULL, 427102840Speter (char *) NULL, (char *) NULL, 0, local, (char *) NULL, 42881404Speter (char *) NULL); 42981404Speter } 43066525Speter 43166525Speter while (log_data.revlist) 43266525Speter { 43366525Speter struct option_revlist *rl = log_data.revlist->next; 43466525Speter if (log_data.revlist->first) 43566525Speter free (log_data.revlist->first); 43666525Speter if (log_data.revlist->last) 43766525Speter free (log_data.revlist->last); 43866525Speter free (log_data.revlist); 43966525Speter log_data.revlist = rl; 44066525Speter } 44166525Speter while (log_data.datelist) 44266525Speter { 44366525Speter struct datelist *nd = log_data.datelist->next; 44466525Speter if (log_data.datelist->start) 44566525Speter free (log_data.datelist->start); 44666525Speter if (log_data.datelist->end) 44766525Speter free (log_data.datelist->end); 44866525Speter free (log_data.datelist); 44966525Speter log_data.datelist = nd; 45066525Speter } 45166525Speter while (log_data.singledatelist) 45266525Speter { 45366525Speter struct datelist *nd = log_data.singledatelist->next; 45466525Speter if (log_data.singledatelist->start) 45566525Speter free (log_data.singledatelist->start); 45666525Speter if (log_data.singledatelist->end) 45766525Speter free (log_data.singledatelist->end); 45866525Speter free (log_data.singledatelist); 45966525Speter log_data.singledatelist = nd; 46066525Speter } 46166525Speter dellist (&log_data.statelist); 46266525Speter dellist (&log_data.authorlist); 46366525Speter 46417721Speter return (err); 46517721Speter} 46617721Speter 46781404Speter 46881404Speterstatic int 46981404Speterrlog_proc (argc, argv, xwhere, mwhere, mfile, shorten, local, mname, msg) 47081404Speter int argc; 47181404Speter char **argv; 47281404Speter char *xwhere; 47381404Speter char *mwhere; 47481404Speter char *mfile; 47581404Speter int shorten; 47681404Speter int local; 47781404Speter char *mname; 47881404Speter char *msg; 47981404Speter{ 48081404Speter /* Begin section which is identical to patch_proc--should this 48181404Speter be abstracted out somehow? */ 48281404Speter char *myargv[2]; 48381404Speter int err = 0; 48481404Speter int which; 48581404Speter char *repository; 48681404Speter char *where; 48781404Speter 48881404Speter if (is_rlog) 48981404Speter { 490128266Speter repository = xmalloc (strlen (current_parsed_root->directory) 491128266Speter + strlen (argv[0]) 49281404Speter + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); 493128266Speter (void)sprintf (repository, "%s/%s", 494128266Speter current_parsed_root->directory, argv[0]); 495128266Speter where = xmalloc (strlen (argv[0]) 496128266Speter + (mfile == NULL ? 0 : strlen (mfile) + 1) 49781404Speter + 1); 49881404Speter (void) strcpy (where, argv[0]); 49981404Speter 500128266Speter /* If mfile isn't null, we need to set up to do only part of theu 501128266Speter * module. 502128266Speter */ 50381404Speter if (mfile != NULL) 50481404Speter { 50581404Speter char *cp; 50681404Speter char *path; 50781404Speter 508128266Speter /* If the portion of the module is a path, put the dir part on 509128266Speter * repos. 510128266Speter */ 51181404Speter if ((cp = strrchr (mfile, '/')) != NULL) 51281404Speter { 51381404Speter *cp = '\0'; 514128266Speter (void)strcat (repository, "/"); 515128266Speter (void)strcat (repository, mfile); 516128266Speter (void)strcat (where, "/"); 517128266Speter (void)strcat (where, mfile); 51881404Speter mfile = cp + 1; 51981404Speter } 52081404Speter 52181404Speter /* take care of the rest */ 52281404Speter path = xmalloc (strlen (repository) + strlen (mfile) + 5); 523128266Speter (void)sprintf (path, "%s/%s", repository, mfile); 52481404Speter if (isdir (path)) 52581404Speter { 52681404Speter /* directory means repository gets the dir tacked on */ 527128266Speter (void)strcpy (repository, path); 528128266Speter (void)strcat (where, "/"); 529128266Speter (void)strcat (where, mfile); 53081404Speter } 53181404Speter else 53281404Speter { 53381404Speter myargv[0] = argv[0]; 53481404Speter myargv[1] = mfile; 53581404Speter argc = 2; 53681404Speter argv = myargv; 53781404Speter } 53881404Speter free (path); 53981404Speter } 54081404Speter 54181404Speter /* cd to the starting repository */ 542128266Speter if (CVS_CHDIR (repository) < 0) 54381404Speter { 54481404Speter error (0, errno, "cannot chdir to %s", repository); 54581404Speter free (repository); 546128266Speter free (where); 547128266Speter return 1; 54881404Speter } 54981404Speter /* End section which is identical to patch_proc. */ 55081404Speter 55181404Speter which = W_REPOS | W_ATTIC; 55281404Speter } 55381404Speter else 55481404Speter { 555128266Speter repository = NULL; 55681404Speter where = NULL; 55781404Speter which = W_LOCAL | W_REPOS | W_ATTIC; 55881404Speter } 55981404Speter 56081404Speter err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc, 56181404Speter (DIRLEAVEPROC) NULL, (void *) &log_data, 562109655Speter argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ, 563128266Speter where, 1, repository); 564128266Speter 565128266Speter if (!(which & W_LOCAL)) free (repository); 566128266Speter if (where) free (where); 567128266Speter 56881404Speter return err; 56981404Speter} 57081404Speter 57181404Speter 572128266Speter 57325839Speter/* 57425839Speter * Parse a revision list specification. 57525839Speter */ 57625839Speterstatic struct option_revlist * 57725839Speterlog_parse_revlist (argstring) 57825839Speter const char *argstring; 57925839Speter{ 58066525Speter char *orig_copy, *copy; 58125839Speter struct option_revlist *ret, **pr; 58225839Speter 58325839Speter /* Unfortunately, rlog accepts -r without an argument to mean that 58425839Speter latest revision on the default branch, so we must support that 58525839Speter for compatibility. */ 58625839Speter if (argstring == NULL) 58766525Speter argstring = ""; 58825839Speter 58925839Speter ret = NULL; 59025839Speter pr = &ret; 59125839Speter 59225839Speter /* Copy the argument into memory so that we can change it. We 59325839Speter don't want to change the argument because, at least as of this 59466525Speter writing, we will use it if we send the arguments to the server. */ 59566525Speter orig_copy = copy = xstrdup (argstring); 59625839Speter while (copy != NULL) 59725839Speter { 59825839Speter char *comma; 59925839Speter struct option_revlist *r; 60025839Speter 60125839Speter comma = strchr (copy, ','); 60225839Speter if (comma != NULL) 60325839Speter *comma++ = '\0'; 60425839Speter 60525839Speter r = (struct option_revlist *) xmalloc (sizeof *r); 60625839Speter r->next = NULL; 60766525Speter r->first = copy; 60866525Speter r->branchhead = 0; 60966525Speter r->last = strchr (copy, ':'); 61066525Speter if (r->last != NULL) 61181404Speter { 61266525Speter *r->last++ = '\0'; 61381404Speter r->inclusive = (*r->last != ':'); 61481404Speter if (!r->inclusive) 61581404Speter r->last++; 61681404Speter } 61725839Speter else 61825839Speter { 61966525Speter r->last = r->first; 62081404Speter r->inclusive = 1; 62166525Speter if (r->first[0] != '\0' && r->first[strlen (r->first) - 1] == '.') 62266525Speter { 62366525Speter r->branchhead = 1; 62466525Speter r->first[strlen (r->first) - 1] = '\0'; 62566525Speter } 62625839Speter } 62725839Speter 62866525Speter if (*r->first == '\0') 62966525Speter r->first = NULL; 63066525Speter if (*r->last == '\0') 63166525Speter r->last = NULL; 63266525Speter 63366525Speter if (r->first != NULL) 63466525Speter r->first = xstrdup (r->first); 63566525Speter if (r->last != NULL) 63666525Speter r->last = xstrdup (r->last); 63766525Speter 63825839Speter *pr = r; 63925839Speter pr = &r->next; 64025839Speter 64125839Speter copy = comma; 64225839Speter } 64325839Speter 64466525Speter free (orig_copy); 64525839Speter return ret; 64625839Speter} 64725839Speter 64817721Speter/* 64925839Speter * Parse a date specification. 65025839Speter */ 65125839Speterstatic void 65225839Speterlog_parse_date (log_data, argstring) 65325839Speter struct log_data *log_data; 65425839Speter const char *argstring; 65525839Speter{ 65625839Speter char *orig_copy, *copy; 65725839Speter 65825839Speter /* Copy the argument into memory so that we can change it. We 65925839Speter don't want to change the argument because, at least as of this 66025839Speter writing, we will use it if we send the arguments to the server. */ 66166525Speter orig_copy = copy = xstrdup (argstring); 66225839Speter while (copy != NULL) 66325839Speter { 66425839Speter struct datelist *nd, **pd; 66525839Speter char *cpend, *cp, *ds, *de; 66625839Speter 66725839Speter nd = (struct datelist *) xmalloc (sizeof *nd); 66825839Speter 66925839Speter cpend = strchr (copy, ';'); 67025839Speter if (cpend != NULL) 67125839Speter *cpend++ = '\0'; 67225839Speter 67325839Speter pd = &log_data->datelist; 67425839Speter nd->inclusive = 0; 67525839Speter 67625839Speter if ((cp = strchr (copy, '>')) != NULL) 67725839Speter { 67825839Speter *cp++ = '\0'; 67925839Speter if (*cp == '=') 68025839Speter { 68125839Speter ++cp; 68225839Speter nd->inclusive = 1; 68325839Speter } 68425839Speter ds = cp; 68525839Speter de = copy; 68625839Speter } 68725839Speter else if ((cp = strchr (copy, '<')) != NULL) 68825839Speter { 68925839Speter *cp++ = '\0'; 69025839Speter if (*cp == '=') 69125839Speter { 69225839Speter ++cp; 69325839Speter nd->inclusive = 1; 69425839Speter } 69525839Speter ds = copy; 69625839Speter de = cp; 69725839Speter } 69825839Speter else 69925839Speter { 70025839Speter ds = NULL; 70125839Speter de = copy; 70225839Speter pd = &log_data->singledatelist; 70325839Speter } 70425839Speter 70525839Speter if (ds == NULL) 70625839Speter nd->start = NULL; 70725839Speter else if (*ds != '\0') 70825839Speter nd->start = Make_Date (ds); 70925839Speter else 71025839Speter { 71125839Speter /* 1970 was the beginning of time, as far as get_date and 71225839Speter Make_Date are concerned. FIXME: That is true only if time_t 71325839Speter is a POSIX-style time and there is nothing in ANSI that 71425839Speter mandates that. It would be cleaner to set a flag saying 71525839Speter whether or not there is a start date. */ 71625839Speter nd->start = Make_Date ("1/1/1970 UTC"); 71725839Speter } 71825839Speter 71925839Speter if (*de != '\0') 72025839Speter nd->end = Make_Date (de); 72125839Speter else 72225839Speter { 72325839Speter /* We want to set the end date to some time sufficiently far 72425839Speter in the future to pick up all revisions that have been 72525839Speter created since the specified date and the time `cvs log' 72625839Speter completes. FIXME: The date in question only makes sense 72725839Speter if time_t is a POSIX-style time and it is 32 bits 72825839Speter and signed. We should instead be setting a flag saying 72925839Speter whether or not there is an end date. Note that using 73025839Speter something like "next week" would break the testsuite (and, 73125839Speter perhaps less importantly, loses if the clock is set grossly 73225839Speter wrong). */ 73325839Speter nd->end = Make_Date ("2038-01-01"); 73425839Speter } 73525839Speter 73625839Speter nd->next = *pd; 73725839Speter *pd = nd; 73825839Speter 73925839Speter copy = cpend; 74025839Speter } 74125839Speter 74225839Speter free (orig_copy); 74325839Speter} 74425839Speter 74525839Speter/* 74625839Speter * Parse a comma separated list of items, and add each one to *PLIST. 74725839Speter */ 74825839Speterstatic void 74925839Speterlog_parse_list (plist, argstring) 75025839Speter List **plist; 75125839Speter const char *argstring; 75225839Speter{ 75325839Speter while (1) 75425839Speter { 75525839Speter Node *p; 75625839Speter char *cp; 75725839Speter 75825839Speter p = getnode (); 75925839Speter 76025839Speter cp = strchr (argstring, ','); 76125839Speter if (cp == NULL) 76225839Speter p->key = xstrdup (argstring); 76325839Speter else 76425839Speter { 76525839Speter size_t len; 76625839Speter 76725839Speter len = cp - argstring; 76825839Speter p->key = xmalloc (len + 1); 76925839Speter strncpy (p->key, argstring, len); 770107484Speter p->key[len] = '\0'; 77125839Speter } 77225839Speter 77325839Speter if (*plist == NULL) 77425839Speter *plist = getlist (); 77525839Speter if (addnode (*plist, p) != 0) 77625839Speter freenode (p); 77725839Speter 77825839Speter if (cp == NULL) 77925839Speter break; 78025839Speter 78125839Speter argstring = cp + 1; 78225839Speter } 78325839Speter} 78425839Speter 78532785Speterstatic int printlock_proc PROTO ((Node *, void *)); 78632785Speter 78732785Speterstatic int 78832785Speterprintlock_proc (lock, foo) 78932785Speter Node *lock; 79032785Speter void *foo; 79132785Speter{ 79232785Speter cvs_output ("\n\t", 2); 79332785Speter cvs_output (lock->data, 0); 79432785Speter cvs_output (": ", 2); 79532785Speter cvs_output (lock->key, 0); 79632785Speter return 0; 79732785Speter} 79832785Speter 799128266Speter 800128266Speter 80125839Speter/* 80217721Speter * Do an rlog on a file 80317721Speter */ 80417721Speterstatic int 80525839Speterlog_fileproc (callerdat, finfo) 80625839Speter void *callerdat; 80717721Speter struct file_info *finfo; 80817721Speter{ 80925839Speter struct log_data *log_data = (struct log_data *) callerdat; 81017721Speter Node *p; 811102840Speter int selrev = -1; 81217721Speter RCSNode *rcsfile; 81325839Speter char buf[50]; 814128266Speter struct revlist *revlist = NULL; 81525839Speter struct log_data_and_rcs log_data_and_rcs; 81617721Speter 81717721Speter if ((rcsfile = finfo->rcs) == NULL) 81817721Speter { 81917721Speter /* no rcs file. What *do* we know about this file? */ 82017721Speter p = findnode (finfo->entries, finfo->file); 82117721Speter if (p != NULL) 82217721Speter { 823128266Speter Entnode *e = p->data; 824128266Speter 82532785Speter if (e->version[0] == '0' && e->version[1] == '\0') 82617721Speter { 82717721Speter if (!really_quiet) 82817721Speter error (0, 0, "%s has been added, but not committed", 82917721Speter finfo->file); 830128266Speter return 0; 83117721Speter } 83217721Speter } 83317721Speter 83417721Speter if (!really_quiet) 83517721Speter error (0, 0, "nothing known about %s", finfo->file); 83617721Speter 837128266Speter return 1; 83817721Speter } 83917721Speter 840102840Speter if (log_data->sup_header || !log_data->nameonly) 841102840Speter { 842102840Speter 843102840Speter /* We will need all the information in the RCS file. */ 844102840Speter RCS_fully_parse (rcsfile); 845102840Speter 846102840Speter /* Turn any symbolic revisions in the revision list into numeric 847102840Speter revisions. */ 848102840Speter revlist = log_expand_revlist (rcsfile, log_data->revlist, 849102840Speter log_data->default_branch); 850128266Speter if (log_data->sup_header 851128266Speter || (!log_data->header && !log_data->long_header)) 852102840Speter { 853102840Speter log_data_and_rcs.log_data = log_data; 854102840Speter log_data_and_rcs.revlist = revlist; 855102840Speter log_data_and_rcs.rcs = rcsfile; 856102840Speter 857102840Speter /* If any single dates were specified, we need to identify the 858102840Speter revisions they select. Each one selects the single 859102840Speter revision, which is otherwise selected, of that date or 860102840Speter earlier. The log_fix_singledate routine will fill in the 861102840Speter start date for each specific revision. */ 862102840Speter if (log_data->singledatelist != NULL) 863102840Speter walklist (rcsfile->versions, log_fix_singledate, 864128266Speter (void *)&log_data_and_rcs); 865102840Speter 866102840Speter selrev = walklist (rcsfile->versions, log_count_print, 867128266Speter (void *)&log_data_and_rcs); 868128266Speter if (log_data->sup_header && selrev == 0) 869128266Speter { 870128266Speter log_free_revlist (revlist); 871128266Speter return 0; 872128266Speter } 873102840Speter } 874102840Speter 875102840Speter } 876102840Speter 87725839Speter if (log_data->nameonly) 87817721Speter { 87925839Speter cvs_output (rcsfile->path, 0); 88025839Speter cvs_output ("\n", 1); 881128266Speter log_free_revlist (revlist); 88225839Speter return 0; 88317721Speter } 88417721Speter 88525839Speter /* The output here is intended to be exactly compatible with the 88625839Speter output of rlog. I'm not sure whether this code should be here 88725839Speter or in rcs.c; I put it here because it is specific to the log 88825839Speter function, even though it uses information gathered by the 88925839Speter functions in rcs.c. */ 89025839Speter 89125839Speter cvs_output ("\n", 1); 89225839Speter 89325839Speter cvs_output ("RCS file: ", 0); 89425839Speter cvs_output (rcsfile->path, 0); 89525839Speter 89681404Speter if (!is_rlog) 89717721Speter { 89881404Speter cvs_output ("\nWorking file: ", 0); 89981404Speter if (finfo->update_dir[0] != '\0') 90081404Speter { 90181404Speter cvs_output (finfo->update_dir, 0); 90281404Speter cvs_output ("/", 0); 90381404Speter } 90425839Speter cvs_output (finfo->file, 0); 90517721Speter } 90617721Speter 90725839Speter cvs_output ("\nhead:", 0); 90825839Speter if (rcsfile->head != NULL) 90917721Speter { 91025839Speter cvs_output (" ", 1); 91125839Speter cvs_output (rcsfile->head, 0); 91217721Speter } 91325839Speter 91425839Speter cvs_output ("\nbranch:", 0); 91525839Speter if (rcsfile->branch != NULL) 91625839Speter { 91725839Speter cvs_output (" ", 1); 91825839Speter cvs_output (rcsfile->branch, 0); 91925839Speter } 92025839Speter 92125839Speter cvs_output ("\nlocks:", 0); 92232785Speter if (rcsfile->strict_locks) 92332785Speter cvs_output (" strict", 0); 92432785Speter walklist (RCS_getlocks (rcsfile), printlock_proc, NULL); 92525839Speter 92625839Speter cvs_output ("\naccess list:", 0); 92732785Speter if (rcsfile->access != NULL) 92825839Speter { 92932785Speter const char *cp; 93032785Speter 93132785Speter cp = rcsfile->access; 93232785Speter while (*cp != '\0') 93325839Speter { 93425839Speter const char *cp2; 93525839Speter 93625839Speter cvs_output ("\n\t", 2); 93725839Speter cp2 = cp; 938128266Speter while (!isspace ((unsigned char) *cp2) && *cp2 != '\0') 93925839Speter ++cp2; 94025839Speter cvs_output (cp, cp2 - cp); 94125839Speter cp = cp2; 94254427Speter while (isspace ((unsigned char) *cp) && *cp != '\0') 94325839Speter ++cp; 94425839Speter } 94525839Speter } 94625839Speter 947128266Speter if (!log_data->notags) 94825839Speter { 94925839Speter List *syms; 95025839Speter 95125839Speter cvs_output ("\nsymbolic names:", 0); 95225839Speter syms = RCS_symbols (rcsfile); 95325839Speter walklist (syms, log_symbol, NULL); 95425839Speter } 95525839Speter 95625839Speter cvs_output ("\nkeyword substitution: ", 0); 95725839Speter if (rcsfile->expand == NULL) 95825839Speter cvs_output ("kv", 2); 95925839Speter else 96025839Speter cvs_output (rcsfile->expand, 0); 96125839Speter 96225839Speter cvs_output ("\ntotal revisions: ", 0); 96325839Speter sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL)); 96425839Speter cvs_output (buf, 0); 96525839Speter 966102840Speter if (selrev >= 0) 96725839Speter { 96825839Speter cvs_output (";\tselected revisions: ", 0); 969102840Speter sprintf (buf, "%d", selrev); 97025839Speter cvs_output (buf, 0); 97125839Speter } 97225839Speter 97325839Speter cvs_output ("\n", 1); 97425839Speter 975128266Speter if (!log_data->header || log_data->long_header) 97625839Speter { 97725839Speter cvs_output ("description:\n", 0); 97832785Speter if (rcsfile->desc != NULL) 97932785Speter cvs_output (rcsfile->desc, 0); 98025839Speter } 98125839Speter 982128266Speter if (!log_data->header && ! log_data->long_header && rcsfile->head != NULL) 98325839Speter { 98425839Speter p = findnode (rcsfile->versions, rcsfile->head); 98525839Speter if (p == NULL) 98625839Speter error (1, 0, "can not find head revision in `%s'", 98725839Speter finfo->fullname); 98825839Speter while (p != NULL) 98925839Speter { 990128266Speter RCSVers *vers = p->data; 99125839Speter 99225839Speter log_version (log_data, revlist, rcsfile, vers, 1); 99325839Speter if (vers->next == NULL) 99425839Speter p = NULL; 99525839Speter else 99625839Speter { 99725839Speter p = findnode (rcsfile->versions, vers->next); 99825839Speter if (p == NULL) 99925839Speter error (1, 0, "can not find next revision `%s' in `%s'", 100025839Speter vers->next, finfo->fullname); 100125839Speter } 100225839Speter } 100325839Speter 100425839Speter log_tree (log_data, revlist, rcsfile, rcsfile->head); 100525839Speter } 100625839Speter 100725839Speter cvs_output("\ 100825839Speter=============================================================================\n", 100925839Speter 0); 101025839Speter 101125839Speter /* Free up the new revlist and restore the old one. */ 101225839Speter log_free_revlist (revlist); 101325839Speter 101425839Speter /* If singledatelist is not NULL, free up the start dates we added 101525839Speter to it. */ 101625839Speter if (log_data->singledatelist != NULL) 101725839Speter { 101825839Speter struct datelist *d; 101925839Speter 102025839Speter for (d = log_data->singledatelist; d != NULL; d = d->next) 102125839Speter { 102225839Speter if (d->start != NULL) 102325839Speter free (d->start); 102425839Speter d->start = NULL; 102525839Speter } 102625839Speter } 102725839Speter 102825839Speter return 0; 102917721Speter} 103017721Speter 1031128266Speter 1032128266Speter 103317721Speter/* 103425839Speter * Fix up a revision list in order to compare it against versions. 103525839Speter * Expand any symbolic revisions. 103625839Speter */ 103725839Speterstatic struct revlist * 103825839Speterlog_expand_revlist (rcs, revlist, default_branch) 103925839Speter RCSNode *rcs; 104025839Speter struct option_revlist *revlist; 104125839Speter int default_branch; 104225839Speter{ 104325839Speter struct option_revlist *r; 104425839Speter struct revlist *ret, **pr; 104525839Speter 104625839Speter ret = NULL; 104725839Speter pr = &ret; 104825839Speter for (r = revlist; r != NULL; r = r->next) 104925839Speter { 105025839Speter struct revlist *nr; 105125839Speter 105225839Speter nr = (struct revlist *) xmalloc (sizeof *nr); 105381404Speter nr->inclusive = r->inclusive; 105425839Speter 105525839Speter if (r->first == NULL && r->last == NULL) 105625839Speter { 105725839Speter /* If both first and last are NULL, it means that we want 105825839Speter just the head of the default branch, which is RCS_head. */ 105925839Speter nr->first = RCS_head (rcs); 106025839Speter nr->last = xstrdup (nr->first); 106125839Speter nr->fields = numdots (nr->first) + 1; 106225839Speter } 106325839Speter else if (r->branchhead) 106425839Speter { 106525839Speter char *branch; 106625839Speter 106725839Speter /* Print just the head of the branch. */ 106854427Speter if (isdigit ((unsigned char) r->first[0])) 106925839Speter nr->first = RCS_getbranch (rcs, r->first, 1); 107025839Speter else 107125839Speter { 107225839Speter branch = RCS_whatbranch (rcs, r->first); 107325839Speter if (branch == NULL) 1074102840Speter nr->first = NULL; 1075102840Speter else 107625839Speter { 1077102840Speter nr->first = RCS_getbranch (rcs, branch, 1); 1078102840Speter free (branch); 107925839Speter } 108025839Speter } 108125839Speter if (nr->first == NULL) 108225839Speter { 1083102840Speter error (0, 0, "warning: no branch `%s' in `%s'", 108425839Speter r->first, rcs->path); 1085102840Speter nr->last = NULL; 1086102840Speter nr->fields = 0; 108725839Speter } 1088102840Speter else 1089102840Speter { 1090102840Speter nr->last = xstrdup (nr->first); 1091102840Speter nr->fields = numdots (nr->first) + 1; 1092102840Speter } 109325839Speter } 109425839Speter else 109525839Speter { 109654427Speter if (r->first == NULL || isdigit ((unsigned char) r->first[0])) 109725839Speter nr->first = xstrdup (r->first); 109825839Speter else 109925839Speter { 110025839Speter if (RCS_nodeisbranch (rcs, r->first)) 110125839Speter nr->first = RCS_whatbranch (rcs, r->first); 110225839Speter else 110325839Speter nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL); 110425839Speter if (nr->first == NULL) 110525839Speter { 110625839Speter error (0, 0, "warning: no revision `%s' in `%s'", 110725839Speter r->first, rcs->path); 110825839Speter } 110925839Speter } 111025839Speter 1111102840Speter if (r->last == r->first || (r->last != NULL && r->first != NULL && 1112102840Speter strcmp (r->last, r->first) == 0)) 111325839Speter nr->last = xstrdup (nr->first); 111454427Speter else if (r->last == NULL || isdigit ((unsigned char) r->last[0])) 111525839Speter nr->last = xstrdup (r->last); 111625839Speter else 111725839Speter { 111825839Speter if (RCS_nodeisbranch (rcs, r->last)) 111925839Speter nr->last = RCS_whatbranch (rcs, r->last); 112025839Speter else 112125839Speter nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL); 112225839Speter if (nr->last == NULL) 112325839Speter { 112425839Speter error (0, 0, "warning: no revision `%s' in `%s'", 112525839Speter r->last, rcs->path); 112625839Speter } 112725839Speter } 112825839Speter 112925839Speter /* Process the revision numbers the same way that rlog 113025839Speter does. This code is a bit cryptic for my tastes, but 113125839Speter keeping the same implementation as rlog ensures a 113225839Speter certain degree of compatibility. */ 1133128266Speter if (r->first == NULL && nr->last != NULL) 113425839Speter { 1135128266Speter nr->fields = numdots (nr->last) + 1; 1136128266Speter if (nr->fields < 2) 1137128266Speter nr->first = xstrdup (".0"); 113825839Speter else 113925839Speter { 1140128266Speter char *cp; 114125839Speter 1142128266Speter nr->first = xstrdup (nr->last); 1143128266Speter cp = strrchr (nr->first, '.'); 1144128266Speter strcpy (cp + 1, "0"); 114525839Speter } 114625839Speter } 1147128266Speter else if (r->last == NULL && nr->first != NULL) 114825839Speter { 114925839Speter nr->fields = numdots (nr->first) + 1; 115025839Speter nr->last = xstrdup (nr->first); 115125839Speter if (nr->fields < 2) 115225839Speter nr->last[0] = '\0'; 115325839Speter else 115425839Speter { 115525839Speter char *cp; 115625839Speter 115725839Speter cp = strrchr (nr->last, '.'); 115825839Speter *cp = '\0'; 115925839Speter } 116025839Speter } 1161107484Speter else if (nr->first == NULL || nr->last == NULL) 1162107484Speter nr->fields = 0; 1163107484Speter else if (strcmp (nr->first, nr->last) == 0) 1164107484Speter nr->fields = numdots (nr->last) + 1; 1165107484Speter else 116625839Speter { 1167107484Speter int ord; 1168107484Speter int dots1 = numdots (nr->first); 1169107484Speter int dots2 = numdots (nr->last); 1170107484Speter if (dots1 > dots2 || (dots1 == dots2 && 1171107484Speter version_compare (nr->first, nr->last, dots1 + 1) > 0)) 117225839Speter { 1173107484Speter char *tmp = nr->first; 1174107484Speter nr->first = nr->last; 1175107484Speter nr->last = tmp; 1176107484Speter nr->fields = dots2 + 1; 1177107484Speter dots2 = dots1; 1178107484Speter dots1 = nr->fields - 1; 1179107484Speter } 1180107484Speter else 1181107484Speter nr->fields = dots1 + 1; 1182107484Speter dots1 += (nr->fields & 1); 1183107484Speter ord = version_compare (nr->first, nr->last, dots1); 1184107484Speter if (ord > 0 || (nr->fields > 2 && ord < 0)) 1185107484Speter { 118625839Speter error (0, 0, 118725839Speter "invalid branch or revision pair %s:%s in `%s'", 118825839Speter r->first, r->last, rcs->path); 118925839Speter free (nr->first); 1190102840Speter nr->first = NULL; 119125839Speter free (nr->last); 1192102840Speter nr->last = NULL; 1193102840Speter nr->fields = 0; 119425839Speter } 1195107484Speter else 119625839Speter { 1197107484Speter if (nr->fields <= dots2 && (nr->fields & 1)) 1198107484Speter { 1199107484Speter char *p = xmalloc (strlen (nr->first) + 3); 1200107484Speter strcpy (p, nr->first); 1201107484Speter strcat (p, ".0"); 1202107484Speter free (nr->first); 1203107484Speter nr->first = p; 1204107484Speter ++nr->fields; 1205107484Speter } 1206107484Speter while (nr->fields <= dots2) 1207107484Speter { 1208107484Speter char *p; 1209107484Speter int i; 121025839Speter 1211107484Speter nr->next = NULL; 1212107484Speter *pr = nr; 1213107484Speter nr = (struct revlist *) xmalloc (sizeof *nr); 1214107484Speter nr->inclusive = 1; 1215107484Speter nr->first = xstrdup ((*pr)->last); 1216107484Speter nr->last = xstrdup ((*pr)->last); 1217107484Speter nr->fields = (*pr)->fields; 1218107484Speter p = (*pr)->last; 1219107484Speter for (i = 0; i < nr->fields; i++) 1220107484Speter p = strchr (p, '.') + 1; 1221107484Speter p[-1] = '\0'; 1222107484Speter p = strchr (nr->first + (p - (*pr)->last), '.'); 1223107484Speter if (p != NULL) 1224107484Speter { 1225107484Speter *++p = '0'; 1226107484Speter *++p = '\0'; 1227107484Speter nr->fields += 2; 1228107484Speter } 1229107484Speter else 1230107484Speter ++nr->fields; 1231107484Speter pr = &(*pr)->next; 1232107484Speter } 123325839Speter } 123425839Speter } 123525839Speter } 123625839Speter 123725839Speter nr->next = NULL; 123825839Speter *pr = nr; 123925839Speter pr = &nr->next; 124025839Speter } 124125839Speter 124225839Speter /* If the default branch was requested, add a revlist entry for 124325839Speter it. This is how rlog handles this option. */ 124425839Speter if (default_branch 124525839Speter && (rcs->head != NULL || rcs->branch != NULL)) 124625839Speter { 124725839Speter struct revlist *nr; 124825839Speter 124925839Speter nr = (struct revlist *) xmalloc (sizeof *nr); 125025839Speter if (rcs->branch != NULL) 125125839Speter nr->first = xstrdup (rcs->branch); 125225839Speter else 125325839Speter { 125425839Speter char *cp; 125525839Speter 125625839Speter nr->first = xstrdup (rcs->head); 125725839Speter cp = strrchr (nr->first, '.'); 125825839Speter *cp = '\0'; 125925839Speter } 126025839Speter nr->last = xstrdup (nr->first); 126125839Speter nr->fields = numdots (nr->first) + 1; 126281404Speter nr->inclusive = 1; 126325839Speter 126425839Speter nr->next = NULL; 126525839Speter *pr = nr; 126625839Speter } 126725839Speter 126825839Speter return ret; 126925839Speter} 127025839Speter 127125839Speter/* 127225839Speter * Free a revlist created by log_expand_revlist. 127325839Speter */ 127425839Speterstatic void 127525839Speterlog_free_revlist (revlist) 127625839Speter struct revlist *revlist; 127725839Speter{ 127825839Speter struct revlist *r; 127925839Speter 128025839Speter r = revlist; 128125839Speter while (r != NULL) 128225839Speter { 128325839Speter struct revlist *next; 128425839Speter 128525839Speter if (r->first != NULL) 128625839Speter free (r->first); 128725839Speter if (r->last != NULL) 128825839Speter free (r->last); 128925839Speter next = r->next; 129025839Speter free (r); 129125839Speter r = next; 129225839Speter } 129325839Speter} 129425839Speter 129525839Speter/* 129625839Speter * Return nonzero if a revision should be printed, based on the 129725839Speter * options provided. 129825839Speter */ 129925839Speterstatic int 130025839Speterlog_version_requested (log_data, revlist, rcs, vnode) 130125839Speter struct log_data *log_data; 130225839Speter struct revlist *revlist; 130325839Speter RCSNode *rcs; 130425839Speter RCSVers *vnode; 130525839Speter{ 130625839Speter /* Handle the list of states from the -s option. */ 130725839Speter if (log_data->statelist != NULL 130825839Speter && findnode (log_data->statelist, vnode->state) == NULL) 130925839Speter { 131025839Speter return 0; 131125839Speter } 131225839Speter 131325839Speter /* Handle the list of authors from the -w option. */ 131425839Speter if (log_data->authorlist != NULL) 131525839Speter { 131625839Speter if (vnode->author != NULL 131725839Speter && findnode (log_data->authorlist, vnode->author) == NULL) 131825839Speter { 131925839Speter return 0; 132025839Speter } 132125839Speter } 132225839Speter 132325839Speter /* rlog considers all the -d options together when it decides 132425839Speter whether to print a revision, so we must be compatible. */ 132525839Speter if (log_data->datelist != NULL || log_data->singledatelist != NULL) 132625839Speter { 132725839Speter struct datelist *d; 132825839Speter 132925839Speter for (d = log_data->datelist; d != NULL; d = d->next) 133025839Speter { 133125839Speter int cmp; 133225839Speter 133325839Speter cmp = RCS_datecmp (vnode->date, d->start); 133425839Speter if (cmp > 0 || (cmp == 0 && d->inclusive)) 133525839Speter { 133625839Speter cmp = RCS_datecmp (vnode->date, d->end); 133725839Speter if (cmp < 0 || (cmp == 0 && d->inclusive)) 133825839Speter break; 133925839Speter } 134025839Speter } 134125839Speter 134225839Speter if (d == NULL) 134325839Speter { 134425839Speter /* Look through the list of specific dates. We want to 134525839Speter select the revision with the exact date found in the 134625839Speter start field. The commit code ensures that it is 134725839Speter impossible to check in multiple revisions of a single 134825839Speter file in a single second, so checking the date this way 134925839Speter should never select more than one revision. */ 135025839Speter for (d = log_data->singledatelist; d != NULL; d = d->next) 135125839Speter { 135225839Speter if (d->start != NULL 135325839Speter && RCS_datecmp (vnode->date, d->start) == 0) 135425839Speter { 135525839Speter break; 135625839Speter } 135725839Speter } 135825839Speter 135925839Speter if (d == NULL) 136025839Speter return 0; 136125839Speter } 136225839Speter } 136325839Speter 136425839Speter /* If the -r or -b options were used, REVLIST will be non NULL, 136525839Speter and we print the union of the specified revisions. */ 136625839Speter if (revlist != NULL) 136725839Speter { 136825839Speter char *v; 136925839Speter int vfields; 137025839Speter struct revlist *r; 137125839Speter 137225839Speter /* This code is taken from rlog. */ 137325839Speter v = vnode->version; 137425839Speter vfields = numdots (v) + 1; 137525839Speter for (r = revlist; r != NULL; r = r->next) 137625839Speter { 137781404Speter if (vfields == r->fields + (r->fields & 1) && 1378102840Speter (r->inclusive ? version_compare (v, r->first, r->fields) >= 0 : 1379102840Speter version_compare (v, r->first, r->fields) > 0) 1380102840Speter && version_compare (v, r->last, r->fields) <= 0) 138125839Speter { 138225839Speter return 1; 138325839Speter } 138425839Speter } 138525839Speter 138625839Speter /* If we get here, then the -b and/or the -r option was used, 138725839Speter but did not match this revision, so we reject it. */ 138825839Speter 138925839Speter return 0; 139025839Speter } 139125839Speter 139225839Speter /* By default, we print all revisions. */ 139325839Speter return 1; 139425839Speter} 139525839Speter 1396128266Speter 1397128266Speter 139825839Speter/* 139925839Speter * Output a single symbol. This is called via walklist. 140025839Speter */ 140125839Speter/*ARGSUSED*/ 140225839Speterstatic int 140325839Speterlog_symbol (p, closure) 140425839Speter Node *p; 140525839Speter void *closure; 140625839Speter{ 140725839Speter cvs_output ("\n\t", 2); 140825839Speter cvs_output (p->key, 0); 140925839Speter cvs_output (": ", 2); 141025839Speter cvs_output (p->data, 0); 141125839Speter return 0; 141225839Speter} 141325839Speter 1414128266Speter 1415128266Speter 141625839Speter/* 141725839Speter * Count the number of entries on a list. This is called via walklist. 141825839Speter */ 141925839Speter/*ARGSUSED*/ 142025839Speterstatic int 142125839Speterlog_count (p, closure) 142225839Speter Node *p; 142325839Speter void *closure; 142425839Speter{ 142525839Speter return 1; 142625839Speter} 142725839Speter 1428128266Speter 1429128266Speter 143025839Speter/* 143125839Speter * Sort out a single date specification by narrowing down the date 143225839Speter * until we find the specific selected revision. 143325839Speter */ 143425839Speterstatic int 143525839Speterlog_fix_singledate (p, closure) 143625839Speter Node *p; 143725839Speter void *closure; 143825839Speter{ 143925839Speter struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; 144025839Speter Node *pv; 144125839Speter RCSVers *vnode; 144225839Speter struct datelist *holdsingle, *holddate; 144325839Speter int requested; 144425839Speter 144525839Speter pv = findnode (data->rcs->versions, p->key); 144625839Speter if (pv == NULL) 144725839Speter error (1, 0, "missing version `%s' in RCS file `%s'", 144825839Speter p->key, data->rcs->path); 1449128266Speter vnode = pv->data; 145025839Speter 145125839Speter /* We are only interested if this revision passes any other tests. 145225839Speter Temporarily clear log_data->singledatelist to avoid confusing 145325839Speter log_version_requested. We also clear log_data->datelist, 145425839Speter because rlog considers all the -d options together. We don't 145525839Speter want to reject a revision because it does not match a date pair 145625839Speter if we are going to select it on the basis of the singledate. */ 145725839Speter holdsingle = data->log_data->singledatelist; 145825839Speter data->log_data->singledatelist = NULL; 145925839Speter holddate = data->log_data->datelist; 146025839Speter data->log_data->datelist = NULL; 146125839Speter requested = log_version_requested (data->log_data, data->revlist, 146225839Speter data->rcs, vnode); 146325839Speter data->log_data->singledatelist = holdsingle; 146425839Speter data->log_data->datelist = holddate; 146525839Speter 146625839Speter if (requested) 146725839Speter { 146825839Speter struct datelist *d; 146925839Speter 147025839Speter /* For each single date, if this revision is before the 147125839Speter specified date, but is closer than the previously selected 147225839Speter revision, select it instead. */ 147325839Speter for (d = data->log_data->singledatelist; d != NULL; d = d->next) 147425839Speter { 147525839Speter if (RCS_datecmp (vnode->date, d->end) <= 0 147625839Speter && (d->start == NULL 147725839Speter || RCS_datecmp (vnode->date, d->start) > 0)) 147825839Speter { 147925839Speter if (d->start != NULL) 148025839Speter free (d->start); 148125839Speter d->start = xstrdup (vnode->date); 148225839Speter } 148325839Speter } 148425839Speter } 148525839Speter 148625839Speter return 0; 148725839Speter} 148825839Speter 1489128266Speter 1490128266Speter 149125839Speter/* 149225839Speter * Count the number of revisions we are going to print. 149325839Speter */ 149425839Speterstatic int 149525839Speterlog_count_print (p, closure) 149625839Speter Node *p; 149725839Speter void *closure; 149825839Speter{ 149925839Speter struct log_data_and_rcs *data = (struct log_data_and_rcs *) closure; 150025839Speter Node *pv; 150125839Speter 150225839Speter pv = findnode (data->rcs->versions, p->key); 150325839Speter if (pv == NULL) 150425839Speter error (1, 0, "missing version `%s' in RCS file `%s'", 150525839Speter p->key, data->rcs->path); 150625839Speter if (log_version_requested (data->log_data, data->revlist, data->rcs, 1507128266Speter pv->data)) 150825839Speter return 1; 150925839Speter else 151025839Speter return 0; 151125839Speter} 151225839Speter 151325839Speter/* 151425839Speter * Print the list of changes, not including the trunk, in reverse 151525839Speter * order for each branch. 151625839Speter */ 151725839Speterstatic void 151825839Speterlog_tree (log_data, revlist, rcs, ver) 151925839Speter struct log_data *log_data; 152025839Speter struct revlist *revlist; 152125839Speter RCSNode *rcs; 152225839Speter const char *ver; 152325839Speter{ 152425839Speter Node *p; 152525839Speter RCSVers *vnode; 152625839Speter 152725839Speter p = findnode (rcs->versions, ver); 152825839Speter if (p == NULL) 152925839Speter error (1, 0, "missing version `%s' in RCS file `%s'", 153025839Speter ver, rcs->path); 1531128266Speter vnode = p->data; 153225839Speter if (vnode->next != NULL) 153325839Speter log_tree (log_data, revlist, rcs, vnode->next); 153425839Speter if (vnode->branches != NULL) 153525839Speter { 153625839Speter Node *head, *branch; 153725839Speter 153825839Speter /* We need to do the branches in reverse order. This breaks 153925839Speter the List abstraction, but so does most of the branch 154025839Speter manipulation in rcs.c. */ 154125839Speter head = vnode->branches->list; 154225839Speter for (branch = head->prev; branch != head; branch = branch->prev) 154325839Speter { 154425839Speter log_abranch (log_data, revlist, rcs, branch->key); 154525839Speter log_tree (log_data, revlist, rcs, branch->key); 154625839Speter } 154725839Speter } 154825839Speter} 154925839Speter 155025839Speter/* 155125839Speter * Log the changes for a branch, in reverse order. 155225839Speter */ 155325839Speterstatic void 155425839Speterlog_abranch (log_data, revlist, rcs, ver) 155525839Speter struct log_data *log_data; 155625839Speter struct revlist *revlist; 155725839Speter RCSNode *rcs; 155825839Speter const char *ver; 155925839Speter{ 156025839Speter Node *p; 156125839Speter RCSVers *vnode; 156225839Speter 156325839Speter p = findnode (rcs->versions, ver); 156425839Speter if (p == NULL) 156525839Speter error (1, 0, "missing version `%s' in RCS file `%s'", 156625839Speter ver, rcs->path); 1567128266Speter vnode = p->data; 156825839Speter if (vnode->next != NULL) 156925839Speter log_abranch (log_data, revlist, rcs, vnode->next); 157025839Speter log_version (log_data, revlist, rcs, vnode, 0); 157125839Speter} 157225839Speter 157325839Speter/* 157425839Speter * Print the log output for a single version. 157525839Speter */ 157625839Speterstatic void 157725839Speterlog_version (log_data, revlist, rcs, ver, trunk) 157825839Speter struct log_data *log_data; 157925839Speter struct revlist *revlist; 158025839Speter RCSNode *rcs; 158125839Speter RCSVers *ver; 158225839Speter int trunk; 158325839Speter{ 158425839Speter Node *p; 158525839Speter int year, mon, mday, hour, min, sec; 158625839Speter char buf[100]; 158725839Speter Node *padd, *pdel; 158825839Speter 158925839Speter if (! log_version_requested (log_data, revlist, rcs, ver)) 159025839Speter return; 159125839Speter 159225839Speter cvs_output ("----------------------------\nrevision ", 0); 159325839Speter cvs_output (ver->version, 0); 159425839Speter 159532785Speter p = findnode (RCS_getlocks (rcs), ver->version); 159625839Speter if (p != NULL) 159725839Speter { 159825839Speter cvs_output ("\tlocked by: ", 0); 159925839Speter cvs_output (p->data, 0); 160025839Speter cvs_output (";", 1); 160125839Speter } 160225839Speter 160325839Speter cvs_output ("\ndate: ", 0); 160425839Speter (void) sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min, 160525839Speter &sec); 160625839Speter if (year < 1900) 160725839Speter year += 1900; 160825839Speter sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday, 160925839Speter hour, min, sec); 161025839Speter cvs_output (buf, 0); 161125839Speter 161225839Speter cvs_output ("; author: ", 0); 161325839Speter cvs_output (ver->author, 0); 161425839Speter 161525839Speter cvs_output ("; state: ", 0); 161625839Speter cvs_output (ver->state, 0); 161725839Speter cvs_output (";", 1); 161825839Speter 161925839Speter if (! trunk) 162025839Speter { 162125839Speter padd = findnode (ver->other, ";add"); 162225839Speter pdel = findnode (ver->other, ";delete"); 162325839Speter } 162425839Speter else if (ver->next == NULL) 162525839Speter { 162625839Speter padd = NULL; 162725839Speter pdel = NULL; 162825839Speter } 162925839Speter else 163025839Speter { 163125839Speter Node *nextp; 163225839Speter RCSVers *nextver; 163325839Speter 163425839Speter nextp = findnode (rcs->versions, ver->next); 163525839Speter if (nextp == NULL) 163625839Speter error (1, 0, "missing version `%s' in `%s'", ver->next, 163725839Speter rcs->path); 1638128266Speter nextver = nextp->data; 163925839Speter pdel = findnode (nextver->other, ";add"); 164025839Speter padd = findnode (nextver->other, ";delete"); 164125839Speter } 164225839Speter 164325839Speter if (padd != NULL) 164425839Speter { 164525839Speter cvs_output (" lines: +", 0); 164625839Speter cvs_output (padd->data, 0); 164725839Speter cvs_output (" -", 2); 164825839Speter cvs_output (pdel->data, 0); 164925839Speter } 165025839Speter 165125839Speter if (ver->branches != NULL) 165225839Speter { 165325839Speter cvs_output ("\nbranches:", 0); 165425839Speter walklist (ver->branches, log_branch, (void *) NULL); 165525839Speter } 165625839Speter 165725839Speter cvs_output ("\n", 1); 165825839Speter 165925839Speter p = findnode (ver->other, "log"); 166032785Speter /* The p->date == NULL case is the normal one for an empty log 166132785Speter message (rcs-14 in sanity.sh). I don't think the case where 166232785Speter p->data is "" can happen (getrcskey in rcs.c checks for an 166332785Speter empty string and set the value to NULL in that case). My guess 166432785Speter would be the p == NULL case would mean an RCS file which was 166532785Speter missing the "log" keyword (which is illegal according to 166632785Speter rcsfile.5). */ 1667128266Speter if (p == NULL || p->data == NULL || *(char *)p->data == '\0') 166825839Speter cvs_output ("*** empty log message ***\n", 0); 166925839Speter else 167025839Speter { 167125839Speter /* FIXME: Technically, the log message could contain a null 167225839Speter byte. */ 167325839Speter cvs_output (p->data, 0); 1674128266Speter if (((char *)p->data)[strlen (p->data) - 1] != '\n') 167525839Speter cvs_output ("\n", 1); 167625839Speter } 167725839Speter} 167825839Speter 167925839Speter/* 168025839Speter * Output a branch version. This is called via walklist. 168125839Speter */ 168225839Speter/*ARGSUSED*/ 168325839Speterstatic int 168425839Speterlog_branch (p, closure) 168525839Speter Node *p; 168625839Speter void *closure; 168725839Speter{ 168825839Speter cvs_output (" ", 2); 168925839Speter if ((numdots (p->key) & 1) == 0) 169025839Speter cvs_output (p->key, 0); 169125839Speter else 169225839Speter { 169325839Speter char *f, *cp; 169425839Speter 169525839Speter f = xstrdup (p->key); 169625839Speter cp = strrchr (f, '.'); 169725839Speter *cp = '\0'; 169825839Speter cvs_output (f, 0); 169925839Speter free (f); 170025839Speter } 170125839Speter cvs_output (";", 1); 170225839Speter return 0; 170325839Speter} 170425839Speter 170525839Speter/* 170617721Speter * Print a warm fuzzy message 170717721Speter */ 170817721Speter/* ARGSUSED */ 170917721Speterstatic Dtype 171025839Speterlog_dirproc (callerdat, dir, repository, update_dir, entries) 171125839Speter void *callerdat; 1712128266Speter const char *dir; 1713128266Speter const char *repository; 1714128266Speter const char *update_dir; 171525839Speter List *entries; 171617721Speter{ 171717721Speter if (!isdir (dir)) 171817721Speter return (R_SKIP_ALL); 171917721Speter 172017721Speter if (!quiet) 172117721Speter error (0, 0, "Logging %s", update_dir); 172217721Speter return (R_PROCESS); 172317721Speter} 172425839Speter 172525839Speter/* 172625839Speter * Compare versions. This is taken from RCS compartial. 172725839Speter */ 172825839Speterstatic int 172925839Speterversion_compare (v1, v2, len) 173025839Speter const char *v1; 173125839Speter const char *v2; 173225839Speter int len; 173325839Speter{ 173425839Speter while (1) 173525839Speter { 173625839Speter int d1, d2, r; 173725839Speter 173825839Speter if (*v1 == '\0') 173925839Speter return 1; 174025839Speter if (*v2 == '\0') 174125839Speter return -1; 174225839Speter 174325839Speter while (*v1 == '0') 174425839Speter ++v1; 174554427Speter for (d1 = 0; isdigit ((unsigned char) v1[d1]); ++d1) 174625839Speter ; 174725839Speter 174825839Speter while (*v2 == '0') 174925839Speter ++v2; 175054427Speter for (d2 = 0; isdigit ((unsigned char) v2[d2]); ++d2) 175125839Speter ; 175225839Speter 175325839Speter if (d1 != d2) 175425839Speter return d1 < d2 ? -1 : 1; 175525839Speter 175625839Speter r = memcmp (v1, v2, d1); 175725839Speter if (r != 0) 175825839Speter return r; 175925839Speter 176025839Speter --len; 176125839Speter if (len == 0) 176225839Speter return 0; 176325839Speter 176425839Speter v1 += d1; 176525839Speter v2 += d1; 176625839Speter 176725839Speter if (*v1 == '.') 176825839Speter ++v1; 176925839Speter if (*v2 == '.') 177025839Speter ++v2; 177125839Speter } 177225839Speter} 1773