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