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