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 || 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 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 * 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 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