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