entries.c revision 17721
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 1.4 kit. 7 * 8 * Entries file to Files file 9 * 10 * Creates the file Files containing the names that comprise the project, from 11 * the Entries file. 12 */ 13 14#include "cvs.h" 15#include "getline.h" 16 17static Node *AddEntryNode PROTO((List * list, Entnode *entnode)); 18 19static Entnode *fgetentent PROTO((FILE *)); 20static int fputentent PROTO((FILE *, Entnode *)); 21 22static FILE *entfile; 23static char *entfilename; /* for error messages */ 24 25/* 26 * Construct an Entnode 27 */ 28Entnode * 29Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) 30 const char *user; 31 const char *vn; 32 const char *ts; 33 const char *options; 34 const char *tag; 35 const char *date; 36 const char *ts_conflict; 37{ 38 Entnode *ent; 39 40 /* Note that timestamp and options must be non-NULL */ 41 ent = (Entnode *) xmalloc (sizeof (Entnode)); 42 ent->user = xstrdup (user); 43 ent->version = xstrdup (vn); 44 ent->timestamp = xstrdup (ts ? ts : ""); 45 ent->options = xstrdup (options ? options : ""); 46 ent->tag = xstrdup (tag); 47 ent->date = xstrdup (date); 48 ent->conflict = xstrdup (ts_conflict); 49 50 return ent; 51} 52 53/* 54 * Destruct an Entnode 55 */ 56void 57Entnode_Destroy (ent) 58 Entnode *ent; 59{ 60 free (ent->user); 61 free (ent->version); 62 free (ent->timestamp); 63 free (ent->options); 64 if (ent->tag) 65 free (ent->tag); 66 if (ent->date) 67 free (ent->date); 68 if (ent->conflict) 69 free (ent->conflict); 70 free (ent); 71} 72 73/* 74 * Write out the line associated with a node of an entries file 75 */ 76static int write_ent_proc PROTO ((Node *, void *)); 77static int 78write_ent_proc (node, closure) 79 Node *node; 80 void *closure; 81{ 82 if (fputentent(entfile, (Entnode *) node->data)) 83 error (1, errno, "cannot write %s", entfilename); 84 85 return (0); 86} 87 88/* 89 * write out the current entries file given a list, making a backup copy 90 * first of course 91 */ 92static void 93write_entries (list) 94 List *list; 95{ 96 /* open the new one and walk the list writing entries */ 97 entfilename = CVSADM_ENTBAK; 98 entfile = open_file (entfilename, "w+"); 99 (void) walklist (list, write_ent_proc, NULL); 100 if (fclose (entfile) == EOF) 101 error (1, errno, "error closing %s", entfilename); 102 103 /* now, atomically (on systems that support it) rename it */ 104 rename_file (entfilename, CVSADM_ENT); 105 106 /* now, remove the log file */ 107 unlink_file (CVSADM_ENTLOG); 108} 109 110/* 111 * Removes the argument file from the Entries file if necessary. 112 */ 113void 114Scratch_Entry (list, fname) 115 List *list; 116 char *fname; 117{ 118 Node *node; 119 120 if (trace) 121#ifdef SERVER_SUPPORT 122 (void) fprintf (stderr, "%c-> Scratch_Entry(%s)\n", 123 (server_active) ? 'S' : ' ', fname); 124#else 125 (void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname); 126#endif 127 128 /* hashlookup to see if it is there */ 129 if ((node = findnode_fn (list, fname)) != NULL) 130 { 131 delnode (node); /* delete the node */ 132#ifdef SERVER_SUPPORT 133 if (server_active) 134 server_scratch (fname); 135#endif 136 if (!noexec) 137 write_entries (list); /* re-write the file */ 138 } 139} 140 141/* 142 * Enters the given file name/version/time-stamp into the Entries file, 143 * removing the old entry first, if necessary. 144 */ 145void 146Register (list, fname, vn, ts, options, tag, date, ts_conflict) 147 List *list; 148 char *fname; 149 char *vn; 150 char *ts; 151 char *options; 152 char *tag; 153 char *date; 154 char *ts_conflict; 155{ 156 Entnode *entnode; 157 Node *node; 158 159#ifdef SERVER_SUPPORT 160 if (server_active) 161 { 162 server_register (fname, vn, ts, options, tag, date, ts_conflict); 163 } 164#endif 165 166 if (trace) 167 { 168#ifdef SERVER_SUPPORT 169 (void) fprintf (stderr, "%c-> Register(%s, %s, %s%s%s, %s, %s %s)\n", 170 (server_active) ? 'S' : ' ', 171 fname, vn, ts ? ts : "", 172 ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", 173 options, tag ? tag : "", date ? date : ""); 174#else 175 (void) fprintf (stderr, "-> Register(%s, %s, %s%s%s, %s, %s %s)\n", 176 fname, vn, ts ? ts : "", 177 ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", 178 options, tag ? tag : "", date ? date : ""); 179#endif 180 } 181 182 entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict); 183 node = AddEntryNode (list, entnode); 184 185 if (!noexec) 186 { 187 entfile = open_file (CVSADM_ENTLOG, "a"); 188 189 write_ent_proc (node, NULL); 190 191 if (fclose (entfile) == EOF) 192 error (1, errno, "error closing %s", CVSADM_ENTLOG); 193 } 194} 195 196/* 197 * Node delete procedure for list-private sticky dir tag/date info 198 */ 199static void 200freesdt (p) 201 Node *p; 202{ 203 struct stickydirtag *sdtp; 204 205 sdtp = (struct stickydirtag *) p->data; 206 if (sdtp->tag) 207 free (sdtp->tag); 208 if (sdtp->date) 209 free (sdtp->date); 210 if (sdtp->options) 211 free (sdtp->options); 212 free ((char *) sdtp); 213} 214 215static Entnode * 216fgetentent(fpin) 217 FILE *fpin; 218{ 219 Entnode *ent; 220 char *line; 221 size_t line_chars_allocated; 222 register char *cp; 223 char *user, *vn, *ts, *options; 224 char *tag_or_date, *tag, *date, *ts_conflict; 225 226 line = NULL; 227 line_chars_allocated = 0; 228 229 ent = NULL; 230 while (getline (&line, &line_chars_allocated, fpin) > 0) 231 { 232 if (line[0] != '/') 233 continue; 234 235 user = line + 1; 236 if ((cp = strchr (user, '/')) == NULL) 237 continue; 238 *cp++ = '\0'; 239 vn = cp; 240 if ((cp = strchr (vn, '/')) == NULL) 241 continue; 242 *cp++ = '\0'; 243 ts = cp; 244 if ((cp = strchr (ts, '/')) == NULL) 245 continue; 246 *cp++ = '\0'; 247 options = cp; 248 if ((cp = strchr (options, '/')) == NULL) 249 continue; 250 *cp++ = '\0'; 251 tag_or_date = cp; 252 if ((cp = strchr (tag_or_date, '\n')) == NULL) 253 continue; 254 *cp = '\0'; 255 tag = (char *) NULL; 256 date = (char *) NULL; 257 if (*tag_or_date == 'T') 258 tag = tag_or_date + 1; 259 else if (*tag_or_date == 'D') 260 date = tag_or_date + 1; 261 262 if ((ts_conflict = strchr (ts, '+'))) 263 *ts_conflict++ = '\0'; 264 265 /* 266 * XXX - Convert timestamp from old format to new format. 267 * 268 * If the timestamp doesn't match the file's current 269 * mtime, we'd have to generate a string that doesn't 270 * match anyways, so cheat and base it on the existing 271 * string; it doesn't have to match the same mod time. 272 * 273 * For an unmodified file, write the correct timestamp. 274 */ 275 { 276 struct stat sb; 277 if (strlen (ts) > 30 && stat (user, &sb) == 0) 278 { 279 char *c = ctime (&sb.st_mtime); 280 281 if (!strncmp (ts + 25, c, 24)) 282 ts = time_stamp (user); 283 else 284 { 285 ts += 24; 286 ts[0] = '*'; 287 } 288 } 289 } 290 291 ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict); 292 break; 293 } 294 295 free (line); 296 return ent; 297} 298 299static int 300fputentent(fp, p) 301 FILE *fp; 302 Entnode *p; 303{ 304 if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0) 305 return 1; 306 if (p->conflict) 307 { 308 if (fprintf (fp, "+%s", p->conflict) < 0) 309 return 1; 310 } 311 if (fprintf (fp, "/%s/", p->options) < 0) 312 return 1; 313 314 if (p->tag) 315 { 316 if (fprintf (fp, "T%s\n", p->tag) < 0) 317 return 1; 318 } 319 else if (p->date) 320 { 321 if (fprintf (fp, "D%s\n", p->date) < 0) 322 return 1; 323 } 324 else 325 { 326 if (fprintf (fp, "\n") < 0) 327 return 1; 328 } 329 330 return 0; 331} 332 333 334/* 335 * Read the entries file into a list, hashing on the file name. 336 */ 337List * 338Entries_Open (aflag) 339 int aflag; 340{ 341 List *entries; 342 Entnode *ent; 343 char *dirtag, *dirdate; 344 int do_rewrite = 0; 345 FILE *fpin; 346 347 /* get a fresh list... */ 348 entries = getlist (); 349 350 /* 351 * Parse the CVS/Tag file, to get any default tag/date settings. Use 352 * list-private storage to tuck them away for Version_TS(). 353 */ 354 ParseTag (&dirtag, &dirdate); 355 if (aflag || dirtag || dirdate) 356 { 357 struct stickydirtag *sdtp; 358 359 sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); 360 memset ((char *) sdtp, 0, sizeof (*sdtp)); 361 sdtp->aflag = aflag; 362 sdtp->tag = xstrdup (dirtag); 363 sdtp->date = xstrdup (dirdate); 364 365 /* feed it into the list-private area */ 366 entries->list->data = (char *) sdtp; 367 entries->list->delproc = freesdt; 368 } 369 370 fpin = fopen (CVSADM_ENT, "r"); 371 if (fpin == NULL) 372 error (0, errno, "cannot open %s for reading", CVSADM_ENT); 373 else 374 { 375 while ((ent = fgetentent (fpin)) != NULL) 376 { 377 (void) AddEntryNode (entries, ent); 378 } 379 380 fclose (fpin); 381 } 382 383 fpin = fopen (CVSADM_ENTLOG, "r"); 384 if (fpin != NULL) 385 { 386 while ((ent = fgetentent (fpin)) != NULL) 387 { 388 (void) AddEntryNode (entries, ent); 389 } 390 do_rewrite = 1; 391 fclose (fpin); 392 } 393 394 if (do_rewrite && !noexec) 395 write_entries (entries); 396 397 /* clean up and return */ 398 if (dirtag) 399 free (dirtag); 400 if (dirdate) 401 free (dirdate); 402 return (entries); 403} 404 405void 406Entries_Close(list) 407 List *list; 408{ 409 if (list) 410 { 411 if (!noexec) 412 { 413 if (isfile (CVSADM_ENTLOG)) 414 write_entries (list); 415 } 416 dellist(&list); 417 } 418} 419 420 421/* 422 * Free up the memory associated with the data section of an ENTRIES type 423 * node 424 */ 425static void 426Entries_delproc (node) 427 Node *node; 428{ 429 Entnode *p; 430 431 p = (Entnode *) node->data; 432 Entnode_Destroy(p); 433} 434 435/* 436 * Get an Entries file list node, initialize it, and add it to the specified 437 * list 438 */ 439static Node * 440AddEntryNode (list, entdata) 441 List *list; 442 Entnode *entdata; 443{ 444 Node *p; 445 446 /* was it already there? */ 447 if ((p = findnode_fn (list, entdata->user)) != NULL) 448 { 449 /* take it out */ 450 delnode (p); 451 } 452 453 /* get a node and fill in the regular stuff */ 454 p = getnode (); 455 p->type = ENTRIES; 456 p->delproc = Entries_delproc; 457 458 /* this one gets a key of the name for hashing */ 459 /* FIXME This results in duplicated data --- the hash package shouldn't 460 assume that the key is dynamically allocated. The user's free proc 461 should be responsible for freeing the key. */ 462 p->key = xstrdup (entdata->user); 463 p->data = (char *) entdata; 464 465 /* put the node into the list */ 466 addnode (list, p); 467 return (p); 468} 469 470/* 471 * Write out/Clear the CVS/Tag file. 472 */ 473void 474WriteTag (dir, tag, date) 475 char *dir; 476 char *tag; 477 char *date; 478{ 479 FILE *fout; 480 char tmp[PATH_MAX]; 481 482 if (noexec) 483 return; 484 485 if (dir == NULL) 486 (void) strcpy (tmp, CVSADM_TAG); 487 else 488 (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG); 489 490 if (tag || date) 491 { 492 fout = open_file (tmp, "w+"); 493 if (tag) 494 { 495 if (fprintf (fout, "T%s\n", tag) < 0) 496 error (1, errno, "write to %s failed", tmp); 497 } 498 else 499 { 500 if (fprintf (fout, "D%s\n", date) < 0) 501 error (1, errno, "write to %s failed", tmp); 502 } 503 if (fclose (fout) == EOF) 504 error (1, errno, "cannot close %s", tmp); 505 } 506 else 507 if (unlink_file (tmp) < 0 && ! existence_error (errno)) 508 error (1, errno, "cannot remove %s", tmp); 509} 510 511/* 512 * Parse the CVS/Tag file for the current directory. 513 */ 514void 515ParseTag (tagp, datep) 516 char **tagp; 517 char **datep; 518{ 519 FILE *fp; 520 521 if (tagp) 522 *tagp = (char *) NULL; 523 if (datep) 524 *datep = (char *) NULL; 525 fp = fopen (CVSADM_TAG, "r"); 526 if (fp) 527 { 528 char *line; 529 int line_length; 530 size_t line_chars_allocated; 531 532 line = NULL; 533 line_chars_allocated = 0; 534 535 if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0) 536 { 537 /* Remove any trailing newline. */ 538 if (line[line_length - 1] == '\n') 539 line[--line_length] = '\0'; 540 if (*line == 'T' && tagp) 541 *tagp = xstrdup (line + 1); 542 else if (*line == 'D' && datep) 543 *datep = xstrdup (line + 1); 544 } 545 (void) fclose (fp); 546 free (line); 547 } 548} 549