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