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