cvs-cvs
[Top][All Lists]
Advanced

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

[Cvs-cvs] Changes to ccvs/src/vers_ts.c [signed-commits2]


From: Derek Robert Price
Subject: [Cvs-cvs] Changes to ccvs/src/vers_ts.c [signed-commits2]
Date: Tue, 22 Nov 2005 20:46:59 -0500

Index: ccvs/src/vers_ts.c
diff -u /dev/null ccvs/src/vers_ts.c:1.65.4.1
--- /dev/null   Wed Nov 23 01:46:59 2005
+++ ccvs/src/vers_ts.c  Wed Nov 23 01:46:55 2005
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ *                                  and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions Copyright (C) 1989-1992, Brian Berliner
+ * 
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ */
+
+#include "cvs.h"
+#include "lstat.h"
+
+#ifdef SERVER_SUPPORT
+static void time_stamp_server (const char *, Vers_TS *, Entnode *);
+#endif
+
+/* Fill in and return a Vers_TS structure for the file FINFO.
+ *
+ * INPUTS
+ *   finfo             struct file_info data about the file to be examined.
+ *   options           Keyword expansion options, I think generally from the
+ *                     command line.  Can be either NULL or "" to indicate
+ *                     none are specified here.
+ *   tag               Tag specified by user on the command line (via -r).
+ *   date              Date specified by user on the command line (via -D).
+ *   force_tag_match   If set and TAG is specified, will only set RET->vn_rcs
+ *                     based on TAG.  Otherwise, if TAG is specified and does
+ *                     not exist in the file, RET->vn_rcs will be set to the
+ *                     head revision.
+ *   set_time          If set, set the last modification time of the user file
+ *                     specified by FINFO to the checkin time of RET->vn_rcs.
+ *
+ * RETURNS
+ *   Vers_TS structure for FINFO.
+ */
+Vers_TS *
+Version_TS (struct file_info *finfo, char *options, char *tag, char *date,
+            int force_tag_match, int set_time)
+{
+    Node *p;
+    RCSNode *rcsdata;
+    Vers_TS *vers_ts;
+    struct stickydirtag *sdtp;
+    Entnode *entdata;
+    char *rcsexpand = NULL;
+
+    TRACE (TRACE_FUNCTION, "Version_TS (%s, %s, %s, %s, %d, %d)",
+          finfo->fullname, options ? options : "(null)", tag ? tag : "(null)",
+          date ? date : "(null)", force_tag_match, set_time);
+
+    /* get a new Vers_TS struct */
+
+    vers_ts = xmalloc (sizeof (Vers_TS));
+    memset (vers_ts, 0, sizeof (*vers_ts));
+
+    /*
+     * look up the entries file entry and fill in the version and timestamp
+     * if entries is NULL, there is no entries file so don't bother trying to
+     * look it up (used by checkout -P)
+     */
+    if (finfo->entries == NULL)
+    {
+       sdtp = NULL;
+       p = NULL;
+    }
+    else
+    {
+       p = findnode_fn (finfo->entries, finfo->file);
+       sdtp = finfo->entries->list->data; /* list-private */
+    }
+
+    if (p == NULL)
+    {
+       TRACE (TRACE_DATA, "Version_TS: No entries data found.");
+       entdata = NULL;
+    }
+    else
+    {
+       entdata = p->data;
+
+       if (entdata->type == ENT_SUBDIR)
+       {
+           /* According to cvs.texinfo, the various fields in the Entries
+              file for a directory (other than the name) do not have a
+              defined meaning.  We need to pass them along without getting
+              confused based on what is in them.  Therefore we make sure
+              not to set vn_user and the like from Entries, add.c and
+              perhaps other code will expect these fields to be NULL for
+              a directory.  */
+           vers_ts->entdata = entdata;
+       }
+       else
+#ifdef SERVER_SUPPORT
+       /* An entries line with "D" in the timestamp indicates that the
+          client sent Is-modified without sending Entry.  So we want to
+          use the entries line for the sole purpose of telling
+          time_stamp_server what is up; we don't want the rest of CVS
+          to think there is an entries line.  */
+       if (strcmp (entdata->timestamp, "D") != 0)
+#endif
+       {
+           vers_ts->vn_user = xstrdup (entdata->version);
+           vers_ts->ts_rcs = xstrdup (entdata->timestamp);
+           vers_ts->ts_conflict = xstrdup (entdata->conflict);
+           if (!(tag || date) && !(sdtp && sdtp->aflag))
+           {
+               vers_ts->tag = xstrdup (entdata->tag);
+               vers_ts->date = xstrdup (entdata->date);
+           }
+           vers_ts->entdata = entdata;
+       }
+       /* Even if we don't have an "entries line" as such
+          (vers_ts->entdata), we want to pick up options which could
+          have been from a Kopt protocol request.  */
+       if (!options || *options == '\0')
+       {
+           if (!(sdtp && sdtp->aflag))
+               vers_ts->options = xstrdup (entdata->options);
+       }
+    }
+
+    /* Always look up the RCS keyword mode when we have an RCS archive.  It
+     * will either be needed as a default or to avoid allowing the -k options
+     * specified on the command line from overriding binary mode (-kb).
+     */
+    if (finfo->rcs != NULL)
+       rcsexpand = RCS_getexpand (finfo->rcs);
+
+    /*
+     * -k options specified on the command line override (and overwrite)
+     * options stored in the entries file and default options from the RCS
+     * archive, except for binary mode (-kb).
+     */
+    if (options && *options != '\0')
+    {
+       if (vers_ts->options != NULL)
+           free (vers_ts->options);
+       if (rcsexpand != NULL && strcmp (rcsexpand, "b") == 0)
+           vers_ts->options = xstrdup ("-kb");
+       else
+           vers_ts->options = xstrdup (options);
+    }
+    else if ((!vers_ts->options || *vers_ts->options == '\0')
+             && rcsexpand != NULL)
+    {
+       /* If no keyword expansion was specified on command line,
+          use whatever was in the rcs file (if there is one).  This
+          is how we, if we are the server, tell the client whether
+          a file is binary.  */
+       if (vers_ts->options != NULL)
+           free (vers_ts->options);
+       vers_ts->options = xmalloc (strlen (rcsexpand) + 3);
+       strcpy (vers_ts->options, "-k");
+       strcat (vers_ts->options, rcsexpand);
+    }
+    if (!vers_ts->options)
+       vers_ts->options = xstrdup ("");
+
+    /*
+     * if tags were specified on the command line, they override what is in
+     * the Entries file
+     */
+    if (tag || date)
+    {
+       vers_ts->tag = xstrdup (tag);
+       vers_ts->date = xstrdup (date);
+    }
+    else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
+    {
+       if (!vers_ts->tag)
+       {
+           vers_ts->tag = xstrdup (sdtp->tag);
+           vers_ts->nonbranch = sdtp->nonbranch;
+       }
+       if (!vers_ts->date)
+           vers_ts->date = xstrdup (sdtp->date);
+    }
+
+    /* Now look up the info on the source controlled file */
+    if (finfo->rcs != NULL)
+    {
+       rcsdata = finfo->rcs;
+       rcsdata->refcount++;
+    }
+    else if (finfo->repository != NULL)
+       rcsdata = RCS_parse (finfo->file, finfo->repository);
+    else
+       rcsdata = NULL;
+
+    if (rcsdata != NULL)
+    {
+       /* squirrel away the rcsdata pointer for others */
+       vers_ts->srcfile = rcsdata;
+
+       if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
+       {
+           vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
+           vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
+       }
+       else
+       {
+           int simple;
+
+           vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
+                                             vers_ts->date, force_tag_match,
+                                             &simple);
+           if (vers_ts->vn_rcs == NULL)
+               vers_ts->vn_tag = NULL;
+           else if (simple)
+               vers_ts->vn_tag = xstrdup (vers_ts->tag);
+           else
+               vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
+       }
+
+       /*
+        * If the source control file exists and has the requested revision,
+        * get the Date the revision was checked in.  If "user" exists, set
+        * its mtime.
+        */
+       if (set_time && vers_ts->vn_rcs != NULL)
+       {
+#ifdef SERVER_SUPPORT
+           if (server_active)
+               server_modtime (finfo, vers_ts);
+           else
+#endif
+           {
+               struct utimbuf t;
+
+               memset (&t, 0, sizeof (t));
+               t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, 0, 0);
+               if (t.modtime != (time_t) -1)
+               {
+#ifdef UTIME_EXPECTS_WRITABLE
+                   int change_it_back = 0;
+#endif
+
+                   (void) time (&t.actime);
+
+#ifdef UTIME_EXPECTS_WRITABLE
+                   if (!iswritable (finfo->file))
+                   {
+                       xchmod (finfo->file, 1);
+                       change_it_back = 1;
+                   }
+#endif  /* UTIME_EXPECTS_WRITABLE  */
+
+                   /* This used to need to ignore existence_errors
+                      (for cases like where update.c now clears
+                      set_time if noexec, but didn't used to).  I
+                      think maybe now it doesn't (server_modtime does
+                      not like those kinds of cases).  */
+                   (void) utime (finfo->file, &t);
+
+#ifdef UTIME_EXPECTS_WRITABLE
+                   if (change_it_back)
+                       xchmod (finfo->file, 0);
+#endif  /*  UTIME_EXPECTS_WRITABLE  */
+               }
+           }
+       }
+    }
+
+    /* get user file time-stamp in ts_user */
+    if (finfo->entries != NULL)
+    {
+#ifdef SERVER_SUPPORT
+       if (server_active)
+           time_stamp_server (finfo->file, vers_ts, entdata);
+       else
+#endif
+           vers_ts->ts_user = time_stamp (finfo->file);
+    }
+
+    return (vers_ts);
+}
+
+
+
+#ifdef SERVER_SUPPORT
+
+/* Set VERS_TS->TS_USER to time stamp for FILE.  */
+
+/* Separate these out to keep the logic below clearer.  */
+#define mark_lost(V)           ((V)->ts_user = 0)
+#define mark_unchanged(V)      ((V)->ts_user = xstrdup ((V)->ts_rcs))
+
+static void
+time_stamp_server (const char *file, Vers_TS *vers_ts, Entnode *entdata)
+{
+    struct stat sb;
+    char *cp;
+
+    TRACE (TRACE_FUNCTION, "time_stamp_server (%s, %s, %s, %s)",
+          file,
+          entdata && entdata->version ? entdata->version : "(null)",
+          entdata && entdata->timestamp ? entdata->timestamp : "(null)",
+          entdata && entdata->conflict ? entdata->conflict : "(null)");
+
+    if (lstat (file, &sb) < 0)
+    {
+       if (! existence_error (errno))
+           error (1, errno, "cannot stat temp file");
+
+       /* Missing file means lost or unmodified; check entries
+          file to see which.
+
+          XXX FIXME - If there's no entries file line, we
+          wouldn't be getting the file at all, so consider it
+          lost.  I don't know that that's right, but it's not
+          clear to me that either choice is.  Besides, would we
+          have an RCS string in that case anyways?  */
+       if (entdata == NULL)
+           mark_lost (vers_ts);
+       else if (entdata->timestamp
+                && entdata->timestamp[0] == '='
+                && entdata->timestamp[1] == '\0')
+           mark_unchanged (vers_ts);
+       else if (entdata->conflict
+                && entdata->conflict[0] == '=')
+       {
+           /* These just need matching content.  Might as well minimize it.  */
+           vers_ts->ts_user = xstrdup ("");
+           vers_ts->ts_conflict = xstrdup ("");
+       }
+       else if (entdata->timestamp
+                && (entdata->timestamp[0] == 'M'
+                    || entdata->timestamp[0] == 'D')
+                && entdata->timestamp[1] == '\0')
+           vers_ts->ts_user = xstrdup ("Is-modified");
+       else
+           mark_lost (vers_ts);
+    }
+    else if (sb.st_mtime == 0)
+    {
+       /* We shouldn't reach this case any more!  */
+       abort ();
+    }
+    else
+    {
+        struct tm *tm_p;
+
+       vers_ts->ts_user = xmalloc (25);
+       /* We want to use the same timestamp format as is stored in the
+          st_mtime.  For unix (and NT I think) this *must* be universal
+          time (UT), so that files don't appear to be modified merely
+          because the timezone has changed.  For VMS, or hopefully other
+          systems where gmtime returns NULL, the modification time is
+          stored in local time, and therefore it is not possible to cause
+          st_mtime to be out of sync by changing the timezone.  */
+       tm_p = gmtime (&sb.st_mtime);
+       cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime);
+       cp[24] = 0;
+       /* Fix non-standard format.  */
+       if (cp[8] == '0') cp[8] = ' ';
+       (void) strcpy (vers_ts->ts_user, cp);
+    }
+}
+
+#endif /* SERVER_SUPPORT */
+
+
+
+/* Given a UNIX seconds since the epoch, return a string in the format used by
+ * the Entries file.
+ *
+ *
+ * INPUTS
+ *   UNIXTIME  The timestamp to be formatted.
+ *
+ * RETURNS
+ *   A freshly allocated string the caller is responsible for disposing of.
+ */
+char *
+entries_time (time_t unixtime)
+{
+    struct tm *tm_p;
+    char *cp;
+
+    /* We want to use the same timestamp format as is stored in the
+       st_mtime.  For unix (and NT I think) this *must* be universal
+       time (UT), so that files don't appear to be modified merely
+       because the timezone has changed.  For VMS, or hopefully other
+       systems where gmtime returns NULL, the modification time is
+       stored in local time, and therefore it is not possible to cause
+       st_mtime to be out of sync by changing the timezone.  */
+    tm_p = gmtime (&unixtime);
+    cp = tm_p ? asctime (tm_p) : ctime (&unixtime);
+    /* Get rid of the EOL */
+    cp[24] = '\0';
+    /* Fix non-standard format.  */
+    if (cp[8] == '0') cp[8] = ' ';
+
+    return Xasprintf ("%s", cp);
+}
+
+
+
+time_t
+unix_time_stamp (const char *file)
+{
+    struct stat sb;
+    time_t mtime = 0L;
+
+    if (!lstat (file, &sb))
+    {
+       mtime = sb.st_mtime;
+    }
+
+    /* If it's a symlink, return whichever is the newest mtime of
+       the link and its target, for safety.
+    */
+    if (!stat (file, &sb))
+    {
+        if (mtime < sb.st_mtime)
+           mtime = sb.st_mtime;
+    }
+
+    return mtime;
+}
+
+
+
+/*
+ * Gets the time-stamp for the file "file" and returns it in space it
+ * allocates
+ */
+char *
+time_stamp (const char *file)
+{
+    time_t mtime = unix_time_stamp (file);
+    return mtime ? entries_time (mtime) : NULL;
+}
+
+
+
+/*
+ * free up a Vers_TS struct
+ */
+void
+freevers_ts (Vers_TS **versp)
+{
+    if ((*versp)->srcfile)
+       freercsnode (&((*versp)->srcfile));
+    if ((*versp)->vn_user)
+       free ((*versp)->vn_user);
+    if ((*versp)->vn_rcs)
+       free ((*versp)->vn_rcs);
+    if ((*versp)->vn_tag)
+       free ((*versp)->vn_tag);
+    if ((*versp)->ts_user)
+       free ((*versp)->ts_user);
+    if ((*versp)->ts_rcs)
+       free ((*versp)->ts_rcs);
+    if ((*versp)->options)
+       free ((*versp)->options);
+    if ((*versp)->tag)
+       free ((*versp)->tag);
+    if ((*versp)->date)
+       free ((*versp)->date);
+    if ((*versp)->ts_conflict)
+       free ((*versp)->ts_conflict);
+    free ((char *) *versp);
+    *versp = NULL;
+}




reply via email to

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