bug-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: history and val-tags locks.


From: Derek Price
Subject: Re: history and val-tags locks.
Date: Fri, 06 May 2005 14:32:24 -0400
User-agent: Mozilla Thunderbird 1.0.2 (Windows/20050317)

Mark D. Baushke wrote:

> I believe that most modern glob() implementations may indeed internally
> be implemented using fnmatch(), but not all of them are. If you are
> considering using an internal glob() instead of relying on a library
> version from the system, then I don't have as much of a problem with it.


Okay, it's still short documentation, but I've attached a patch against
stable which implements the HistoryLogPath and HistorySearchPath config
options.  I don't think I want to try and import glob() on stable, so
HistorySearchPath currently only handles fnmatch() metacharacters in the
final element of the path name.

Any consenses on whether importing this to stable is justified to deal
with the log rotation issue?

Cheers,

Derek


Index: src/client.c
===================================================================
RCS file: /cvs/ccvs/src/client.c,v
retrieving revision 1.318.4.27
diff -u -p -r1.318.4.27 client.c
--- src/client.c        17 Mar 2005 15:39:09 -0000      1.318.4.27
+++ src/client.c        6 May 2005 18:26:21 -0000
@@ -4903,7 +4903,7 @@ start_rsh_server (root, to_server, from_
 /* Send an argument STRING.  */
 void
 send_arg (string)
-    char *string;
+    const char *string;
 {
     char buf[1];
     char *p = string;
@@ -5953,8 +5953,8 @@ client_notify (repository, update_dir, f
  */
 void
 option_with_arg (option, arg)
-    char *option;
-    char *arg;
+    const char *option;
+    const char *arg;
 {
     if (arg == NULL)
        return;
Index: src/client.h
===================================================================
RCS file: /cvs/ccvs/src/client.h,v
retrieving revision 1.39.6.2
diff -u -p -r1.39.6.2 client.h
--- src/client.h        20 Mar 2004 18:14:56 -0000      1.39.6.2
+++ src/client.h        6 May 2005 18:26:21 -0000
@@ -83,7 +83,7 @@ void read_from_server PROTO((char *buf, 
 
 /* Internal functions that handle client communication to server, etc.  */
 int supported_request PROTO ((char *));
-void option_with_arg PROTO((char *option, char *arg));
+void option_with_arg PROTO((const char *option, const char *arg));
 
 /* Get the responses and then close the connection.  */
 extern int get_responses_and_close PROTO((void));
@@ -119,7 +119,7 @@ send_files PROTO((int argc, char **argv,
 
 /* Send an argument to the remote server.  */
 void
-send_arg PROTO((char *string));
+send_arg PROTO((const char *string));
 
 /* Send a string of single-char options to the remote server, one by one.  */
 void
Index: src/cvs.h
===================================================================
RCS file: /cvs/ccvs/src/cvs.h,v
retrieving revision 1.235.4.31
diff -u -p -r1.235.4.31 cvs.h
--- src/cvs.h   2 May 2005 17:06:56 -0000       1.235.4.31
+++ src/cvs.h   6 May 2005 18:26:21 -0000
@@ -931,3 +931,6 @@ extern void cvs_outerr PROTO ((const cha
 extern void cvs_flusherr PROTO ((void));
 extern void cvs_flushout PROTO ((void));
 extern void cvs_output_tagged PROTO ((const char *, const char *));
+
+/* From find_names.c.  */
+List *find_files PROTO ((const char *dir, const char *pat));
Index: src/find_names.c
===================================================================
RCS file: /cvs/ccvs/src/find_names.c,v
retrieving revision 1.30.6.2
diff -u -p -r1.30.6.2 find_names.c
--- src/find_names.c    31 Jan 2005 22:15:11 -0000      1.30.6.2
+++ src/find_names.c    6 May 2005 18:26:21 -0000
@@ -22,10 +22,11 @@
  */
 
 #include "cvs.h"
+#include <assert.h>
 
 static int find_dirs PROTO((char *dir, List * list, int checkadm,
                            List *entries));
-static int find_rcs PROTO((char *dir, List * list));
+static int find_rcs PROTO((const char *dir, List * list));
 static int add_subdir_proc PROTO((Node *, void *));
 static int register_subdir_proc PROTO((Node *, void *));
 
@@ -249,55 +250,134 @@ Find_Directories (repository, which, ent
     return (dirlist);
 }
 
-/*
- * Finds all the ,v files in the argument directory, and adds them to the
- * files list.  Returns 0 for success and non-zero if the argument directory
- * cannot be opened, in which case errno is set to indicate the error.
- * In the error case LIST is left in some reasonable state (unchanged, or
- * containing the files which were found before the error occurred).
+
+
+/* Finds all the files matching PAT in the directory DIR, and adds them to a
+ * new List.  Returns the new List for success and NULL if the argument
+ * directory cannot be opened, in which case errno is set to indicate the
+ * error.
+ *
+ * NOTES
+ *   If GNULIB ever gets a glob module, this function could be replaced with
+ *   the glob function and a few tweaks to callers.
+ *
+ *   Currently, users may come to rely on the way history uses the unsorted
+ *   list this function returns since I believe that the default inode ordering
+ *   should be roughly the order of file creation.  It might be a good idea to
+ *   discourage this in favor of a specific sort order since we may otherwise
+ *   get complaints like, "I copied my repository to a new location and the
+ *   history command stopped working."
+ *
+ * INPUTS
+ *   dir       The directory to open for read.
+ *   pat       The pattern to match against, via fnmatch().
+ *
+ * GLOBALS
+ *   errno     Set on error.
+ *
+ * RETURNS
+ *   A pointer to a List of matching file names, on success.
+ *   NULL, on error.
  */
-static int
-find_rcs (dir, list)
-    char *dir;
-    List *list;
+List *
+find_files (dir, pat)
+    const char *dir;
+    const char *pat;
 {
     Node *p;
+    List *list;
     struct dirent *dp;
     DIR *dirp;
 
     /* set up to read the dir */
-    if ((dirp = CVS_OPENDIR (dir)) == NULL)
-       return (1);
+    if (!(dirp = CVS_OPENDIR (dir)))
+       return NULL;
+
+    list = getlist();
 
-    /* read the dir, grabbing the ,v files */
+    /* read the dir, grabbing the files matching PAT */
     errno = 0;
-    while ((dp = CVS_READDIR (dirp)) != NULL)
+    while ((dp = CVS_READDIR (dirp)))
     {
-       if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0) 
+       /* Skip CWD and parent.  */
+       if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, ".."))
+           continue;
+       /* Look for a match.  */
+       if (!CVS_FNMATCH (pat, dp->d_name, 0)) 
        {
-           char *comma;
-
-           comma = strrchr (dp->d_name, ',');  /* strip the ,v */
-           *comma = '\0';
            p = getnode ();
            p->type = FILES;
            p->key = xstrdup (dp->d_name);
-           if (addnode (list, p) != 0)
+           if (addnode (list, p))
                freenode (p);
        }
        errno = 0;
     }
-    if (errno != 0)
+    if (errno)
     {
        int save_errno = errno;
        (void) CVS_CLOSEDIR (dirp);
+       dellist (&list);
        errno = save_errno;
-       return 1;
+       return NULL;
     }
     (void) CVS_CLOSEDIR (dirp);
-    return (0);
+    return list;
+}
+
+
+
+/* walklist() proc which strips a trailing RCSEXT from node keys.
+ */
+static int strip_rcsext PROTO ((Node *p, void *closure));
+static int
+strip_rcsext (p, closure)
+    Node *p;
+    void *closure;
+{
+    char *s = p->key + strlen (p->key) - strlen (RCSEXT);
+    assert (!strcmp (s, RCSEXT));
+    *s = '\0'; /* strip the ,v */
+    return 0;
+}
+
+
+
+/*
+ * Finds all the ,v files in the directory DIR, and adds them to the LIST.
+ * Returns 0 for success and non-zero if DIR cannot be opened, in which case
+ * ERRNO is set to indicate the error.  In the error case, LIST is left in some
+ * reasonable state (unchanged, or containing the files which were found before
+ * the error occurred).
+ *
+ * INPUTS
+ *   dir       The directory to open for read.
+ *
+ * OUTPUTS
+ *   list      Where to store matching file entries.
+ *
+ * GLOBALS
+ *   errno     Set on error.
+ *
+ * RETURNS
+ *   0, for success.
+ *   <> 0, on error.
+ */
+static int
+find_rcs (dir, list)
+    const char *dir;
+    List *list;
+{
+    List *newlist;
+    if (!(newlist = find_files (dir, RCSPAT)))
+       return 1;
+    walklist (newlist, strip_rcsext, NULL);
+    mergelists (list, &newlist);
+    return 0;
 }
 
+
+
 /*
  * Finds all the subdirectories of the argument dir and adds them to
  * the specified list.  Sub-directories without a CVS administration
Index: src/hash.c
===================================================================
RCS file: /cvs/ccvs/src/hash.c,v
retrieving revision 1.32.6.4
diff -u -p -r1.32.6.4 hash.c
--- src/hash.c  31 Jan 2005 22:15:11 -0000      1.32.6.4
+++ src/hash.c  6 May 2005 18:26:21 -0000
@@ -130,6 +130,51 @@ dellist (listp)
     *listp = (List *) NULL;
 }
 
+
+
+/*
+ * remove a node from it's list (maybe hash list too)
+ */
+static void removenode PROTO ((Node *p));
+static void
+removenode (p)
+    Node *p;
+{
+    if (!p) return;
+
+    /* take it out of the list */
+    p->next->prev = p->prev;
+    p->prev->next = p->next;
+
+    /* if it was hashed, remove it from there too */
+    if (p->hashnext)
+    {
+       p->hashnext->hashprev = p->hashprev;
+       p->hashprev->hashnext = p->hashnext;
+    }
+}
+
+
+
+void
+mergelists (dest, src)
+    List *dest;
+    List **src;
+{
+    Node *head, *p, *n;
+
+    head = (*src)->list;
+    for (p = head->next; p != head; p = n)
+    {
+       n = p->next;
+       removenode (p);
+       addnode (dest, p);
+    }
+    dellist (src);
+}
+
+
+
 /*
  * get a new list node
  */
@@ -157,6 +202,8 @@ getnode ()
     return (p);
 }
 
+
+
 /*
  * remove a node from it's list (maybe hash list too) and free it
  */
@@ -164,24 +211,15 @@ void
 delnode (p)
     Node *p;
 {
-    if (p == (Node *) NULL)
-       return;
-
-    /* take it out of the list */
-    p->next->prev = p->prev;
-    p->prev->next = p->next;
-
-    /* if it was hashed, remove it from there too */
-    if (p->hashnext != (Node *) NULL)
-    {
-       p->hashnext->hashprev = p->hashprev;
-       p->hashprev->hashnext = p->hashnext;
-    }
-
+    if (!p) return;
+    /* remove it */
+    removenode (p);
     /* free up the storage */
     freenode (p);
 }
 
+
+
 /*
  * free up the storage associated with a node
  */
Index: src/hash.h
===================================================================
RCS file: /cvs/ccvs/src/hash.h,v
retrieving revision 1.14.6.2
diff -u -p -r1.14.6.2 hash.h
--- src/hash.h  31 Jan 2005 22:15:11 -0000      1.14.6.2
+++ src/hash.h  6 May 2005 18:26:21 -0000
@@ -57,6 +57,7 @@ int addnode PROTO((List * list, Node * p
 int addnode_at_front PROTO((List * list, Node * p));
 int walklist PROTO((List * list, int (*)(Node *n, void *closure), void 
*closure));
 int list_isempty PROTO ((List *list));
+void mergelists PROTO ((List *dest, List **src));
 void dellist PROTO((List ** listp));
 void delnode PROTO((Node * p));
 void freenode PROTO((Node * p));
Index: src/history.c
===================================================================
RCS file: /cvs/ccvs/src/history.c,v
retrieving revision 1.58.4.11
diff -u -p -r1.58.4.11 history.c
--- src/history.c       28 Apr 2005 20:00:55 -0000      1.58.4.11
+++ src/history.c       6 May 2005 18:26:22 -0000
@@ -204,7 +204,7 @@ static int select_hrec PROTO((struct hre
 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 read_hrecs PROTO((const char *dir, List *flist));
 static void report_hrecs PROTO((void));
 static void save_file PROTO((char *dir, char *name, char *module));
 static void save_module PROTO((char *module));
@@ -236,7 +236,10 @@ static short tz_local;
 static time_t tz_seconds_east_of_GMT;
 static char *tz_name = "+0000";
 
+/* From CVSROOT/config.  */
 char *logHistory;
+char *HistoryLogPath;
+char *HistorySearchPath;
 
 /* -r, -t, or -b options, malloc'd.  These are "" if the option in
    question is not specified or is overridden by another option.  The
@@ -276,8 +279,6 @@ static char **mod_list;             /* Ptr to array
 static int mod_max;            /* Number of elements allocated */
 static int 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
@@ -368,13 +369,57 @@ sort_order (l, r)
     return (left->idx - right->idx);
 }
 
+
+
+/* Get the name of the history log, either from CVSROOT/config, or via the
+ * hard-coded default.
+ */
+static const char *get_history_log_name PROTO((time_t now));
+static const char *
+get_history_log_name (now)
+    time_t now;
+{
+    char *log_name;
+
+    if (HistoryLogPath)
+    {
+       /* ~, $VARs, and were expanded via expand_path() when CVSROOT/config
+        * was parsed.
+        */
+       log_name = xmalloc (PATH_MAX);
+       if (!now) now = time (NULL);
+       if (!strftime (log_name, PATH_MAX, HistoryLogPath, localtime (&now)))
+       {
+           error (0, 0, "Invalid date format in HistoryLogPath.");
+           free (HistoryLogPath);
+           HistoryLogPath = NULL;
+       }
+    }
+
+    if (!HistoryLogPath)
+    {
+       /* Use the default.  */
+       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 (argc, argv)
     int argc;
     char **argv;
 {
     int i, c;
-    char *fname;
+    const char *fname = NULL;
+    List *flist;
+    char *dir;
 
     if (argc == -1)
        usage (history_usg);
@@ -417,7 +462,7 @@ history (argc, argv)
                break;
            case 'X':                   /* Undocumented debugging flag */
 #ifdef DEBUG
-               histfile = optarg;
+               fname = optarg;
 #endif
                break;
 
@@ -566,8 +611,8 @@ history (argc, argv)
            send_arg("-o");
        if (working)
            send_arg("-w");
-       if (histfile)
-           send_arg("-X");
+       if (fname)
+           option_with_arg ("-X", fname);
        if (since_date)
            client_senddate (since_date);
        if (backto[0] != '\0')
@@ -673,34 +718,69 @@ history (argc, argv)
        (void) strcat (rec_types, "T");
     }
 
-    if (histfile)
-       fname = xstrdup (histfile);
+    if (fname)
+    {
+       Node *p;
+
+       flist = getlist ();
+       p = getnode ();
+       p->type = FILES;
+       p->key = xstrdup (fname);
+       addnode (flist, p);
+
+       dir = xmalloc (strlen (current_parsed_root->directory)
+                      + strlen (CVSROOTADM) + 2);
+       sprintf (dir, "%s/%s",
+                current_parsed_root->directory, CVSROOTADM);
+    }
     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);
+       char *pat;
+
+       if (HistorySearchPath)
+       {
+           dir = xstrdup (HistorySearchPath);
+           pat = strrchr (dir, '/');
+           /* No need to check that strrchr worked - parse_config verified
+            * that HistorySearchPath is absolute, which means it must contain
+            * at least one '/'.
+            */
+           *pat++ = '\0';
+       }
+       else
+       {
+           dir = xmalloc (strlen (current_parsed_root->directory)
+                          + strlen (CVSROOTADM) + 2);
+           sprintf (dir, "%s/%s",
+                    current_parsed_root->directory, CVSROOTADM);
+           pat = CVSROOTADM_HISTORY;
+       }
+
+       flist = find_files (*dir ? dir : "/", pat);
     }
 
-    read_hrecs (fname);
+    read_hrecs (dir, flist);
     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);
+    free (dir);
 
-    return (0);
+    return 0;
 }
 
+
+
+/* An empty LogHistory string in CVSROOT/config will turn logging off.
+ */
 void
 history_write (type, update_dir, revs, name, repository)
     int type;
@@ -709,7 +789,7 @@ history_write (type, update_dir, revs, n
     const char *name;
     const char *repository;
 {
-    char *fname;
+    const char *fname;
     char *workdir;
     char *username = getcaller ();
     int fd;
@@ -719,6 +799,7 @@ history_write (type, update_dir, revs, n
     int i;
     static char *tilde = "";
     static char *PrCurDir = NULL;
+    time_t now;
 
     if (logoff)                        /* History is turned off by noexec or
                                 * readonlyfs.
@@ -726,49 +807,10 @@ history_write (type, update_dir, revs, n
        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)
@@ -887,11 +929,38 @@ history_write (type, update_dir, revs, n
        revs = "";
     line = xmalloc (strlen (username) + strlen (workdir) + strlen (repos)
                    + strlen (revs) + strlen (name) + 100);
+    now = time (NULL);
     sprintf (line, "%c%08lx|%s|%s|%s|%s|%s\n",
-            type, (long) time ((time_t *) NULL),
-            username, workdir, repos, revs, name);
+            type, now, username, workdir, repos, revs, name);
+
+    fname = get_history_log_name (now);
+
+    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 | O_CREAT | OPEN_BINARY, 0666);
+    if (fd < 0)
+    {
+       if (! really_quiet)
+        {
+            error (0, errno, "warning: cannot write to history file %s",
+                   fname);
+        }
+        goto out;
+    }
+    if (trace)
+        fprintf (stderr, "%s-> fopen(%s,a)\n",
+                 CLIENT_SERVER_STR, fname);
 
-    /* Lessen some race conditions on non-Posix-compliant hosts.  */
+    /* Lessen some race conditions on non-Posix-compliant hosts.
+     *
+     * FIXME:  I'm guessing the following was necessary for NFS when multiple
+     * simultaneous writes to the same file are possible, since NFS does not
+     * natively support append mode and it must be emulated via lseek().  Now
+     * that the history file is locked for write, the following lseek() may be
+     * unnecessary.
+     */
     if (lseek (fd, (off_t) 0, SEEK_END) == -1)
        error (1, errno, "cannot seek to end of history file: %s", fname);
 
@@ -903,9 +972,10 @@ history_write (type, update_dir, revs, n
     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.
@@ -1079,7 +1149,7 @@ fill_hrec (line, hr)
 #endif
 
 
-/* read_hrecs's job is to read the history file and fill in all the "hrec"
+/* read_hrecs_file's job is to read a history file and fill in new "hrec"
  * (history record) array elements with the ones we need to print.
  *
  * Logic:
@@ -1090,33 +1160,41 @@ fill_hrec (line, hr)
  * 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;
+static int read_hrecs_file PROTO ((Node *p, void *closure));
+static int
+read_hrecs_file (p, closure)
+    Node *p;
+    void *closure;
 {
     unsigned 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 (1, errno, "cannot open history file: %s", fname);
+    {
+       error (0, errno, "cannot open history file `%s'", fname);
+       return 0;
+    }
 
     if (fstat (fd, &st_buf) < 0)
-       error (1, errno, "can't stat history file");
+    {
+       error (0, errno, "can't stat history file `%s'", fname);
+       return 0;
+    }
 
     if (!(st_buf.st_size))
-       error (1, 0, "history file is empty");
+    {
+       error (0, 0, "history file `%s' is empty", fname);
+       return 0;
+    }
 
     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++)
@@ -1141,9 +1219,13 @@ read_hrecs (fname)
                continue;
            }
            if (i < 0)
-               error (1, errno, "error reading history file");
+           {
+               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");
+           error (0, 0, "warning: no newline at end of history file `%s'",
+                  fname);
        }
        *nl = '\0';
 
@@ -1177,6 +1259,36 @@ read_hrecs (fname)
     }
     free (cpstart);
     close (fd);
+    return 1;
+}
+
+
+
+/* Read the history records in from a list of history files.  */
+static void
+read_hrecs (dir, flist)
+    const char *dir;
+    List *flist;
+{
+    int files_read;
+    struct saved_cwd cwd;
+
+    /* The global history records are already initialized to 0 according to
+     * ANSI C.
+     */
+    hrec_max = HREC_INCREMENT;
+    hrec_head = xmalloc (hrec_max * sizeof (struct hrec));
+    hrec_idx = 0;
+
+    /* save our current directory and static vars */
+    if (save_cwd (&cwd)) error_exit ();
+    CVS_CHDIR (dir);
+
+    files_read = walklist (flist, read_hrecs_file, NULL);
+    if (!files_read)
+       error (1, 0, "No history files read.");
+
+    if (restore_cwd (&cwd, NULL)) error_exit ();
 
     /* 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
@@ -1197,6 +1309,8 @@ read_hrecs (fname)
     }
 }
 
+
+
 /* Utility program for determining whether "find" is inside "string" */
 static int
 within (find, string)
Index: src/history.h
===================================================================
RCS file: /cvs/ccvs/src/history.h,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 history.h
--- src/history.h       31 Jan 2005 22:15:11 -0000      1.1.2.2
+++ src/history.h       6 May 2005 18:26:22 -0000
@@ -13,3 +13,7 @@
  */
 
 #define ALL_HISTORY_REC_TYPES "TOEFWUPCGMAR"
+
+/* From CVSROOT/config.  */
+extern char *HistoryLogPath;
+extern char *HistorySearchPath;
Index: src/parseinfo.c
===================================================================
RCS file: /cvs/ccvs/src/parseinfo.c,v
retrieving revision 1.37.4.8
diff -u -p -r1.37.4.8 parseinfo.c
--- src/parseinfo.c     16 Mar 2005 22:00:44 -0000      1.37.4.8
+++ src/parseinfo.c     6 May 2005 18:26:22 -0000
@@ -239,6 +239,7 @@ parse_config (cvsroot)
     char *infopath;
     FILE *fp_info;
     char *line = NULL;
+    unsigned int ln;           /* Input file line counter.  */
     size_t line_allocated = 0;
     size_t len;
     char *p;
@@ -281,8 +282,11 @@ parse_config (cvsroot)
        goto set_defaults_and_return;
     }
 
+    ln = 0;  /* Have not read any lines yet.  */
     while (getline (&line, &line_allocated, fp_info) >= 0)
     {
+        ln++; /* Keep track of input file line number for error messages.  */
+
        /* Skip comments.  */
        if (line[0] == '#')
            continue;
@@ -398,6 +402,34 @@ warning: this CVS does not support Prese
               opendir it or something, but I don't see any particular
               reason to do that now rather than waiting until lock.c.  */
        }
+       else if (strcmp (line, "HistoryLogPath") == 0)
+       {
+           if (HistoryLogPath) free (HistoryLogPath);
+
+           /* Expand ~ & $VARs.  */
+           HistoryLogPath = expand_path (p, infopath, ln);
+
+           if (HistoryLogPath && !isabsolute (HistoryLogPath))
+           {
+               error (0, 0, "%s [%u]: HistoryLogPath must be absolute.",
+                      infopath, ln);
+               free (HistoryLogPath);
+               HistoryLogPath = NULL;
+           }
+       }
+       else if (strcmp (line, "HistorySearchPath") == 0)
+       {
+           if (HistorySearchPath) free (HistorySearchPath);
+           HistorySearchPath = expand_path (p, infopath, ln);
+
+           if (HistorySearchPath && !isabsolute (HistorySearchPath))
+           {
+               error (0, 0, "%s [%u]: HistorySearchPath must be absolute.",
+                      infopath, ln);
+               free (HistorySearchPath);
+               HistorySearchPath = NULL;
+           }
+       }
        else if (strcmp (line, "LogHistory") == 0)
        {
            if (strcmp (p, "all") != 0)
Index: src/sanity.sh
===================================================================
RCS file: /cvs/ccvs/src/sanity.sh,v
retrieving revision 1.752.2.170
diff -u -p -r1.752.2.170 sanity.sh
--- src/sanity.sh       2 May 2005 17:06:56 -0000       1.752.2.170
+++ src/sanity.sh       6 May 2005 18:26:26 -0000
@@ -3997,9 +3997,6 @@ ${PROG} update: Updating dir1/dir2"
                # ditch that notion and require GNU expr (or dejagnu or....)
                # since it seems to be so painful.
 
-               # why are there two lines at the end of the local output
-               # which don't exist in the remote output?  would seem to be
-               # a CVS bug.
                dotest basic2-64 "${testcvs} his -x TOFWUPCGMAR -a" \
 "O [0-9-]* [0-9:]* ${PLUS}0000 ${username} first-dir           =first-dir= 
${TESTDIR}/\*
 A [0-9-]* [0-9:]* ${PLUS}0000 ${username} 1\.1 file6     first-dir           
== ${TESTDIR}
@@ -18015,15 +18012,58 @@ ${CVSROOT_DIRNAME}/CVSROOT/config,v  <--
 new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
 done
 ${PROG} commit: Rebuilding administrative file database"
-         echo '# No config is a good config' > config
-         dotest config-5 "${testcvs} -q ci -m change-to-comment" \
-"${PROG} [a-z]*: ${CVSROOT_DIRNAME}/CVSROOT/config: unrecognized keyword 
'BogusOption'
+
+         mkdir $TESTDIR/historylogs
+         echo >config \
+              'HistoryLogPath=$CVSROOT/../historylogs/%Y-%m-%d-%H-%M-%S'
+         echo 'HistorySearchPath=$CVSROOT/../historylogs/*' >>config
+         dotest config-5 "$testcvs -q ci -m set-HistoryLogPath" \
+"$PROG [a-z]*: $CVSROOT_DIRNAME/CVSROOT/config: unrecognized keyword 
'BogusOption'
 Checking in config;
-${CVSROOT_DIRNAME}/CVSROOT/config,v  <--  config
+$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
 new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
 done
-${PROG} commit: Rebuilding administrative file database"
-         dotest config-6 "${testcvs} -q update" ''
+$PROG commit: Rebuilding administrative file database"
+
+         echo '# noop' >> config
+         dotest config-6 "$testcvs -q ci -mlog-commit" \
+"Checking in config;
+$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+done
+$PROG commit: Rebuilding administrative file database"
+
+         sleep 1
+         echo '# noop' >> config
+         dotest config-7 "$testcvs -q ci -mlog-commit" \
+"Checking in config;
+$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+done
+$PROG commit: Rebuilding administrative file database"
+
+         # The log entry was intentionally split across multiple files.
+         dotest config-8 "ls -l $TESTDIR/historylogs/*" \
+"-rw-rw-r--.*$TESTDIR/historylogs/2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]-[0-2][0-9]-[0-5][0-9]-[0-5][0-9]
+-rw-rw-r--.*$TESTDIR/historylogs/2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]-[0-2][0-9]-[0-5][0-9]-[0-5][0-9]"
+
+         # Should still see both commits.
+         dotest config-9 "$testcvs history -ea" \
+"M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT == 
\($TESTDIR/wnt/CVSROOT\|<remote>\)
+M [0-9-]* [0-9:]* ${PLUS}0000 $username 1\.[0-9]* config CVSROOT == 
\($TESTDIR/wnt/CVSROOT\|<remote>\)"
+
+         # Remove this now to see what kind of error messages we get.
+         rm -r $TESTDIR/historylogs
+
+         echo '# No config is a good config' > config
+         dotest config-cleanup-1 "$testcvs -q ci -m change-to-comment" \
+"Checking in config;
+$CVSROOT_DIRNAME/CVSROOT/config,v  <--  config
+new revision: 1\.[0-9]*; previous revision: 1\.[0-9]*
+done
+$PROG commit: warning: cannot write to history file 
$CVSROOT_DIRNAME/../historylogs/2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]-[0-2][0-9]-[0-5][0-9]-[0-5][0-9]:
 No such file or directory
+$PROG commit: Rebuilding administrative file database"
+         dotest config-cleanup-2 "$testcvs -q update"
 
          cd ..
          rm -r CVSROOT
@@ -19513,13 +19553,7 @@ Annotations for $file
                continue
            fi
 
-           # For remote, just create the repository.  We don't yet do
-           # the various other tests above for remote but that should be
-           # changed.
-           mkdir crerepos
-           mkdir crerepos/CVSROOT
-
-            # Make sure server ignores real ${HOME}/.cvsrc:
+            # Make sure server ignores real $HOME/.cvsrc:
             cat >$TESTDIR/cvs-setHome <<EOF
 #!/bin/sh
 HOME=$HOME
@@ -19531,48 +19565,48 @@ EOF
            # Note that we set CVS_SERVER at the beginning.
            CVS_SERVER=$TESTDIR/cvs-setHome; export CVS_SERVER
            CREREPOS_ROOT=:ext:$host$TESTDIR/crerepos
-         else
-
-           # First, if the repository doesn't exist at all...
-           dotest_fail crerepos-1 \
-"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
-"${PROG} \[checkout aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*"
-           mkdir crerepos
-
-           # The repository exists but CVSROOT doesn't.
-           dotest_fail crerepos-2 \
-"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
-"${PROG} \[checkout aborted\]: ${TESTDIR}/crerepos/CVSROOT: .*"
-           mkdir crerepos/CVSROOT
-
-           # Checkout of nonexistent module
-           dotest_fail crerepos-3 \
-"${testcvs} -d ${TESTDIR}/crerepos co cvs-sanity" \
-"${PROG} checkout: cannot find module .cvs-sanity. - ignored"
-
-           # Now test that CVS works correctly without a modules file
-           # or any of that other stuff.  In particular, it *must*
-           # function if administrative files added to CVS recently (since
-           # CVS 1.3) do not exist, because the repository might have
-           # been created with an old version of CVS.
-           mkdir 1; cd 1
-           dotest crerepos-4 \
-"${testcvs} -q -d ${TESTDIR}/crerepos co CVSROOT" \
-''
-           if echo yes | \
-${testcvs} -d ${TESTDIR}/crerepos release -d CVSROOT >>${LOGFILE}; then
-             pass crerepos-5
-           else
-             fail crerepos-5
-           fi
-           rm -rf CVS
-           cd ..
-           # The directory 1 should be empty
-           dotest crerepos-6 "rmdir 1"
+         else # local
+           CREREPOS_ROOT=$TESTDIR/crerepos
+         fi
 
-           CREREPOS_ROOT=${TESTDIR}/crerepos
+         # First, if the repository doesn't exist at all...
+         dotest_fail crerepos-1 \
+"$testcvs -d $CREREPOS_ROOT co cvs-sanity" \
+"$PROG \[checkout aborted\]: $TESTDIR/crerepos/CVSROOT: .*" \
+"Cannot access $TESTDIR/crerepos/CVSROOT$DOTSTAR"
+         mkdir crerepos
+
+         # The repository exists but CVSROOT doesn't.
+         dotest_fail crerepos-2 \
+"$testcvs -d $CREREPOS_ROOT co cvs-sanity" \
+"$PROG \[checkout aborted\]: $TESTDIR/crerepos/CVSROOT: .*" \
+"Cannot access $TESTDIR/crerepos/CVSROOT$DOTSTAR"
+         mkdir crerepos/CVSROOT
+
+         # Checkout of nonexistent module
+         dotest_fail crerepos-3 \
+"$testcvs -d $CREREPOS_ROOT co cvs-sanity" \
+"$PROG checkout: cannot find module .cvs-sanity. - ignored" \
+"$PROG server: cannot find module .cvs-sanity. - ignored
+cvs \[checkout aborted\]: cannot expand modules"
+
+         # Now test that CVS works correctly without a modules file
+         # or any of that other stuff.  In particular, it *must*
+         # function if administrative files added to CVS recently (since
+         # CVS 1.3) do not exist, because the repository might have
+         # been created with an old version of CVS.
+         mkdir 1; cd 1
+         dotest crerepos-4 \
+"$testcvs -q -d $CREREPOS_ROOT co CVSROOT"
 
-         fi
+         dotest crerepos-5 \
+"echo yes |$testcvs -d $CREREPOS_ROOT release -d CVSROOT" \
+"You have \[0\] altered files in this repository\.
+Are you sure you want to release (and delete) directory \`CVSROOT': "
+         rm -rf CVS
+         cd ..
+         # The directory 1 should be empty
+         dotest crerepos-6 "rmdir 1"
 
          if $remote; then
            # Test that CVS rejects a relative path in CVSROOT.
@@ -19622,11 +19656,10 @@ ${PROG} \[init aborted\]: Bad CVSROOT: .
            rm -r 1
          fi # end of tests to be skipped for remote
 
-         # CVS better not create a history file--if the administrator 
+         # CVS should have created a history file.  If the administrator 
          # doesn't need it and wants to save on disk space, they just
-         # delete it.
-         dotest_fail crerepos-7 \
-"test -f ${TESTDIR}/crerepos/CVSROOT/history" ''
+         # delete it and set LogHistory = the empty string in config.
+         dotest crerepos-7 "test -f $TESTDIR/crerepos/CVSROOT/history"
 
          # Now test mixing repositories.  This kind of thing tends to
          # happen accidentally when people work with several repositories.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]