xref: /openbsd/gnu/usr.bin/cvs/src/vers_ts.c (revision 133306f0)
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 *
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)
91 	    {
92 		if (!(sdtp && sdtp->aflag))
93 		    vers_ts->tag = xstrdup (entdata->tag);
94 	    }
95 	    if (!date)
96 	    {
97 		if (!(sdtp && sdtp->aflag))
98 		    vers_ts->date = xstrdup (entdata->date);
99 	    }
100 	    vers_ts->entdata = entdata;
101 	}
102 	/* Even if we don't have an "entries line" as such
103 	   (vers_ts->entdata), we want to pick up options which could
104 	   have been from a Kopt protocol request.  */
105 	if (!options || (options && *options == '\0'))
106 	{
107 	    if (!(sdtp && sdtp->aflag))
108 		vers_ts->options = xstrdup (entdata->options);
109 	}
110     }
111 
112     /*
113      * -k options specified on the command line override (and overwrite)
114      * options stored in the entries file
115      */
116     if (options && *options != '\0')
117 	vers_ts->options = xstrdup (options);
118     else if (!vers_ts->options || *vers_ts->options == '\0')
119     {
120 	if (finfo->rcs != NULL)
121 	{
122 	    /* If no keyword expansion was specified on command line,
123 	       use whatever was in the rcs file (if there is one).  This
124 	       is how we, if we are the server, tell the client whether
125 	       a file is binary.  */
126 	    char *rcsexpand = RCS_getexpand (finfo->rcs);
127 	    if (rcsexpand != NULL)
128 	    {
129 		vers_ts->options = xmalloc (strlen (rcsexpand) + 3);
130 		strcpy (vers_ts->options, "-k");
131 		strcat (vers_ts->options, rcsexpand);
132 	    }
133 	}
134     }
135     if (!vers_ts->options)
136 	vers_ts->options = xstrdup ("");
137 
138     /*
139      * if tags were specified on the command line, they override what is in
140      * the Entries file
141      */
142     if (tag || date)
143     {
144 	vers_ts->tag = xstrdup (tag);
145 	vers_ts->date = xstrdup (date);
146     }
147     else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
148     {
149 	if (!vers_ts->tag)
150 	{
151 	    vers_ts->tag = xstrdup (sdtp->tag);
152 	    vers_ts->nonbranch = sdtp->nonbranch;
153 	}
154 	if (!vers_ts->date)
155 	    vers_ts->date = xstrdup (sdtp->date);
156     }
157 
158     /* Now look up the info on the source controlled file */
159     if (finfo->rcs != NULL)
160     {
161 	rcsdata = finfo->rcs;
162 	rcsdata->refcount++;
163     }
164     else if (finfo->repository != NULL)
165 	rcsdata = RCS_parse (finfo->file, finfo->repository);
166     else
167 	rcsdata = NULL;
168 
169     if (rcsdata != NULL)
170     {
171 	/* squirrel away the rcsdata pointer for others */
172 	vers_ts->srcfile = rcsdata;
173 
174 	if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
175 	{
176 	    vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
177 	    vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
178 	}
179 	else
180 	{
181 	    int simple;
182 
183 	    vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
184 					      vers_ts->date, force_tag_match,
185 					      &simple);
186 	    if (vers_ts->vn_rcs == NULL)
187 		vers_ts->vn_tag = NULL;
188 	    else if (simple)
189 		vers_ts->vn_tag = xstrdup (vers_ts->tag);
190 	    else
191 		vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
192 	}
193 
194 	/*
195 	 * If the source control file exists and has the requested revision,
196 	 * get the Date the revision was checked in.  If "user" exists, set
197 	 * its mtime.
198 	 */
199 	if (set_time && vers_ts->vn_rcs != NULL)
200 	{
201 #ifdef SERVER_SUPPORT
202 	    if (server_active)
203 		server_modtime (finfo, vers_ts);
204 	    else
205 #endif
206 	    {
207 		struct utimbuf t;
208 
209 		memset (&t, 0, sizeof (t));
210 		t.modtime =
211 		    RCS_getrevtime (rcsdata, vers_ts->vn_rcs, 0, 0);
212 		if (t.modtime != (time_t) -1)
213 		{
214 		    t.actime = t.modtime;
215 
216 #ifdef UTIME_EXPECTS_WRITABLE
217 		    if (!iswritable (finfo->file))
218 		    {
219 			xchmod (finfo->file, 1);
220 			change_it_back = 1;
221 		    }
222 #endif  /* UTIME_EXPECTS_WRITABLE  */
223 
224 		    /* This used to need to ignore existence_errors
225 		       (for cases like where update.c now clears
226 		       set_time if noexec, but didn't used to).  I
227 		       think maybe now it doesn't (server_modtime does
228 		       not like those kinds of cases).  */
229 		    (void) utime (finfo->file, &t);
230 
231 #ifdef UTIME_EXPECTS_WRITABLE
232 		    if (change_it_back == 1)
233 		    {
234 			xchmod (finfo->file, 0);
235 			change_it_back = 0;
236 		    }
237 #endif  /*  UTIME_EXPECTS_WRITABLE  */
238 		}
239 	    }
240 	}
241     }
242 
243     /* get user file time-stamp in ts_user */
244     if (finfo->entries != (List *) NULL)
245     {
246 #ifdef SERVER_SUPPORT
247 	if (server_active)
248 	    time_stamp_server (finfo->file, vers_ts, entdata);
249 	else
250 #endif
251 	    vers_ts->ts_user = time_stamp (finfo->file);
252     }
253 
254     return (vers_ts);
255 }
256 
257 #ifdef SERVER_SUPPORT
258 
259 /* Set VERS_TS->TS_USER to time stamp for FILE.  */
260 
261 /* Separate these out to keep the logic below clearer.  */
262 #define mark_lost(V)		((V)->ts_user = 0)
263 #define mark_unchanged(V)	((V)->ts_user = xstrdup ((V)->ts_rcs))
264 
265 static void
266 time_stamp_server (file, vers_ts, entdata)
267     char *file;
268     Vers_TS *vers_ts;
269     Entnode *entdata;
270 {
271     struct stat sb;
272     char *cp;
273 
274     if (CVS_LSTAT (file, &sb) < 0)
275     {
276 	if (! existence_error (errno))
277 	    error (1, errno, "cannot stat temp file");
278 
279 	/* Missing file means lost or unmodified; check entries
280 	   file to see which.
281 
282 	   XXX FIXME - If there's no entries file line, we
283 	   wouldn't be getting the file at all, so consider it
284 	   lost.  I don't know that that's right, but it's not
285 	   clear to me that either choice is.  Besides, would we
286 	   have an RCS string in that case anyways?  */
287 	if (entdata == NULL)
288 	    mark_lost (vers_ts);
289 	else if (entdata->timestamp
290 		 && entdata->timestamp[0] == '=')
291 	    mark_unchanged (vers_ts);
292 	else if (entdata->timestamp != NULL
293 		 && (entdata->timestamp[0] == 'M'
294 		     || entdata->timestamp[0] == 'D')
295 		 && entdata->timestamp[1] == '\0')
296 	    vers_ts->ts_user = xstrdup ("Is-modified");
297 	else
298 	    mark_lost (vers_ts);
299     }
300     else if (sb.st_mtime == 0)
301     {
302 	/* We shouldn't reach this case any more!  */
303 	abort ();
304     }
305     else
306     {
307         struct tm *tm_p;
308         struct tm local_tm;
309 
310 	vers_ts->ts_user = xmalloc (25);
311 	/* We want to use the same timestamp format as is stored in the
312 	   st_mtime.  For unix (and NT I think) this *must* be universal
313 	   time (UT), so that files don't appear to be modified merely
314 	   because the timezone has changed.  For VMS, or hopefully other
315 	   systems where gmtime returns NULL, the modification time is
316 	   stored in local time, and therefore it is not possible to cause
317 	   st_mtime to be out of sync by changing the timezone.  */
318 	tm_p = gmtime (&sb.st_mtime);
319 	if (tm_p)
320 	{
321 	    memcpy (&local_tm, tm_p, sizeof (local_tm));
322 	    cp = asctime (&local_tm);	/* copy in the modify time */
323 	}
324 	else
325 	    cp = ctime (&sb.st_mtime);
326 
327 	cp[24] = 0;
328 	(void) strcpy (vers_ts->ts_user, cp);
329     }
330 }
331 
332 #endif /* SERVER_SUPPORT */
333 /*
334  * Gets the time-stamp for the file "file" and returns it in space it
335  * allocates
336  */
337 char *
338 time_stamp (file)
339     char *file;
340 {
341     struct stat sb;
342     char *cp;
343     char *ts;
344 
345     if (CVS_LSTAT (file, &sb) < 0)
346     {
347 	ts = NULL;
348     }
349     else
350     {
351 	struct tm *tm_p;
352         struct tm local_tm;
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 	if (tm_p)
363 	{
364 	    memcpy (&local_tm, tm_p, sizeof (local_tm));
365 	    cp = asctime (&local_tm);	/* copy in the modify time */
366 	}
367 	else
368 	    cp = ctime(&sb.st_mtime);
369 
370 	cp[24] = 0;
371 	(void) strcpy (ts, cp);
372     }
373 
374     return (ts);
375 }
376 
377 /*
378  * free up a Vers_TS struct
379  */
380 void
381 freevers_ts (versp)
382     Vers_TS **versp;
383 {
384     if ((*versp)->srcfile)
385 	freercsnode (&((*versp)->srcfile));
386     if ((*versp)->vn_user)
387 	free ((*versp)->vn_user);
388     if ((*versp)->vn_rcs)
389 	free ((*versp)->vn_rcs);
390     if ((*versp)->vn_tag)
391 	free ((*versp)->vn_tag);
392     if ((*versp)->ts_user)
393 	free ((*versp)->ts_user);
394     if ((*versp)->ts_rcs)
395 	free ((*versp)->ts_rcs);
396     if ((*versp)->options)
397 	free ((*versp)->options);
398     if ((*versp)->tag)
399 	free ((*versp)->tag);
400     if ((*versp)->date)
401 	free ((*versp)->date);
402     if ((*versp)->ts_conflict)
403 	free ((*versp)->ts_conflict);
404     free ((char *) *versp);
405     *versp = (Vers_TS *) NULL;
406 }
407