[BACK]Return to entries.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Diff for /src/usr.bin/cvs/entries.c between version 1.56 and 1.57

version 1.56, 2006/04/14 02:45:35 version 1.57, 2006/05/27 03:30:30
Line 1 
Line 1 
 /*      $OpenBSD$       */  /*      $OpenBSD$       */
 /*  /*
  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>   * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
  * All rights reserved.  
  *   *
  * Redistribution and use in source and binary forms, with or without   * Permission to use, copy, modify, and distribute this software for any
  * modification, are permitted provided that the following conditions   * purpose with or without fee is hereby granted, provided that the above
  * are met:   * copyright notice and this permission notice appear in all copies.
  *   *
  * 1. Redistributions of source code must retain the above copyright   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  *    notice, this list of conditions and the following disclaimer.   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * 2. The name of the author may not be used to endorse or promote products   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  *    derived from this software without specific prior written permission.   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  *   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL  
  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  
  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,  
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;  
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR  
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF  
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  */   */
   
 #include "includes.h"  #include "includes.h"
Line 29 
Line 20 
 #include "cvs.h"  #include "cvs.h"
 #include "log.h"  #include "log.h"
   
   
 #define CVS_ENTRIES_NFIELDS     6  #define CVS_ENTRIES_NFIELDS     6
 #define CVS_ENTRIES_DELIM       '/'  #define CVS_ENTRIES_DELIM       '/'
   
   static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *);
   
 /*  
  * cvs_ent_open()  
  *  
  * Open the CVS Entries file for the directory <dir>.  
  * Returns a pointer to the CVSENTRIES file structure on success, or NULL  
  * on failure.  
  */  
 CVSENTRIES *  CVSENTRIES *
 cvs_ent_open(const char *dir, int flags)  cvs_ent_open(const char *dir)
 {  {
         size_t len;  
         int exists, nodir;  
         char bpath[MAXPATHLEN], *p;  
         char cdpath[MAXPATHLEN], ebuf[CVS_ENT_MAXLINELEN], entpath[MAXPATHLEN];  
         char mode[4];  
         FILE *fp;          FILE *fp;
         struct stat st;          size_t len;
         struct cvs_ent *ent;  
         CVSENTRIES *ep;          CVSENTRIES *ep;
           char *p, buf[MAXPATHLEN];
           struct cvs_ent *ent;
           struct cvs_ent_line *line;
   
         exists = 0;          ep = (CVSENTRIES *)xmalloc(sizeof(*ep));
         nodir = 1;          memset(ep, 0, sizeof(*ep));
         memset(mode, 0, sizeof(mode));  
   
         /*          cvs_path_cat(dir, CVS_PATH_ENTRIES, buf, sizeof(buf));
          * Check if the CVS/ dir does exist. If it does,          ep->cef_path = xstrdup(buf);
          * maybe the Entries file was deleted by accident,  
          * display error message. Else we might be doing a fresh  
          * update or checkout of a module.  
          */  
         len = cvs_path_cat(dir, CVS_PATH_CVSDIR, cdpath, sizeof(cdpath));  
         if (len >= sizeof(cdpath))  
                 return (NULL);  
   
         if (stat(cdpath, &st) == 0 && S_ISDIR(st.st_mode))          cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, buf, sizeof(buf));
                 nodir = 0;      /* the CVS/ directory does exist */          ep->cef_bpath = xstrdup(buf);
   
         len = cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, bpath, sizeof(bpath));          cvs_path_cat(dir, CVS_PATH_LOGENTRIES, buf, sizeof(buf));
         if (len >= sizeof(entpath))          ep->cef_lpath = xstrdup(buf);
                 return (NULL);  
   
         len = cvs_path_cat(dir, CVS_PATH_ENTRIES, entpath, sizeof(entpath));  
         if (len >= sizeof(entpath))  
                 return (NULL);  
   
         switch (flags & O_ACCMODE) {  
         case O_WRONLY:  
         case O_RDWR:  
                 /* we have to use append otherwise the file gets truncated */  
                 mode[0] = 'w';  
                 mode[1] = '+';  
                 break;  
         case O_RDONLY:  
                 mode[0] = 'r';  
                 break;  
         }  
   
         /* we can use 'r' if the file already exists */  
         if (stat(entpath, &st) == 0) {  
                 exists = 1;  
                 mode[0] = 'r';  
         }  
   
         fp = fopen(entpath, mode);  
         if (fp == NULL) {  
                 if (nodir == 0)  
                         cvs_log(LP_ERRNO, "cannot open %s for %s", entpath,  
                             mode[1] == '+' ? "writing" : "reading");  
                 return (NULL);  
         }  
   
         ep = xcalloc(1, sizeof(*ep));  
   
         ep->cef_path = xstrdup(entpath);  
         ep->cef_bpath = xstrdup(bpath);  
         ep->cef_cur = NULL;  
         TAILQ_INIT(&(ep->cef_ent));          TAILQ_INIT(&(ep->cef_ent));
   
         while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) {          if ((fp = fopen(ep->cef_path, "r")) != NULL) {
                 len = strlen(ebuf);                  while (fgets(buf, sizeof(buf), fp)) {
                 if (len > 0 && ebuf[len - 1] == '\n')                          len = strlen(buf);
                         ebuf[--len] = '\0';                          if (len > 0 && buf[len - 1] == '\n')
                 if (ebuf[0] == 'D' && ebuf[1] == '\0')                                  buf[len - 1] = '\0';
                         break;  
                 ent = cvs_ent_parse(ebuf);  
                 if (ent == NULL)  
                         continue;  
   
                 TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list);                          if (buf[0] == 'D' && buf[1] == '\0')
         }                                  break;
   
         if (ferror(fp)) {                          line = (struct cvs_ent_line *)xmalloc(sizeof(*line));
                 cvs_log(LP_ERRNO, "read error on %s", entpath);                          line->buf = xstrdup(buf);
                           TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list);
                   }
   
                 (void)fclose(fp);                  (void)fclose(fp);
                 cvs_ent_close(ep);  
                 return (NULL);  
         }          }
   
         /* only keep a pointer to the open file if we're in writing mode */          if ((fp = fopen(ep->cef_lpath, "r")) != NULL) {
         if ((flags & O_WRONLY) || (flags & O_RDWR))                  while (fgets(buf, sizeof(buf), fp)) {
                 ep->cef_flags |= CVS_ENTF_WR;                          len = strlen(buf);
                           if (len > 0 && buf[strlen(buf) - 1] == '\n')
                                   buf[strlen(buf) - 1] = '\0';
   
         (void)fclose(fp);                          p = &buf[1];
   
         /*                          if (buf[0] == 'A') {
          * look for Entries.Log and add merge it together with our                                  line = xmalloc(sizeof(*line));
          * list of things.                                  line->buf = xstrdup(p);
          */                                  TAILQ_INSERT_TAIL(&(ep->cef_ent), line,
         len = cvs_path_cat(dir, CVS_PATH_LOGENTRIES, entpath, sizeof(entpath));                                      entries_list);
         if (len >= sizeof(entpath)) {                          } else if (buf[0] == 'R') {
                 cvs_ent_close(ep);                                  ent = cvs_ent_parse(p);
                 return (NULL);                                  line = ent_get_line(ep, ent->ce_name);
         }                                  if (line != NULL)
                                           TAILQ_REMOVE(&(ep->cef_ent), line,
         fp = fopen(entpath, "r");                                              entries_list);
         if (fp != NULL) {                                  cvs_ent_free(ent);
                 while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) {                          }
                         len = strlen(ebuf);  
                         if (len > 0 && ebuf[len - 1] == '\n')  
                                 ebuf[--len] = '\0';  
   
                         p = &ebuf[2];  
                         ent = cvs_ent_parse(p);  
                         if (ent == NULL)  
                                 continue;  
   
                         if (ebuf[0] == 'A')  
                                 cvs_ent_add(ep, ent);  
                         else if (ebuf[0] == 'R')  
                                 cvs_ent_remove(ep, ent->ce_name, 0);  
                 }                  }
                 (void)fclose(fp);  
   
                 /* always un-synced here, because we                  (void)fclose(fp);
                  * just added or removed entries.  
                  */  
                 ep->cef_flags &= ~CVS_ENTF_SYNC;  
         } else {  
                 if (exists == 1)  
                         ep->cef_flags |= CVS_ENTF_SYNC;  
         }          }
   
         return (ep);          return (ep);
 }  }
   
   struct cvs_ent *
 /*  cvs_ent_parse(const char *entry)
  * cvs_ent_close()  
  *  
  * Close the Entries file <ep> and free all data.  Any reference to entries  
  * structure within that file become invalid.  
  */  
 void  
 cvs_ent_close(CVSENTRIES *ep)  
 {  {
           int i;
         struct cvs_ent *ent;          struct cvs_ent *ent;
           char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;
   
         if (cvs_noexec == 0 && (ep->cef_flags & CVS_ENTF_WR) &&          buf = xstrdup(entry);
             !(ep->cef_flags & CVS_ENTF_SYNC)) {          sp = buf;
                 /* implicit sync with disk */          i = 0;
                 (void)cvs_ent_write(ep);          do {
         }                  dp = strchr(sp, CVS_ENTRIES_DELIM);
                   if (dp != NULL)
                           *(dp++) = '\0';
                   fields[i++] = sp;
                   sp = dp;
           } while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
   
         if (ep->cef_path != NULL)          if (i < CVS_ENTRIES_NFIELDS)
                 xfree(ep->cef_path);                  fatal("missing fields in entry line '%s'", entry);
   
         if (ep->cef_bpath != NULL)          ent = (struct cvs_ent *)xmalloc(sizeof(*ent));
                 xfree(ep->cef_bpath);          ent->ce_buf = buf;
   
         while (!TAILQ_EMPTY(&(ep->cef_ent))) {          if (*fields[0] == '\0')
                 ent = TAILQ_FIRST(&(ep->cef_ent));                  ent->ce_type = CVS_ENT_FILE;
                 TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list);          else if (*fields[0] == 'D')
                 cvs_ent_free(ent);                  ent->ce_type = CVS_ENT_DIR;
         }          else
                   ent->ce_type = CVS_ENT_NONE;
   
         xfree(ep);          ent->ce_status = CVS_ENT_REG;
 }          ent->ce_name = fields[1];
           ent->ce_rev = NULL;
   
           if (ent->ce_type == CVS_ENT_FILE) {
                   if (*fields[2] == '-') {
                           ent->ce_status = CVS_ENT_REMOVED;
                           sp = fields[2] + 1;
                   } else {
                           sp = fields[2];
                           if (fields[2][0] == '0' && fields[2][1] == '\0')
                                   ent->ce_status = CVS_ENT_ADDED;
                   }
   
 /*                  if ((ent->ce_rev = rcsnum_parse(sp)) == NULL)
  * cvs_ent_add()                          fatal("failed to parse entry revision '%s'", entry);
  *  
  * Add the entry <ent> to the Entries file <ef>.  The disk contents are not  
  * modified until a call to cvs_ent_write() is performed.  This is done  
  * implicitly on a call to cvs_ent_close() on an Entries file that has been  
  * opened for writing.  
  * Returns 0 on success, or -1 on failure.  
  */  
 int  
 cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent)  
 {  
         if (!(ef->cef_flags & CVS_ENTF_WR)) {  
                 cvs_log(LP_ERR, "Entries file is opened in read-only mode");  
                 return (-1);  
         }  
   
         if (cvs_ent_get(ef, ent->ce_name) != NULL) {                  if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 ||
                 cvs_log(LP_ERR, "attempt to add duplicate entry for `%s'",                      strncmp(fields[3], "Initial ", 8) == 0 ||
                     ent->ce_name);                      strncmp(fields[3], "Result of merge", 15) == 0)
                 return (-1);                          ent->ce_mtime = CVS_DATE_DMSEC;
                   else
                           ent->ce_mtime = cvs_date_parse(fields[3]);
         }          }
   
         TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);          ent->ce_conflict = fields[3];
           if ((dp = strchr(ent->ce_conflict, '+')) != NULL)
                   *dp = '\0';
           else
                   ent->ce_conflict = NULL;
   
         ef->cef_flags &= ~CVS_ENTF_SYNC;          if (strcmp(fields[4], ""))
                   ent->ce_opts = fields[4];
           else
                   ent->ce_opts = NULL;
   
         return (0);          if (strcmp(fields[5], ""))
                   ent->ce_tag = fields[5];
           else
                   ent->ce_tag = NULL;
   
           return (ent);
 }  }
   
   struct cvs_ent *
 /*  cvs_ent_get(CVSENTRIES *ep, const char *name)
  * cvs_ent_addln()  
  *  
  * Add a line to the Entries file.  
  */  
 int  
 cvs_ent_addln(CVSENTRIES *ef, const char *line)  
 {  {
         struct cvs_ent *ent;          struct cvs_ent *ent;
           struct cvs_ent_line *l;
   
         if (!(ef->cef_flags & CVS_ENTF_WR)) {          l = ent_get_line(ep, name);
                 cvs_log(LP_ERR, "Entries file is opened in read-only mode");          if (l == NULL)
                 return (-1);                  return (NULL);
         }  
   
         ent = cvs_ent_parse(line);          ent = cvs_ent_parse(l->buf);
         if (ent == NULL)          return (ent);
                 return (-1);  }
   
         if (cvs_ent_get(ef, ent->ce_name) != NULL)  int
                 return (-1);  cvs_ent_exists(CVSENTRIES *ep, const char *name)
   {
           struct cvs_ent_line *l;
   
         TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);          l = ent_get_line(ep, name);
         ef->cef_flags &= ~CVS_ENTF_SYNC;          if (l == NULL)
                   return (0);
   
         return (0);          return (1);
 }  }
   
   void
 /*  cvs_ent_close(CVSENTRIES *ep, int writefile)
  * cvs_ent_remove()  
  *  
  * Remove an entry from the Entries file <ef>.  The entry's name is given  
  * by <name>.  
  */  
 int  
 cvs_ent_remove(CVSENTRIES *ef, const char *name, int useprev)  
 {  {
         struct cvs_ent *ent;          FILE *fp;
           struct cvs_ent_line *l;
   
         cvs_log(LP_TRACE, "cvs_ent_remove(%s)", name);          if (writefile) {
                   if ((fp = fopen(ep->cef_bpath, "w")) == NULL)
                           fatal("cvs_ent_close: failed to write %s",
                               ep->cef_path);
           }
   
         ent = cvs_ent_get(ef, name);          while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) {
         if (ent == NULL)                  if (writefile) {
                 return (-1);                          fputs(l->buf, fp);
                           fputc('\n', fp);
         if (ef->cef_cur == ent) {  
                 /* if this element was the last one retrieved through a  
                  * call to cvs_ent_next(), point to the next element to avoid  
                  * keeping an invalid reference.  
                  */  
                 if (useprev) {  
                         ef->cef_cur = TAILQ_PREV(ef->cef_cur,  
                             cvsentrieshead, ce_list);  
                 } else {  
                         ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);  
                 }                  }
   
                   TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
                   xfree(l->buf);
                   xfree(l);
         }          }
         TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list);  
         cvs_ent_free(ent);  
   
         ef->cef_flags &= ~CVS_ENTF_SYNC;          if (writefile) {
                   fputc('D', fp);
                   (void)fclose(fp);
   
         return (0);                  if (rename(ep->cef_bpath, ep->cef_path) == -1)
 }                          fatal("cvs_ent_close: %s: %s", ep->cef_path,
                                strerror(errno));
   
                   (void)unlink(ep->cef_lpath);
           }
   
 /*          xfree(ep->cef_path);
  * cvs_ent_get()          xfree(ep->cef_bpath);
  *          xfree(ep->cef_lpath);
  * Get the CVS entry from the Entries file <ef> whose 'name' portion matches          xfree(ep);
  * <file>.  }
  * Returns a pointer to the cvs entry structure on success, or NULL on failure.  
  */  void
 struct cvs_ent *  cvs_ent_add(CVSENTRIES *ep, const char *line)
 cvs_ent_get(CVSENTRIES *ef, const char *file)  
 {  {
           FILE *fp;
           struct cvs_ent_line *l;
         struct cvs_ent *ent;          struct cvs_ent *ent;
   
         TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list)          if ((ent = cvs_ent_parse(line)) == NULL)
                 if (strcmp(ent->ce_name, file) == 0)                  fatal("cvs_ent_add: parsing failed '%s'", line);
                         return (ent);  
   
         return (NULL);          l = ent_get_line(ep, ent->ce_name);
 }          if (l != NULL)
                   cvs_ent_remove(ep, ent->ce_name);
   
           cvs_ent_free(ent);
   
 /*          cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line);
  * cvs_ent_next()  
  *  
  * This function is used to iterate over the entries in an Entries file.  The  
  * first call will return the first entry of the file and each subsequent call  
  * will return the entry following the last one returned.  
  * Returns a pointer to the cvs entry structure on success, or NULL on failure.  
  */  
 struct cvs_ent *  
 cvs_ent_next(CVSENTRIES *ef)  
 {  
         if (ef->cef_cur == NULL)  
                 ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent));  
         else  
                 ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);  
         return (ef->cef_cur);  
 }  
   
           if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
                   fatal("cvs_ent_add: failed to open '%s'", ep->cef_lpath);
   
 /*          fputc('A', fp);
  * cvs_ent_parse()          fputs(line, fp);
  *          fputc('\n', fp);
  * Parse a single line from a CVS/Entries file and return a cvs_ent structure  
  * containing all the parsed information.  
  */  
 struct cvs_ent*  
 cvs_ent_parse(const char *entry)  
 {  
         int i;  
         char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;  
         struct cvs_ent *ent;  
   
         buf = xstrdup(entry);          (void)fclose(fp);
         sp = buf;  
         i = 0;  
         do {  
                 dp = strchr(sp, CVS_ENTRIES_DELIM);  
                 if (dp != NULL)  
                         *(dp++) = '\0';  
                 fields[i++] = sp;  
                 sp = dp;  
         } while (dp != NULL && i < CVS_ENTRIES_NFIELDS);  
   
         if (i < CVS_ENTRIES_NFIELDS) {          l = (struct cvs_ent_line *)xmalloc(sizeof(*l));
                 cvs_log(LP_ERR, "missing fields in entry line `%s'", entry);          l->buf = xstrdup(line);
                 return (NULL);          TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list);
         }  }
   
         ent = xcalloc(1, sizeof(*ent));  void
         ent->ce_buf = buf;  cvs_ent_remove(CVSENTRIES *ep, const char *name)
   {
           FILE *fp;
           struct cvs_ent_line *l;
   
         if (*fields[0] == '\0')          cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name);
                 ent->ce_type = CVS_ENT_FILE;  
         else if (*fields[0] == 'D')  
                 ent->ce_type = CVS_ENT_DIR;  
         else  
                 ent->ce_type = CVS_ENT_NONE;  
   
         ent->ce_status = CVS_ENT_REG;          l = ent_get_line(ep, name);
         ent->ce_name = fields[1];          if (l == NULL)
         ent->processed = 0;                  return;
   
         if (ent->ce_type == CVS_ENT_FILE) {          if ((fp = fopen(ep->cef_lpath, "a")) == NULL)
                 if (*fields[2] == '-') {                  fatal("cvs_ent_remove: failed to open '%s'",
                         ent->ce_status = CVS_ENT_REMOVED;                      ep->cef_lpath);
                         sp = fields[2] + 1;  
                 } else {  
                         sp = fields[2];  
                         if (fields[2][0] == '0' && fields[2][1] == '\0')  
                                 ent->ce_status = CVS_ENT_ADDED;  
                 }  
   
                 if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {          fputc('R', fp);
                         cvs_ent_free(ent);          fputs(l->buf, fp);
                         return (NULL);          fputc('\n', fp);
                 }  
   
                 if (cvs_cmdop == CVS_OP_SERVER) {          (void)fclose(fp);
                         if (!strcmp(fields[3], "up to date"))  
                                 ent->ce_status = CVS_ENT_UPTODATE;  
                 } else {  
                         if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 ||  
                             strncmp(fields[3], "Initial ", 8) == 0)  
                                 ent->ce_mtime = CVS_DATE_DMSEC;  
                         else  
                                 ent->ce_mtime = cvs_date_parse(fields[3]);  
                 }  
         }  
   
         ent->ce_opts = fields[4];          TAILQ_REMOVE(&(ep->cef_ent), l, entries_list);
         ent->ce_tag = fields[5];          xfree(l->buf);
         return (ent);          xfree(l);
 }  }
   
 /*  
  * cvs_ent_free()  
  *  
  * Free a single CVS entries structure.  
  */  
 void  void
 cvs_ent_free(struct cvs_ent *ent)  cvs_ent_free(struct cvs_ent *ent)
 {  {
         if (ent->ce_rev != NULL)          if (ent->ce_rev != NULL)
                 rcsnum_free(ent->ce_rev);                  rcsnum_free(ent->ce_rev);
         if (ent->ce_buf != NULL)          xfree(ent->ce_buf);
                 xfree(ent->ce_buf);  
         xfree(ent);          xfree(ent);
 }  }
   
 /*  static struct cvs_ent_line *
  * cvs_ent_write()  ent_get_line(CVSENTRIES *ep, const char *name)
  *  
  * Explicitly write the contents of the Entries file <ef> to disk.  
  * Returns 0 on success, or -1 on failure.  
  */  
 int  
 cvs_ent_write(CVSENTRIES *ef)  
 {  {
         size_t len;          char *p, *s;
         char revbuf[64], timebuf[32];          struct cvs_ent_line *l;
         struct cvs_ent *ent;  
         FILE *fp;  
   
         if (ef->cef_flags & CVS_ENTF_SYNC)          TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) {
                 return (0);                  if (l->buf[0] == 'D')
                           p = &(l->buf[2]);
                   else
                           p = &(l->buf[1]);
   
         if ((fp = fopen(ef->cef_bpath, "w")) == NULL) {                  if ((s = strchr(p, '/')) == NULL)
                 cvs_log(LP_ERRNO, "failed to open Entries `%s'", ef->cef_bpath);                          fatal("ent_get_line: bad entry line '%s'", l->buf);
                 return (-1);  
         }  
   
         TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) {                  *s = '\0';
                 if (ent->ce_type == CVS_ENT_DIR) {  
                         putc('D', fp);  
                         timebuf[0] = '\0';  
                         revbuf[0] = '\0';  
                 } else {  
                         rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf));  
                         if (ent->ce_mtime == CVS_DATE_DMSEC &&  
                             ent->ce_status != CVS_ENT_ADDED)  
                                 strlcpy(timebuf, CVS_DATE_DUMMY,  
                                     sizeof(timebuf));  
                         else if (ent->ce_status == CVS_ENT_ADDED) {  
                                 strlcpy(timebuf, "Initial ", sizeof(timebuf));  
                                 strlcat(timebuf, ent->ce_name, sizeof(timebuf));  
                         } else {  
                                 ctime_r(&(ent->ce_mtime), timebuf);  
                                 len = strlen(timebuf);  
                                 if (len > 0 && timebuf[len - 1] == '\n')  
                                         timebuf[--len] = '\0';  
                         }  
                 }  
   
                 if (cvs_cmdop == CVS_OP_SERVER) {                  if (!strcmp(p, name)) {
                         if (ent->ce_status == CVS_ENT_UPTODATE)                          *s = '/';
                                 strlcpy(timebuf, "up to date", sizeof(timebuf));                          return (l);
                         else  
                                 timebuf[0] = '\0';  
                 }                  }
   
                 fprintf(fp, "/%s/%s%s/%s/%s/%s\n", ent->ce_name,                  *s = '/';
                     (ent->ce_status == CVS_ENT_REMOVED) ? "-" : "", revbuf,  
                     timebuf, (ent->ce_opts != NULL) ? ent->ce_opts : "",  
                     (ent->ce_tag != NULL) ? ent->ce_tag : "");  
         }          }
   
         /* terminating line */          return (NULL);
         putc('D', fp);  
         putc('\n', fp);  
   
         ef->cef_flags |= CVS_ENTF_SYNC;  
         fclose(fp);  
   
         /* rename Entries.Backup to Entries */  
         cvs_rename(ef->cef_bpath, ef->cef_path);  
   
         /* remove Entries.Log */  
         cvs_unlink(CVS_PATH_LOGENTRIES);  
   
         return (0);  
 }  }

Legend:
Removed from v.1.56  
changed lines
  Added in v.1.57