home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / entries.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  18KB  |  824 lines

  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.  
  17. static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
  18.  
  19. static Entnode *fgetentent PROTO((FILE *, char *, int *));
  20. static int   fputentent PROTO((FILE *, Entnode *));
  21.  
  22. static Entnode *subdir_record PROTO((int, const char *, const char *));
  23.  
  24. static FILE *entfile;
  25. static char *entfilename;        /* for error messages */
  26.  
  27. /*
  28.  * Construct an Entnode
  29.  */
  30. Entnode *
  31. Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict)
  32.     enum ent_type type;
  33.     const char *user;
  34.     const char *vn;
  35.     const char *ts;
  36.     const char *options;
  37.     const char *tag;
  38.     const char *date;
  39.     const char *ts_conflict;
  40. {
  41.     Entnode *ent;
  42.     
  43.     /* Note that timestamp and options must be non-NULL */
  44.     ent = (Entnode *) xmalloc (sizeof (Entnode));
  45.     ent->type      = type;
  46.     ent->user      = xstrdup (user);
  47.     ent->version   = xstrdup (vn);
  48.     ent->timestamp = xstrdup (ts ? ts : "");
  49.     ent->options   = xstrdup (options ? options : "");
  50.     ent->tag       = xstrdup (tag);
  51.     ent->date      = xstrdup (date);
  52.     ent->conflict  = xstrdup (ts_conflict);
  53.  
  54.     return ent;
  55. }
  56.  
  57. /*
  58.  * Destruct an Entnode
  59.  */
  60. void
  61. Entnode_Destroy (ent)
  62.     Entnode *ent;
  63. {
  64.     free (ent->user);
  65.     free (ent->version);
  66.     free (ent->timestamp);
  67.     free (ent->options);
  68.     if (ent->tag)
  69.     free (ent->tag);
  70.     if (ent->date)
  71.     free (ent->date);
  72.     if (ent->conflict)
  73.     free (ent->conflict);
  74.     free (ent);
  75. }
  76.  
  77. /*
  78.  * Write out the line associated with a node of an entries file
  79.  */
  80. static int write_ent_proc PROTO ((Node *, void *));
  81. static int
  82. write_ent_proc (node, closure)
  83.      Node *node;
  84.      void *closure;
  85. {
  86.     Entnode *entnode;
  87.  
  88.     entnode = (Entnode *) node->data;
  89.  
  90.     if (closure != NULL && entnode->type != ENT_FILE)
  91.     *(int *) closure = 1;
  92.  
  93.     if (fputentent(entfile, entnode))
  94.     error (1, errno, "cannot write %s", entfilename);
  95.  
  96.     return (0);
  97. }
  98.  
  99. /*
  100.  * write out the current entries file given a list,  making a backup copy
  101.  * first of course
  102.  */
  103. static void
  104. write_entries (list)
  105.     List *list;
  106. {
  107.     int sawdir;
  108.  
  109.     sawdir = 0;
  110.  
  111.     /* open the new one and walk the list writing entries */
  112.     entfilename = CVSADM_ENTBAK;
  113.     entfile = open_file (entfilename, "w+");
  114.     (void) walklist (list, write_ent_proc, (void *) &sawdir);
  115.     if (! sawdir)
  116.     {
  117.     struct stickydirtag *sdtp;
  118.  
  119.     /* We didn't write out any directories.  Check the list
  120.            private data to see whether subdirectory information is
  121.            known.  If it is, we need to write out an empty D line.  */
  122.     sdtp = (struct stickydirtag *) list->list->data;
  123.     if (sdtp == NULL || sdtp->subdirs)
  124.         if (fprintf (entfile, "D\n") < 0)
  125.         error (1, errno, "cannot write %s", entfilename);
  126.     }
  127.     if (fclose (entfile) == EOF)
  128.     error (1, errno, "error closing %s", entfilename);
  129.  
  130.     /* now, atomically (on systems that support it) rename it */
  131.     rename_file (entfilename, CVSADM_ENT);
  132.  
  133.     /* now, remove the log file */
  134.     unlink_file (CVSADM_ENTLOG);
  135. }
  136.  
  137. /*
  138.  * Removes the argument file from the Entries file if necessary.
  139.  */
  140. void
  141. Scratch_Entry (list, fname)
  142.     List *list;
  143.     char *fname;
  144. {
  145.     Node *node;
  146.  
  147.     if (trace)
  148. #ifdef SERVER_SUPPORT
  149.     (void) fprintf (stderr, "%c-> Scratch_Entry(%s)\n",
  150.             (server_active) ? 'S' : ' ', fname);
  151. #else
  152.     (void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
  153. #endif
  154.  
  155.     /* hashlookup to see if it is there */
  156.     if ((node = findnode_fn (list, fname)) != NULL)
  157.     {
  158.     if (!noexec)
  159.     {
  160.         entfilename = CVSADM_ENTLOG;
  161.         entfile = open_file (entfilename, "a");
  162.  
  163.         if (fprintf (entfile, "R ") < 0)
  164.         error (1, errno, "cannot write %s", entfilename);
  165.  
  166.         write_ent_proc (node, NULL);
  167.  
  168.         if (fclose (entfile) == EOF)
  169.         error (1, errno, "error closing %s", entfilename);
  170.     }
  171.  
  172.     delnode (node);            /* delete the node */
  173.  
  174. #ifdef SERVER_SUPPORT
  175.     if (server_active)
  176.         server_scratch (fname);
  177. #endif
  178.     }
  179. }
  180.  
  181. /*
  182.  * Enters the given file name/version/time-stamp into the Entries file,
  183.  * removing the old entry first, if necessary.
  184.  */
  185. void
  186. Register (list, fname, vn, ts, options, tag, date, ts_conflict)
  187.     List *list;
  188.     char *fname;
  189.     char *vn;
  190.     char *ts;
  191.     char *options;
  192.     char *tag;
  193.     char *date;
  194.     char *ts_conflict;
  195. {
  196.     Entnode *entnode;
  197.     Node *node;
  198.  
  199. #ifdef SERVER_SUPPORT
  200.     if (server_active)
  201.     {
  202.     server_register (fname, vn, ts, options, tag, date, ts_conflict);
  203.     }
  204. #endif
  205.  
  206.     if (trace)
  207.     {
  208. #ifdef SERVER_SUPPORT
  209.     (void) fprintf (stderr, "%c-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
  210.             (server_active) ? 'S' : ' ',
  211.             fname, vn, ts ? ts : "",
  212.             ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
  213.             options, tag ? tag : "", date ? date : "");
  214. #else
  215.     (void) fprintf (stderr, "-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
  216.             fname, vn, ts ? ts : "",
  217.             ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
  218.             options, tag ? tag : "", date ? date : "");
  219. #endif
  220.     }
  221.  
  222.     entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date,
  223.                   ts_conflict);
  224.     node = AddEntryNode (list, entnode);
  225.  
  226.     if (!noexec)
  227.     {
  228.     entfilename = CVSADM_ENTLOG;
  229.     entfile = open_file (entfilename, "a");
  230.  
  231.     if (fprintf (entfile, "A ") < 0)
  232.         error (1, errno, "cannot write %s", entfilename);
  233.  
  234.     write_ent_proc (node, NULL);
  235.  
  236.         if (fclose (entfile) == EOF)
  237.         error (1, errno, "error closing %s", entfilename);
  238.     }
  239. }
  240.  
  241. /*
  242.  * Node delete procedure for list-private sticky dir tag/date info
  243.  */
  244. static void
  245. freesdt (p)
  246.     Node *p;
  247. {
  248.     struct stickydirtag *sdtp;
  249.  
  250.     sdtp = (struct stickydirtag *) p->data;
  251.     if (sdtp->tag)
  252.     free (sdtp->tag);
  253.     if (sdtp->date)
  254.     free (sdtp->date);
  255.     free ((char *) sdtp);
  256. }
  257.  
  258. static Entnode *
  259. fgetentent(fpin, cmd, sawdir)
  260.     FILE *fpin;
  261.     char *cmd;
  262.     int *sawdir;
  263. {
  264.     Entnode *ent;
  265.     char *line;
  266.     size_t line_chars_allocated;
  267.     register char *cp;
  268.     enum ent_type type;
  269.     char *l, *user, *vn, *ts, *options;
  270.     char *tag_or_date, *tag, *date, *ts_conflict;
  271.  
  272.     line = NULL;
  273.     line_chars_allocated = 0;
  274.  
  275.     ent = NULL;
  276.     while (getline (&line, &line_chars_allocated, fpin) > 0)
  277.     {
  278.     l = line;
  279.  
  280.     /* If CMD is not NULL, we are reading an Entries.Log file.
  281.        Each line in the Entries.Log file starts with a single
  282.        character command followed by a space.  For backward
  283.        compatibility, the absence of a space indicates an add
  284.        command.  */
  285.     if (cmd != NULL)
  286.     {
  287.         if (l[1] != ' ')
  288.         *cmd = 'A';
  289.         else
  290.         {
  291.         *cmd = l[0];
  292.         l += 2;
  293.         }
  294.     }
  295.  
  296.     type = ENT_FILE;
  297.  
  298.     if (l[0] == 'D')
  299.     {
  300.         type = ENT_SUBDIR;
  301.         *sawdir = 1;
  302.         ++l;
  303.         /* An empty D line is permitted; it is a signal that this
  304.            Entries file lists all known subdirectories.  */
  305.     }
  306.  
  307.     if (l[0] != '/')
  308.         continue;
  309.  
  310.     user = l + 1;
  311.     if ((cp = strchr (user, '/')) == NULL)
  312.         continue;
  313.     *cp++ = '\0';
  314.     vn = cp;
  315.     if ((cp = strchr (vn, '/')) == NULL)
  316.         continue;
  317.     *cp++ = '\0';
  318.     ts = cp;
  319.     if ((cp = strchr (ts, '/')) == NULL)
  320.         continue;
  321.     *cp++ = '\0';
  322.     options = cp;
  323.     if ((cp = strchr (options, '/')) == NULL)
  324.         continue;
  325.     *cp++ = '\0';
  326.     tag_or_date = cp;
  327.     if ((cp = strchr (tag_or_date, '\n')) == NULL)
  328.         continue;
  329.     *cp = '\0';
  330.     tag = (char *) NULL;
  331.     date = (char *) NULL;
  332.     if (*tag_or_date == 'T')
  333.         tag = tag_or_date + 1;
  334.     else if (*tag_or_date == 'D')
  335.         date = tag_or_date + 1;
  336.  
  337.     if ((ts_conflict = strchr (ts, '+')))
  338.         *ts_conflict++ = '\0';
  339.         
  340.     /*
  341.      * XXX - Convert timestamp from old format to new format.
  342.      *
  343.      * If the timestamp doesn't match the file's current
  344.      * mtime, we'd have to generate a string that doesn't
  345.      * match anyways, so cheat and base it on the existing
  346.      * string; it doesn't have to match the same mod time.
  347.      *
  348.      * For an unmodified file, write the correct timestamp.
  349.      */
  350.     {
  351.         struct stat sb;
  352.         if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0)
  353.         {
  354.         char *c = ctime (&sb.st_mtime);
  355.         
  356.         if (!strncmp (ts + 25, c, 24))
  357.             ts = time_stamp (user);
  358.         else
  359.         {
  360.             ts += 24;
  361.             ts[0] = '*';
  362.         }
  363.         }
  364.     }
  365.  
  366.     ent = Entnode_Create (type, user, vn, ts, options, tag, date,
  367.                   ts_conflict);
  368.     break;
  369.     }
  370.  
  371.     free (line);
  372.     return ent;
  373. }
  374.  
  375. static int
  376. fputentent(fp, p)
  377.     FILE *fp;
  378.     Entnode *p;
  379. {
  380.     switch (p->type)
  381.     {
  382.     case ENT_FILE:
  383.         break;
  384.     case ENT_SUBDIR:
  385.         if (fprintf (fp, "D") < 0)
  386.         return 1;
  387.     break;
  388.     }
  389.  
  390.     if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
  391.     return 1;
  392.     if (p->conflict)
  393.     {
  394.     if (fprintf (fp, "+%s", p->conflict) < 0)
  395.         return 1;
  396.     }
  397.     if (fprintf (fp, "/%s/", p->options) < 0)
  398.     return 1;
  399.  
  400.     if (p->tag)
  401.     {
  402.     if (fprintf (fp, "T%s\n", p->tag) < 0)
  403.         return 1;
  404.     }
  405.     else if (p->date)
  406.     {
  407.     if (fprintf (fp, "D%s\n", p->date) < 0)
  408.         return 1;
  409.     }
  410.     else 
  411.     {
  412.     if (fprintf (fp, "\n") < 0)
  413.         return 1;
  414.     }
  415.  
  416.     return 0;
  417. }
  418.  
  419.  
  420. /*
  421.  * Read the entries file into a list, hashing on the file name.
  422.  */
  423. List *
  424. Entries_Open (aflag)
  425.     int aflag;
  426. {
  427.     List *entries;
  428.     struct stickydirtag *sdtp = NULL;
  429.     Entnode *ent;
  430.     char *dirtag, *dirdate;
  431.     int do_rewrite = 0;
  432.     FILE *fpin;
  433.     int sawdir;
  434.  
  435.     /* get a fresh list... */
  436.     entries = getlist ();
  437.  
  438.     /*
  439.      * Parse the CVS/Tag file, to get any default tag/date settings. Use
  440.      * list-private storage to tuck them away for Version_TS().
  441.      */
  442.     ParseTag (&dirtag, &dirdate);
  443.     if (aflag || dirtag || dirdate)
  444.     {
  445.     sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
  446.     memset ((char *) sdtp, 0, sizeof (*sdtp));
  447.     sdtp->aflag = aflag;
  448.     sdtp->tag = xstrdup (dirtag);
  449.     sdtp->date = xstrdup (dirdate);
  450.  
  451.     /* feed it into the list-private area */
  452.     entries->list->data = (char *) sdtp;
  453.     entries->list->delproc = freesdt;
  454.     }
  455.  
  456.     sawdir = 0;
  457.  
  458.     fpin = CVS_FOPEN (CVSADM_ENT, "r");
  459.     if (fpin == NULL)
  460.     error (0, errno, "cannot open %s for reading", CVSADM_ENT);
  461.     else
  462.     {
  463.     while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) 
  464.     {
  465.         (void) AddEntryNode (entries, ent);
  466.     }
  467.  
  468.     fclose (fpin);
  469.     }
  470.  
  471.     fpin = CVS_FOPEN (CVSADM_ENTLOG, "r");
  472.     if (fpin != NULL) 
  473.     {
  474.     char cmd;
  475.     Node *node;
  476.  
  477.     while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL)
  478.     {
  479.         switch (cmd)
  480.         {
  481.         case 'A':
  482.         (void) AddEntryNode (entries, ent);
  483.         break;
  484.         case 'R':
  485.         node = findnode_fn (entries, ent->user);
  486.         if (node != NULL)
  487.             delnode (node);
  488.         Entnode_Destroy (ent);
  489.         break;
  490.         default:
  491.         /* Ignore unrecognized commands.  */
  492.             break;
  493.         }
  494.     }
  495.     do_rewrite = 1;
  496.     fclose (fpin);
  497.     }
  498.  
  499.     /* Update the list private data to indicate whether subdirectory
  500.        information is known.  Nonexistent list private data is taken
  501.        to mean that it is known.  */
  502.     if (sdtp != NULL)
  503.     sdtp->subdirs = sawdir;
  504.     else if (! sawdir)
  505.     {
  506.     sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
  507.     memset ((char *) sdtp, 0, sizeof (*sdtp));
  508.     sdtp->subdirs = 0;
  509.     entries->list->data = (char *) sdtp;
  510.     entries->list->delproc = freesdt;
  511.     }
  512.  
  513.     if (do_rewrite && !noexec)
  514.     write_entries (entries);
  515.  
  516.     /* clean up and return */
  517.     if (dirtag)
  518.     free (dirtag);
  519.     if (dirdate)
  520.     free (dirdate);
  521.     return (entries);
  522. }
  523.  
  524. void
  525. Entries_Close(list)
  526.     List *list;
  527. {
  528.     if (list)
  529.     {
  530.     if (!noexec) 
  531.         {
  532.             if (isfile (CVSADM_ENTLOG))
  533.         write_entries (list);
  534.     }
  535.     dellist(&list);
  536.     }
  537. }
  538.  
  539.  
  540. /*
  541.  * Free up the memory associated with the data section of an ENTRIES type
  542.  * node
  543.  */
  544. static void
  545. Entries_delproc (node)
  546.     Node *node;
  547. {
  548.     Entnode *p;
  549.  
  550.     p = (Entnode *) node->data;
  551.     Entnode_Destroy(p);
  552. }
  553.  
  554. /*
  555.  * Get an Entries file list node, initialize it, and add it to the specified
  556.  * list
  557.  */
  558. static Node *
  559. AddEntryNode (list, entdata)
  560.     List *list;
  561.     Entnode *entdata;
  562. {
  563.     Node *p;
  564.  
  565.     /* was it already there? */
  566.     if ((p  = findnode_fn (list, entdata->user)) != NULL)
  567.     {
  568.     /* take it out */
  569.     delnode (p);
  570.     }
  571.  
  572.     /* get a node and fill in the regular stuff */
  573.     p = getnode ();
  574.     p->type = ENTRIES;
  575.     p->delproc = Entries_delproc;
  576.  
  577.     /* this one gets a key of the name for hashing */
  578.     /* FIXME This results in duplicated data --- the hash package shouldn't
  579.        assume that the key is dynamically allocated.  The user's free proc
  580.        should be responsible for freeing the key. */
  581.     p->key = xstrdup (entdata->user);
  582.     p->data = (char *) entdata;
  583.  
  584.     /* put the node into the list */
  585.     addnode (list, p);
  586.     return (p);
  587. }
  588.  
  589. /*
  590.  * Write out/Clear the CVS/Tag file.
  591.  */
  592. void
  593. WriteTag (dir, tag, date)
  594.     char *dir;
  595.     char *tag;
  596.     char *date;
  597. {
  598.     FILE *fout;
  599.     char tmp[PATH_MAX];
  600.  
  601.     if (noexec)
  602.     return;
  603.  
  604.     if (dir == NULL)
  605.     (void) strcpy (tmp, CVSADM_TAG);
  606.     else
  607.     (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
  608.  
  609.     if (tag || date)
  610.     {
  611.     fout = open_file (tmp, "w+");
  612.     if (tag)
  613.     {
  614.         if (fprintf (fout, "T%s\n", tag) < 0)
  615.         error (1, errno, "write to %s failed", tmp);
  616.     }
  617.     else
  618.     {
  619.         if (fprintf (fout, "D%s\n", date) < 0)
  620.         error (1, errno, "write to %s failed", tmp);
  621.     }
  622.     if (fclose (fout) == EOF)
  623.         error (1, errno, "cannot close %s", tmp);
  624.     }
  625.     else
  626.     if (unlink_file (tmp) < 0 && ! existence_error (errno))
  627.         error (1, errno, "cannot remove %s", tmp);
  628. }
  629.  
  630. /*
  631.  * Parse the CVS/Tag file for the current directory.
  632.  */
  633. void
  634. ParseTag (tagp, datep)
  635.     char **tagp;
  636.     char **datep;
  637. {
  638.     FILE *fp;
  639.  
  640.     if (tagp)
  641.     *tagp = (char *) NULL;
  642.     if (datep)
  643.     *datep = (char *) NULL;
  644.     fp = CVS_FOPEN (CVSADM_TAG, "r");
  645.     if (fp)
  646.     {
  647.     char *line;
  648.     int line_length;
  649.     size_t line_chars_allocated;
  650.  
  651.     line = NULL;
  652.     line_chars_allocated = 0;
  653.       
  654.     if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
  655.     {
  656.         /* Remove any trailing newline.  */
  657.         if (line[line_length - 1] == '\n')
  658.             line[--line_length] = '\0';
  659.         if (*line == 'T' && tagp)
  660.         *tagp = xstrdup (line + 1);
  661.         else if (*line == 'D' && datep)
  662.         *datep = xstrdup (line + 1);
  663.     }
  664.     (void) fclose (fp);
  665.     free (line);
  666.     }
  667. }
  668.  
  669. /*
  670.  * This is called if all subdirectory information is known, but there
  671.  * aren't any subdirectories.  It records that fact in the list
  672.  * private data.
  673.  */
  674.  
  675. void
  676. Subdirs_Known (entries)
  677.      List *entries;
  678. {
  679.     struct stickydirtag *sdtp;
  680.  
  681.     /* If there is no list private data, that means that the
  682.        subdirectory information is known.  */
  683.     sdtp = (struct stickydirtag *) entries->list->data;
  684.     if (sdtp != NULL && ! sdtp->subdirs)
  685.     {
  686.     FILE *fp;
  687.  
  688.     sdtp->subdirs = 1;
  689.     /* Create Entries.Log so that Entries_Close will do something.  */
  690.     fp = open_file (CVSADM_ENTLOG, "a");
  691.     if (fclose (fp) == EOF)
  692.         error (1, errno, "cannot close %s", CVSADM_ENTLOG);
  693.     }
  694. }
  695.  
  696. /* Record subdirectory information.  */
  697.  
  698. static Entnode *
  699. subdir_record (cmd, parent, dir)
  700.      int cmd;
  701.      const char *parent;
  702.      const char *dir;
  703. {
  704.     Entnode *entnode;
  705.  
  706.     /* None of the information associated with a directory is
  707.        currently meaningful.  */
  708.     entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "",
  709.                   (char *) NULL, (char *) NULL,
  710.                   (char *) NULL);
  711.  
  712.     if (!noexec)
  713.     {
  714.     if (parent == NULL)
  715.         entfilename = CVSADM_ENTLOG;
  716.     else
  717.     {
  718.         entfilename = xmalloc (strlen (parent)
  719.                    + sizeof CVSADM_ENTLOG
  720.                    + 10);
  721.         sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG);
  722.     }
  723.  
  724.     entfile = CVS_FOPEN (entfilename, "a");
  725.     if (entfile == NULL)
  726.     {
  727.         int save_errno = errno;
  728.  
  729.         /* It is not an error if there is no CVS administration
  730.                directory.  Permitting this case simplifies some
  731.                calling code.  */
  732.  
  733.         if (parent == NULL)
  734.         {
  735.         if (! isdir (CVSADM))
  736.             return entnode;
  737.         }
  738.         else
  739.         {
  740.         sprintf (entfilename, "%s/%s", parent, CVSADM);
  741.         if (! isdir (entfilename))
  742.         {
  743.             free (entfilename);
  744.             entfilename = NULL;
  745.             return entnode;
  746.         }
  747.         }
  748.  
  749.         error (1, save_errno, "cannot open %s", entfilename);
  750.     }
  751.  
  752.     if (fprintf (entfile, "%c ", cmd) < 0)
  753.         error (1, errno, "cannot write %s", entfilename);
  754.  
  755.     if (fputentent (entfile, entnode) != 0)
  756.         error (1, errno, "cannot write %s", entfilename);
  757.  
  758.     if (fclose (entfile) == EOF)
  759.         error (1, errno, "error closing %s", entfilename);
  760.  
  761.     if (parent != NULL)
  762.     {
  763.         free (entfilename);
  764.         entfilename = NULL;
  765.     }
  766.     }
  767.  
  768.     return entnode;
  769. }
  770.  
  771. /*
  772.  * Record the addition of a new subdirectory DIR in PARENT.  PARENT
  773.  * may be NULL, which means the current directory.  ENTRIES is the
  774.  * current entries list; it may be NULL, which means that it need not
  775.  * be updated.
  776.  */
  777.  
  778. void
  779. Subdir_Register (entries, parent, dir)
  780.      List *entries;
  781.      const char *parent;
  782.      const char *dir;
  783. {
  784.     Entnode *entnode;
  785.  
  786.     /* Ignore attempts to register ".".  These can happen in the
  787.        server code.  */
  788.     if (dir[0] == '.' && dir[1] == '\0')
  789.     return;
  790.  
  791.     entnode = subdir_record ('A', parent, dir);
  792.  
  793.     if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
  794.     (void) AddEntryNode (entries, entnode);
  795.     else
  796.     Entnode_Destroy (entnode);
  797. }
  798.  
  799. /*
  800.  * Record the removal of a subdirectory.  The arguments are the same
  801.  * as for Subdir_Register.
  802.  */
  803.  
  804. void
  805. Subdir_Deregister (entries, parent, dir)
  806.      List *entries;
  807.      const char *parent;
  808.      const char *dir;
  809. {
  810.     Entnode *entnode;
  811.  
  812.     entnode = subdir_record ('R', parent, dir);
  813.     Entnode_Destroy (entnode);
  814.  
  815.     if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
  816.     {
  817.     Node *p;
  818.  
  819.     p = findnode_fn (entries, dir);
  820.     if (p != NULL)
  821.         delnode (p);
  822.     }
  823. }
  824.