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