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