summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/history.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/history.c')
-rw-r--r--contrib/cvs/src/history.c1666
1 files changed, 0 insertions, 1666 deletions
diff --git a/contrib/cvs/src/history.c b/contrib/cvs/src/history.c
deleted file mode 100644
index 78fa0fe..0000000
--- a/contrib/cvs/src/history.c
+++ /dev/null
@@ -1,1666 +0,0 @@
-/*
- * Copyright (C) 1994-2005 The Free Software Foundation, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-/* **************** History of Users and Module ****************
- *
- * LOGGING: Append record to "${CVSROOT}/CVSROOTADM/CVSROOTADM_HISTORY".
- *
- * On For each Tag, Add, Checkout, Commit, Update or Release command,
- * one line of text is written to a History log.
- *
- * X date | user | CurDir | special | rev(s) | argument '\n'
- *
- * where: [The spaces in the example line above are not in the history file.]
- *
- * X is a single character showing the type of event:
- * T "Tag" cmd.
- * O "Checkout" cmd.
- * E "Export" cmd.
- * F "Release" cmd.
- * W "Update" cmd - No User file, Remove from Entries file.
- * U "Update" cmd - File was checked out over User file.
- * P "Update" cmd - User file was patched.
- * G "Update" cmd - File was merged successfully.
- * C "Update" cmd - File was merged and shows overlaps.
- * M "Commit" cmd - "Modified" file.
- * A "Commit" cmd - "Added" file.
- * R "Commit" cmd - "Removed" file.
- *
- * date is a fixed length 8-char hex representation of a Unix time_t.
- * [Starting here, variable fields are delimited by '|' chars.]
- *
- * user is the username of the person who typed the command.
- *
- * CurDir The directory where the action occurred. This should be the
- * absolute path of the directory which is at the same level as
- * the "Repository" field (for W,U,P,G,C & M,A,R).
- *
- * Repository For record types [W,U,P,G,C,M,A,R] this field holds the
- * repository read from the administrative data where the
- * command was typed.
- * T "A" --> New Tag, "D" --> Delete Tag
- * Otherwise it is the Tag or Date to modify.
- * O,F,E A "" (null field)
- *
- * rev(s) Revision number or tag.
- * T The Tag to apply.
- * O,E The Tag or Date, if specified, else "" (null field).
- * F "" (null field)
- * W The Tag or Date, if specified, else "" (null field).
- * U,P The Revision checked out over the User file.
- * G,C The Revision(s) involved in merge.
- * M,A,R RCS Revision affected.
- *
- * argument The module (for [TOEF]) or file (for [WUPGCMAR]) affected.
- *
- *
- *** Report categories: "User" and "Since" modifiers apply to all reports.
- * [For "sort" ordering see the "sort_order" routine.]
- *
- * Extract list of record types
- *
- * -e, -x [TOEFWUPGCMAR]
- *
- * Extracted records are simply printed, No analysis is performed.
- * All "field" modifiers apply. -e chooses all types.
- *
- * Checked 'O'ut modules
- *
- * -o, -w
- * Checked out modules. 'F' and 'O' records are examined and if
- * the last record for a repository/file is an 'O', a line is
- * printed. "-w" forces the "working dir" to be used in the
- * comparison instead of the repository.
- *
- * Committed (Modified) files
- *
- * -c, -l, -w
- * All 'M'odified, 'A'dded and 'R'emoved records are examined.
- * "Field" modifiers apply. -l forces a sort by file within user
- * and shows only the last modifier. -w works as in Checkout.
- *
- * Warning: Be careful with what you infer from the output of
- * "cvs hi -c -l". It means the last time *you*
- * changed the file, not the list of files for which
- * you were the last changer!!!
- *
- * Module history for named modules.
- * -m module, -l
- *
- * This is special. If one or more modules are specified, the
- * module names are remembered and the files making up the
- * modules are remembered. Only records matching exactly those
- * files and repositories are shown. Sorting by "module", then
- * filename, is implied. If -l ("last modified") is specified,
- * then "update" records (types WUPCG), tag and release records
- * are ignored and the last (by date) "modified" record.
- *
- * TAG history
- *
- * -T All Tag records are displayed.
- *
- *** Modifiers.
- *
- * Since ... [All records contain a timestamp, so any report
- * category can be limited by date.]
- *
- * -D date - The "date" is parsed into a Unix "time_t" and
- * records with an earlier time stamp are ignored.
- * -r rev/tag - A "rev" begins with a digit. A "tag" does not. If
- * you use this option, every file is searched for the
- * indicated rev/tag.
- * -t tag - The "tag" is searched for in the history file and no
- * record is displayed before the tag is found. An
- * error is printed if the tag is never found.
- * -b string - Records are printed only back to the last reference
- * to the string in the "module", "file" or
- * "repository" fields.
- *
- * Field Selections [Simple comparisons on existing fields. All field
- * selections are repeatable.]
- *
- * -a - All users.
- * -u user - If no user is given and '-a' is not given, only
- * records for the user typing the command are shown.
- * ==> If -a or -u is not specified, just use "self".
- *
- * -f filematch - Only records in which the "file" field contains the
- * string "filematch" are considered.
- *
- * -p repository - Only records in which the "repository" string is a
- * prefix of the "repos" field are considered.
- *
- * -n modulename - Only records which contain "modulename" in the
- * "module" field are considered.
- *
- *
- * EXAMPLES: ("cvs history", "cvs his" or "cvs hi")
- *
- *** Checked out files for username. (default self, e.g. "dgg")
- * cvs hi [equivalent to: "cvs hi -o -u dgg"]
- * cvs hi -u user [equivalent to: "cvs hi -o -u user"]
- * cvs hi -o [equivalent to: "cvs hi -o -u dgg"]
- *
- *** Committed (modified) files from the beginning of the file.
- * cvs hi -c [-u user]
- *
- *** Committed (modified) files since Midnight, January 1, 1990:
- * cvs hi -c -D 'Jan 1 1990' [-u user]
- *
- *** Committed (modified) files since tag "TAG" was stored in the history file:
- * cvs hi -c -t TAG [-u user]
- *
- *** Committed (modified) files since tag "TAG" was placed on the files:
- * cvs hi -c -r TAG [-u user]
- *
- *** Who last committed file/repository X?
- * cvs hi -c -l -[fp] X
- *
- *** Modified files since tag/date/file/repos?
- * cvs hi -c {-r TAG | -D Date | -b string}
- *
- *** Tag history
- * cvs hi -T
- *
- *** History of file/repository/module X.
- * cvs hi -[fpn] X
- *
- *** History of user "user".
- * cvs hi -e -u user
- *
- *** Dump (eXtract) specified record types
- * cvs hi -x [TOEFWUPGCMAR]
- *
- *
- * FUTURE: J[Join], I[Import] (Not currently implemented.)
- *
- */
-
-#include "cvs.h"
-#include "history.h"
-#include "savecwd.h"
-
-static struct hrec
-{
- char *type; /* Type of record (In history record) */
- char *user; /* Username (In history record) */
- char *dir; /* "Compressed" Working dir (In history record) */
- char *repos; /* (Tag is special.) Repository (In history record) */
- char *rev; /* Revision affected (In history record) */
- char *file; /* Filename (In history record) */
- char *end; /* Ptr into repository to copy at end of workdir */
- char *mod; /* The module within which the file is contained */
- time_t date; /* Calculated from date stored in record */
- long idx; /* Index of record, for "stable" sort. */
-} *hrec_head;
-static long hrec_idx;
-
-
-static void fill_hrec PROTO((char *line, struct hrec * hr));
-static int accept_hrec PROTO((struct hrec * hr, struct hrec * lr));
-static int select_hrec PROTO((struct hrec * hr));
-static int sort_order PROTO((const PTR l, const PTR r));
-static int within PROTO((char *find, char *string));
-static void expand_modules PROTO((void));
-static void read_hrecs PROTO((char *fname));
-static void report_hrecs PROTO((void));
-static void save_file PROTO((char *dir, char *name, char *module));
-static void save_module PROTO((char *module));
-static void save_user PROTO((char *name));
-
-#define USER_INCREMENT 2
-#define FILE_INCREMENT 128
-#define MODULE_INCREMENT 5
-#define HREC_INCREMENT 128
-
-static short report_count;
-
-static short extract;
-static short extract_all;
-static short v_checkout;
-static short modified;
-static short tag_report;
-static short module_report;
-static short working;
-static short last_entry;
-static short all_users;
-
-static short user_sort;
-static short repos_sort;
-static short file_sort;
-static short module_sort;
-
-static short tz_local;
-static time_t tz_seconds_east_of_GMT;
-static char *tz_name = "+0000";
-
-char *logHistory;
-
-/* -r, -t, or -b options, malloc'd. These are "" if the option in
- question is not specified or is overridden by another option. The
- main reason for using "" rather than NULL is historical. Together
- with since_date, these are a mutually exclusive set; one overrides the
- others. */
-static char *since_rev;
-static char *since_tag;
-static char *backto;
-/* -D option, or 0 if not specified. RCS format. */
-static char * since_date;
-
-static struct hrec *last_since_tag;
-static struct hrec *last_backto;
-
-/* Record types to look for, malloc'd. Probably could be statically
- allocated, but only if we wanted to check for duplicates more than
- we do. */
-static char *rec_types;
-
-static size_t hrec_count;
-static size_t hrec_max;
-
-static char **user_list; /* Ptr to array of ptrs to user names */
-static size_t user_max; /* Number of elements allocated */
-static size_t user_count; /* Number of elements used */
-
-static struct file_list_str
-{
- char *l_file;
- char *l_module;
-} *file_list; /* Ptr to array file name structs */
-static size_t file_max; /* Number of elements allocated */
-static size_t file_count; /* Number of elements used */
-
-static char **mod_list; /* Ptr to array of ptrs to module names */
-static size_t mod_max; /* Number of elements allocated */
-static size_t mod_count; /* Number of elements used */
-
-static char *histfile; /* Ptr to the history file name */
-
-/* This is pretty unclear. First of all, separating "flags" vs.
- "options" (I think the distinction is that "options" take arguments)
- is nonstandard, and not something we do elsewhere in CVS. Second of
- all, what does "reports" mean? I think it means that you can only
- supply one of those options, but "reports" hardly has that meaning in
- a self-explanatory way. */
-static const char *const history_usg[] =
-{
- "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n",
- " Reports:\n",
- " -T Produce report on all TAGs\n",
- " -c Committed (Modified) files\n",
- " -o Checked out modules\n",
- " -m <module> Look for specified module (repeatable)\n",
- " -x [" ALL_HISTORY_REC_TYPES "] Extract by record type\n",
- " -e Everything (same as -x, but all record types)\n",
- " Flags:\n",
- " -a All users (Default is self)\n",
- " -l Last modified (committed or modified report)\n",
- " -w Working directory must match\n",
- " Options:\n",
- " -D <date> Since date (Many formats)\n",
- " -b <str> Back to record with str in module/file/repos field\n",
- " -f <file> Specified file (same as command line) (repeatable)\n",
- " -n <modulename> In module (repeatable)\n",
- " -p <repos> In repository (repeatable)\n",
- " -r <rev/tag> Since rev or tag (looks inside RCS files!)\n",
- " -t <tag> Since tag record placed in history file (by anyone).\n",
- " -u <user> For user name (repeatable)\n",
- " -z <tz> Output for time zone <tz> (e.g. -z -0700)\n",
- NULL};
-
-/* Sort routine for qsort:
- - If a user is selected at all, sort it first. User-within-file is useless.
- - If a module was selected explicitly, sort next on module.
- - Then sort by file. "File" is "repository/file" unless "working" is set,
- then it is "workdir/file". (Revision order should always track date.)
- - Always sort timestamp last.
-*/
-static int
-sort_order (l, r)
- const PTR l;
- const PTR r;
-{
- int i;
- const struct hrec *left = (const struct hrec *) l;
- const struct hrec *right = (const struct hrec *) r;
-
- if (user_sort) /* If Sort by username, compare users */
- {
- if ((i = strcmp (left->user, right->user)) != 0)
- return (i);
- }
- if (module_sort) /* If sort by modules, compare module names */
- {
- if (left->mod && right->mod)
- if ((i = strcmp (left->mod, right->mod)) != 0)
- return (i);
- }
- if (repos_sort) /* If sort by repository, compare them. */
- {
- if ((i = strcmp (left->repos, right->repos)) != 0)
- return (i);
- }
- if (file_sort) /* If sort by filename, compare files, NOT dirs. */
- {
- if ((i = strcmp (left->file, right->file)) != 0)
- return (i);
-
- if (working)
- {
- if ((i = strcmp (left->dir, right->dir)) != 0)
- return (i);
-
- if ((i = strcmp (left->end, right->end)) != 0)
- return (i);
- }
- }
-
- /*
- * By default, sort by date, time
- * XXX: This fails after 2030 when date slides into sign bit
- */
- if ((i = ((long) (left->date) - (long) (right->date))) != 0)
- return (i);
-
- /* For matching dates, keep the sort stable by using record index */
- return (left->idx - right->idx);
-}
-
-int
-history (argc, argv)
- int argc;
- char **argv;
-{
- int i, c;
- char *fname;
-
- if (argc == -1)
- usage (history_usg);
-
- since_rev = xstrdup ("");
- since_tag = xstrdup ("");
- backto = xstrdup ("");
- rec_types = xstrdup ("");
- optind = 0;
- while ((c = getopt (argc, argv, "+Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1)
- {
- switch (c)
- {
- case 'T': /* Tag list */
- report_count++;
- tag_report++;
- break;
- case 'a': /* For all usernames */
- all_users++;
- break;
- case 'c':
- report_count++;
- modified = 1;
- break;
- case 'e':
- report_count++;
- extract_all++;
- free (rec_types);
- rec_types = xstrdup (ALL_HISTORY_REC_TYPES);
- break;
- case 'l': /* Find Last file record */
- last_entry = 1;
- break;
- case 'o':
- report_count++;
- v_checkout = 1;
- break;
- case 'w': /* Match Working Dir (CurDir) fields */
- working = 1;
- break;
- case 'X': /* Undocumented debugging flag */
-#ifdef DEBUG
- histfile = optarg;
-#endif
- break;
-
- case 'D': /* Since specified date */
- if (*since_rev || *since_tag || *backto)
- {
- error (0, 0, "date overriding rev/tag/backto");
- *since_rev = *since_tag = *backto = '\0';
- }
- since_date = Make_Date (optarg);
- break;
- case 'b': /* Since specified file/Repos */
- if (since_date || *since_rev || *since_tag)
- {
- error (0, 0, "backto overriding date/rev/tag");
- *since_rev = *since_tag = '\0';
- if (since_date != NULL)
- free (since_date);
- since_date = NULL;
- }
- free (backto);
- backto = xstrdup (optarg);
- break;
- case 'f': /* For specified file */
- save_file (NULL, optarg, NULL);
- break;
- case 'm': /* Full module report */
- if (!module_report++) report_count++;
- /* fall through */
- case 'n': /* Look for specified module */
- save_module (optarg);
- break;
- case 'p': /* For specified directory */
- save_file (optarg, NULL, NULL);
- break;
- case 'r': /* Since specified Tag/Rev */
- if (since_date || *since_tag || *backto)
- {
- error (0, 0, "rev overriding date/tag/backto");
- *since_tag = *backto = '\0';
- if (since_date != NULL)
- free (since_date);
- since_date = NULL;
- }
- free (since_rev);
- since_rev = xstrdup (optarg);
- break;
- case 't': /* Since specified Tag/Rev */
- if (since_date || *since_rev || *backto)
- {
- error (0, 0, "tag overriding date/marker/file/repos");
- *since_rev = *backto = '\0';
- if (since_date != NULL)
- free (since_date);
- since_date = NULL;
- }
- free (since_tag);
- since_tag = xstrdup (optarg);
- break;
- case 'u': /* For specified username */
- save_user (optarg);
- break;
- case 'x':
- report_count++;
- extract++;
- {
- char *cp;
-
- for (cp = optarg; *cp; cp++)
- if (!strchr (ALL_HISTORY_REC_TYPES, *cp))
- error (1, 0, "%c is not a valid report type", *cp);
- }
- free (rec_types);
- rec_types = xstrdup (optarg);
- break;
- case 'z':
- tz_local =
- (optarg[0] == 'l' || optarg[0] == 'L')
- && (optarg[1] == 't' || optarg[1] == 'T')
- && !optarg[2];
- if (tz_local)
- tz_name = optarg;
- else
- {
- /*
- * Convert a known time with the given timezone to time_t.
- * Use the epoch + 23 hours, so timezones east of GMT work.
- */
- static char f[] = "1/1/1970 23:00 %s";
- char *buf = xmalloc (sizeof (f) - 2 + strlen (optarg));
- time_t t;
- sprintf (buf, f, optarg);
- t = get_date (buf, (struct timeb *) NULL);
- free (buf);
- if (t == (time_t) -1)
- error (0, 0, "%s is not a known time zone", optarg);
- else
- {
- /*
- * Convert to seconds east of GMT, removing the
- * 23-hour offset mentioned above.
- */
- tz_seconds_east_of_GMT = (time_t)23 * 60 * 60 - t;
- tz_name = optarg;
- }
- }
- break;
- case '?':
- default:
- usage (history_usg);
- break;
- }
- }
- argc -= optind;
- argv += optind;
- for (i = 0; i < argc; i++)
- save_file (NULL, argv[i], NULL);
-
-
- /* ================ Now analyze the arguments a bit */
- if (!report_count)
- v_checkout++;
- else if (report_count > 1)
- error (1, 0, "Only one report type allowed from: \"-Tcomxe\".");
-
-#ifdef CLIENT_SUPPORT
- if (current_parsed_root->isremote)
- {
- struct file_list_str *f1;
- char **mod;
-
- /* We're the client side. Fire up the remote server. */
- start_server ();
-
- ign_setup ();
-
- if (tag_report)
- send_arg("-T");
- if (all_users)
- send_arg("-a");
- if (modified)
- send_arg("-c");
- if (last_entry)
- send_arg("-l");
- if (v_checkout)
- send_arg("-o");
- if (working)
- send_arg("-w");
- if (histfile)
- send_arg("-X");
- if (since_date)
- client_senddate (since_date);
- if (backto[0] != '\0')
- option_with_arg ("-b", backto);
- for (f1 = file_list; f1 < &file_list[file_count]; ++f1)
- {
- if (f1->l_file[0] == '*')
- option_with_arg ("-p", f1->l_file + 1);
- else
- option_with_arg ("-f", f1->l_file);
- }
- if (module_report)
- send_arg("-m");
- for (mod = mod_list; mod < &mod_list[mod_count]; ++mod)
- option_with_arg ("-n", *mod);
- if (*since_rev)
- option_with_arg ("-r", since_rev);
- if (*since_tag)
- option_with_arg ("-t", since_tag);
- for (mod = user_list; mod < &user_list[user_count]; ++mod)
- option_with_arg ("-u", *mod);
- if (extract_all)
- send_arg("-e");
- if (extract)
- option_with_arg ("-x", rec_types);
- option_with_arg ("-z", tz_name);
-
- send_to_server ("history\012", 0);
- return get_responses_and_close ();
- }
-#endif
-
- if (all_users)
- save_user ("");
-
- if (mod_list)
- expand_modules ();
-
- if (tag_report)
- {
- if (!strchr (rec_types, 'T'))
- {
- rec_types = xrealloc (rec_types, strlen (rec_types) + 5);
- (void) strcat (rec_types, "T");
- }
- }
- else if (extract || extract_all)
- {
- if (user_list)
- user_sort++;
- }
- else if (modified)
- {
- free (rec_types);
- rec_types = xstrdup ("MAR");
- /*
- * If the user has not specified a date oriented flag ("Since"), sort
- * by Repository/file before date. Default is "just" date.
- */
- if (last_entry
- || (!since_date && !*since_rev && !*since_tag && !*backto))
- {
- repos_sort++;
- file_sort++;
- /*
- * If we are not looking for last_modified and the user specified
- * one or more users to look at, sort by user before filename.
- */
- if (!last_entry && user_list)
- user_sort++;
- }
- }
- else if (module_report)
- {
- free (rec_types);
- rec_types = xstrdup (last_entry ? "OMAR" : ALL_HISTORY_REC_TYPES);
- module_sort++;
- repos_sort++;
- file_sort++;
- working = 0; /* User's workdir doesn't count here */
- }
- else
- /* Must be "checkout" or default */
- {
- free (rec_types);
- rec_types = xstrdup ("OF");
- /* See comments in "modified" above */
- if (!last_entry && user_list)
- user_sort++;
- if (last_entry
- || (!since_date && !*since_rev && !*since_tag && !*backto))
- file_sort++;
- }
-
- /* If no users were specified, use self (-a saves a universal ("") user) */
- if (!user_list)
- save_user (getcaller ());
-
- /* If we're looking back to a Tag value, must consider "Tag" records */
- if (*since_tag && !strchr (rec_types, 'T'))
- {
- rec_types = xrealloc (rec_types, strlen (rec_types) + 5);
- (void) strcat (rec_types, "T");
- }
-
- if (histfile)
- fname = xstrdup (histfile);
- else
- {
- fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM)
- + sizeof (CVSROOTADM_HISTORY) + 10);
- (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory,
- CVSROOTADM, CVSROOTADM_HISTORY);
- }
-
- read_hrecs (fname);
- if(hrec_count>0)
- {
- qsort ((PTR) hrec_head, hrec_count,
- sizeof (struct hrec), sort_order);
- }
- report_hrecs ();
- free (fname);
- if (since_date != NULL)
- free (since_date);
- free (since_rev);
- free (since_tag);
- free (backto);
- free (rec_types);
-
- return (0);
-}
-
-void
-history_write (type, update_dir, revs, name, repository)
- int type;
- const char *update_dir;
- const char *revs;
- const char *name;
- const char *repository;
-{
- char *fname;
- char *workdir;
- char *username = getcaller ();
- int fd;
- char *line;
- char *slash = "", *cp;
- const char *cp2, *repos;
- int i;
- static char *tilde = "";
- static char *PrCurDir = NULL;
-
- if (logoff) /* History is turned off by noexec or
- * readonlyfs.
- */
- return;
- if (strchr (logHistory, type) == NULL)
- return;
- fname = xmalloc (strlen (current_parsed_root->directory)
- + sizeof (CVSROOTADM)
- + sizeof (CVSROOTADM_HISTORY) + 3);
- (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory,
- CVSROOTADM, CVSROOTADM_HISTORY);
-
- /* turn off history logging if the history file does not exist */
- /* FIXME: This should check for write permissions instead. This way,
- * O_CREATE could be added back into the call to open() below and
- * there would be no race condition involved in log rotation.
- *
- * Note that the new method of turning off logging would be either via
- * the CVSROOT/config file (probably the quicker method, but would need
- * to be added, or at least checked for, too) or by creating a dummy
- * history file with 0444 permissions.
- */
- if (!isfile (fname))
- {
- logoff = 1;
- goto out;
- }
-
- if (trace)
- fprintf (stderr, "%s-> fopen(%s,a)\n",
- CLIENT_SERVER_STR, fname);
- if (noexec)
- goto out;
-
- if (!history_lock (current_parsed_root->directory))
- /* history_lock() will already have printed an error on failure. */
- goto out;
-
- fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | OPEN_BINARY, 0666);
- if (fd < 0)
- {
- if (! really_quiet)
- {
- error (0, errno, "warning: cannot write to history file %s",
- fname);
- }
- goto out;
- }
-
- repos = Short_Repository (repository);
-
- if (!PrCurDir)
- {
- char *pwdir;
-
- pwdir = get_homedir ();
- PrCurDir = CurDir;
- if (pwdir != NULL)
- {
- /* Assumes neither CurDir nor pwdir ends in '/' */
- i = strlen (pwdir);
- if (!strncmp (CurDir, pwdir, i))
- {
- PrCurDir += i; /* Point to '/' separator */
- tilde = "~";
- }
- else
- {
- /* Try harder to find a "homedir" */
- struct saved_cwd cwd;
- char *homedir;
-
- if (save_cwd (&cwd))
- error_exit ();
-
- if (CVS_CHDIR (pwdir) < 0 || (homedir = xgetwd ()) == NULL)
- homedir = pwdir;
-
- if (restore_cwd (&cwd, NULL))
- error_exit ();
- free_cwd (&cwd);
-
- i = strlen (homedir);
- if (!strncmp (CurDir, homedir, i))
- {
- PrCurDir += i; /* Point to '/' separator */
- tilde = "~";
- }
-
- if (homedir != pwdir)
- free (homedir);
- }
- }
- }
-
- if (type == 'T')
- {
- repos = update_dir;
- update_dir = "";
- }
- else if (update_dir && *update_dir)
- slash = "/";
- else
- update_dir = "";
-
- workdir = xmalloc (strlen (tilde) + strlen (PrCurDir) + strlen (slash)
- + strlen (update_dir) + 10);
- (void) sprintf (workdir, "%s%s%s%s", tilde, PrCurDir, slash, update_dir);
-
- /*
- * "workdir" is the directory where the file "name" is. ("^~" == $HOME)
- * "repos" is the Repository, relative to $CVSROOT where the RCS file is.
- *
- * "$workdir/$name" is the working file name.
- * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository.
- *
- * First, note that the history format was intended to save space, not
- * to be human readable.
- *
- * The working file directory ("workdir") and the Repository ("repos")
- * usually end with the same one or more directory elements. To avoid
- * duplication (and save space), the "workdir" field ends with
- * an integer offset into the "repos" field. This offset indicates the
- * beginning of the "tail" of "repos", after which all characters are
- * duplicates.
- *
- * In other words, if the "workdir" field has a '*' (a very stupid thing
- * to put in a filename) in it, then every thing following the last '*'
- * is a hex offset into "repos" of the first character from "repos" to
- * append to "workdir" to finish the pathname.
- *
- * It might be easier to look at an example:
- *
- * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
- *
- * Indicates that the workdir is really "~/work/cvs/examples", saving
- * 10 characters, where "~/work*d" would save 6 characters and mean that
- * the workdir is really "~/work/examples". It will mean more on
- * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term
- *
- * "workdir" is always an absolute pathname (~/xxx is an absolute path)
- * "repos" is always a relative pathname. So we can assume that we will
- * never run into the top of "workdir" -- there will always be a '/' or
- * a '~' at the head of "workdir" that is not matched by anything in
- * "repos". On the other hand, we *can* run off the top of "repos".
- *
- * Only "compress" if we save characters.
- */
-
- if (!repos)
- repos = "";
-
- cp = workdir + strlen (workdir) - 1;
- cp2 = repos + strlen (repos) - 1;
- for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--)
- i++;
-
- if (i > 2)
- {
- i = strlen (repos) - i;
- (void) sprintf ((cp + 1), "*%x", i);
- }
-
- if (!revs)
- revs = "";
- line = xmalloc (strlen (username) + strlen (workdir) + strlen (repos)
- + strlen (revs) + strlen (name) + 100);
- sprintf (line, "%c%08lx|%s|%s|%s|%s|%s\n",
- type, (long) time ((time_t *) NULL),
- username, workdir, repos, revs, name);
-
- /* Lessen some race conditions on non-Posix-compliant hosts. */
- if (lseek (fd, (off_t) 0, SEEK_END) == -1)
- error (1, errno, "cannot seek to end of history file: %s", fname);
-
- if (write (fd, line, strlen (line)) < 0)
- error (1, errno, "cannot write to history file: %s", fname);
- free (line);
- if (close (fd) != 0)
- error (1, errno, "cannot close history file: %s", fname);
- free (workdir);
- out:
- clear_history_lock ();
- free (fname);
-}
-
-/*
- * save_user() adds a user name to the user list to select. Zero-length
- * username ("") matches any user.
- */
-static void
-save_user (name)
- char *name;
-{
- if (user_count == user_max)
- {
- user_max = xsum (user_max, USER_INCREMENT);
- if (user_count == user_max
- || size_overflow_p (xtimes (user_max, sizeof (char *))))
- {
- error (0, 0, "save_user: too many users");
- return;
- }
- user_list = xrealloc (user_list, xtimes (user_max, sizeof (char *)));
- }
- user_list[user_count++] = xstrdup (name);
-}
-
-/*
- * save_file() adds file name and associated module to the file list to select.
- *
- * If "dir" is null, store a file name as is.
- * If "name" is null, store a directory name with a '*' on the front.
- * Else, store concatenated "dir/name".
- *
- * Later, in the "select" stage:
- * - if it starts with '*', it is prefix-matched against the repository.
- * - if it has a '/' in it, it is matched against the repository/file.
- * - else it is matched against the file name.
- */
-static void
-save_file (dir, name, module)
- char *dir;
- char *name;
- char *module;
-{
- char *cp;
- struct file_list_str *fl;
-
- if (file_count == file_max)
- {
- file_max = xsum (file_max, FILE_INCREMENT);
- if (file_count == file_max
- || size_overflow_p (xtimes (file_max, sizeof (*fl))))
- {
- error (0, 0, "save_file: too many files");
- return;
- }
- file_list = xrealloc (file_list, xtimes (file_max, sizeof (*fl)));
- }
- fl = &file_list[file_count++];
- fl->l_file = cp = xmalloc (dir ? strlen (dir) : 0
- + name ? strlen (name) : 0
- + 2);
- fl->l_module = module;
-
- if (dir && *dir)
- {
- if (name && *name)
- {
- (void) strcpy (cp, dir);
- (void) strcat (cp, "/");
- (void) strcat (cp, name);
- }
- else
- {
- *cp++ = '*';
- (void) strcpy (cp, dir);
- }
- }
- else
- {
- if (name && *name)
- {
- (void) strcpy (cp, name);
- }
- else
- {
- error (0, 0, "save_file: null dir and file name");
- }
- }
-}
-
-static void
-save_module (module)
- char *module;
-{
- if (mod_count == mod_max)
- {
- mod_max = xsum (mod_max, MODULE_INCREMENT);
- if (mod_count == mod_max
- || size_overflow_p (xtimes (mod_max, sizeof (char *))))
- {
- error (0, 0, "save_module: too many modules");
- return;
- }
- mod_list = xrealloc (mod_list, xtimes (mod_max, sizeof (char *)));
- }
- mod_list[mod_count++] = xstrdup (module);
-}
-
-static void
-expand_modules ()
-{
-}
-
-/* fill_hrec
- *
- * Take a ptr to 7-part history line, ending with a newline, for example:
- *
- * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
- *
- * Split it into 7 parts and drop the parts into a "struct hrec".
- * Return a pointer to the character following the newline.
- *
- */
-
-#define NEXT_BAR(here) do { \
- while (isspace(*line)) line++; \
- hr->here = line; \
- while ((c = *line++) && c != '|') ; \
- if (!c) return; line[-1] = '\0'; \
- } while (0)
-
-static void
-fill_hrec (line, hr)
- char *line;
- struct hrec *hr;
-{
- char *cp;
- int c;
-
- hr->type = hr->user = hr->dir = hr->repos = hr->rev = hr->file =
- hr->end = hr->mod = NULL;
- hr->date = -1;
- hr->idx = ++hrec_idx;
-
- while (isspace ((unsigned char) *line))
- line++;
-
- hr->type = line++;
- hr->date = strtoul (line, &cp, 16);
- if (cp == line || *cp != '|')
- return;
- line = cp + 1;
- NEXT_BAR (user);
- NEXT_BAR (dir);
- if ((cp = strrchr (hr->dir, '*')) != NULL)
- {
- *cp++ = '\0';
- hr->end = line + strtoul (cp, NULL, 16);
- }
- else
- hr->end = line - 1; /* A handy pointer to '\0' */
- NEXT_BAR (repos);
- NEXT_BAR (rev);
- if (strchr ("FOET", *(hr->type)))
- hr->mod = line;
-
- NEXT_BAR (file);
-}
-
-
-#ifndef STAT_BLOCKSIZE
-#if HAVE_STRUCT_STAT_ST_BLKSIZE
-#define STAT_BLOCKSIZE(s) (s).st_blksize
-#else
-#define STAT_BLOCKSIZE(s) (4 * 1024)
-#endif
-#endif
-
-
-/* read_hrecs's job is to read the history file and fill in all the "hrec"
- * (history record) array elements with the ones we need to print.
- *
- * Logic:
- * - Read a block from the file.
- * - Walk through the block parsing line into hr records.
- * - if the hr isn't used, free its strings, if it is, bump the hrec counter
- * - at the end of a block, copy the end of the current block to the start
- * of space for the next block, then read in the next block. If we get less
- * than the whole block, we're done.
- */
-static void
-read_hrecs (fname)
- char *fname;
-{
- unsigned char *cpstart, *cpend, *cp, *nl;
- char *hrline;
- int i;
- int fd;
- struct stat st_buf;
-
- if ((fd = CVS_OPEN (fname, O_RDONLY | OPEN_BINARY)) < 0)
- error (1, errno, "cannot open history file: %s", fname);
-
- if (fstat (fd, &st_buf) < 0)
- error (1, errno, "can't stat history file");
-
- if (!(st_buf.st_size))
- error (1, 0, "history file is empty");
-
- cpstart = xmalloc (2 * STAT_BLOCKSIZE(st_buf));
- cpstart[0] = '\0';
- cp = cpend = cpstart;
-
- hrec_max = HREC_INCREMENT;
- hrec_head = xmalloc (hrec_max * sizeof (struct hrec));
- hrec_idx = 0;
-
- for (;;)
- {
- for (nl = cp; nl < cpend && *nl != '\n'; nl++)
- if (!isprint(*nl)) *nl = ' ';
-
- if (nl >= cpend)
- {
- if (nl - cp >= STAT_BLOCKSIZE(st_buf))
- {
- error(1, 0, "history line %ld too long (> %lu)", hrec_idx + 1,
- (unsigned long) STAT_BLOCKSIZE(st_buf));
- }
- if (nl > cp)
- memmove (cpstart, cp, nl - cp);
- nl = cpstart + (nl - cp);
- cp = cpstart;
- i = read (fd, nl, STAT_BLOCKSIZE(st_buf));
- if (i > 0)
- {
- cpend = nl + i;
- *cpend = '\0';
- continue;
- }
- if (i < 0)
- error (1, errno, "error reading history file");
- if (nl == cp) break;
- error (0, 0, "warning: no newline at end of history file");
- }
- *nl = '\0';
-
- if (hrec_count == hrec_max)
- {
- struct hrec *old_head = hrec_head;
-
- hrec_max = xsum (hrec_max, HREC_INCREMENT);
- if (hrec_count == hrec_max
- || size_overflow_p (xtimes (hrec_max, sizeof (struct hrec))))
- error (1, 0, "Too many history records in history file.");
-
- hrec_head = xrealloc (hrec_head,
- xtimes (hrec_max, sizeof (struct hrec)));
- if (last_since_tag)
- last_since_tag = hrec_head + (last_since_tag - old_head);
- if (last_backto)
- last_backto = hrec_head + (last_backto - old_head);
- }
-
- /* fill_hrec dates from when history read the entire
- history file in one chunk, and then records were pulled out
- by pointing to the various parts of this big chunk. This is
- why there are ugly hacks here: I don't want to completely
- re-write the whole history stuff right now. */
-
- hrline = xstrdup ((char *)cp);
- fill_hrec (hrline, &hrec_head[hrec_count]);
- if (select_hrec (&hrec_head[hrec_count]))
- hrec_count++;
- else
- free(hrline);
-
- cp = nl + 1;
- }
- free (cpstart);
- close (fd);
-
- /* Special selection problem: If "since_tag" is set, we have saved every
- * record from the 1st occurrence of "since_tag", when we want to save
- * records since the *last* occurrence of "since_tag". So what we have
- * to do is bump hrec_head forward and reduce hrec_count accordingly.
- */
- if (last_since_tag)
- {
- hrec_count -= (last_since_tag - hrec_head);
- hrec_head = last_since_tag;
- }
-
- /* Much the same thing is necessary for the "backto" option. */
- if (last_backto)
- {
- hrec_count -= (last_backto - hrec_head);
- hrec_head = last_backto;
- }
-}
-
-/* Utility program for determining whether "find" is inside "string" */
-static int
-within (find, string)
- char *find, *string;
-{
- int c, len;
-
- if (!find || !string)
- return (0);
-
- c = *find++;
- len = strlen (find);
-
- while (*string)
- {
- if (!(string = strchr (string, c)))
- return (0);
- string++;
- if (!strncmp (find, string, len))
- return (1);
- }
- return (0);
-}
-
-/* The purpose of "select_hrec" is to apply the selection criteria based on
- * the command arguments and defaults and return a flag indicating whether
- * this record should be remembered for printing.
- */
-static int
-select_hrec (hr)
- struct hrec *hr;
-{
- char **cpp, *cp, *cp2;
- struct file_list_str *fl;
- int count;
-
- /* basic validity checking */
- if (!hr->type || !hr->user || !hr->dir || !hr->repos || !hr->rev ||
- !hr->file || !hr->end)
- {
- error (0, 0, "warning: history line %ld invalid", hr->idx);
- return (0);
- }
-
- /* "Since" checking: The argument parser guarantees that only one of the
- * following four choices is set:
- *
- * 1. If "since_date" is set, it contains the date specified on the
- * command line. hr->date fields earlier than "since_date" are ignored.
- * 2. If "since_rev" is set, it contains either an RCS "dotted" revision
- * number (which is of limited use) or a symbolic TAG. Each RCS file
- * is examined and the date on the specified revision (or the revision
- * corresponding to the TAG) in the RCS file (CVSROOT/repos/file) is
- * compared against hr->date as in 1. above.
- * 3. If "since_tag" is set, matching tag records are saved. The field
- * "last_since_tag" is set to the last one of these. Since we don't
- * know where the last one will be, all records are saved from the
- * first occurrence of the TAG. Later, at the end of "select_hrec"
- * records before the last occurrence of "since_tag" are skipped.
- * 4. If "backto" is set, all records with a module name or file name
- * matching "backto" are saved. In addition, all records with a
- * repository field with a *prefix* matching "backto" are saved.
- * The field "last_backto" is set to the last one of these. As in
- * 3. above, "select_hrec" adjusts to include the last one later on.
- */
- if (since_date)
- {
- char *ourdate = date_from_time_t (hr->date);
- count = RCS_datecmp (ourdate, since_date);
- free (ourdate);
- if (count < 0)
- return (0);
- }
- else if (*since_rev)
- {
- Vers_TS *vers;
- time_t t;
- struct file_info finfo;
-
- memset (&finfo, 0, sizeof finfo);
- finfo.file = hr->file;
- /* Not used, so don't worry about it. */
- finfo.update_dir = NULL;
- finfo.fullname = finfo.file;
- finfo.repository = hr->repos;
- finfo.entries = NULL;
- finfo.rcs = NULL;
-
- vers = Version_TS (&finfo, (char *) NULL, since_rev, (char *) NULL,
- 1, 0);
- if (vers->vn_rcs)
- {
- if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0))
- != (time_t) 0)
- {
- if (hr->date < t)
- {
- freevers_ts (&vers);
- return (0);
- }
- }
- }
- freevers_ts (&vers);
- }
- else if (*since_tag)
- {
- if (*(hr->type) == 'T')
- {
- /*
- * A 'T'ag record, the "rev" field holds the tag to be set,
- * while the "repos" field holds "D"elete, "A"dd or a rev.
- */
- if (within (since_tag, hr->rev))
- {
- last_since_tag = hr;
- return (1);
- }
- else
- return (0);
- }
- if (!last_since_tag)
- return (0);
- }
- else if (*backto)
- {
- if (within (backto, hr->file) || within (backto, hr->mod) ||
- within (backto, hr->repos))
- last_backto = hr;
- else
- return (0);
- }
-
- /* User checking:
- *
- * Run down "user_list", match username ("" matches anything)
- * If "" is not there and actual username is not there, return failure.
- */
- if (user_list && hr->user)
- {
- for (cpp = user_list, count = user_count; count; cpp++, count--)
- {
- if (!**cpp)
- break; /* null user == accept */
- if (!strcmp (hr->user, *cpp)) /* found listed user */
- break;
- }
- if (!count)
- return (0); /* Not this user */
- }
-
- /* Record type checking:
- *
- * 1. If Record type is not in rec_types field, skip it.
- * 2. If mod_list is null, keep everything. Otherwise keep only modules
- * on mod_list.
- * 3. If neither a 'T', 'F' nor 'O' record, run through "file_list". If
- * file_list is null, keep everything. Otherwise, keep only files on
- * file_list, matched appropriately.
- */
- if (!strchr (rec_types, *(hr->type)))
- return (0);
- if (!strchr ("TFOE", *(hr->type))) /* Don't bother with "file" if "TFOE" */
- {
- if (file_list) /* If file_list is null, accept all */
- {
- for (fl = file_list, count = file_count; count; fl++, count--)
- {
- /* 1. If file_list entry starts with '*', skip the '*' and
- * compare it against the repository in the hrec.
- * 2. If file_list entry has a '/' in it, compare it against
- * the concatenation of the repository and file from hrec.
- * 3. Else compare the file_list entry against the hrec file.
- */
- char *cmpfile = NULL;
-
- if (*(cp = fl->l_file) == '*')
- {
- cp++;
- /* if argument to -p is a prefix of repository */
- if (!strncmp (cp, hr->repos, strlen (cp)))
- {
- hr->mod = fl->l_module;
- break;
- }
- }
- else
- {
- if (strchr (cp, '/'))
- {
- cmpfile = xmalloc (strlen (hr->repos)
- + strlen (hr->file)
- + 10);
- (void) sprintf (cmpfile, "%s/%s",
- hr->repos, hr->file);
- cp2 = cmpfile;
- }
- else
- {
- cp2 = hr->file;
- }
-
- /* if requested file is found within {repos}/file fields */
- if (within (cp, cp2))
- {
- hr->mod = fl->l_module;
- if (cmpfile != NULL)
- free (cmpfile);
- break;
- }
- if (cmpfile != NULL)
- free (cmpfile);
- }
- }
- if (!count)
- return (0); /* String specified and no match */
- }
- }
- if (mod_list)
- {
- for (cpp = mod_list, count = mod_count; count; cpp++, count--)
- {
- if (hr->mod && !strcmp (hr->mod, *cpp)) /* found module */
- break;
- }
- if (!count)
- return (0); /* Module specified & this record is not one of them. */
- }
-
- return (1); /* Select this record unless rejected above. */
-}
-
-/* The "sort_order" routine (when handed to qsort) has arranged for the
- * hrecs files to be in the right order for the report.
- *
- * Most of the "selections" are done in the select_hrec routine, but some
- * selections are more easily done after the qsort by "accept_hrec".
- */
-static void
-report_hrecs ()
-{
- struct hrec *hr, *lr;
- struct tm *tm;
- int i, count, ty;
- char *cp;
- int user_len, file_len, rev_len, mod_len, repos_len;
-
- if (*since_tag && !last_since_tag)
- {
- (void) printf ("No tag found: %s\n", since_tag);
- return;
- }
- else if (*backto && !last_backto)
- {
- (void) printf ("No module, file or repository with: %s\n", backto);
- return;
- }
- else if (hrec_count < 1)
- {
- (void) printf ("No records selected.\n");
- return;
- }
-
- user_len = file_len = rev_len = mod_len = repos_len = 0;
-
- /* Run through lists and find maximum field widths */
- hr = lr = hrec_head;
- hr++;
- for (count = hrec_count; count--; lr = hr, hr++)
- {
- char *repos;
-
- if (!count)
- hr = NULL;
- if (!accept_hrec (lr, hr))
- continue;
-
- ty = *(lr->type);
- repos = xstrdup (lr->repos);
- if ((cp = strrchr (repos, '/')) != NULL)
- {
- if (lr->mod && !strcmp (++cp, lr->mod))
- {
- (void) strcpy (cp, "*");
- }
- }
- if ((i = strlen (lr->user)) > user_len)
- user_len = i;
- if ((i = strlen (lr->file)) > file_len)
- file_len = i;
- if (ty != 'T' && (i = strlen (repos)) > repos_len)
- repos_len = i;
- if (ty != 'T' && (i = strlen (lr->rev)) > rev_len)
- rev_len = i;
- if (lr->mod && (i = strlen (lr->mod)) > mod_len)
- mod_len = i;
- free (repos);
- }
-
- /* Walk through hrec array setting "lr" (Last Record) to each element.
- * "hr" points to the record following "lr" -- It is NULL in the last
- * pass.
- *
- * There are two sections in the loop below:
- * 1. Based on the report type (e.g. extract, checkout, tag, etc.),
- * decide whether the record should be printed.
- * 2. Based on the record type, format and print the data.
- */
- for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++)
- {
- char *workdir;
- char *repos;
-
- if (!hrec_count)
- hr = NULL;
- if (!accept_hrec (lr, hr))
- continue;
-
- ty = *(lr->type);
- if (!tz_local)
- {
- time_t t = lr->date + tz_seconds_east_of_GMT;
- tm = gmtime (&t);
- }
- else
- tm = localtime (&(lr->date));
-
- (void) printf ("%c %04d-%02d-%02d %02d:%02d %s %-*s", ty,
- tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
- tm->tm_min, tz_name, user_len, lr->user);
-
- workdir = xmalloc (strlen (lr->dir) + strlen (lr->end) + 10);
- (void) sprintf (workdir, "%s%s", lr->dir, lr->end);
- if ((cp = strrchr (workdir, '/')) != NULL)
- {
- if (lr->mod && !strcmp (++cp, lr->mod))
- {
- (void) strcpy (cp, "*");
- }
- }
- repos = xmalloc (strlen (lr->repos) + 10);
- (void) strcpy (repos, lr->repos);
- if ((cp = strrchr (repos, '/')) != NULL)
- {
- if (lr->mod && !strcmp (++cp, lr->mod))
- {
- (void) strcpy (cp, "*");
- }
- }
-
- switch (ty)
- {
- case 'T':
- /* 'T'ag records: repository is a "tag type", rev is the tag */
- (void) printf (" %-*s [%s:%s]", mod_len, lr->mod, lr->rev,
- repos);
- if (working)
- (void) printf (" {%s}", workdir);
- break;
- case 'F':
- case 'E':
- case 'O':
- if (lr->rev && *(lr->rev))
- (void) printf (" [%s]", lr->rev);
- (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod,
- mod_len + 1 - (int) strlen (lr->mod),
- "=", workdir);
- break;
- case 'W':
- case 'U':
- case 'P':
- case 'C':
- case 'G':
- case 'M':
- case 'A':
- case 'R':
- (void) printf (" %-*s %-*s %-*s =%s= %s", rev_len, lr->rev,
- file_len, lr->file, repos_len, repos,
- lr->mod ? lr->mod : "", workdir);
- break;
- default:
- (void) printf ("Hey! What is this junk? RecType[0x%2.2x]", ty);
- break;
- }
- (void) putchar ('\n');
- free (workdir);
- free (repos);
- }
-}
-
-static int
-accept_hrec (lr, hr)
- struct hrec *hr, *lr;
-{
- int ty;
-
- ty = *(lr->type);
-
- if (last_since_tag && ty == 'T')
- return (1);
-
- if (v_checkout)
- {
- if (ty != 'O')
- return (0); /* Only interested in 'O' records */
-
- /* We want to identify all the states that cause the next record
- * ("hr") to be different from the current one ("lr") and only
- * print a line at the allowed boundaries.
- */
-
- if (!hr || /* The last record */
- strcmp (hr->user, lr->user) || /* User has changed */
- strcmp (hr->mod, lr->mod) ||/* Module has changed */
- (working && /* If must match "workdir" */
- (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
- strcmp (hr->end, lr->end)))) /* the 2nd parts differ */
-
- return (1);
- }
- else if (modified)
- {
- if (!last_entry || /* Don't want only last rec */
- !hr || /* Last entry is a "last entry" */
- strcmp (hr->repos, lr->repos) || /* Repository has changed */
- strcmp (hr->file, lr->file))/* File has changed */
- return (1);
-
- if (working)
- { /* If must match "workdir" */
- if (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
- strcmp (hr->end, lr->end)) /* the 2nd parts differ */
- return (1);
- }
- }
- else if (module_report)
- {
- if (!last_entry || /* Don't want only last rec */
- !hr || /* Last entry is a "last entry" */
- strcmp (hr->mod, lr->mod) ||/* Module has changed */
- strcmp (hr->repos, lr->repos) || /* Repository has changed */
- strcmp (hr->file, lr->file))/* File has changed */
- return (1);
- }
- else
- {
- /* "extract" and "tag_report" always print selected records. */
- return (1);
- }
-
- return (0);
-}
OpenPOWER on IntegriCloud