xref: /openbsd/gnu/usr.bin/cvs/src/vers_ts.c (revision 43c1707e)
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