]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/vers_ts.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / cvs / src / vers_ts.c
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  * 
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  */
13
14 #include "cvs.h"
15
16 #ifdef SERVER_SUPPORT
17 static void time_stamp_server PROTO((const char *, Vers_TS *, Entnode *));
18 #endif
19
20
21
22 /* Fill in and return a Vers_TS structure for the file FINFO.  TAG and
23    DATE are from the command line.  */
24
25 Vers_TS *
26 Version_TS (finfo, options, tag, date, force_tag_match, set_time)
27     struct file_info *finfo;
28
29     /* Keyword expansion options, I think generally from the command
30        line.  Can be either NULL or "" to indicate none are specified
31        here.  */
32     char *options;
33     char *tag;
34     char *date;
35     int force_tag_match;
36     int set_time;
37 {
38     Node *p;
39     RCSNode *rcsdata;
40     Vers_TS *vers_ts;
41     struct stickydirtag *sdtp;
42     Entnode *entdata;
43     char *rcsexpand = NULL;
44
45 #ifdef UTIME_EXPECTS_WRITABLE
46     int change_it_back = 0;
47 #endif
48
49     /* get a new Vers_TS struct */
50     vers_ts = (Vers_TS *) xmalloc (sizeof (Vers_TS));
51     memset ((char *) vers_ts, 0, sizeof (*vers_ts));
52
53     /*
54      * look up the entries file entry and fill in the version and timestamp
55      * if entries is NULL, there is no entries file so don't bother trying to
56      * look it up (used by checkout -P)
57      */
58     if (finfo->entries == NULL)
59     {
60         sdtp = NULL;
61         p = NULL;
62     }
63     else
64     {
65         p = findnode_fn (finfo->entries, finfo->file);
66         sdtp = finfo->entries->list->data; /* list-private */
67     }
68
69     entdata = NULL;
70     if (p != NULL)
71     {
72         entdata = p->data;
73
74         if (entdata->type == ENT_SUBDIR)
75         {
76             /* According to cvs.texinfo, the various fields in the Entries
77                file for a directory (other than the name) do not have a
78                defined meaning.  We need to pass them along without getting
79                confused based on what is in them.  Therefore we make sure
80                not to set vn_user and the like from Entries, add.c and
81                perhaps other code will expect these fields to be NULL for
82                a directory.  */
83             vers_ts->entdata = entdata;
84         }
85         else
86 #ifdef SERVER_SUPPORT
87         /* An entries line with "D" in the timestamp indicates that the
88            client sent Is-modified without sending Entry.  So we want to
89            use the entries line for the sole purpose of telling
90            time_stamp_server what is up; we don't want the rest of CVS
91            to think there is an entries line.  */
92         if (strcmp (entdata->timestamp, "D") != 0)
93 #endif
94         {
95             vers_ts->vn_user = xstrdup (entdata->version);
96             vers_ts->ts_rcs = xstrdup (entdata->timestamp);
97             vers_ts->ts_conflict = xstrdup (entdata->conflict);
98             if (!(tag || date) && !(sdtp && sdtp->aflag))
99             {
100                 vers_ts->tag = xstrdup (entdata->tag);
101                 vers_ts->date = xstrdup (entdata->date);
102             }
103             vers_ts->entdata = entdata;
104         }
105         /* Even if we don't have an "entries line" as such
106            (vers_ts->entdata), we want to pick up options which could
107            have been from a Kopt protocol request.  */
108         if (!options || *options == '\0')
109         {
110             if (!(sdtp && sdtp->aflag))
111                 vers_ts->options = xstrdup (entdata->options);
112         }
113     }
114
115     /* Always look up the RCS keyword mode when we have an RCS archive.  It
116      * will either be needed as a default or to avoid allowing the -k options
117      * specified on the command line from overriding binary mode (-kb).
118      */
119     if (finfo->rcs != NULL)
120         rcsexpand = RCS_getexpand (finfo->rcs);
121
122     /*
123      * -k options specified on the command line override (and overwrite)
124      * options stored in the entries file and default options from the RCS
125      * archive, except for binary mode (-kb).
126      */
127     if (options && *options != '\0')
128     {
129         if (vers_ts->options != NULL)
130             free (vers_ts->options);
131         if (rcsexpand != NULL && strcmp (rcsexpand, "b") == 0)
132             vers_ts->options = xstrdup ("-kb");
133         else
134             vers_ts->options = xstrdup (options);
135     }
136     else if ((!vers_ts->options || *vers_ts->options == '\0')
137              && rcsexpand != NULL)
138     {
139         /* If no keyword expansion was specified on command line,
140            use whatever was in the rcs file (if there is one).  This
141            is how we, if we are the server, tell the client whether
142            a file is binary.  */
143         if (vers_ts->options != NULL)
144             free (vers_ts->options);
145         vers_ts->options = xmalloc (strlen (rcsexpand) + 3);
146         strcpy (vers_ts->options, "-k");
147         strcat (vers_ts->options, rcsexpand);
148     }
149     if (!vers_ts->options)
150         vers_ts->options = xstrdup ("");
151
152     /*
153      * if tags were specified on the command line, they override what is in
154      * the Entries file
155      */
156     if (tag || date)
157     {
158         vers_ts->tag = xstrdup (tag);
159         vers_ts->date = xstrdup (date);
160     }
161     else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
162     {
163         if (!vers_ts->tag)
164         {
165             vers_ts->tag = xstrdup (sdtp->tag);
166             vers_ts->nonbranch = sdtp->nonbranch;
167         }
168         if (!vers_ts->date)
169             vers_ts->date = xstrdup (sdtp->date);
170     }
171
172     /* Now look up the info on the source controlled file */
173     if (finfo->rcs != NULL)
174     {
175         rcsdata = finfo->rcs;
176         rcsdata->refcount++;
177     }
178     else if (finfo->repository != NULL)
179         rcsdata = RCS_parse (finfo->file, finfo->repository);
180     else
181         rcsdata = NULL;
182
183     if (rcsdata != NULL)
184     {
185         /* squirrel away the rcsdata pointer for others */
186         vers_ts->srcfile = rcsdata;
187
188         if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
189         {
190             vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
191             vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
192         }
193         else
194         {
195             int simple;
196
197             vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
198                                               vers_ts->date, force_tag_match,
199                                               &simple);
200             if (vers_ts->vn_rcs == NULL)
201                 vers_ts->vn_tag = NULL;
202             else if (simple)
203                 vers_ts->vn_tag = xstrdup (vers_ts->tag);
204             else
205                 vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
206         }
207
208         /*
209          * If the source control file exists and has the requested revision,
210          * get the Date the revision was checked in.  If "user" exists, set
211          * its mtime.
212          */
213         if (set_time && vers_ts->vn_rcs != NULL)
214         {
215 #ifdef SERVER_SUPPORT
216             if (server_active)
217                 server_modtime (finfo, vers_ts);
218             else
219 #endif
220             {
221                 struct utimbuf t;
222
223                 memset (&t, 0, sizeof (t));
224                 t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs, 0, 0);
225                 if (t.modtime != (time_t) -1)
226                 {
227                     (void) time (&t.actime);
228
229 #ifdef UTIME_EXPECTS_WRITABLE
230                     if (!iswritable (finfo->file))
231                     {
232                         xchmod (finfo->file, 1);
233                         change_it_back = 1;
234                     }
235 #endif  /* UTIME_EXPECTS_WRITABLE  */
236
237                     /* This used to need to ignore existence_errors
238                        (for cases like where update.c now clears
239                        set_time if noexec, but didn't used to).  I
240                        think maybe now it doesn't (server_modtime does
241                        not like those kinds of cases).  */
242                     (void) utime (finfo->file, &t);
243
244 #ifdef UTIME_EXPECTS_WRITABLE
245                     if (change_it_back)
246                     {
247                         xchmod (finfo->file, 0);
248                         change_it_back = 0;
249                     }
250 #endif  /*  UTIME_EXPECTS_WRITABLE  */
251                 }
252             }
253         }
254     }
255
256     /* get user file time-stamp in ts_user */
257     if (finfo->entries != (List *) NULL)
258     {
259 #ifdef SERVER_SUPPORT
260         if (server_active)
261             time_stamp_server (finfo->file, vers_ts, entdata);
262         else
263 #endif
264             vers_ts->ts_user = time_stamp (finfo->file);
265     }
266
267     return (vers_ts);
268 }
269
270 #ifdef SERVER_SUPPORT
271
272 /* Set VERS_TS->TS_USER to time stamp for FILE.  */
273
274 /* Separate these out to keep the logic below clearer.  */
275 #define mark_lost(V)            ((V)->ts_user = 0)
276 #define mark_unchanged(V)       ((V)->ts_user = xstrdup ((V)->ts_rcs))
277
278 static void
279 time_stamp_server (file, vers_ts, entdata)
280     const char *file;
281     Vers_TS *vers_ts;
282     Entnode *entdata;
283 {
284     struct stat sb;
285     char *cp;
286
287     if (CVS_LSTAT (file, &sb) < 0)
288     {
289         if (! existence_error (errno))
290             error (1, errno, "cannot stat temp file");
291
292         /* Missing file means lost or unmodified; check entries
293            file to see which.
294
295            XXX FIXME - If there's no entries file line, we
296            wouldn't be getting the file at all, so consider it
297            lost.  I don't know that that's right, but it's not
298            clear to me that either choice is.  Besides, would we
299            have an RCS string in that case anyways?  */
300         if (entdata == NULL)
301             mark_lost (vers_ts);
302         else if (entdata->timestamp
303                  && entdata->timestamp[0] == '=')
304             mark_unchanged (vers_ts);
305         else if (entdata->conflict
306                  && entdata->conflict[0] == '=')
307         {
308             /* These just need matching content.  Might as well minimize it.  */
309             vers_ts->ts_user = xstrdup ("");
310             vers_ts->ts_conflict = xstrdup ("");
311         }
312         else if (entdata->timestamp
313                  && (entdata->timestamp[0] == 'M'
314                      || entdata->timestamp[0] == 'D')
315                  && entdata->timestamp[1] == '\0')
316             vers_ts->ts_user = xstrdup ("Is-modified");
317         else
318             mark_lost (vers_ts);
319     }
320     else if (sb.st_mtime == 0)
321     {
322         /* We shouldn't reach this case any more!  */
323         abort ();
324     }
325     else
326     {
327         struct tm *tm_p;
328
329         vers_ts->ts_user = xmalloc (25);
330         /* We want to use the same timestamp format as is stored in the
331            st_mtime.  For unix (and NT I think) this *must* be universal
332            time (UT), so that files don't appear to be modified merely
333            because the timezone has changed.  For VMS, or hopefully other
334            systems where gmtime returns NULL, the modification time is
335            stored in local time, and therefore it is not possible to cause
336            st_mtime to be out of sync by changing the timezone.  */
337         tm_p = gmtime (&sb.st_mtime);
338         cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime);
339         cp[24] = 0;
340         /* Fix non-standard format.  */
341         if (cp[8] == '0') cp[8] = ' ';
342         (void) strcpy (vers_ts->ts_user, cp);
343     }
344 }
345
346 #endif /* SERVER_SUPPORT */
347 /*
348  * Gets the time-stamp for the file "file" and returns it in space it
349  * allocates
350  */
351 char *
352 time_stamp (file)
353     const char *file;
354 {
355     struct stat sb;
356     char *cp;
357     char *ts = NULL;
358     time_t mtime = 0L;
359
360     if (!CVS_LSTAT (file, &sb))
361     {
362         mtime = sb.st_mtime;
363     }
364     else if (! existence_error (errno))
365         error (0, errno, "cannot lstat %s", file);
366
367     /* If it's a symlink, return whichever is the newest mtime of
368        the link and its target, for safety.
369     */
370     if (!CVS_STAT (file, &sb))
371     {
372         if (mtime < sb.st_mtime)
373             mtime = sb.st_mtime;
374     }
375     else if (! existence_error (errno))
376         error (0, errno, "cannot stat %s", file);
377
378     if (mtime)
379     {
380         struct tm *tm_p;
381         ts = xmalloc (25);
382         /* We want to use the same timestamp format as is stored in the
383            st_mtime.  For unix (and NT I think) this *must* be universal
384            time (UT), so that files don't appear to be modified merely
385            because the timezone has changed.  For VMS, or hopefully other
386            systems where gmtime returns NULL, the modification time is
387            stored in local time, and therefore it is not possible to cause
388            st_mtime to be out of sync by changing the timezone.  */
389         tm_p = gmtime (&sb.st_mtime);
390         cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime);
391         cp[24] = 0;
392         /* Fix non-standard format.  */
393         if (cp[8] == '0') cp[8] = ' ';
394         (void) strcpy (ts, cp);
395     }
396
397     return (ts);
398 }
399
400 /*
401  * free up a Vers_TS struct
402  */
403 void
404 freevers_ts (versp)
405     Vers_TS **versp;
406 {
407     if ((*versp)->srcfile)
408         freercsnode (&((*versp)->srcfile));
409     if ((*versp)->vn_user)
410         free ((*versp)->vn_user);
411     if ((*versp)->vn_rcs)
412         free ((*versp)->vn_rcs);
413     if ((*versp)->vn_tag)
414         free ((*versp)->vn_tag);
415     if ((*versp)->ts_user)
416         free ((*versp)->ts_user);
417     if ((*versp)->ts_rcs)
418         free ((*versp)->ts_rcs);
419     if ((*versp)->options)
420         free ((*versp)->options);
421     if ((*versp)->tag)
422         free ((*versp)->tag);
423     if ((*versp)->date)
424         free ((*versp)->date);
425     if ((*versp)->ts_conflict)
426         free ((*versp)->ts_conflict);
427     free ((char *) *versp);
428     *versp = (Vers_TS *) NULL;
429 }