#include "cvs.h"
#include "history.h"
#include "save-cwd.h"
static struct hrec
{
char *type;
char *user;
char *dir;
char *repos;
char *rev;
char *file;
char *end;
char *mod;
time_t date;
long idx;
} *hrec_head;
static long hrec_idx;
static void fill_hrec (char *line, struct hrec * hr);
static int accept_hrec (struct hrec * hr, struct hrec * lr);
static int select_hrec (struct hrec * hr);
static int sort_order (const void *l, const void *r);
static int within (char *find, char *string);
static void expand_modules (void);
static void read_hrecs (List *flist);
static void report_hrecs (void);
static void save_file (char *dir, char *name, char *module);
static void save_module (char *module);
static void save_user (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";
static char *since_rev;
static char *since_tag;
static char *backto;
static char * since_date;
static struct hrec *last_since_tag;
static struct hrec *last_backto;
static char *rec_types;
static int hrec_count;
static int hrec_max;
static char **user_list;
static int user_max;
static int user_count;
static struct file_list_str
{
char *l_file;
char *l_module;
} *file_list;
static int file_max;
static int file_count;
static char **mod_list;
static int mod_max;
static int mod_count;
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};
static int
sort_order (const void *l, const void *r)
{
int i;
const struct hrec *left = l;
const struct hrec *right = r;
if (user_sort)
{
if ((i = strcmp (left->user, right->user)) != 0)
return i;
}
if (module_sort)
{
if (left->mod && right->mod)
if ((i = strcmp (left->mod, right->mod)) != 0)
return i;
}
if (repos_sort)
{
if ((i = strcmp (left->repos, right->repos)) != 0)
return i;
}
if (file_sort)
{
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;
}
}
if ((i = ((long) (left->date) - (long) (right->date))) != 0)
return i;
return left->idx - right->idx;
}
static const char *
get_history_log_name (time_t now)
{
char *log_name;
if (config->HistoryLogPath)
{
log_name = xmalloc (PATH_MAX);
if (!now) now = time (NULL);
if (!strftime (log_name, PATH_MAX, config->HistoryLogPath,
localtime (&now)))
{
error (0, 0, "Invalid date format in HistoryLogPath.");
free (config->HistoryLogPath);
config->HistoryLogPath = NULL;
}
}
if (!config->HistoryLogPath)
{
log_name = xmalloc (strlen (current_parsed_root->directory)
+ sizeof (CVSROOTADM)
+ sizeof (CVSROOTADM_HISTORY) + 3);
sprintf (log_name, "%s/%s/%s", current_parsed_root->directory,
CVSROOTADM, CVSROOTADM_HISTORY);
}
return log_name;
}
int
history (int argc, char **argv)
{
int i, c;
const char *fname = NULL;
List *flist;
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':
report_count++;
tag_report++;
break;
case 'a':
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':
last_entry = 1;
break;
case 'o':
report_count++;
v_checkout = 1;
break;
case 'w':
working = 1;
break;
case 'X':
#ifdef DEBUG
fname = optarg;
#endif
break;
case 'D':
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':
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':
save_file (NULL, optarg, NULL);
break;
case 'm':
if (!module_report++) report_count++;
case 'n':
save_module (optarg);
break;
case 'p':
save_file (optarg, NULL, NULL);
break;
case 'r':
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':
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':
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
{
struct timespec t;
char *buf = Xasprintf ("1/1/1970 23:00 %s", optarg);
if (get_date (&t, buf, NULL))
{
tz_seconds_east_of_GMT = (time_t)23 * 60 * 60
- t.tv_sec;
tz_name = optarg;
}
else
error (0, 0, "%s is not a known time zone", optarg);
free (buf);
}
break;
case '?':
default:
usage (history_usg);
break;
}
}
argc -= optind;
argv += optind;
for (i = 0; i < argc; i++)
save_file (NULL, argv[i], NULL);
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;
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 (fname)
option_with_arg ("-X", fname);
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 (last_entry
|| (!since_date && !*since_rev && !*since_tag && !*backto))
{
repos_sort++;
file_sort++;
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;
}
else
{
free (rec_types);
rec_types = xstrdup ("OF");
if (!last_entry && user_list)
user_sort++;
if (last_entry
|| (!since_date && !*since_rev && !*since_tag && !*backto))
file_sort++;
}
if (!user_list)
save_user (getcaller ());
if (*since_tag && !strchr (rec_types, 'T'))
{
rec_types = xrealloc (rec_types, strlen (rec_types) + 5);
(void) strcat (rec_types, "T");
}
if (fname)
{
Node *p;
flist = getlist ();
p = getnode ();
p->type = FILES;
p->key = Xasprintf ("%s/%s/%s",
current_parsed_root->directory, CVSROOTADM, fname);
addnode (flist, p);
}
else
{
char *pat;
if (config->HistorySearchPath)
pat = config->HistorySearchPath;
else
pat = Xasprintf ("%s/%s/%s",
current_parsed_root->directory, CVSROOTADM,
CVSROOTADM_HISTORY);
flist = find_files (NULL, pat);
if (pat != config->HistorySearchPath) free (pat);
}
read_hrecs (flist);
if (hrec_count > 0)
qsort (hrec_head, hrec_count, sizeof (struct hrec), sort_order);
report_hrecs ();
if (since_date != NULL)
free (since_date);
free (since_rev);
free (since_tag);
free (backto);
free (rec_types);
return 0;
}
void
history_write (int type, const char *update_dir, const char *revs,
const char *name, const char *repository)
{
const 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;
time_t now;
if (logoff)
return;
if (!strchr (config->logHistory, type))
return;
if (noexec)
goto out;
repos = Short_Repository (repository);
if (!PrCurDir)
{
char *pwdir;
pwdir = get_homedir ();
PrCurDir = CurDir;
if (pwdir != NULL)
{
i = strlen (pwdir);
if (!strncmp (CurDir, pwdir, i))
{
PrCurDir += i;
tilde = "~";
}
else
{
struct saved_cwd cwd;
char *homedir;
if (save_cwd (&cwd))
error (1, errno, "Failed to save current directory.");
if (CVS_CHDIR (pwdir) < 0 || (homedir = xgetcwd ()) == NULL)
homedir = pwdir;
if (restore_cwd (&cwd))
error (1, errno,
"Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
i = strlen (homedir);
if (!strncmp (CurDir, homedir, i))
{
PrCurDir += i;
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 = Xasprintf ("%s%s%s%s", tilde, PrCurDir, slash, update_dir);
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 = "";
now = time (NULL);
line = Xasprintf ("%c%08lx|%s|%s|%s|%s|%s\n", type, (long) now,
username, workdir, repos, revs, name);
fname = get_history_log_name (now);
if (!history_lock (current_parsed_root->directory))
goto out;
fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666);
if (fd < 0)
{
if (!really_quiet)
error (0, errno,
"warning: cannot open history file `%s' for write", fname);
goto out;
}
TRACE (TRACE_FUNCTION, "open (`%s', a)", fname);
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 ();
}
static void
save_user (char *name)
{
if (user_count == user_max)
{
user_max = xsum (user_max, USER_INCREMENT);
if (size_overflow_p (xtimes (user_max, sizeof (char *))))
{
error (0, 0, "save_user: too many users");
return;
}
user_list = xnrealloc (user_list, user_max, sizeof (char *));
}
user_list[user_count++] = xstrdup (name);
}
static void
save_file (char *dir, char *name, char *module)
{
struct file_list_str *fl;
if (file_count == file_max)
{
file_max = xsum (file_max, FILE_INCREMENT);
if (size_overflow_p (xtimes (file_max, sizeof (*fl))))
{
error (0, 0, "save_file: too many files");
return;
}
file_list = xnrealloc (file_list, file_max, sizeof (*fl));
}
fl = &file_list[file_count++];
fl->l_module = module;
if (dir && *dir)
{
if (name && *name)
fl->l_file = Xasprintf ("%s/%s", dir, name);
else
fl->l_file = Xasprintf ("*%s", dir);
}
else
{
if (name && *name)
fl->l_file = xstrdup (name);
else
error (0, 0, "save_file: null dir and file name");
}
}
static void
save_module (char *module)
{
if (mod_count == mod_max)
{
mod_max = xsum (mod_max, MODULE_INCREMENT);
if (size_overflow_p (xtimes (mod_max, sizeof (char *))))
{
error (0, 0, "save_module: too many modules");
return;
}
mod_list = xnrealloc (mod_list, mod_max, sizeof (char *));
}
mod_list[mod_count++] = xstrdup (module);
}
static void
expand_modules (void)
{
}
#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 (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;
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
static int
read_hrecs_file (Node *p, void *closure)
{
char *cpstart, *cpend, *cp, *nl;
char *hrline;
int i;
int fd;
struct stat st_buf;
const char *fname = p->key;
if ((fd = CVS_OPEN (fname, O_RDONLY | OPEN_BINARY)) < 0)
{
error (0, errno, "cannot open history file `%s'", fname);
return 0;
}
if (fstat (fd, &st_buf) < 0)
{
error (0, errno, "can't stat history file `%s'", fname);
return 0;
}
if (!(st_buf.st_size))
{
error (0, 0, "history file `%s' is empty", fname);
return 0;
}
cpstart = xnmalloc (2, STAT_BLOCKSIZE (st_buf));
cpstart[0] = '\0';
cp = cpend = cpstart;
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 (0, errno, "error reading history file `%s'", fname);
return 0;
}
if (nl == cp) break;
error (0, 0, "warning: no newline at end of history file `%s'",
fname);
}
*nl = '\0';
if (hrec_count == hrec_max)
{
struct hrec *old_head = hrec_head;
hrec_max += HREC_INCREMENT;
hrec_head = xnrealloc (hrec_head, 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);
}
hrline = xstrdup (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);
return 1;
}
static void
read_hrecs (List *flist)
{
int files_read;
hrec_max = HREC_INCREMENT;
hrec_head = xmalloc (hrec_max * sizeof (struct hrec));
hrec_idx = 0;
files_read = walklist (flist, read_hrecs_file, NULL);
if (!files_read)
error (1, 0, "No history files read.");
if (last_since_tag)
{
hrec_count -= (last_since_tag - hrec_head);
hrec_head = last_since_tag;
}
if (last_backto)
{
hrec_count -= (last_backto - hrec_head);
hrec_head = last_backto;
}
}
static int
within (char *find, char *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;
}
static int
select_hrec (struct hrec *hr)
{
char **cpp, *cp, *cp2;
struct file_list_str *fl;
int count;
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;
}
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;
finfo.update_dir = NULL;
finfo.fullname = finfo.file;
finfo.repository = hr->repos;
finfo.entries = NULL;
finfo.rcs = NULL;
vers = Version_TS (&finfo, NULL, since_rev, NULL, 1, 0);
if (vers->vn_rcs)
{
if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, NULL, 0))
!= (time_t) 0)
{
if (hr->date < t)
{
freevers_ts (&vers);
return 0;
}
}
}
freevers_ts (&vers);
}
else if (*since_tag)
{
if (*(hr->type) == 'T')
{
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;
}
if (user_list && hr->user)
{
for (cpp = user_list, count = user_count; count; cpp++, count--)
{
if (!**cpp)
break;
if (!strcmp (hr->user, *cpp))
break;
}
if (!count)
return 0;
}
if (!strchr (rec_types, *(hr->type)))
return 0;
if (!strchr ("TFOE", *(hr->type)))
{
if (file_list)
{
for (fl = file_list, count = file_count; count; fl++, count--)
{
char *cmpfile = NULL;
if (*(cp = fl->l_file) == '*')
{
cp++;
if (!strncmp (cp, hr->repos, strlen (cp)))
{
hr->mod = fl->l_module;
break;
}
}
else
{
if (strchr (cp, '/'))
{
cmpfile = Xasprintf ("%s/%s", hr->repos, hr->file);
cp2 = cmpfile;
}
else
{
cp2 = hr->file;
}
if (within (cp, cp2))
{
hr->mod = fl->l_module;
if (cmpfile != NULL)
free (cmpfile);
break;
}
if (cmpfile != NULL)
free (cmpfile);
}
}
if (!count)
return 0;
}
}
if (mod_list)
{
for (cpp = mod_list, count = mod_count; count; cpp++, count--)
{
if (hr->mod && !strcmp (hr->mod, *cpp))
break;
}
if (!count)
return 0;
}
return 1;
}
static void
report_hrecs (void)
{
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;
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);
}
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':
(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 (struct hrec *lr, struct hrec *hr)
{
int ty;
ty = *(lr->type);
if (last_since_tag && ty == 'T')
return 1;
if (v_checkout)
{
if (ty != 'O')
return 0;
if (!hr ||
strcmp (hr->user, lr->user) ||
strcmp (hr->mod, lr->mod) ||
(working &&
(strcmp (hr->dir, lr->dir) ||
strcmp (hr->end, lr->end))))
return 1;
}
else if (modified)
{
if (!last_entry ||
!hr ||
strcmp (hr->repos, lr->repos) ||
strcmp (hr->file, lr->file))
return 1;
if (working)
{
if (strcmp (hr->dir, lr->dir) ||
strcmp (hr->end, lr->end))
return 1;
}
}
else if (module_report)
{
if (!last_entry ||
!hr ||
strcmp (hr->mod, lr->mod) ||
strcmp (hr->repos, lr->repos) ||
strcmp (hr->file, lr->file))
return 1;
}
else
{
return 1;
}
return 0;
}