/contrib/cvs/src/entries.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1263 lines · 879 code · 162 blank · 222 comment · 248 complexity · fb2f9e3f0f631337e9f84c7f090680bc MD5 · raw file

  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. * Entries file to Files file
  14. *
  15. * Creates the file Files containing the names that comprise the project, from
  16. * the Entries file.
  17. */
  18. /*
  19. * $FreeBSD$
  20. */
  21. #include "cvs.h"
  22. #include "getline.h"
  23. static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
  24. static Entnode *fgetentent PROTO((FILE *, char *, int *));
  25. static int fputentent PROTO((FILE *, Entnode *));
  26. static Entnode *subdir_record PROTO((int, const char *, const char *));
  27. static FILE *entfile;
  28. static char *entfilename; /* for error messages */
  29. /*
  30. * Construct an Entnode
  31. */
  32. static Entnode *Entnode_Create PROTO ((enum ent_type, const char *,
  33. const char *, const char *,
  34. const char *, const char *,
  35. const char *, const char *));
  36. static Entnode *
  37. Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict)
  38. enum ent_type type;
  39. const char *user;
  40. const char *vn;
  41. const char *ts;
  42. const char *options;
  43. const char *tag;
  44. const char *date;
  45. const char *ts_conflict;
  46. {
  47. Entnode *ent;
  48. /* Note that timestamp and options must be non-NULL */
  49. ent = (Entnode *) xmalloc (sizeof (Entnode));
  50. ent->type = type;
  51. ent->user = xstrdup (user);
  52. ent->version = xstrdup (vn);
  53. ent->timestamp = xstrdup (ts ? ts : "");
  54. ent->options = xstrdup (options ? options : "");
  55. ent->tag = xstrdup (tag);
  56. ent->date = xstrdup (date);
  57. ent->conflict = xstrdup (ts_conflict);
  58. return ent;
  59. }
  60. /*
  61. * Destruct an Entnode
  62. */
  63. static void Entnode_Destroy PROTO ((Entnode *));
  64. static void
  65. Entnode_Destroy (ent)
  66. Entnode *ent;
  67. {
  68. free (ent->user);
  69. free (ent->version);
  70. free (ent->timestamp);
  71. free (ent->options);
  72. if (ent->tag)
  73. free (ent->tag);
  74. if (ent->date)
  75. free (ent->date);
  76. if (ent->conflict)
  77. free (ent->conflict);
  78. free (ent);
  79. }
  80. /*
  81. * Write out the line associated with a node of an entries file
  82. */
  83. static int write_ent_proc PROTO ((Node *, void *));
  84. static int
  85. write_ent_proc (node, closure)
  86. Node *node;
  87. void *closure;
  88. {
  89. Entnode *entnode = node->data;
  90. if (closure != NULL && entnode->type != ENT_FILE)
  91. *(int *) closure = 1;
  92. if (fputentent(entfile, entnode))
  93. error (1, errno, "cannot write %s", entfilename);
  94. return (0);
  95. }
  96. /*
  97. * write out the current entries file given a list, making a backup copy
  98. * first of course
  99. */
  100. static void
  101. write_entries (list)
  102. List *list;
  103. {
  104. int sawdir;
  105. sawdir = 0;
  106. /* open the new one and walk the list writing entries */
  107. entfilename = CVSADM_ENTBAK;
  108. entfile = CVS_FOPEN (entfilename, "w+");
  109. if (entfile == NULL)
  110. {
  111. /* Make this a warning, not an error. For example, one user might
  112. have checked out a working directory which, for whatever reason,
  113. contains an Entries.Log file. A second user, without write access
  114. to that working directory, might want to do a "cvs log". The
  115. problem rewriting Entries shouldn't affect the ability of "cvs log"
  116. to work, although the warning is probably a good idea so that
  117. whether Entries gets rewritten is not an inexplicable process. */
  118. /* FIXME: should be including update_dir in message. */
  119. error (0, errno, "cannot rewrite %s", entfilename);
  120. /* Now just return. We leave the Entries.Log file around. As far
  121. as I know, there is never any data lying around in 'list' that
  122. is not in Entries.Log at this time (if there is an error writing
  123. Entries.Log that is a separate problem). */
  124. return;
  125. }
  126. (void) walklist (list, write_ent_proc, (void *) &sawdir);
  127. if (! sawdir)
  128. {
  129. struct stickydirtag *sdtp;
  130. /* We didn't write out any directories. Check the list
  131. private data to see whether subdirectory information is
  132. known. If it is, we need to write out an empty D line. */
  133. sdtp = list->list->data;
  134. if (sdtp == NULL || sdtp->subdirs)
  135. if (fprintf (entfile, "D\n") < 0)
  136. error (1, errno, "cannot write %s", entfilename);
  137. }
  138. if (fclose (entfile) == EOF)
  139. error (1, errno, "error closing %s", entfilename);
  140. /* now, atomically (on systems that support it) rename it */
  141. rename_file (entfilename, CVSADM_ENT);
  142. /* now, remove the log file */
  143. if (unlink_file (CVSADM_ENTLOG) < 0
  144. && !existence_error (errno))
  145. error (0, errno, "cannot remove %s", CVSADM_ENTLOG);
  146. }
  147. /*
  148. * Removes the argument file from the Entries file if necessary.
  149. */
  150. void
  151. Scratch_Entry (list, fname)
  152. List *list;
  153. const char *fname;
  154. {
  155. Node *node;
  156. if (trace)
  157. (void) fprintf (stderr, "%s-> Scratch_Entry(%s)\n",
  158. CLIENT_SERVER_STR, fname);
  159. /* hashlookup to see if it is there */
  160. if ((node = findnode_fn (list, fname)) != NULL)
  161. {
  162. if (!noexec)
  163. {
  164. entfilename = CVSADM_ENTLOG;
  165. entfile = open_file (entfilename, "a");
  166. if (fprintf (entfile, "R ") < 0)
  167. error (1, errno, "cannot write %s", entfilename);
  168. write_ent_proc (node, NULL);
  169. if (fclose (entfile) == EOF)
  170. error (1, errno, "error closing %s", entfilename);
  171. }
  172. delnode (node); /* delete the node */
  173. #ifdef SERVER_SUPPORT
  174. if (server_active)
  175. server_scratch (fname);
  176. #endif
  177. }
  178. }
  179. /*
  180. * Enters the given file name/version/time-stamp into the Entries file,
  181. * removing the old entry first, if necessary.
  182. */
  183. void
  184. Register (list, fname, vn, ts, options, tag, date, ts_conflict)
  185. List *list;
  186. const char *fname;
  187. const char *vn;
  188. const char *ts;
  189. const char *options;
  190. const char *tag;
  191. const char *date;
  192. const char *ts_conflict;
  193. {
  194. Entnode *entnode;
  195. Node *node;
  196. #ifdef SERVER_SUPPORT
  197. if (server_active)
  198. {
  199. server_register (fname, vn, ts, options, tag, date, ts_conflict);
  200. }
  201. #endif
  202. if (trace)
  203. {
  204. (void) fprintf (stderr, "%s-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
  205. CLIENT_SERVER_STR,
  206. fname, vn, ts ? ts : "",
  207. ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
  208. options, tag ? tag : "", date ? date : "");
  209. }
  210. entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date,
  211. ts_conflict);
  212. node = AddEntryNode (list, entnode);
  213. if (!noexec)
  214. {
  215. entfilename = CVSADM_ENTLOG;
  216. entfile = CVS_FOPEN (entfilename, "a");
  217. if (entfile == NULL)
  218. {
  219. /* Warning, not error, as in write_entries. */
  220. /* FIXME-update-dir: should be including update_dir in message. */
  221. error (0, errno, "cannot open %s", entfilename);
  222. return;
  223. }
  224. if (fprintf (entfile, "A ") < 0)
  225. error (1, errno, "cannot write %s", entfilename);
  226. write_ent_proc (node, NULL);
  227. if (fclose (entfile) == EOF)
  228. error (1, errno, "error closing %s", entfilename);
  229. }
  230. }
  231. /*
  232. * Node delete procedure for list-private sticky dir tag/date info
  233. */
  234. static void
  235. freesdt (p)
  236. Node *p;
  237. {
  238. struct stickydirtag *sdtp = p->data;
  239. if (sdtp->tag)
  240. free (sdtp->tag);
  241. if (sdtp->date)
  242. free (sdtp->date);
  243. free ((char *) sdtp);
  244. }
  245. /* Return the next real Entries line. On end of file, returns NULL.
  246. On error, prints an error message and returns NULL. */
  247. static Entnode *
  248. fgetentent(fpin, cmd, sawdir)
  249. FILE *fpin;
  250. char *cmd;
  251. int *sawdir;
  252. {
  253. Entnode *ent;
  254. char *line;
  255. size_t line_chars_allocated;
  256. register char *cp;
  257. enum ent_type type;
  258. char *l, *user, *vn, *ts, *options;
  259. char *tag_or_date, *tag, *date, *ts_conflict;
  260. int line_length;
  261. line = NULL;
  262. line_chars_allocated = 0;
  263. ent = NULL;
  264. while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 0)
  265. {
  266. l = line;
  267. /* If CMD is not NULL, we are reading an Entries.Log file.
  268. Each line in the Entries.Log file starts with a single
  269. character command followed by a space. For backward
  270. compatibility, the absence of a space indicates an add
  271. command. */
  272. if (cmd != NULL)
  273. {
  274. if (l[1] != ' ')
  275. *cmd = 'A';
  276. else
  277. {
  278. *cmd = l[0];
  279. l += 2;
  280. }
  281. }
  282. type = ENT_FILE;
  283. if (l[0] == 'D')
  284. {
  285. type = ENT_SUBDIR;
  286. *sawdir = 1;
  287. ++l;
  288. /* An empty D line is permitted; it is a signal that this
  289. Entries file lists all known subdirectories. */
  290. }
  291. if (l[0] != '/')
  292. continue;
  293. user = l + 1;
  294. if ((cp = strchr (user, '/')) == NULL)
  295. continue;
  296. *cp++ = '\0';
  297. vn = cp;
  298. if ((cp = strchr (vn, '/')) == NULL)
  299. continue;
  300. *cp++ = '\0';
  301. ts = cp;
  302. if ((cp = strchr (ts, '/')) == NULL)
  303. continue;
  304. *cp++ = '\0';
  305. options = cp;
  306. if ((cp = strchr (options, '/')) == NULL)
  307. continue;
  308. *cp++ = '\0';
  309. tag_or_date = cp;
  310. if ((cp = strchr (tag_or_date, '\n')) == NULL)
  311. continue;
  312. *cp = '\0';
  313. tag = (char *) NULL;
  314. date = (char *) NULL;
  315. if (*tag_or_date == 'T')
  316. tag = tag_or_date + 1;
  317. else if (*tag_or_date == 'D')
  318. date = tag_or_date + 1;
  319. if ((ts_conflict = strchr (ts, '+')))
  320. *ts_conflict++ = '\0';
  321. /*
  322. * XXX - Convert timestamp from old format to new format.
  323. *
  324. * If the timestamp doesn't match the file's current
  325. * mtime, we'd have to generate a string that doesn't
  326. * match anyways, so cheat and base it on the existing
  327. * string; it doesn't have to match the same mod time.
  328. *
  329. * For an unmodified file, write the correct timestamp.
  330. */
  331. {
  332. struct stat sb;
  333. if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0)
  334. {
  335. char *c = ctime (&sb.st_mtime);
  336. /* Fix non-standard format. */
  337. if (c[8] == '0') c[8] = ' ';
  338. if (!strncmp (ts + 25, c, 24))
  339. ts = time_stamp (user);
  340. else
  341. {
  342. ts += 24;
  343. ts[0] = '*';
  344. }
  345. }
  346. }
  347. ent = Entnode_Create (type, user, vn, ts, options, tag, date,
  348. ts_conflict);
  349. break;
  350. }
  351. if (line_length < 0 && !feof (fpin))
  352. error (0, errno, "cannot read entries file");
  353. free (line);
  354. return ent;
  355. }
  356. static int
  357. fputentent(fp, p)
  358. FILE *fp;
  359. Entnode *p;
  360. {
  361. switch (p->type)
  362. {
  363. case ENT_FILE:
  364. break;
  365. case ENT_SUBDIR:
  366. if (fprintf (fp, "D") < 0)
  367. return 1;
  368. break;
  369. }
  370. if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
  371. return 1;
  372. if (p->conflict)
  373. {
  374. if (fprintf (fp, "+%s", p->conflict) < 0)
  375. return 1;
  376. }
  377. if (fprintf (fp, "/%s/", p->options) < 0)
  378. return 1;
  379. if (p->tag)
  380. {
  381. if (fprintf (fp, "T%s\n", p->tag) < 0)
  382. return 1;
  383. }
  384. else if (p->date)
  385. {
  386. if (fprintf (fp, "D%s\n", p->date) < 0)
  387. return 1;
  388. }
  389. else
  390. {
  391. if (fprintf (fp, "\n") < 0)
  392. return 1;
  393. }
  394. return 0;
  395. }
  396. /* Read the entries file into a list, hashing on the file name.
  397. UPDATE_DIR is the name of the current directory, for use in error
  398. messages, or NULL if not known (that is, noone has gotten around
  399. to updating the caller to pass in the information). */
  400. List *
  401. Entries_Open (aflag, update_dir)
  402. int aflag;
  403. char *update_dir;
  404. {
  405. List *entries;
  406. struct stickydirtag *sdtp = NULL;
  407. Entnode *ent;
  408. char *dirtag, *dirdate;
  409. int dirnonbranch;
  410. int do_rewrite = 0;
  411. FILE *fpin;
  412. int sawdir;
  413. /* get a fresh list... */
  414. entries = getlist ();
  415. /*
  416. * Parse the CVS/Tag file, to get any default tag/date settings. Use
  417. * list-private storage to tuck them away for Version_TS().
  418. */
  419. ParseTag (&dirtag, &dirdate, &dirnonbranch);
  420. if (aflag || dirtag || dirdate)
  421. {
  422. sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
  423. memset ((char *) sdtp, 0, sizeof (*sdtp));
  424. sdtp->aflag = aflag;
  425. sdtp->tag = xstrdup (dirtag);
  426. sdtp->date = xstrdup (dirdate);
  427. sdtp->nonbranch = dirnonbranch;
  428. /* feed it into the list-private area */
  429. entries->list->data = sdtp;
  430. entries->list->delproc = freesdt;
  431. }
  432. sawdir = 0;
  433. fpin = CVS_FOPEN (CVSADM_ENT, "r");
  434. if (fpin == NULL)
  435. {
  436. if (update_dir != NULL)
  437. error (0, 0, "in directory %s:", update_dir);
  438. error (0, errno, "cannot open %s for reading", CVSADM_ENT);
  439. }
  440. else
  441. {
  442. while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL)
  443. {
  444. (void) AddEntryNode (entries, ent);
  445. }
  446. if (fclose (fpin) < 0)
  447. /* FIXME-update-dir: should include update_dir in message. */
  448. error (0, errno, "cannot close %s", CVSADM_ENT);
  449. }
  450. fpin = CVS_FOPEN (CVSADM_ENTLOG, "r");
  451. if (fpin != NULL)
  452. {
  453. char cmd;
  454. Node *node;
  455. while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL)
  456. {
  457. switch (cmd)
  458. {
  459. case 'A':
  460. (void) AddEntryNode (entries, ent);
  461. break;
  462. case 'R':
  463. node = findnode_fn (entries, ent->user);
  464. if (node != NULL)
  465. delnode (node);
  466. Entnode_Destroy (ent);
  467. break;
  468. default:
  469. /* Ignore unrecognized commands. */
  470. Entnode_Destroy (ent);
  471. break;
  472. }
  473. }
  474. do_rewrite = 1;
  475. if (fclose (fpin) < 0)
  476. /* FIXME-update-dir: should include update_dir in message. */
  477. error (0, errno, "cannot close %s", CVSADM_ENTLOG);
  478. }
  479. /* Update the list private data to indicate whether subdirectory
  480. information is known. Nonexistent list private data is taken
  481. to mean that it is known. */
  482. if (sdtp != NULL)
  483. sdtp->subdirs = sawdir;
  484. else if (! sawdir)
  485. {
  486. sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
  487. memset ((char *) sdtp, 0, sizeof (*sdtp));
  488. sdtp->subdirs = 0;
  489. entries->list->data = sdtp;
  490. entries->list->delproc = freesdt;
  491. }
  492. if (do_rewrite && !noexec)
  493. write_entries (entries);
  494. /* clean up and return */
  495. if (dirtag)
  496. free (dirtag);
  497. if (dirdate)
  498. free (dirdate);
  499. return (entries);
  500. }
  501. void
  502. Entries_Close(list)
  503. List *list;
  504. {
  505. if (list)
  506. {
  507. if (!noexec)
  508. {
  509. if (isfile (CVSADM_ENTLOG))
  510. write_entries (list);
  511. }
  512. dellist(&list);
  513. }
  514. }
  515. /*
  516. * Free up the memory associated with the data section of an ENTRIES type
  517. * node
  518. */
  519. static void
  520. Entries_delproc (node)
  521. Node *node;
  522. {
  523. Entnode *p = node->data;
  524. Entnode_Destroy(p);
  525. }
  526. /*
  527. * Get an Entries file list node, initialize it, and add it to the specified
  528. * list
  529. */
  530. static Node *
  531. AddEntryNode (list, entdata)
  532. List *list;
  533. Entnode *entdata;
  534. {
  535. Node *p;
  536. /* was it already there? */
  537. if ((p = findnode_fn (list, entdata->user)) != NULL)
  538. {
  539. /* take it out */
  540. delnode (p);
  541. }
  542. /* get a node and fill in the regular stuff */
  543. p = getnode ();
  544. p->type = ENTRIES;
  545. p->delproc = Entries_delproc;
  546. /* this one gets a key of the name for hashing */
  547. /* FIXME This results in duplicated data --- the hash package shouldn't
  548. assume that the key is dynamically allocated. The user's free proc
  549. should be responsible for freeing the key. */
  550. p->key = xstrdup (entdata->user);
  551. p->data = entdata;
  552. /* put the node into the list */
  553. addnode (list, p);
  554. return (p);
  555. }
  556. static char *root_template;
  557. static int
  558. get_root_template(const char *repository, const char *path)
  559. {
  560. if (root_template) {
  561. if (strcmp(path, root_template) == 0)
  562. return(0);
  563. free(root_template);
  564. }
  565. if ((root_template = strdup(path)) == NULL)
  566. return(-1);
  567. return(0);
  568. }
  569. /*
  570. * Write out/Clear the CVS/Template file.
  571. */
  572. void
  573. WriteTemplate (dir, update_dir)
  574. const char *dir;
  575. const char *update_dir;
  576. {
  577. char *tmp = NULL;
  578. struct stat st1;
  579. struct stat st2;
  580. if (Parse_Info(CVSROOTADM_RCSINFO, "cvs", get_root_template, 1) < 0)
  581. return;
  582. if (asprintf(&tmp, "%s/%s", dir, CVSADM_TEMPLATE) < 0)
  583. error (1, errno, "out of memory");
  584. if (stat(root_template, &st1) == 0) {
  585. if (stat(tmp, &st2) < 0 || st1.st_mtime != st2.st_mtime) {
  586. FILE *fi;
  587. FILE *fo;
  588. if ((fi = open_file(root_template, "r")) != NULL) {
  589. if ((fo = open_file(tmp, "w")) != NULL) {
  590. int n;
  591. char buf[256];
  592. while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
  593. fwrite(buf, 1, n, fo);
  594. fflush(fo);
  595. if (ferror(fi) || ferror(fo)) {
  596. fclose(fo);
  597. remove(tmp);
  598. error (1, errno, "error copying Template");
  599. } else {
  600. struct timeval times[2];
  601. fclose(fo);
  602. times[0].tv_sec = st1.st_mtime;
  603. times[0].tv_usec = 0;
  604. times[1] = times[0];
  605. utimes(tmp, times);
  606. }
  607. }
  608. fclose(fi);
  609. }
  610. }
  611. }
  612. free(tmp);
  613. }
  614. /*
  615. * Write out/Clear the CVS/Tag file.
  616. */
  617. void
  618. WriteTag (dir, tag, date, nonbranch, update_dir, repository)
  619. const char *dir;
  620. const char *tag;
  621. const char *date;
  622. int nonbranch;
  623. const char *update_dir;
  624. const char *repository;
  625. {
  626. FILE *fout;
  627. char *tmp;
  628. if (noexec)
  629. return;
  630. tmp = xmalloc ((dir ? strlen (dir) : 0)
  631. + sizeof (CVSADM_TAG)
  632. + 10);
  633. if (dir == NULL)
  634. (void) strcpy (tmp, CVSADM_TAG);
  635. else
  636. (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
  637. if (tag || date)
  638. {
  639. fout = open_file (tmp, "w+");
  640. if (tag)
  641. {
  642. if (nonbranch)
  643. {
  644. if (fprintf (fout, "N%s\n", tag) < 0)
  645. error (1, errno, "write to %s failed", tmp);
  646. }
  647. else
  648. {
  649. if (fprintf (fout, "T%s\n", tag) < 0)
  650. error (1, errno, "write to %s failed", tmp);
  651. }
  652. }
  653. else
  654. {
  655. if (fprintf (fout, "D%s\n", date) < 0)
  656. error (1, errno, "write to %s failed", tmp);
  657. }
  658. if (fclose (fout) == EOF)
  659. error (1, errno, "cannot close %s", tmp);
  660. }
  661. else
  662. if (unlink_file (tmp) < 0 && ! existence_error (errno))
  663. error (1, errno, "cannot remove %s", tmp);
  664. free (tmp);
  665. #ifdef SERVER_SUPPORT
  666. if (server_active)
  667. server_set_sticky (update_dir, repository, tag, date, nonbranch);
  668. #endif
  669. }
  670. /* Parse the CVS/Tag file for the current directory.
  671. If it contains a date, sets *DATEP to the date in a newly malloc'd
  672. string, *TAGP to NULL, and *NONBRANCHP to an unspecified value.
  673. If it contains a branch tag, sets *TAGP to the tag in a newly
  674. malloc'd string, *NONBRANCHP to 0, and *DATEP to NULL.
  675. If it contains a nonbranch tag, sets *TAGP to the tag in a newly
  676. malloc'd string, *NONBRANCHP to 1, and *DATEP to NULL.
  677. If it does not exist, or contains something unrecognized by this
  678. version of CVS, set *DATEP and *TAGP to NULL and *NONBRANCHP to
  679. an unspecified value.
  680. If there is an error, print an error message, set *DATEP and *TAGP
  681. to NULL, and return. */
  682. void
  683. ParseTag (tagp, datep, nonbranchp)
  684. char **tagp;
  685. char **datep;
  686. int *nonbranchp;
  687. {
  688. FILE *fp;
  689. if (tagp)
  690. *tagp = (char *) NULL;
  691. if (datep)
  692. *datep = (char *) NULL;
  693. /* Always store a value here, even in the 'D' case where the value
  694. is unspecified. Shuts up tools which check for references to
  695. uninitialized memory. */
  696. if (nonbranchp != NULL)
  697. *nonbranchp = 0;
  698. fp = CVS_FOPEN (CVSADM_TAG, "r");
  699. if (fp)
  700. {
  701. char *line;
  702. int line_length;
  703. size_t line_chars_allocated;
  704. line = NULL;
  705. line_chars_allocated = 0;
  706. if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
  707. {
  708. /* Remove any trailing newline. */
  709. if (line[line_length - 1] == '\n')
  710. line[--line_length] = '\0';
  711. switch (*line)
  712. {
  713. case 'T':
  714. if (tagp != NULL)
  715. *tagp = xstrdup (line + 1);
  716. break;
  717. case 'D':
  718. if (datep != NULL)
  719. *datep = xstrdup (line + 1);
  720. break;
  721. case 'N':
  722. if (tagp != NULL)
  723. *tagp = xstrdup (line + 1);
  724. if (nonbranchp != NULL)
  725. *nonbranchp = 1;
  726. break;
  727. default:
  728. /* Silently ignore it; it may have been
  729. written by a future version of CVS which extends the
  730. syntax. */
  731. break;
  732. }
  733. }
  734. if (line_length < 0)
  735. {
  736. /* FIXME-update-dir: should include update_dir in messages. */
  737. if (feof (fp))
  738. error (0, 0, "cannot read %s: end of file", CVSADM_TAG);
  739. else
  740. error (0, errno, "cannot read %s", CVSADM_TAG);
  741. }
  742. if (fclose (fp) < 0)
  743. /* FIXME-update-dir: should include update_dir in message. */
  744. error (0, errno, "cannot close %s", CVSADM_TAG);
  745. free (line);
  746. }
  747. else if (!existence_error (errno))
  748. /* FIXME-update-dir: should include update_dir in message. */
  749. error (0, errno, "cannot open %s", CVSADM_TAG);
  750. }
  751. /*
  752. * This is called if all subdirectory information is known, but there
  753. * aren't any subdirectories. It records that fact in the list
  754. * private data.
  755. */
  756. void
  757. Subdirs_Known (entries)
  758. List *entries;
  759. {
  760. struct stickydirtag *sdtp = entries->list->data;
  761. /* If there is no list private data, that means that the
  762. subdirectory information is known. */
  763. if (sdtp != NULL && ! sdtp->subdirs)
  764. {
  765. FILE *fp;
  766. sdtp->subdirs = 1;
  767. if (!noexec)
  768. {
  769. /* Create Entries.Log so that Entries_Close will do something. */
  770. entfilename = CVSADM_ENTLOG;
  771. fp = CVS_FOPEN (entfilename, "a");
  772. if (fp == NULL)
  773. {
  774. int save_errno = errno;
  775. /* As in subdir_record, just silently skip the whole thing
  776. if there is no CVSADM directory. */
  777. if (! isdir (CVSADM))
  778. return;
  779. error (1, save_errno, "cannot open %s", entfilename);
  780. }
  781. else
  782. {
  783. if (fclose (fp) == EOF)
  784. error (1, errno, "cannot close %s", entfilename);
  785. }
  786. }
  787. }
  788. }
  789. /* Record subdirectory information. */
  790. static Entnode *
  791. subdir_record (cmd, parent, dir)
  792. int cmd;
  793. const char *parent;
  794. const char *dir;
  795. {
  796. Entnode *entnode;
  797. /* None of the information associated with a directory is
  798. currently meaningful. */
  799. entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "",
  800. (char *) NULL, (char *) NULL,
  801. (char *) NULL);
  802. if (!noexec)
  803. {
  804. if (parent == NULL)
  805. entfilename = CVSADM_ENTLOG;
  806. else
  807. {
  808. entfilename = xmalloc (strlen (parent)
  809. + sizeof CVSADM_ENTLOG
  810. + 10);
  811. sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG);
  812. }
  813. entfile = CVS_FOPEN (entfilename, "a");
  814. if (entfile == NULL)
  815. {
  816. int save_errno = errno;
  817. /* It is not an error if there is no CVS administration
  818. directory. Permitting this case simplifies some
  819. calling code. */
  820. if (parent == NULL)
  821. {
  822. if (! isdir (CVSADM))
  823. return entnode;
  824. }
  825. else
  826. {
  827. sprintf (entfilename, "%s/%s", parent, CVSADM);
  828. if (! isdir (entfilename))
  829. {
  830. free (entfilename);
  831. entfilename = NULL;
  832. return entnode;
  833. }
  834. }
  835. error (1, save_errno, "cannot open %s", entfilename);
  836. }
  837. if (fprintf (entfile, "%c ", cmd) < 0)
  838. error (1, errno, "cannot write %s", entfilename);
  839. if (fputentent (entfile, entnode) != 0)
  840. error (1, errno, "cannot write %s", entfilename);
  841. if (fclose (entfile) == EOF)
  842. error (1, errno, "error closing %s", entfilename);
  843. if (parent != NULL)
  844. {
  845. free (entfilename);
  846. entfilename = NULL;
  847. }
  848. }
  849. return entnode;
  850. }
  851. /*
  852. * Record the addition of a new subdirectory DIR in PARENT. PARENT
  853. * may be NULL, which means the current directory. ENTRIES is the
  854. * current entries list; it may be NULL, which means that it need not
  855. * be updated.
  856. */
  857. void
  858. Subdir_Register (entries, parent, dir)
  859. List *entries;
  860. const char *parent;
  861. const char *dir;
  862. {
  863. Entnode *entnode;
  864. /* Ignore attempts to register ".". These can happen in the
  865. server code. */
  866. if (dir[0] == '.' && dir[1] == '\0')
  867. return;
  868. entnode = subdir_record ('A', parent, dir);
  869. if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
  870. (void) AddEntryNode (entries, entnode);
  871. else
  872. Entnode_Destroy (entnode);
  873. }
  874. /*
  875. * Record the removal of a subdirectory. The arguments are the same
  876. * as for Subdir_Register.
  877. */
  878. void
  879. Subdir_Deregister (entries, parent, dir)
  880. List *entries;
  881. const char *parent;
  882. const char *dir;
  883. {
  884. Entnode *entnode;
  885. entnode = subdir_record ('R', parent, dir);
  886. Entnode_Destroy (entnode);
  887. if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
  888. {
  889. Node *p;
  890. p = findnode_fn (entries, dir);
  891. if (p != NULL)
  892. delnode (p);
  893. }
  894. }
  895. /* OK, the following base_* code tracks the revisions of the files in
  896. CVS/Base. We do this in a file CVS/Baserev. Separate from
  897. CVS/Entries because it needs to go in separate data structures
  898. anyway (the name in Entries must be unique), so this seemed
  899. cleaner. The business of rewriting the whole file in
  900. base_deregister and base_register is the kind of thing we used to
  901. do for Entries and which turned out to be slow, which is why there
  902. is now the Entries.Log machinery. So maybe from that point of
  903. view it is a mistake to do this separately from Entries, I dunno.
  904. We also need something analogous for:
  905. 1. CVS/Template (so we can update the Template file automagically
  906. without the user needing to check out a new working directory).
  907. Updating would probably print a message (that part might be
  908. optional, although probably it should be visible because not all
  909. cvs commands would make the update happen and so it is a
  910. user-visible behavior). Constructing version number for template
  911. is a bit hairy (base it on the timestamp on the server? Or see if
  912. the template is in checkoutlist and if yes use its versioning and
  913. if no don't version it?)....
  914. 2. cvsignore (need to keep a copy in the working directory to do
  915. "cvs release" on the client side; see comment at src/release.c
  916. (release). Would also allow us to stop needing Questionable. */
  917. enum base_walk {
  918. /* Set the revision for FILE to *REV. */
  919. BASE_REGISTER,
  920. /* Get the revision for FILE and put it in a newly malloc'd string
  921. in *REV, or put NULL if not mentioned. */
  922. BASE_GET,
  923. /* Remove FILE. */
  924. BASE_DEREGISTER
  925. };
  926. static void base_walk PROTO ((enum base_walk, struct file_info *, char **));
  927. /* Read through the lines in CVS/Baserev, taking the actions as documented
  928. for CODE. */
  929. static void
  930. base_walk (code, finfo, rev)
  931. enum base_walk code;
  932. struct file_info *finfo;
  933. char **rev;
  934. {
  935. FILE *fp;
  936. char *line;
  937. size_t line_allocated;
  938. FILE *newf;
  939. char *baserev_fullname;
  940. char *baserevtmp_fullname;
  941. line = NULL;
  942. line_allocated = 0;
  943. newf = NULL;
  944. /* First compute the fullnames for the error messages. This
  945. computation probably should be broken out into a separate function,
  946. as recurse.c does it too and places like Entries_Open should be
  947. doing it. */
  948. baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV)
  949. + strlen (finfo->update_dir)
  950. + 2);
  951. baserev_fullname[0] = '\0';
  952. baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP)
  953. + strlen (finfo->update_dir)
  954. + 2);
  955. baserevtmp_fullname[0] = '\0';
  956. if (finfo->update_dir[0] != '\0')
  957. {
  958. strcat (baserev_fullname, finfo->update_dir);
  959. strcat (baserev_fullname, "/");
  960. strcat (baserevtmp_fullname, finfo->update_dir);
  961. strcat (baserevtmp_fullname, "/");
  962. }
  963. strcat (baserev_fullname, CVSADM_BASEREV);
  964. strcat (baserevtmp_fullname, CVSADM_BASEREVTMP);
  965. fp = CVS_FOPEN (CVSADM_BASEREV, "r");
  966. if (fp == NULL)
  967. {
  968. if (!existence_error (errno))
  969. {
  970. error (0, errno, "cannot open %s for reading", baserev_fullname);
  971. goto out;
  972. }
  973. }
  974. switch (code)
  975. {
  976. case BASE_REGISTER:
  977. case BASE_DEREGISTER:
  978. newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w");
  979. if (newf == NULL)
  980. {
  981. error (0, errno, "cannot open %s for writing",
  982. baserevtmp_fullname);
  983. goto out;
  984. }
  985. break;
  986. case BASE_GET:
  987. *rev = NULL;
  988. break;
  989. }
  990. if (fp != NULL)
  991. {
  992. while (getline (&line, &line_allocated, fp) >= 0)
  993. {
  994. char *linefile;
  995. char *p;
  996. char *linerev;
  997. if (line[0] != 'B')
  998. /* Ignore, for future expansion. */
  999. continue;
  1000. linefile = line + 1;
  1001. p = strchr (linefile, '/');
  1002. if (p == NULL)
  1003. /* Syntax error, ignore. */
  1004. continue;
  1005. linerev = p + 1;
  1006. p = strchr (linerev, '/');
  1007. if (p == NULL)
  1008. continue;
  1009. linerev[-1] = '\0';
  1010. if (fncmp (linefile, finfo->file) == 0)
  1011. {
  1012. switch (code)
  1013. {
  1014. case BASE_REGISTER:
  1015. case BASE_DEREGISTER:
  1016. /* Don't copy over the old entry, we don't want it. */
  1017. break;
  1018. case BASE_GET:
  1019. *p = '\0';
  1020. *rev = xstrdup (linerev);
  1021. *p = '/';
  1022. goto got_it;
  1023. }
  1024. }
  1025. else
  1026. {
  1027. linerev[-1] = '/';
  1028. switch (code)
  1029. {
  1030. case BASE_REGISTER:
  1031. case BASE_DEREGISTER:
  1032. if (fprintf (newf, "%s\n", line) < 0)
  1033. error (0, errno, "error writing %s",
  1034. baserevtmp_fullname);
  1035. break;
  1036. case BASE_GET:
  1037. break;
  1038. }
  1039. }
  1040. }
  1041. if (ferror (fp))
  1042. error (0, errno, "cannot read %s", baserev_fullname);
  1043. }
  1044. got_it:
  1045. if (code == BASE_REGISTER)
  1046. {
  1047. if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0)
  1048. error (0, errno, "error writing %s",
  1049. baserevtmp_fullname);
  1050. }
  1051. out:
  1052. if (line != NULL)
  1053. free (line);
  1054. if (fp != NULL)
  1055. {
  1056. if (fclose (fp) < 0)
  1057. error (0, errno, "cannot close %s", baserev_fullname);
  1058. }
  1059. if (newf != NULL)
  1060. {
  1061. if (fclose (newf) < 0)
  1062. error (0, errno, "cannot close %s", baserevtmp_fullname);
  1063. rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV);
  1064. }
  1065. free (baserev_fullname);
  1066. free (baserevtmp_fullname);
  1067. }
  1068. /* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev,
  1069. or NULL if not listed. */
  1070. char *
  1071. base_get (finfo)
  1072. struct file_info *finfo;
  1073. {
  1074. char *rev;
  1075. base_walk (BASE_GET, finfo, &rev);
  1076. return rev;
  1077. }
  1078. /* Set the revision for FILE to REV. */
  1079. void
  1080. base_register (finfo, rev)
  1081. struct file_info *finfo;
  1082. char *rev;
  1083. {
  1084. base_walk (BASE_REGISTER, finfo, &rev);
  1085. }
  1086. /* Remove FILE. */
  1087. void
  1088. base_deregister (finfo)
  1089. struct file_info *finfo;
  1090. {
  1091. base_walk (BASE_DEREGISTER, finfo, NULL);
  1092. }